您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關django訂單模塊的實現方法,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
Django設計的訂單相關的表如下所示:
由于每一個訂單中的商品種類與數量都不定,因此單獨將訂單商品提出為一個表,為一對多的關系。
訂單的提交
從購物車頁面提交是通過form形式提交的,在checkbox元素中定義參數value并設為對應的商品id,則傳遞到后端的為一個由選中商品id組成的列表,在后端中的業務流程為:
①獲取參數并校驗,表單中的checkbox只有被選中時其value才會被提交,若不選中則不提交,若有多個name相同,則使用POST.getlist獲取到一個由多個value(checkbox的value)組成的列表;
②從redis中獲取該用戶購物車的信息,從mysql中獲取商品id對應的信息,計算數量與總價格,并將其動態添加到查詢到的sku模型類實例中;
③處理運費與實付款;
④組織參數,渲染訂單創建的頁面。
訂單的提交并不復雜,其中重點在于如何將選中的商品id傳入后端,以及傳入訂單創建頁面的參數選擇。
訂單的創建
訂單創建的前端頁面
共分為三部分:
①收貨地址的選擇,此處將默認收貨地址默認選中,可以在該用戶已有的收貨地址中選擇,也可以跳轉到收貨地址的編輯頁面;
②支付方式的選擇,因為此處只做了支付寶的接口因此其他支付方式暫時無法支付,但是可以提交;
③將要提交的商品按條目顯示(在訂單提交后端中傳入),并顯示其數量、單價、小計、單位等信息,顯示總價、運費、總數量信息;
注:①在訂單創建頁面不使用form進行post提交,使用ajax post提交,訂單創建成功時跳轉到訂單頁面,創建失敗時不跳轉,顯示后端傳遞的具體信息;
②由于訂單中的信息是從購物車中查詢到的,這個信息可能與真實情況不符(如商品下架,庫存賣完等),因此在訂單創建時必須要進行多次校驗之后才可以確定訂單是否可以創建成功。
訂單創建的參數傳遞
從購物車提交至訂單創建頁面再到訂單創建后端,其傳遞選中的商品都是通過傳遞sku_id然后通過查詢redis中的用戶購物車信息來進行的(并不直接傳遞具體的商品信息),但由于訂單創建功能不與購物車頁面交互,因此將被選中商品的sku_ids重構為一個字符串,直接從購物車傳遞到訂單創建頁面,并在訂單創建頁面中動態添加一個屬性為sku_ids(此方法常用于在頁面中獲取變量)并使用ajax post請求發回后端,在訂單創建流程中使用。
訂單創建的注意事項
(1)關于訂單提交失敗:由于可能因為各種原因導致的訂單無法完成,但訂單記錄會在數據庫中生成,因此使用mysql事務來對訂單提交(即把在兩個表中的創建打包為一組事務,不允許出現空訂單的情況);
(2)關于并發訂單的處理:
①悲觀鎖:在某一條ORM查詢語句上加鎖(objects.select_for_update()),則多個用戶同時進行訂單提交時,哪個線程先執行到該語句則獲取鎖,待事務結束后才會釋放,其他線程在執行至此時阻塞等待獲取鎖,保證了同一時刻只有一個事務在運行;
②樂觀鎖:查詢時不加鎖,在變更時對比原數據與當前重新查詢的數據,若不一致則失敗(即樂觀的認為當前沒有其他線程在同時進行此過程),一般采用3次循環重復此過程,但由于數據庫事務的隔離性,查詢到的原數據可能不會更新,因此需要修正數據庫事務隔離的級別為read committed(在Django2.0版本中已經自動將所有數據庫的事務隔離級別修改為read-committed,因此無需專門修改mysql數據庫隔離級別),此時樂觀鎖可執行;
③使用方式:在沖突較少時使用樂觀鎖(省去加鎖、釋放鎖的開銷,提高性能),在沖突較多時使用悲觀鎖(省去大量無用的循環),且若樂觀鎖重復操作的代價比較大也選用悲觀鎖。
注:一般將整個事務的過程都放置于try中。
訂單創建的后端流程
①校驗參數;
②添加事務,在事務中進行樂觀/悲觀鎖設置,創建訂單模型類實例,通過傳入的sku_ids在redis購物車中進行查詢,查詢到sku商品實例后操作庫存值并添加訂單商品實例;
③重復②中對sku_ids的操作,對所有選中的商品都進行此操作,其中在操作失效、校驗失敗時需要進行回滾,操作成功后更新訂單模型類實例的內容并提交;
④清空用戶購物車中已提交的記錄,提交事務,并返回應答,當創建成功時跳轉到個人訂單頁面,創建失敗時提示錯誤信息。
class OrderCommitView(View): '''訂單提交創建''' @transaction.atomic def post(self, request): # 判斷用戶是否登錄 user = request.user if not user.is_authenticated: return JsonResponse({'res':0, 'errmsg':'請先登錄'}) # 獲取參數 addr_id = request.POST.get('addr_id') pay_method = int(request.POST.get('pay_method')) sku_ids = request.POST.get('sku_ids') # 校驗參數 if not all([addr_id, pay_method, sku_ids]): return JsonResponse({'res':1, 'errmsg':'數據不完整'}) # 校驗支付方式 if pay_method not in OrderInfo.PAY_METHOD.keys(): print(pay_method, type(pay_method)) return JsonResponse({'res':2, 'errmsg':'非法的支付方式'}) # 校驗地址 try: addr = Address.objects.get(id=addr_id) except Address.DoesNotExist: return JsonResponse({'res':3, 'errmsg':'地址不存在'}) # 創建訂單核心業務 # 創建訂單信息缺少的內容 # 訂單ID,使用年月日時分秒+用戶ID創建訂單編號 order_id = datetime.now().strftime('%Y%m%d%H%M%S')+str(user.id) # 運費 transit_price = 10 # 總數目和總金額,添加記錄,先使用默認值,后續修改 total_count = 0 total_price = 0 # 添加數據庫事務的保存點 save_id = transaction.savepoint() try: # 向訂單信息表中添加記錄 order = OrderInfo.objects.create(order_id=order_id, user=user, addr=addr, pay_method=pay_method, transit_price=transit_price, total_price=total_price, total_count=total_count) if order.pay_method == 1: order.order_status = 2 # 獲取訂單商品表的參數 conn = get_redis_connection('default') cart_key = 'cart_{}'.format(user.id) sku_ids = sku_ids.split(',') for sku_id in sku_ids: for i in range(3): try: sku = GoodsSKU.objects.get(id=sku_id) except GoodsSKU.DoesNotExist: # 回滾到保存點,此處的回滾是為了撤銷已創建的表 transaction.savepoint_rollback(save_id) return JsonResponse({'res':4, 'errmsg':'商品不存在'}) # 獲取商品的數量 count = conn.hget(cart_key, sku_id) # 校驗庫存值 if int(count)>sku.stock: # 回滾到保存點 transaction.savepoint_rollback(save_id) return JsonResponse({'res':5, 'errmsg':'商品庫存不足'}) # 保存原庫存與新庫存 origin_stock = sku.stock new_stock = origin_stock - int(count) new_sales = sku.sales + int(count) print('user:{} times:{} stock:{}'.format(user.id, i, sku.stock)) # 返回受影響的行數,0/1 res = GoodsSKU.objects.filter(id=sku_id, stock=origin_stock).update(stock=new_stock, sales=new_sales) if res == 0: if i == 2: transaction.savepoint_rollback(save_id) return JsonResponse({'res':7, 'errmsg':'訂單創建失敗'}) continue # 向訂單商品表中添加記錄,由于此處并沒有設置保存點,因此將判斷放在添加記錄的前面,防止重復添加 OrderGoods.objects.create(order=order, sku=sku, count=count, price=sku.price) # 更新相關商品的銷量和庫存 sku.stock -= int(count) sku.sales += int(count) sku.save() # 計算訂單商品的總數量和總價格 amount = sku.price*int(count) total_count += int(count) total_price += amount break # 更新訂單詳情表中的總數量和總價格 order.total_count = total_count order.total_price = total_price order.save() # 清除用戶購物車中的記錄 conn.hdel(cart_key, *sku_ids) except Exception: transaction.savepoint_rollback(save_id) return JsonResponse({'res':7, 'errmsg':'訂單創建失敗'}) # 提交事務,返回應答 transaction.savepoint_commit(save_id) return JsonResponse({'res':6, 'message':'訂單創建成功'})
訂單的顯示
訂單顯示在個人中心中,根據用戶從訂單模型類中取出所有的訂單實例,再根據每個訂單實例從訂單商品類中取出對應的商品(此處不從sku商品表中取,因為訂單提交時的價格與當前價格可能不同),計算小計并動態添加屬性,然后進行分頁,對分頁對象進行處理。
注:①操作與商品列表分頁類似,對于訂單的排序此處略過,默認按照創建時間進行排序;
②在前端中,根據用戶訂單的支付狀態和支付方式確定提供給用戶的按鈕文字,并根據支付狀態來判斷點擊時進行的邏輯(去支付/去評論)。
訂單的支付
此處調用支付寶的測試接口進行支付,關于支付寶接口的調用詳情可參支付寶沙箱環境開發文檔.
訂單支付結果的查詢
如上圖所示,支付寶在支付結果產生后會向網站返回支付結果,但可能由于網絡原因并不準確,因此主動去調用支付寶接口查詢支付狀態,在引導用戶去支付頁面之后就發起ajax post請求用于獲取支付結果,循環調用支付寶接口進行查詢(此接口的返回值中包括等待用戶付款),若支付成功則更改訂單支付狀態并返回,若支付失敗則返回錯誤信息。
注:訂單的支付是由用戶與支付寶交互完成的,網站服務器不參與,網站服務器只負責提供支付鏈接與查詢支付結果,通過支付寶的返回狀態來對當前用戶訂單狀態進行更改。
商品評論
①評論頁面顯示:當訂單支付成功,直接跳轉到商品評論頁面,商品評論頁面中單個訂單的多個商品都有自己的評論框,使用form表單的形式進行提交。
注:由于有多個商品,而顯示的時候是循環顯示,因此使用forloop.counter給商品和評論框起名,這樣可以對它們進行綁定,避免混淆。
②評論內容提交:如上所述,在后端中獲取提交的評論內容并將其添加到訂單商品類中,更改訂單狀態,訂單完成,重定向到訂單頁面。
注:由于一個訂單中的商品可能有的評價有的不評價,會造成訂單狀態的不確定,因此可以設置若不評價則給出默認值或評論框不允許為空,即只要點擊進入評論頁面,無論如何處理只要邏輯完成則更改訂單狀態為已完成。
關于django訂單模塊的實現方法就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。