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