要我说课程设计选“基于Django的数码产品电商平台主数据管理系统”这个题目算是踩在了一个特别合适的交叉点上——既有电商业务那套完整的“用户-商品-订单”逻辑又能把Django框架的MTV模式、ORM、Admin后台、认证体系这些核心能力全用上。更重要的是它带着“主数据管理”这四个字意味着你不仅仅是在写一个CRUD练习而是在设计一套能支撑前台业务、后台维护、数据治理的真实系统骨架。这篇文章我就以过来人的身份把我当时做这个项目的完整思路、数据库设计、核心模块实现、部署踩坑以及最后论文和答辩怎么准备一次性拆给你看。整个过程没有藏着掖着的你照着走一遍哪怕是从零基础开始也能把项目立起来。1. 项目定位与整体设计思路1.1 为什么选Django做主数据管理系统先说个很实际的问题市面上做电商后台的框架不少Spring Boot有、Node.js有、PHP也有为什么课程设计偏偏选Django我的答案很简单——开发效率和安全兜底。Django自带的Admin后台能让你在项目初期甚至不做任何前端页面的情况下就把数据模型的管理界面跑起来。这意味着你可以在一个小时内完成数据建模然后马上就能在Admin里录入数据、验证表结构是否合理。对于课程设计这种时间紧、任务重的场景这个优势是压倒性的。再一个就是ORM和自带认证系统。主数据管理的核心是对数据做增删改查Django的ORM让你不用写一行原生SQL就能完成复杂联表查询而且模型一旦定义好迁移工具会自动帮你建表、改表不会出现手写SQL时字段不一致的尴尬问题。自带的安全认证、密码加密、CSRF防护、XSS过滤这些也直接省去了你自己造轮子的时间。技术栈选型对比我自己在动手前也对比过几种方案这里直接给你一张我当时整理的对比表方案开发效率学习成本适合场景我的评价Django SQLite/MySQL高中课程设计、毕业设计、快速原型最推荐文档多、社区大Spring Boot Vue中低高企业级前后端分离项目周期长课程设计容易失控Flask 自建Admin中低小型接口服务Admin能力太弱主数据维护不便Node.js Express中低中实时交互应用数据建模和ORM不如Django成熟PHP Laravel中中低传统外包项目课程设计用偏冷门答辩不好讲1.2 主数据管理与普通电商系统的区别很多同学容易把“主数据管理系统”和“电商网站”搞混这是答辩时候最容易翻车的地方。我一开始也犯了这个错误被导师一句话点醒你要做的不是给消费者逛街用的商城而是给运营、仓库、采购人员用来维护核心业务数据的内部系统。主数据Master Data指的是企业在业务运作中反复使用、共享的底层数据这里就是商品信息、品牌、分类、供应商、用户档案这些。它的典型特征有三个跨部门共享、相对稳定、被交易数据反复引用。所以你的系统核心职责不是促销、不是购物车、不是支付网关而是保证这些基础数据的准确性、一致性和可追踪性。这么说可能还是有点绕我用一个生活化的类比解释一下。如果把整个电商平台比作一家大型超市那么前台的商城网站是顾客看到货架和收银台而主数据管理系统就是超市后台的中央信息库——所有商品的进价、库位、供应商、品类归属都在这里统一维护。货架上摆什么、价格签写多少、哪种商品下架都取决于这个信息库有没有被正确管理。你的系统要解决的就是“这个信息库怎么管才不出乱子”的问题。所以在这个项目里我把重点放在了商品主数据的全生命周期管理上从供应商引入商品、创建SKU、设置价格与参数到商品的上下架、编辑审核、分类调整再到数据导出和变更留痕。中间会产生订单、购物车等交易数据但它们作为引用方反过来校验主数据的完整性和规范性。1.3 项目功能模块划分与整体架构整个系统我按照Django的MTV模式拆成了四个应用app每个应用内部自治对外通过URL路由和视图函数提供服务。你新建项目的时候不必一上来就追求微服务那种大而全的拆分但至少要做到业务边界清晰否则答辩时老师问你“订单逻辑直接写在商品应用里行不行”你会很难自圆其说。用户应用users负责用户注册、登录、个人信息、密码修改重点是基于Django自带User模型做扩展同时对接订单和收藏功能。商品应用goods这是主数据管理的核心应用包含商品信息、品牌、分类、规格参数、库存等模型也实现了后台的增删改查、导入导出、上下架等操作。订单应用orders负责购物车、订单创建、订单状态流转、收货地址管理。订单不是主数据但它依赖主数据所以这里能看到大量的外键引用。数据分析与看板dashboard基于商品和订单数据提供简单的统计信息比如热销商品、库存预警、分类占比为运营决策提供支持。整体数据流向是这样的运营人员在后台维护商品主数据写到数据库用户在网站浏览商品列表和详情把商品加入购物车生成订单订单状态变更后系统回写库存并在看板页面呈现统计结果。整个链路闭合逻辑清晰答辩时画个流程图老师基本不会再纠结你的设计能力。2. 数据库设计与模型层实现2.1 数据模型整体规划Django的ORM是你最趁手的工具但前提是你要先把表结构设计明白。我做这个项目的时候数据库设计阶段花的时间占到整个项目的三分之一。这块如果偷懒后期改数据模型的成本会呈指数级上升而且会直接把你论文里的E-R图、数据字典拉胯。整个系统的核心模型有8个用户扩展Profile、商品分类Category、品牌Brand、商品Spu、商品规格Sku、订单Order、订单项OrderItem、购物车CartItem。再加上辅助的收货地址Address、供应商Supplier、操作日志OperationLog共11张表。这些表已经覆盖了一个中等复杂度管理系统的全部常用场景。建模的时候有一个很重要的原则不要把Excel思维搬进数据库。有些同学喜欢在商品表里直接写“品牌名称”这种文本字段那不是关系型数据库该干的事而是应该建品牌表用外键关联。这样做的好处是数据冗余小、一致性高品牌改名只要改一处而且后续按品牌筛选、统计也会非常方便。核心模型字段设计细节拿商品Spu和Sku来举例这组模型是整个系统的灵魂。from django.db import models from django.contrib.auth.models import User class Category(models.Model): name models.CharField(分类名称, max_length50, uniqueTrue) parent models.ForeignKey(self, verbose_name父级分类, nullTrue, blankTrue, on_deletemodels.CASCADE) sort_order models.IntegerField(排序, default0) created_at models.DateTimeField(创建时间, auto_now_addTrue) class Meta: verbose_name 商品分类 verbose_name_plural verbose_name ordering [sort_order, id] def __str__(self): return self.name class Brand(models.Model): name models.CharField(品牌名称, max_length50, uniqueTrue) logo models.ImageField(品牌Logo, upload_tobrands/%Y/%m/, blankTrue) description models.TextField(品牌介绍, blankTrue) created_at models.DateTimeField(创建时间, auto_now_addTrue) class Meta: verbose_name 品牌 verbose_name_plural verbose_name def __str__(self): return self.name class Supplier(models.Model): name models.CharField(供应商名称, max_length100) contact models.CharField(联系人, max_length20) phone models.CharField(联系电话, max_length20) address models.CharField(联系地址, max_length200, blankTrue) is_active models.BooleanField(是否合作中, defaultTrue) class Meta: verbose_name 供应商 verbose_name_plural verbose_name def __str__(self): return self.name class Product(models.Model): 商品SPU代表一个商品的公共属性 STATUS_CHOICES ( (draft, 草稿), (on, 上架), (off, 下架), ) name models.CharField(商品名称, max_length200) subtitle models.CharField(副标题, max_length300, blankTrue) category models.ForeignKey(Category, verbose_name分类, on_deletemodels.PROTECT) brand models.ForeignKey(Brand, verbose_name品牌, on_deletemodels.PROTECT) supplier models.ForeignKey(Supplier, verbose_name供应商, nullTrue, blankTrue, on_deletemodels.SET_NULL) main_image models.ImageField(主图, upload_toproducts/%Y/%m/, blankTrue) detail models.TextField(商品详情, blankTrue) status models.CharField(状态, max_length10, choicesSTATUS_CHOICES, defaultdraft) created_at models.DateTimeField(创建时间, auto_now_addTrue) updated_at models.DateTimeField(更新时间, auto_nowTrue) class Meta: verbose_name 商品SPU verbose_name_plural verbose_name ordering [-created_at] indexes [ models.Index(fields[status, category]), ] def __str__(self): return self.name class Sku(models.Model): 商品SKU一个商品的SKU属性如颜色、版本 product models.ForeignKey(Product, verbose_name所属商品, on_deletemodels.CASCADE, related_nameskus) sku_code models.CharField(SKU编码, max_length50, uniqueTrue) specs models.JSONField(规格参数, defaultdict) # 如 {颜色: 黑色, 版本: 8GB256GB} price models.DecimalField(售价, max_digits10, decimal_places2) original_price models.DecimalField(原价, max_digits10, decimal_places2, default0) stock models.IntegerField(库存, default0) sales models.IntegerField(销量, default0) is_active models.BooleanField(是否启用, defaultTrue) class Meta: verbose_name 商品SKU verbose_name_plural verbose_name def __str__(self): return f{self.product.name} - {self.sku_code}这组模型你必须注意三个细节。第一Category用到了自关联外键老爹字段指向自己用来做无限级分类比如“手机数码 手机 智能手机”而且parent允许为空顶级分类的parent就是None。第二Product的category和brand用了PROTECT级联这意味着有商品引用某个分类时你不能直接删掉那个分类这正好符合主数据管理的“引用完整性”要求。第三Sku表里的specs字段用的是Django 3.1之后支持的JSONField直接存一个字典把不同规格的属性塞进去这个设计很实用——你不需要为每个SKU单独建一堆可选的规格列。OrderItem模型的位置订单这块有个容易想当然的坑为什么不直接用外键引用Sku表还要单独把商品名称、价格快照存一份答案是为了保留历史快照。如果用户下单之后你把Sku的价格改了或者把商品下架删掉了订单里如果没有快照就会出现“订单详情查不到商品”的尴尬。所以OrderItem里除了外键还冗余了商品名、单价、图片这些字段。这个设计体现了主数据系统里很重要的一个概念——交易数据引用主数据但不受主数据变更影响。class Order(models.Model): STATUS_CHOICES ( (unpaid, 待付款), (paid, 已付款), (shipped, 已发货), (completed, 已完成), (canceled, 已取消), ) user models.ForeignKey(User, verbose_name用户, on_deletemodels.CASCADE, related_nameorders) order_no models.CharField(订单号, max_length32, uniqueTrue) total_amount models.DecimalField(总金额, max_digits10, decimal_places2) receiver_name models.CharField(收货人, max_length20) receiver_phone models.CharField(收货电话, max_length20) receiver_address models.CharField(收货地址, max_length200) status models.CharField(订单状态, max_length10, choicesSTATUS_CHOICES, defaultunpaid) created_at models.DateTimeField(下单时间, auto_now_addTrue) class Meta: verbose_name 订单 verbose_name_plural verbose_name ordering [-created_at] def __str__(self): return self.order_no class OrderItem(models.Model): order models.ForeignKey(Order, verbose_name所属订单, on_deletemodels.CASCADE, related_nameitems) sku models.ForeignKey(Sku, verbose_nameSKU, on_deletemodels.SET_NULL, nullTrue) product_name models.CharField(商品名称, max_length200) sku_specs models.CharField(规格描述, max_length200, blankTrue) price models.DecimalField(成交单价, max_digits10, decimal_places2) quantity models.IntegerField(购买数量, default1) subtotal models.DecimalField(小计, max_digits10, decimal_places2) class Meta: verbose_name 订单明细 verbose_name_plural verbose_name def save(self, *args, **kwargs): self.subtotal self.price * self.quantity super().save(*args, **kwargs)注意到OrderItem里的save方法了吗这是一种很常用的模型层逻辑处理方式在保存时自动计算小计金额避免视图里到处写重复的计算代码。类似的处理还可以放在商品下订单时自动扣减库存。2.2 用Django迁移管理表结构模型定义好之后Django的数据库迁移是跟着模型走的不需要你手动建库建表。# 生成迁移文件 python manage.py makemigrations users goods orders # 查看迁移的SQL python manage.py sqlmigrate goods 0001 # 执行迁移 python manage.py migrate我看不少初学者喜欢直接去数据库客户端里手动建表然后让Django连上使用——千万别这么干。一方面手写SQL容易和ORM的预期不一致另一方面Django的迁移记录django_migrations表会丢失后面你加字段做迁移就会报一堆“表已经存在”的错误。最稳妥的做法是Django建表你只负责设计模型和审核生成SQL。另外SQLite和MySQL我都试过。如果课程设计没有特殊要求直接用项目默认的SQLite就行零配置文件即数据库交作业也方便。但如果你计划部署到云服务器或者数据量会到几十万条建议用MySQL。切换也很容易改一下settings里的DATABASES配置和驱动库就行。2.3 用Admin后台快速搭建管理界面Django自带的Admin后台是主数据管理系统的天然利器。你不需要写HTML页面只需要在admin.py里注册模型一套完备的管理界面就出来了。这也是这个项目“主数据管理系统”定位的底气所在。from django.contrib import admin from .models import Category, Brand, Supplier, Product, Sku class SkuInline(admin.TabularInline): model Sku extra 1 admin.register(Product) class ProductAdmin(admin.ModelAdmin): list_display (name, category, brand, status, updated_at) list_filter (status, category, brand) search_fields (name, subtitle) list_editable (status,) autocomplete_fields (category, brand) inlines [SkuInline] list_per_page 20 admin.register(Category) class CategoryAdmin(admin.ModelAdmin): list_display (name, sort_order, created_at) search_fields (name,) prepopulated_fields {slug: (name,)} # 如果有slug字段 admin.register(Brand) class BrandAdmin(admin.ModelAdmin): list_display (name, description) search_fields (name,)这里有几个提升体验的细节你值得抄作业。list_editable允许你在列表页直接修改状态对于运营人员批量上下架商品很实用。SkuInline让商品编辑页下方直接关联操作SKU你不需要跳转到另一个页面。autocomplete_fields则解决了一个真实痛点——如果分类和品牌有上千条下拉框会卡成PPT改用自动补全加载就顺畅多了。还有一个我踩过的坑默认Admin页面没有对图片预览。建议自定义一个方法显示缩略图一行代码就能搞定from django.utils.html import format_html admin.register(Product) class ProductAdmin(admin.ModelAdmin): ... def image_tag(self, obj): if obj.main_image: return format_html(img src{} stylewidth:50px;height:50px;object-fit:cover;/, obj.main_image.url) return image_tag.short_description 主图 list_display (name, category, brand, status, image_tag, updated_at)3. 核心功能模块与代码实现3.1 用户认证与权限控制Django自带一套完整的用户认证体系包括登录、登出、会话管理、密码加密。我在这里主要做了两件事扩展用户模型、设置登录校验逻辑。扩展用户模型有两条路线继承AbstractUser写自定义用户表或者建立Profile表用OneToOne关联到内置User表。课程设计场景我推荐后者因为改动小而且你和Django Admin用户的关联不受影响。from django.db import models from django.contrib.auth.models import User class Profile(models.Model): user models.OneToOneField(User, on_deletemodels.CASCADE, related_nameprofile) nickname models.CharField(昵称, max_length20, blankTrue) phone models.CharField(手机号, max_length11, uniqueTrue, nullTrue, blankTrue) avatar models.ImageField(头像, upload_toavatars/%Y/%m/, blankTrue) gender models.CharField(性别, max_length4, choices((M,男),(F,女)), defaultM) birth_date models.DateField(生日, nullTrue, blankTrue) def __str__(self): return self.user.username注册逻辑其实没什么神秘的就是创建一个User实例再关联创建Profile。但是这里有个细节你必须注意注册时要捕获用户名重复的异常。def register(request): if request.method POST: username request.POST.get(username) password1 request.POST.get(password1) password2 request.POST.get(password2) if not username or not password1 or not password2: messages.error(request, 请完整填写注册信息) return redirect(register) if password1 ! password2: messages.error(request, 两次密码不一致) return redirect(register) if User.objects.filter(usernameusername).exists(): messages.error(request, 用户名已被占用) return redirect(register) # create_user会自动对密码做哈希处理千万别用create user User.objects.create_user(usernameusername, passwordpassword1) profile Profile.objects.create(useruser, nicknameusername) # 注册后自动登录 login(request, user) return redirect(index) return render(request, users/register.html)权限控制我分了两层前台用户的登录状态校验用Django的login_required装饰器就能搞定后台的运营管理权限依赖Django自带的is_staff字段。但你可以在中间件或Admin里做更细的划分比如只有某个Group的成员才能访问商品管理页。如果你项目里需要做“运营只能管商品、不能管用户”这种角色分割可以给AdminModelAdmin重写get_queryset或has_module_permission也可以配一套简单的RBAC。答辩时能说出“我用的是Django自带的Permission机制做的权限粒度控制”这就是妥妥的加分项。3.2 商品主数据的增删改查这个模块是整个项目的脸面也是主数据管理的核心能力。前端页面我用的是函数视图加模板渲染结构清晰便于你讲解。后台有Admin兜底前台则提供一套更符合“电商系统风格”的操作页面。商品列表页要支持按分类、品牌、状态筛选还要支持按名称搜索。我在这里用了一个链表查询的技巧def product_list(request): products Product.objects.select_related(category, brand).all() # 搜索 keyword request.GET.get(keyword, ) if keyword: products products.filter(name__icontainskeyword) # 筛选 category_id request.GET.get(category) if category_id: products products.filter(category_idcategory_id) brand_id request.GET.get(brand) if brand_id: products products.filter(brand_idbrand_id) status request.GET.get(status) if status: products products.filter(statusstatus) return render(request, goods/product_list.html, { products: products, categories: Category.objects.all(), brands: Brand.objects.all(), keyword: keyword, })select_related是Django一个非常重要的性能优化手段它会在查询时用JOIN一次性把外键关联的对象加载到内存避免逐个访问外键时产生N1查询。你可以试试在列表页不写select_related给每件商品取分类名称然后打开Debug工具栏看看SQL执行次数数据多的时候差距非常明显。新增和编辑商品我用了ModelForm来绑定表单和校验。ModelForm的优势是自动根据模型生成表单字段自动做基础类型校验还能在clean方法里加自定义业务校验。class ProductForm(forms.ModelForm): class Meta: model Product fields [name, subtitle, category, brand, supplier, main_image, detail, status] widgets { detail: forms.Textarea(attrs{rows: 10}), subtitle: forms.TextInput(attrs{class: form-control}), } def clean_name(self): name self.cleaned_data.get(name) if len(name) 3: raise forms.ValidationError(商品名称至少需要3个字符) return name删除操作我并没有做成物理删除而是倾向于软删除——标记商品状态为下架保留历史数据。这么设计一是避免订单明细的外键失效二是符合主数据管理的审计需要。如果你打算做真正的物理删除记得考虑外键级联的方式像Sku是CASCADEProduct对Category是PROTECT删除顺序不同结果差异很大。实际操作里我给商品表加了一个is_deleted布尔字段列表页默认过滤掉已删除数据后台可恢复。3.3 数据的导入导出与批量维护主数据管理有个高频需求就是批量导入数据尤其是从Excel往数据库里初始化商品的时候。我在这里集成了django-import-export这算是Django生态里做数据导入导出的标准库了。使用这个库需要两步定义Resource然后在Admin里注册。from import_export import resources, fields from import_export.admin import ImportExportModelAdmin from .models import Product, Sku class ProductResource(resources.ModelResource): category fields.Field(column_name分类, attributecategory, widgetForeignKeyWidget(Category, fieldname)) brand fields.Field(column_name品牌, attributebrand, widgetForeignKeyWidget(Brand, fieldname)) class Meta: model Product fields (id, name, subtitle, category, brand, status) admin.register(Product) class ProductAdmin(ImportExportModelAdmin): resource_class ProductResource这段代码看起来很简短但作用很大导入Excel时你可以在表头里直接写分类名称“手机数码”而不是那个分类的IDImportExport会自动根据分类名字查到对应的ID并写进外键字段。批量初始化商品数据时这个功能的体验远胜于一行一行用表单录入。也因为这个用途我把列出的字段都做成可导入方便从供应商给的电子表格直接初始化系统。导出功能也是类似的道理管理员可以一键把所有商品导成Excel带去做数据备份。论文里如果写“系统支持商品主数据的批量导入导出提升运营效率”这句话是有实际功能支撑的不是空话。3.4 订单流程与库存联动订单这块我采用了经典的“购物车 → 确认订单 → 创建订单 → 扣库存”的流程。在设计这一环时有一个重要的逻辑你要想清楚什么时候扣库存我的方案是创建订单并且付款成功后统一扣减避免了“加购不付款导致库存全锁死”的问题。库存扣减的关键是必须用ORM的原子更新操作。如果你用的是“先取出当前库存再减去1再存回去”在并发场景下会出现库存超卖。正确的姿势是用F表达式from django.db.models import F Sku.objects.filter(idsku_id, stock__gtequantity).update(stockF(stock) - quantity)这行代码是原子性的数据库层面执行不会因为两个请求同时读到同一份库存而造成超卖。同样地销量累加也可以用F表达式Sku.objects.filter(idsku_id).update(salesF(sales) quantity)创建订单时还得把订单号生成好。订单号我用的是时间戳加随机数格式类似20240524153012897号这个格式的好处是全局唯一而且带有时间信息方便后续按订单号做分库分表。另外我用了Django的数据库事务来保证“创建订单 扣库存 生成订单明细”这三步要么全部成功、要么全部回滚。这个可以写在视图函数上用transaction.atomic装饰器。from django.db import transaction login_required transaction.atomic def create_order(request): if request.method POST: sku_id request.POST.get(sku_id) quantity int(request.POST.get(quantity, 1)) sku Sku.objects.select_for_update().get(idsku_id) if sku.stock quantity: return JsonResponse({code: 1, msg: 库存不足}) # 计算金额 amount sku.price * quantity # 创建订单和订单项 order Order.objects.create(...) OrderItem.objects.create(...) # 扣库存 sku.stock - quantity sku.save() return JsonResponse({code: 0, order_id: order.id})注意select_for_update这个操作它会在数据库层面给对应行加锁直到事务结束才释放整个过程串行化。这对课程设计来说可能显得有点“过度设计”但答辩时你能主动讲出“我的系统在创建订单时使用了行级锁和事务防止并发超卖”这个技术深度绝对会让老师眼前一亮。4. 前台页面渲染与用户操作链路4.1 页面设计思路和模板组织Django的模板系统自带了一套继承和包含机制我用三个基础模板文件来管理全站复用部分base.html全站公共头尾、base_list.html列表页通用模板、base_form.html表单页通用模板。这是很多课程设计容易忽略的一环——页面文件直接复制粘贴几十个很丑很难维护。使用模板继承之后新增一个页面只写核心内容块就行简历上写“前端使用模板继承技术大幅提升页面复用率”也是加分项。首页展示的商品列表我用了Bootstrap的卡片栅格加上简单的筛选和分页。这里有一个体验优化的细节商品图片如果没上传就不要渲染img标签否则页面会有一堆碎图。用Django模板的if判断就行{% if product.main_image %} img src{{ product.main_image.url }} alt{{ product.name }} {% else %} div classno-image暂无图片/div {% endif %}分类菜单我用了自定义模板标签templatetags从数据库里拉取顶级分类导致你只要在后台增加新的顶级分类前台菜单自动就更新了不需要改页面代码。类似的功能Django都有官方推荐做法照着文档写就好。4.2 用户登录注册与订单操作用户点击“加入购物车”按钮后会走POST请求到购物车视图把SKU和数量存到CartItem表。我的购物车数据是直接存数据库的而不是session这样用户在另一台设备登录也能看到自己的购物车。具体实现上CartItem有一个user外键指向当前用户每次加购时判断是否存在同SKU的记录有就直接加数量没有就新建。订单列表和订单详情页需要注意状态判断。按钮要随订单状态不同而不同待付款显示“去支付”已付款显示“确认收货”已经完成的显示“再次购买”。这种业务状态分支用Django模板的if/elif/else就能写代码里要保证状态枚举和模板里写死的中文文案对齐别出现数据库状态和页面文案不一致的情况。5. 部署上线与常见问题排查5.1 从开发环境到生产环境的配置切换本地跑Django的时候settings.py里DEBUGTrue静态文件由Django开发服务器直接托管。真到了云服务器部署或者即便只是一个给老师演示的线上demo你也得把DEBUG降下来否则会出现两个严重问题安全检查不过用户能看到详细信息报错静态文件全部404。这就意味着你要用collectstatic收集全部静态文件然后交给Nginx托管。我的部署经验是腾讯云轻量服务器 Ubuntu Nginx Gunicorn MySQL。你不需要买很贵的配置2核4G跑这个课程设计绰绰有余。部署简版流程如下# 1. 服务器上安装基础环境 sudo apt update sudo apt install python3-pip python3-venv nginx mysql-server # 2. 拉取项目代码到 /var/www/myproject # 3. 创建虚拟环境并安装依赖 cd /var/www/myproject python3 -m venv venv source venv/bin/activate pip install -r requirements.txt pip install gunicorn # 4. 配置数据库和迁移 vim myproject/settings.py # 修改数据库连接 python manage.py migrate python manage.py collectstatic # 5. 启动gunicorn gunicorn myproject.wsgi:application -b 127.0.0.1:8000Nginx配置文件的要点是把80端口流量指到Gunicorn的127.0.0.1:8000同时把静态文件目录指到collectstatic的输出目录。这样Django进程专注业务逻辑静态文件由Nginx高效分发。如果感觉Gunicorn单独跑一个进程不稳可以用supervisor或者systemd托管进程实现自动拉起服务器重启后服务也能自动恢复。这个细节写进部署文档整个项目会显得特别正规。5.2 新手常见错误集合第一个坑是时区问题。Django的settings.py里默认TIME_ZONEUTCUSE_TZTrue。如果你直接用datetime.now()取当前时间存库数据库里存的是UTC时间页面显示就比北京时间慢了8小时。正确做法是使用django.utils.timezone.now()然后在模板渲染时Django会自动转换成当前时区前提是settings里的USE_TZ保持True且TIME_ZONE设为Asia/Shanghai。第二个坑是静态文件404。大部分课程设计在本地跑得通一部署就各种图片样式不见了十有八九是没跑collectstatic或者Nginx没有配置静态文件alias。调试方法其实很简单先用curl访问一下静态文件URL如果返回404先看是不是Django的static配置问题如果返回403再检查一下服务器上的目录权限。第三个坑是密码显示明文。如果用户表里password字段直接存了明文那你很可能用了User.objects.create()而不是create_user()。create_user会自动调用set_password对密码进行不可逆的哈希加密。这个操作没有补救办法只能重写注册逻辑和重新录入数据。第四个坑是CSRF验证失败。Django的CSRF保护默认开启凡是POST表单模板里必须加{% csrf_token %}。每次看到“CSRF verification failed”的403页面先检查模板里有没有这个标签而不是去关闭中间件。把这个保护关了答辩时被问到安全问题会很尴尬。第五个坑是外键关联查询爆N1。列表页显示商品分类或品牌名称如果没做select_related一次列表加载20个商品可能触发20次额外的数据库查询。打开django-debug-toolbar你会看到SQL查询数成倍膨胀。解决办法就是第3节提到的select_related一次查询把关联对象全拿出来。5.3 答辩演示与论文撰写经验这是临门一脚但很多技术能力不错的人在这上面栽跟头。演示环节我强烈建议你准备三套数据一套演示数据包含10个左右的分类、30个商品、若干订单数据要有真实感一套空数据库方便现场演示从零初始化一套异常数据比如库存为0的商品、不完整信息的供应商用来展示系统的容错处理。演示的流程控制在8分钟内先讲首页再进Admin展示商品导入导出最后下一单看库存变化。这四步走完系统的能力展示就已经很充分了不需要再画蛇添足讲一堆理论。论文这块主数据管理系统是天然的好题目。可以按以下章节布局第一章绪论写选题背景和国内外研究现状第二章相关技术介绍重点讲Django、MTV、ORM、MySQL第三章系统分析包括可行性分析、功能需求分析、数据流图、用例图第四章系统设计E-R图、表结构设计、系统架构图第五章系统实现按功能模块截图加关键代码讲解最后一章系统测试用黑盒测试用例表来体现测试过程。整篇论文写出来字数完全能满足要求。有一个我必须提醒你的点论文里的图表要自己画网上模板的截图、搜到的基础图拼进去知网查重和老师抽检都比较容易被盯上。流程图画图工具用Draw.io就行免费导出SVG插入Word非常清晰。6. 项目扩展与后续优化方向课程设计做到这个程度已经算完整了但你如果学有余力想把这个项目再往上拔高这里有三个扩展方向。第一个方向是引入缓存。商品详情页往往是访问量最大的页面而商品数据相对稳定非常适合做缓存。Django里可以用cache_page装饰器配合Redis把详情页缓存5分钟能显著降低数据库压力。更重要的是这个优化点可以直接写进论文的性能测试章节用响应时间对比图证明自己做了优化。第二个方向是数据报表与可视化。主数据管理系统最终要给决策者使用所以你可以用ECharts做一个简单的分析看板按分类统计商品数量、品牌分布、库存预警、近30天订单趋势。ECharts的文档很全你在Django视图里返回JSON数据前端拉取后用ECharts渲染图表。这个功能会让项目的“管理”属性更上一个层次而不只是单纯的CRUD。第三个方向是接入全文检索引擎。当商品数据上万条之后SQL的LIKE模糊查询性能就不够了。Django生态里可以考虑django-haystack接入Elasticsearch或Whoosh让商品搜索支持分词、拼音、聚合排序。这个方向偏进阶如果你的毕业设计想走搜索方向这倒是一个非常有话可讲的切入点。我个人在实际操作中的体会是这个系统的价值真的不在“电商”两个字而在“主数据管理”这五个字。如果我只做个能下单的商城那和网上现成教程没有任何区别但一旦你站在主数据管理的角度去设计商品生命周期、去规划数据一致性和审计留痕这个项目的站位就完全不同了。最后再分享一个小技巧做课程设计最忌讳“闷头撸代码”每个功能模块做好之后先用手机录一版操作演示视频再写文档和论文最后答辩前对着PPT和演示视频各练三遍整个流程下来基本没有不过的可能。这套项目做下来你不仅练了Django还把完整的软件工程流程走了一遍值回票价。