当前位置: 首页 > news >正文

Django 从 0 到 1 打造完整电商平台:Django 模型进阶与数据迁移

IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。


大家好,我是IT策士。上一节我们完成了电商项目的需求分析,并把 10 张核心表写进了模型代码,成功在数据库里“落了户”。但模型只是建出来还不够,实际开发中你会频繁遇到这些场景:修改字段类型、加索引、调整关联关系,甚至还可能要做数据迁移——把老数据“搬”到新结构中。这些操作如果不理解底层原理,早晚会踩坑。

今天我们就来一次 Django 模型进阶与数据迁移的深度解析,帮你彻底搞懂 ORM 的高级用法,并掌握迁移系统的正确打开方式。


一、上节回顾与今天目标

上节回顾:

  • 梳理了电商实体关系,编写了 users、products、cart、orders、payment 五个 app 的模型代码;

  • 配置了自定义用户模型AUTH_USER_MODEL = 'users.User'

  • 执行了makemigrationsmigrate,把所有表建在了 SQLite 中。

今天目标:

  1. 深入 Django 模型字段的高级选项(on_deleterelated_name、索引、元选项等);

  2. 理解 Django 迁移系统的工作原理;

  3. 实战:给已有模型添加索引、修改字段属性、创建数据迁移;

  4. 掌握迁移回滚、查看 SQL、合并迁移等常用操作。


二、模型进阶——那些你该掌握的细节

2.1on_delete的六种行为

外键字段都必须指定on_delete。它决定了被关联对象被删除时,当前对象的行为。六种取值如下:

在我们的项目中:

  • Order.sku使用了PROTECT,意思是商品在被订单引用时不允许删除,防止历史订单出现“幽灵商品”。

  • CartItem.sku使用了CASCADE商品删除,购物车里对应的条目也应该消失

  • Order.user使用PROTECT用户不能在有订单的情况下被直接删除

不同的业务场景需要不同的删除策略,这是模型设计的一个关键细节。

2.2related_name与反向查询

ForeignKey会默认创建一个反向关系,名字是模型名小写_set,例如user.order_set.all()。为了语义更清晰,我们通常会自定义related_name

在我们的Order模型中:

user=models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.PROTECT,related_name='orders',# 自定义反向名)

这样就能用user.orders.all()直接获取该用户的所有订单。如果你不想创建反向关系,可以设置related_name='+'

2.3 模型元选项Meta

每个模型都可以通过内部类Meta配置元数据,常用的有:

  • db_table:自定义表名,我们统一用了tb_前缀。

  • verbose_name / verbose_name_plural:可读名称,Admin 后台会用到。

  • ordering:默认排序,如['-create_time']

  • unique_together:联合唯一约束。我们在购物车模型中用了('user', 'sku')防止重复添加。

  • indexes:声明数据库索引(后面详解)。

2.4 字段属性详解

挑几个项目中重要且容易忽略的字段属性说一说:

  • decimal_placesDecimalField的精度至关重要。价格我们用max_digits=10, decimal_places=2,表示最大 10 位数,其中 2 位小数,能表示到 99,999,999.99。

  • auto_now_addvsauto_nowcreate_timeauto_now_add(新增时自动取值),update_timeauto_now(每次保存都自动更新)。

  • null=Truevsblank=True:前者是数据库层面的允许 NULL,后者是表单验证层面的允许为空。CharField一般不要用null=True,Django 推荐用空字符串代替。

  • JSONField:Django 3.1 起原生支持 JSON 字段(除 SQLite 外需要数据库支持)。我们用 JSON 存储商品规格快照和地址快照,灵活方便。


三、模型优化——给已有表加索引和默认值

索引能极大提升查询效率。电商系统里哪些字段经常作为查询条件?order_no(订单号)、user(用户外键)、create_time(时间范围查询)等。我们来给它们加上索引。

3.1 给订单表添加索引

编辑apps/orders/models.py,在Order类的Meta中添加indexes

class Order(models.Model):# ... 字段省略 ...class Meta: db_table='tb_order'verbose_name='订单'verbose_name_plural=verbose_name ordering=['-create_time']indexes=[models.Index(fields=['user','-create_time'],name='idx_user_time'), models.Index(fields=['status'],name='idx_status'),]

我们创建了两个索引:

  • 联合索引idx_user_time:用户 ID 加创建时间降序,加速“某用户的订单列表”查询。

  • 单列索引idx_status:加速按状态筛选订单。

3.2 给 SKU 表添加索引

编辑apps/products/models.py,在SKUMeta里加上:

class SKU(models.Model):# ... 字段省略 ...class Meta: db_table='tb_sku'verbose_name='SKU'verbose_name_plural=verbose_name indexes=[models.Index(fields=['spu','is_active'],name='idx_spu_active'),]

联合索引idx_spu_active用于加速“查询某个 SPU 下所有上架 SKU”。

3.3 给商品 name 字段增加长度上限优化

SKU.name目前长度 200,对于某些长规格名的商品可能不够,我们扩展到 500,同时加上数据库级别的默认空字符串(避免 NULL):

name=models.CharField(max_length=500,default='',verbose_name='SKU 名称')

四、数据迁移——从原理到实战

4.1 Django 迁移系统是如何工作的?

每次执行makemigrations,Django 会扫描所有已注册 app 的models.py,与上一次迁移后的状态做对比,生成一个迁移文件(存在各 app 的migrations/目录下)。迁移文件里有两个关键部分:

  • state_operations:描述模型“应该长什么样”。

  • database_operations:描述要在数据库上执行的操作(ALTER TABLE、CREATE INDEX 等)。

执行migrate时,Django 会根据迁移文件里的操作,翻译成对应的数据库 DDL 语句。

4.2 生成新的迁移

修改完模型后,运行:

python manage.py makemigrations

控制台输出:

Migrationsfor'orders':apps/orders/migrations/0002_auto_20260519_1430.py - Create index idx_user_time on field(s)user,-create_timeof model order - Create index idx_status on field(s)status of model order Migrationsfor'products':apps/products/migrations/0002_alter_sku_name_add_index.py - Alter field name on sku - Create index idx_spu_active on field(s)spu, is_active of model sku

如果你对迁移文件名不满意,可以用--name参数自定义:

python manage.py makemigrations orders--nameadd_order_indexes
4.3 查看迁移对应的 SQL(很实用!)

在执行迁移前,我们最好先看一眼它会在数据库里执行哪些 SQL。使用sqlmigrate命令:

python manage.py sqlmigrate orders 0002

输出示例(SQLite 版):

BEGIN;-- -- Create index idx_user_time on field(s)user,-create_timeof model order -- CREATE INDEX"idx_user_time"ON"tb_order"("user_id","create_time"DESC);-- -- Create index idx_status on field(s)status of model order -- CREATE INDEX"idx_status"ON"tb_order"("status");COMMIT;

换成 MySQL 或 PostgreSQL,生成的 DDL 会相应变化。这个命令可以让你在动手之前“审阅”SQL,避免意外。

4.4 执行迁移

确认无误后,执行:

控制台输出:

Operations to perform: Apply all migrations: orders, products Running migrations: Applying orders.0002_add_order_indexes... OK Applying products.0002_alter_sku_name_add_index... OK

这时索引已经建立在数据库里了。可以用dbshell进入 SQLite,输入.indexes tb_order查看索引列表。


五、迁移的“后悔药”——回滚与恢复

5.1 撤销最近一次迁移

假设刚才的迁移有问题,我们可以退回到指定状态:

python manage.py migrate orders 0001

这个命令会将ordersapp 的迁移状态退回到0001,对应的表结构也会回退(索引会被删除)。

执行后会提示:

Operations to perform: Target specific migration: 0001_initial, from orders Running migrations: Rendering model states... DONE Unapplying orders.0002_add_order_indexes... OK
5.2 撤销所有迁移(慎用)

如果想回到零迁移状态(表清空),可以指定zero

python manage.py migrate orders zero

但在生产环境千万别这么做,除非你真的要删除整个 app 的数据表。


六、高级话题:数据迁移(Data Migration)

有时候你不仅要改表结构,还需要搬移或转换数据。比如我们想在tb_users表里给现有用户批量生成一个默认的“手机号”字段,让其不再是 NULL。

6.1 创建空迁移作为数据迁移的壳
python manage.py makemigrationsusers--empty--namepopulate_user_phone

这会生成一个空的迁移文件apps/users/migrations/0002_populate_user_phone.py。我们打开它,在operations列表里写上数据迁移逻辑:

from django.dbimportmigrations def populate_phone_default(apps, schema_editor): User=apps.get_model('users','User')# 将 phone 为空的用户设置一个临时占位手机号,实际中可根据业务逻辑处理foruserinUser.objects.filter(phone__isnull=True): user.phone=f"1000000{user.id:04d}"# 例如 10000000001user.save(update_fields=['phone'])def reverse_phone(apps, schema_editor):# 回滚逻辑:将生成的手机号置空User=apps.get_model('users','User')User.objects.filter(phone__startswith='1000000').update(phone=None)class Migration(migrations.Migration): dependencies=[('users','0001_initial'),]operations=[migrations.RunPython(populate_phone_default, reverse_phone),]

然后执行迁移:

这样既修改了表结构,也填充了历史数据。


七、常用迁移命令速查


八、总结与下集预告

今天我们把 Django 模型从“会用”提升到了“懂得内部机制”的水平:

  • 吃透了on_deleterelated_nameMeta选项的用法;

  • 给订单和商品表添加了关键索引,优化查询;

  • 深入理解了 Django 迁移的原理,学会了生成、查看、回滚迁移;

  • 初次体验了数据迁移,处理了历史数据问题。

有了扎实的模型基础,下一步就该让数据“活”起来了。第 4 天,我们将迎来 Django 最让人兴奋的功能之一——Admin 后台管理。我会教你如何在 10 分钟内搭出一个能管理用户、商品、订单的后台,并实现一些高级定制,比如列表过滤、搜索、自定义表单……让运营人员也能直接上手。

想了解更多还可以去其它平台搜索「IT策士」,一起升级 IT 思维 !


本文为《Django 从 0 到 1 打造完整电商平台》系列第 3 篇,作者:IT策士,未经授权禁止转载。

http://www.jsqmd.com/news/852837/

相关文章:

  • 包装机械行业如何做线上推广获客?2026全网获客指南与服务商盘点 - 年度推荐企业名录
  • 如何用SlopeCraft将普通图片变成Minecraft立体地图艺术
  • 商业空间设计行业如何做新媒体AI智能获客?2026全网推广指南与服务商盘点 - 优质企业观察收录
  • Windows 11玩机技巧:除了.md,还能给右键菜单添加哪些‘新建’格式?(JSON/YAML/Env文件实战)
  • 数据链路层与二层交换:从MAC地址表到VLAN的局域网通信核心
  • 2026武汉优质 GEO 优化公司排行:抢占ai搜索流量 - 资讯焦点
  • NoFences:5分钟拯救杂乱桌面的终极免费桌面整理工具指南
  • TVA 颠覆常规 AI 视觉的底层逻辑(17)
  • Kafka 日志目录磁盘空间不足导致 Broker 停止服务如何应急?
  • 进阶使用VS Code:解锁AI编程助手的引擎模式
  • 免费解锁二手iPhone:applera1n激活锁绕过工具终极指南
  • 奇安信Qcode Agents重磅升级,正式解锁操作系统级漏洞挖掘能力
  • 深入Activiti 5.22内核:从命令模式与拦截器链看流程引擎的执行机制
  • 跟着 MDN 学CSS day_1:(CSS 基石与色彩的艺术)
  • 从澡堂到家庭:“秦老大”为何能成为澡巾行业的“标尺” - 中媒介
  • 如何5分钟制作专业MDX词典:AutoMdxBuilder智能生成器完整指南
  • 矩阵从0到自动化运转的4个阶段:90%的团队死在第2阶段
  • 不熬夜、不焦虑、不踩坑:用百考通AI 无痛搞定本科毕业论文
  • 毕业季论文 “自救” 指南:从选题到定稿,这 9 款 AI 工具帮你告别熬夜内耗
  • VK视频下载终极指南:3种方法轻松保存珍贵回忆
  • 跟着 MDN 学CSS day_2:(连接样式表与选择器的实战艺术)
  • 保姆级教程:在RK3588 Android 12上搞定HDMI输入(从DTS配置到音频调试)
  • 机械臂关节电机场景下的优化控制方法【附代码】
  • 别再踩坑了!用HBuilderX和Xcode离线打包iOS App的完整流程与权限避坑指南
  • 2026 甘肃超声炮哪家好?5 家正规机构推荐(兰州超声炮机构口碑榜单) - 深度智识库
  • 多模态大模型微调为什么一上图文交错数据就开始视觉退化:从 Modality Collapse 到 Progressive Unfreeze 的工程实战
  • YOLOv8实时目标检测与自适应控制技术在游戏辅助系统中的应用研究
  • 中创商业咨询这个公司做并购如何?并购实战派的深度拆解 - 服务品牌热点
  • 从VMware共享文件夹到完整环境:手把手带你为ZYNQ开发板搭建Petalinux 2018.3开发栈
  • ADRC入门避坑指南:搞懂跟踪微分器,别再混淆‘斜坡信号’与‘微分信号’了