Có 3 cách để tái dùng hoặc định nghĩa lại model mà không phải tạo bảng mới bừa bãi.
Meta.abstract = True biến class thành template — không tạo bảng, class con kế thừa field + method nhưng mỗi class có bảng riêng. Hay dùng cho TimestampedModel (created_at, updated_at) tái sử dụng khắp project. Meta (không abstract) thì để cấu hình bảng thật: ordering, indexes, constraints, verbose_name, db_table, UniqueConstraint (nên dùng thay cho unique_together đã deprecated). Còn Meta.proxy = True thì dùng chung bảng với class cha, chỉ thêm method/manager khác — tiện khi cần 2 admin view khác nhau cho cùng một bảng.
class TimestampedModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True # không tạo bảng
class Post(TimestampedModel):
title = models.CharField(max_length=200)
class Meta:
indexes = [models.Index(fields=['-created_at'])]Một cái bẫy cần né: multi-table inheritance (kế thừa model thật, không có abstract) sinh ngầm một OneToOneField giữa cha-con, khiến mỗi truy vấn đều phải JOIN.
Đừng dùng nếu không thực sự cần polymorphism ở DB.
Three ways to reuse or redefine a model without arbitrarily creating new tables:
Meta.abstract = True— the class is a template, no table is created. Subclasses inherit fields + methods but get their own table. Use it forTimestampedModel(created_at, updated_at) reused across the project.Meta(non-abstract) — configures the actual table:ordering,indexes,constraints,verbose_name,db_table,unique_together(deprecated, preferUniqueConstraint).Meta.proxy = True— same table as the parent, only adds different methods/managers. Often used to give one table two distinct admin views.
class TimestampedModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True # no table
class Post(TimestampedModel):
title = models.CharField(max_length=200)
class Meta:
indexes = [models.Index(fields=['-created_at'])]Pitfall: Multi-table inheritance (dropping abstract) silently creates a OneToOneField between parent and child → every query JOINs; avoid unless you truly need DB-level polymorphism.