目實(shí)戰(zhàn):從核心架構(gòu)到部署優(yōu)化的完整開發(fā)指南)
簡介這是一套基于Python Django框架開發(fā)的完整電商商城項(xiàng)目源碼面向Web后端初學(xué)者與Django進(jìn)階開發(fā)者可用于學(xué)習(xí)典型B2C商城的核心業(yè)務(wù)邏輯與工程實(shí)踐。資源包含403個文件主體為53個Python后端代碼文件含models、views、urls等模塊、11個HTML模板頁、8個CSS樣式文件如proList.css、detail.css、login.css等體現(xiàn)多頁面布局、以及293張商品圖片素材整體壓縮包33.44MB結(jié)構(gòu)清晰覆蓋用戶認(rèn)證、商品展示、購物車、訂單管理等關(guān)鍵功能模塊。已有3684人下載學(xué)習(xí)適合通過真實(shí)項(xiàng)目理解Django MTV架構(gòu)、ORM建模、模板渲染、靜態(tài)資源組織及前后端協(xié)同開發(fā)流程是開展課程設(shè)計、畢業(yè)設(shè)計或技術(shù)面試準(zhǔn)備的實(shí)用參考范例。1. 項(xiàng)目概述一個Django商城項(xiàng)目的核心價值最近在整理硬盤翻出來一個幾年前用Django寫的商城項(xiàng)目源碼打包成了商城項(xiàng)目源碼.zip。這個項(xiàng)目雖然不是什么驚天動地的架構(gòu)但麻雀雖小五臟俱全從用戶注冊登錄到商品下單支付再到后臺管理整個電商的核心鏈路都跑通了。對于想從零開始理解一個Web應(yīng)用是如何構(gòu)建特別是想深入Django這個“大而全”框架的朋友來說這個源碼包的價值可能比看十篇教程都來得實(shí)在。Django在國內(nèi)Web開發(fā)領(lǐng)域的應(yīng)用非常廣泛尤其是在需要快速構(gòu)建穩(wěn)健后臺管理系統(tǒng)的場景下它的ORM、Admin、表單和認(rèn)證系統(tǒng)能幫你省下大量重復(fù)造輪子的時間。這個項(xiàng)目就是一個典型的例子它沒有追求最新最炫的技術(shù)棧而是扎扎實(shí)實(shí)地用Django的核心組件解決了一個商城系統(tǒng)的基本問題。無論你是剛學(xué)完P(guān)ython語法想找個實(shí)戰(zhàn)項(xiàng)目練手還是已經(jīng)有一定基礎(chǔ)想看看一個完整項(xiàng)目的代碼組織邏輯這份源碼都能提供一個清晰的參考。2. 項(xiàng)目整體架構(gòu)與設(shè)計思路拆解2.1 技術(shù)選型與架構(gòu)模式這個項(xiàng)目采用了經(jīng)典的MVC在Django中更準(zhǔn)確地說是MTVModel-Template-View架構(gòu)模式。選擇Django而非其他框架如Flask或FastAPI核心考量在于其“開箱即用”的特性。對于一個商城系統(tǒng)用戶認(rèn)證、后臺管理、表單處理、數(shù)據(jù)庫ORM都是剛需Django內(nèi)置的這些組件成熟穩(wěn)定能讓我們把精力集中在業(yè)務(wù)邏輯而非基礎(chǔ)設(shè)施上。后端框架自然是Django版本鎖定在3.2 LTS這是一個長期支持版本在穩(wěn)定性和社區(qū)支持上找到了很好的平衡點(diǎn)。數(shù)據(jù)庫選擇了MySQL 5.7/8.0考慮到商城業(yè)務(wù)涉及較多的事務(wù)和關(guān)聯(lián)查詢關(guān)系型數(shù)據(jù)庫依然是可靠的選擇。前端沒有采用前后端分離的復(fù)雜架構(gòu)而是使用了Django原生的模板引擎Django Templates配合Bootstrap 5來構(gòu)建頁面。這種選擇對于中小型、以內(nèi)容展示和表單交互為主的商城項(xiàng)目來說開發(fā)效率極高避免了分離部署、接口聯(lián)調(diào)等額外復(fù)雜度。靜態(tài)資源使用Whitenoise中間件在本地和生產(chǎn)環(huán)境提供高效服務(wù)。項(xiàng)目結(jié)構(gòu)清晰遵循了Django的最佳實(shí)踐。根目錄下除了標(biāo)準(zhǔn)的manage.py和配置文件主要應(yīng)用App按功能模塊劃分users: 處理用戶注冊、登錄、個人中心、收貨地址管理。goods: 核心的商品模塊包括商品分類、品牌、SPU標(biāo)準(zhǔn)產(chǎn)品單元、SKU庫存量單位模型以及商品列表、詳情頁的展示邏輯。orders: 訂單模塊涵蓋購物車、訂單創(chuàng)建、狀態(tài)流轉(zhuǎn)、支付回調(diào)集成模擬支付和訂單管理。payments: 支付模塊抽象了支付網(wǎng)關(guān)接口目前實(shí)現(xiàn)了支付寶沙箱和微信支付模擬接口便于理解支付流程。utils: 存放公共工具函數(shù)如自定義的裝飾器、驗(yàn)證碼生成、短信發(fā)送模擬客戶端等。2.2 核心數(shù)據(jù)模型設(shè)計解析數(shù)據(jù)模型是任何應(yīng)用的基石商城系統(tǒng)的模型設(shè)計尤其關(guān)鍵。在這個項(xiàng)目中我重點(diǎn)設(shè)計了幾個核心模型并充分考慮了擴(kuò)展性。首先是用戶模型UserProfile。我沒有直接使用Django內(nèi)置的AbstractUser進(jìn)行簡單擴(kuò)展而是采用了與內(nèi)置User模型一對一關(guān)聯(lián)OneToOneField的方式。這樣做的好處是既可以利用Django強(qiáng)大的原生認(rèn)證系統(tǒng)auth模塊又可以為商城用戶定制大量額外字段如昵稱、頭像、性別、生日、默認(rèn)收貨地址等而不會污染原始的auth_user表結(jié)構(gòu)。這種設(shè)計在需要頻繁升級Django版本或與其他使用標(biāo)準(zhǔn)用戶模型的App集成時靈活性更高。商品模型的設(shè)計是另一個重點(diǎn)采用了電商領(lǐng)域常見的SPUSKU模式。GoodsSPU表定義了一個標(biāo)準(zhǔn)產(chǎn)品的抽象信息如商品名稱、副標(biāo)題、商品描述、所屬分類和品牌。GoodsSKU表則定義了具體的銷售屬性如顏色、尺寸、規(guī)格以及獨(dú)立的價格、庫存、銷量和上下架狀態(tài)。一個SPU對應(yīng)多個SKU。這種設(shè)計將商品的基本信息與銷售屬性解耦既能高效管理商品公共信息又能靈活應(yīng)對多規(guī)格商品的庫存和定價。分類GoodsCategory采用了自關(guān)聯(lián)parent_category字段實(shí)現(xiàn)無限級分類并通過db_indexTrue為頻繁查詢的字段建立索引。訂單模型OrderInfo和訂單商品模型OrderGoods構(gòu)成了訂單系統(tǒng)的核心。OrderInfo記錄了訂單的宏觀信息訂單號唯一、用戶、總金額、支付方式、訂單狀態(tài)使用choices選項(xiàng)定義如“待支付”、“待發(fā)貨”、“待收貨”、“已完成”、“已取消”、收貨地址快照等。OrderGoods則是一個關(guān)聯(lián)表記錄了訂單中每個SKU商品的購買數(shù)量、成交單價下單時快照與商品實(shí)時價格解耦和評價狀態(tài)。這種設(shè)計確保了訂單數(shù)據(jù)的不可變性即使用戶后來修改了收貨地址或商品價格發(fā)生了變化歷史訂單信息依然保持不變。注意在模型字段設(shè)計時對于金額類字段如商品價格、訂單總價務(wù)必使用DecimalField并指定精確的max_digits總位數(shù)和decimal_places小數(shù)位數(shù)例如max_digits10, decimal_places2。絕對不要使用FloatField因?yàn)楦↑c(diǎn)數(shù)在計算和存儲時可能存在精度丟失問題這在金融相關(guān)的業(yè)務(wù)中是致命的。3. 核心功能模塊的詳細(xì)實(shí)現(xiàn)與實(shí)操要點(diǎn)3.1 用戶系統(tǒng)從注冊登錄到個人中心用戶模塊是流量的入口。注冊功能除了常規(guī)的用戶名、密碼、手機(jī)號驗(yàn)證我還集成了圖形驗(yàn)證碼和短信驗(yàn)證碼模擬來防止惡意注冊。驗(yàn)證碼的生成使用Pillow庫繪制并將驗(yàn)證碼文本存入Redis或Session中用于校驗(yàn)。短信服務(wù)則對接了一個模擬接口在實(shí)際部署時可以輕松替換為阿里云、騰訊云等提供的短信服務(wù)SDK。登錄功能直接使用了Django內(nèi)置的authenticate()和login()函數(shù)安全可靠。但有一個關(guān)鍵細(xì)節(jié)在登錄視圖View中我不僅校驗(yàn)用戶名密碼還檢查了用戶是否被激活is_active以及是否被后臺管理員禁用。這為后續(xù)可能需要的用戶管理提供了鉤子。登錄成功后使用Django的login_required裝飾器來保護(hù)需要登錄才能訪問的視圖例如個人中心、收貨地址管理頁面。個人中心頁面展示了用戶的基本信息、訂單概覽和收貨地址列表。收貨地址管理實(shí)現(xiàn)了增刪改查全套操作并允許用戶設(shè)置一個默認(rèn)地址。在創(chuàng)建或編輯地址時前端通過三級聯(lián)動選擇省市區(qū)這里我預(yù)先將全國行政區(qū)劃數(shù)據(jù)導(dǎo)入到了一個單獨(dú)的Area模型中通過Ajax異步加載實(shí)現(xiàn)無刷新聯(lián)動提升了用戶體驗(yàn)。3.2 商品展示系統(tǒng)列表、詳情與搜索商品列表頁是商城的門面。視圖View中我首先處理了查詢參數(shù)分類ID、排序方式按價格、銷量、上新、頁碼。通過Django ORM的select_related和prefetch_related方法我有效地避免了在模板中渲染商品時因外鍵關(guān)聯(lián)而產(chǎn)生的“N1查詢問題”。例如在獲取商品列表時一次性通過select_related(category, spu)將分類和SPU信息關(guān)聯(lián)查詢出來而不是在循環(huán)中逐條查詢。商品詳情頁除了展示SKU的詳細(xì)信息、圖片輪播、規(guī)格參數(shù)外最關(guān)鍵的是庫存判斷。當(dāng)用戶選擇不同規(guī)格如顏色、尺寸時前端通過Ajax請求后端后端根據(jù)SKU ID實(shí)時查詢庫存并返回。庫存數(shù)量是電商系統(tǒng)的生命線所有涉及庫存增減的操作如下單、取消訂單、后臺修改都必須放在數(shù)據(jù)庫事務(wù)中處理并使用F()表達(dá)式進(jìn)行原子操作防止超賣。例如在用戶下單時from django.db import transaction, models with transaction.atomic(): # 1. 創(chuàng)建訂單保存點(diǎn) sid transaction.savepoint() try: # 2. 查詢SKU并判斷庫存使用select_for_update加行鎖防止并發(fā)修改 sku GoodsSKU.objects.select_for_update().get(idsku_id, is_launchedTrue) if sku.stock buy_count: transaction.savepoint_rollback(sid) return JsonResponse({code: 400, errmsg: 庫存不足}) # 3. 使用F表達(dá)式原子更新庫存和銷量 GoodsSKU.objects.filter(idsku_id).update( stockmodels.F(stock) - buy_count, salesmodels.F(sales) buy_count ) # 4. 創(chuàng)建訂單記錄... transaction.savepoint_commit(sid) except Exception as e: transaction.savepoint_rollback(sid) # 記錄日志并返回錯誤搜索功能基于Django的Q對象實(shí)現(xiàn)簡單的多條件過濾。對于更復(fù)雜的全文搜索需求項(xiàng)目預(yù)留了接口可以方便地集成像django-haystackWhoosh或Elasticsearch這樣的專業(yè)搜索引擎。3.3 購物車與訂單流程的完整實(shí)現(xiàn)購物車設(shè)計采用了混合方案用戶未登錄時購物車數(shù)據(jù)存儲在瀏覽器的localStorage中用戶登錄后則同步到服務(wù)器端的數(shù)據(jù)庫Cart表中。這樣做既保證了未登錄用戶的體驗(yàn)又實(shí)現(xiàn)了登錄后數(shù)據(jù)的持久化和多端同步。購物車表Cart很簡單主要字段是用戶、SKU、商品數(shù)量和添加時間。訂單流程是電商最核心、最復(fù)雜的鏈路我將其拆解為幾個清晰的步驟并在視圖函數(shù)中嚴(yán)格校驗(yàn)每一步訂單確認(rèn)頁用戶從購物車選擇商品進(jìn)入。視圖需要校驗(yàn)所選SKU是否有效、庫存是否充足、用戶是否有默認(rèn)收貨地址。這里會計算商品總價、運(yùn)費(fèi)生成一個訂單總金額的預(yù)覽。所有價格信息都是從數(shù)據(jù)庫實(shí)時查詢并計算的確保準(zhǔn)確性。訂單創(chuàng)建用戶提交訂單。這是整個系統(tǒng)里對數(shù)據(jù)一致性和事務(wù)性要求最高的地方。我使用了Django的transaction.atomic()裝飾器來確保以下操作在一個數(shù)據(jù)庫事務(wù)中完成從購物車中取出選中的商品。遍歷每個商品使用select_for_update()鎖定SKU行再次檢查庫存。使用F()表達(dá)式原子性地減少庫存、增加銷量。生成唯一的訂單號采用“時間戳用戶ID隨機(jī)數(shù)”的算法。創(chuàng)建OrderInfo主訂單記錄和多個OrderGoods子訂單記錄。將購物車中對應(yīng)的商品項(xiàng)刪除。 任何一步失敗整個事務(wù)都會回滾庫存和銷量數(shù)據(jù)保持不變保證了數(shù)據(jù)的強(qiáng)一致性。訂單支付訂單創(chuàng)建成功后跳轉(zhuǎn)到支付選擇頁。項(xiàng)目集成了支付寶和微信支付的模擬流程。以支付寶為例視圖會調(diào)用支付寶提供的Python SDK生成一個包含訂單號、金額、商品描述等信息的支付請求參數(shù)前端使用這些參數(shù)跳轉(zhuǎn)到支付寶的支付頁面或喚起手機(jī)支付寶App。支付成功后支付寶會異步通知回調(diào)我們指定的一個后端接口/payments/alipay/notify/。這個回調(diào)接口的處理至關(guān)重要必須驗(yàn)證支付寶傳來的簽名確保通知的真實(shí)性然后根據(jù)通知中的訂單號更新本地訂單狀態(tài)為“已支付”最后返回一個success字符串給支付寶否則支付寶會認(rèn)為通知失敗而反復(fù)重試。訂單狀態(tài)管理用戶可以在個人中心查看訂單列表和詳情。后臺管理員則可以通過Django Admin或自定義的管理后臺操作訂單狀態(tài)發(fā)貨、完成、取消等。每次狀態(tài)變更都應(yīng)記錄日志并可以考慮通過消息隊(duì)列異步發(fā)送通知如短信、郵件給用戶。實(shí)操心得在開發(fā)支付回調(diào)接口時一定要做好日志記錄將支付寶/微信支付回調(diào)過來的所有參數(shù)無論是成功還是失敗都詳細(xì)記錄下來。因?yàn)橹Ц董h(huán)境復(fù)雜網(wǎng)絡(luò)抖動、用戶中途關(guān)閉頁面等都可能導(dǎo)致問題。有了完整的日志當(dāng)用戶投訴“付了款但訂單沒更新”時你才能快速定位是支付平臺的通知沒發(fā)出來還是我們的接口處理失敗了。此外回調(diào)接口的代碼必須做冪等處理即同一條支付成功的通知即使因?yàn)榫W(wǎng)絡(luò)原因被重復(fù)調(diào)用多次最終結(jié)果也應(yīng)該是訂單只被標(biāo)記為支付成功一次避免重復(fù)給用戶加積分或發(fā)貨。4. 后臺管理系統(tǒng)的快速搭建與深度定制Django Admin是這個項(xiàng)目的一大亮點(diǎn)它幾乎零代碼就為我們生成了一個功能強(qiáng)大的商品和訂單管理后臺。但默認(rèn)的Admin往往不能滿足實(shí)際業(yè)務(wù)需求需要進(jìn)行深度定制。4.1 基礎(chǔ)模型注冊與列表優(yōu)化首先在各自App的admin.py文件中使用admin.site.register(YourModel)進(jìn)行注冊。但很快你會發(fā)現(xiàn)默認(rèn)的列表頁只顯示對象的字符串表示信息量很少。這時可以通過定義ModelAdmin類來定制from django.contrib import admin from .models import GoodsSKU admin.register(GoodsSKU) class GoodsSKUAdmin(admin.ModelAdmin): # 列表頁顯示的字段 list_display [id, name, spu, category, price, stock, sales, is_launched] # 支持直接編輯的字段無需進(jìn)入詳情頁 list_editable [price, stock, is_launched] # 右側(cè)過濾器 list_filter [category, is_launched, create_time] # 搜索框支持的字段 search_fields [name, spu__name, id] # 每頁顯示數(shù)量 list_per_page 50這樣管理員就能在一個頁面里快速瀏覽、搜索、篩選和編輯大量商品SKU了。4.2 復(fù)雜表單與內(nèi)聯(lián)編輯對于商品SPUGoodsSPU它下面有多個SKU我們希望在編輯SPU的同時能直接編輯或新增其關(guān)聯(lián)的SKU。這就要用到TabularInline或StackedInline。class GoodsSKUInline(admin.TabularInline): model GoodsSKU extra 1 # 默認(rèn)顯示一個空白的SKU表單 fields [name, price, stock, sales, spec] admin.register(GoodsSPU) class GoodsSPUAdmin(admin.ModelAdmin): inlines [GoodsSKUInline] # ... 其他配置現(xiàn)在在SPU的編輯頁面下方會以一個表格的形式列出所有關(guān)聯(lián)的SKU并可以直接修改或新增管理效率大大提升。4.3 自定義Action與批量操作后臺經(jīng)常需要執(zhí)行批量操作比如將選中的商品批量上架或下架。Django Admin可以很方便地定義自定義Action。def make_launched(modeladmin, request, queryset): queryset.update(is_launchedTrue) make_launched.short_description 批量上架選中的商品 def make_unlaunched(modeladmin, request, queryset): queryset.update(is_launchedFalse) make_unlaunched.short_description 批量下架選中的商品 class GoodsSKUAdmin(admin.ModelAdmin): actions [make_launched, make_unlaunched] # ... 其他配置這樣管理員在商品列表頁勾選多個商品后就可以從Action下拉菜單中執(zhí)行批量操作。4.4 字段重寫與富文本編輯器集成商品描述通常需要富文本編輯。Django Admin默認(rèn)是普通文本框我們可以集成第三方編輯器比如django-ckeditor。安裝配置后只需在ModelAdmin中重寫對應(yīng)字段的form即可from django import forms from ckeditor.widgets import CKEditorWidget class GoodsSPUAdminForm(forms.ModelForm): description forms.CharField(widgetCKEditorWidget()) class Meta: model GoodsSPU fields __all__ class GoodsSPUAdmin(admin.ModelAdmin): form GoodsSPUAdminForm現(xiàn)在SPU的描述字段就變成了一個功能強(qiáng)大的富文本編輯器。5. 項(xiàng)目部署、性能優(yōu)化與安全加固實(shí)錄5.1 從開發(fā)到生產(chǎn)關(guān)鍵配置遷移開發(fā)時我們使用Django自帶的開發(fā)服務(wù)器和SQLite但生產(chǎn)環(huán)境完全不同。首先在settings.py中通過環(huán)境變量區(qū)分不同配置。關(guān)鍵改動包括數(shù)據(jù)庫切換到MySQL或PostgreSQL配置連接池如使用django-db-connections。密鑰與調(diào)試SECRET_KEY必須從環(huán)境變量讀取絕對不能硬編碼在代碼中。DEBUG必須設(shè)置為False。靜態(tài)文件使用python manage.py collectstatic收集所有靜態(tài)文件到指定目錄如/static/然后通過Nginx直接代理或者使用白名單中間件Whitenoise對于中小流量項(xiàng)目足夠。Allowed Hosts必須設(shè)置ALLOWED_HOSTS [你的域名, 你的服務(wù)器IP]否則Django會拒絕服務(wù)。中間件生產(chǎn)環(huán)境需要添加安全相關(guān)的中間件如SecurityMiddleware自動添加安全頭部、SessionMiddleware配置為使用數(shù)據(jù)庫或Redis緩存存儲Session。5.2 性能優(yōu)化實(shí)戰(zhàn)技巧隨著商品和用戶量增長性能瓶頸會逐漸暴露。以下是我在實(shí)踐中總結(jié)的幾個優(yōu)化點(diǎn)數(shù)據(jù)庫查詢優(yōu)化這是最常見的瓶頸。持續(xù)使用django-debug-toolbar監(jiān)控頁面產(chǎn)生的SQL查詢。堅(jiān)決杜絕N1查詢善用select_related用于一對一、多對一正向關(guān)聯(lián)和prefetch_related用于多對多、一對多反向關(guān)聯(lián)。對于復(fù)雜的聚合查詢考慮使用annotate和aggregate在數(shù)據(jù)庫層面完成計算。緩存策略Django提供了強(qiáng)大的緩存框架。我將以下內(nèi)容納入了緩存全站緩存對于變化不頻繁的頁面如商品詳情頁在商品信息更新時手動清除緩存可以使用CacheMiddleware進(jìn)行全頁面緩存。片段緩存在模板中使用{% cache 300 sidebar %}...{% endcache %}緩存頁面中某個部分如商品分類導(dǎo)航欄。數(shù)據(jù)緩存使用from django.core.cache import cacheAPI緩存昂貴的查詢結(jié)果如首頁的熱銷商品排行榜。緩存后端可以配置為Redis或Memcached。靜態(tài)文件與媒體文件務(wù)必使用CDN分發(fā)靜態(tài)文件CSS, JS, 圖片。用戶上傳的媒體文件商品圖、用戶頭像建議存儲到對象存儲服務(wù)如阿里云OSS、騰訊云COS它們帶寬大、成本低、有圖片處理能力縮略圖、水印。5.3 安全加固清單安全無小事尤其是涉及支付和用戶數(shù)據(jù)的商城系統(tǒng)。CSRF保護(hù)Django默認(rèn)已開啟確保所有POST表單都包含{% csrf_token %}。XSS防護(hù)Django模板默認(rèn)自動轉(zhuǎn)義HTML標(biāo)簽。但如果某些字段需要富文本在渲染時必須使用|safe過濾器并確保輸入時已經(jīng)過清洗如使用bleach庫。SQL注入堅(jiān)持使用Django ORM或參數(shù)化查詢基本可以免疫。敏感信息密碼必須使用make_password哈希存儲絕對禁止明文。數(shù)據(jù)庫連接密碼、第三方API密鑰等必須通過環(huán)境變量配置。文件上傳限制上傳文件的類型通過MIME類型和后綴名雙重校驗(yàn)、大小并對圖片進(jìn)行重命名如使用UUID防止用戶上傳惡意文件或通過路徑遍歷攻擊服務(wù)器。權(quán)限控制除了前端的登錄檢查后端每個視圖函數(shù)都要進(jìn)行權(quán)限校驗(yàn)。使用login_required、permission_required裝飾器或自定義裝飾器檢查用戶角色。5.4 常見問題與排查技巧實(shí)錄在開發(fā)和維護(hù)這個項(xiàng)目的過程中我踩過不少坑也總結(jié)了一些排查問題的經(jīng)驗(yàn)。問題一靜態(tài)文件在開發(fā)環(huán)境正常部署后404。排查首先檢查settings.py中的STATIC_URL和STATIC_ROOT配置。STATIC_ROOT是執(zhí)行collectstatic后文件收集的目錄需要確保Nginx或Apache的配置正確指向了這個目錄或者STATICFILES_DIRS配置正確。使用python manage.py findstatic css/style.css命令可以查找Django認(rèn)為的靜態(tài)文件路徑。解決對于使用Nginx的情況在配置文件中添加一個location塊location /static/ { alias /path/to/your/static_root/; }。確保Nginx用戶對該目錄有讀取權(quán)限。問題二使用F()表達(dá)式更新后從數(shù)據(jù)庫重新取出的值好像沒變排查這是一個常見的誤解。F()表達(dá)式更新是在數(shù)據(jù)庫層面進(jìn)行的原子操作但更新后當(dāng)前Python內(nèi)存中的模型實(shí)例對象obj的字段值并不會自動刷新。解決如果需要獲取更新后的值必須從數(shù)據(jù)庫重新加載該對象obj.refresh_from_db()。問題三支付回調(diào)接口被重復(fù)調(diào)用導(dǎo)致訂單狀態(tài)異常更新。排查檢查支付回調(diào)接口的日志看是否收到了多條內(nèi)容相同的POST請求。這通常是支付平臺的網(wǎng)絡(luò)重試機(jī)制導(dǎo)致的。解決實(shí)現(xiàn)接口的冪等性。在回調(diào)處理邏輯的最開始根據(jù)支付平臺傳過來的唯一交易號如支付寶的trade_no去查詢本地是否已存在處理成功的記錄。如果已存在直接返回成功響應(yīng)不再執(zhí)行后續(xù)的訂單更新邏輯??梢栽跀?shù)據(jù)庫中為這個交易號建立唯一索引從數(shù)據(jù)庫層面防止重復(fù)記錄。問題四后臺Admin操作速度很慢特別是有關(guān)聯(lián)數(shù)據(jù)的列表頁。排查使用django-debug-toolbar查看該Admin頁面生成的SQL很可能是因?yàn)樵诹斜眄擄@示外鍵字段如list_display [spu]時每條記錄都去單獨(dú)查詢了一次關(guān)聯(lián)表。解決在自定義的ModelAdmin中重寫get_queryset方法使用select_related提前加載關(guān)聯(lián)數(shù)據(jù)def get_queryset(self, request): return super().get_queryset(request).select_related(spu, category)。這個Django商城項(xiàng)目源碼就像一本涵蓋了需求分析、設(shè)計、編碼、調(diào)試和部署思考的實(shí)戰(zhàn)筆記。它可能沒有用到最前沿的微服務(wù)或云原生架構(gòu)但其中對業(yè)務(wù)邏輯的梳理、對數(shù)據(jù)一致性的處理、對常見問題的規(guī)避方案都是構(gòu)建一個可靠Web應(yīng)用的通用經(jīng)驗(yàn)。代碼是死的但解決問題的思路是活的。希望你在閱讀和運(yùn)行這份代碼時能更關(guān)注“為什么這么設(shè)計”而不僅僅是“怎么實(shí)現(xiàn)”。當(dāng)你理解了背后的權(quán)衡與考量也就具備了獨(dú)立設(shè)計和開發(fā)更復(fù)雜系統(tǒng)的能力。本文還有配套的精品資源點(diǎn)擊獲取