抽象基类(abstract=True)不生成数据库表,仅用于代码复用,其字段被复制到各子类表中;多表继承则为父类建真实表,并通过OneToOneField关联子类。

抽象基类 abstract=True 不会生成数据库表
抽象基类只是代码复用机制,Django 在迁移时完全跳过它,不会为 abstract=True 的模型建表。它的字段会“复制”到每个继承它的具体模型中,就像手动把字段定义粘贴过去一样。
常见错误现象:python manage.py makemigrations 后发现抽象基类没出现在迁移文件里,或执行 python manage.py showmigrations 时看不到对应记录——这正常,不是漏了。
- 所有子类各自生成独立表,字段重复存在(比如
created_at在User和Article表里各有一份) - 不能对抽象基类做
MyAbstractModel.objects.all(),会报AttributeError: type object 'MyAbstractModel' has no attribute 'objects' - 如果基类定义了
Meta.ordering或Meta.verbose_name,子类默认继承,但可被覆盖
多表继承会为父类建真实表并加 OneToOneField 隐式关联
只要不设 abstract=True,哪怕父类没有显式字段,Django 也会为它单独建一张表,并在子类表里加一个指向它的 OneToOneField(字段名默认是父类小写名,如 place_ptr)。
使用场景:需要复用逻辑 + 共享部分数据(比如多个模型都共用地址、状态、审核人等),且这些字段值在不同子类型间要保持唯一或可统一查询。
立即学习“Python免费学习笔记(深入)”;
- 父类实例必须先保存才能创建子类实例,否则抛
ValueError: "Place" needs to have a value for field "id" before this many-to-one relationship can be used. - 子类查询时默认走 JOIN(如
Restaurant.objects.select_related('place')),性能敏感时得留意 N+1 或冗余 JOIN - 父类表的主键(
id)会同时作为子类表的主键和外键,所以Restaurant.objects.get(id=1)和Place.objects.get(id=1)实际指向同一行物理记录
abstract=True 下字段定义位置影响实际效果
字段写在抽象基类里,还是在子类里重定义,结果可能不同。尤其涉及 related_name、db_column、default 这类参数时,容易误以为“继承了配置”,其实只是文本复制。
例如:抽象基类定义了 status = models.CharField(max_length=20, default='draft'),子类没重写,那每个子表都会带这个字段和默认值;但如果子类自己再写一遍 status,哪怕只改 default,就变成两个独立字段定义,Django 不合并也不警告。
-
related_name必须显式设置,否则 Django 会自动生成(如user_set),多个子类继承同一抽象类时若不设,会因反向名冲突导致makemigrations失败 -
db_column在抽象类中指定后,子类表字段名就是那个值;但如果子类又定义同名字段,就覆盖了——不是叠加,是替换 - 函数默认值(如
default=timezone.now)没问题,但写成default=timezone.now()就会在模块加载时执行一次,所有实例共享同一个时间戳
什么时候该选 abstract=True,什么时候该用多表继承
本质区别不在“要不要复用代码”,而在于“要不要共享数据行”。如果字段只是结构相似、语义无关(比如所有模型都要有 created_at),用抽象基类;如果不同模型实例之间存在事实上的父子归属(比如 Restaurant 是一种 Place),且业务上需要按 Place 统一筛选、更新、计数,那就得多表继承。
容易踩的坑:为了图省事把本该是多表继承的场景硬改成抽象基类,结果后面发现没法统一查“所有营业场所”,只能靠 UNION 或应用层聚合,反而更重。
- 抽象基类适合:审计字段(
created_at,updated_by)、软删除标记(is_deleted)、通用状态机字段 - 多表继承适合:有明确 IS-A 关系的领域模型(
Employee/Manager都是Person)、需统一管理生命周期的资源(ImageFile,VideoFile都是MediaFile) - 两者都不能解决“一个子类想继承多个父类”的需求——Django 不支持多重继承,此时只能靠组合(
ForeignKey)或第三方库如django-model-utils的InheritanceManager
抽象基类看着轻量,但字段复制多了,迁移和索引维护成本会上升;多表继承看着重,但数据关系清晰,SQL 层面更容易优化。选哪个,取决于你愿不愿意让数据库知道“它们是一家人”。










