In Django, which Meta options does a child model inherit from an abstract base class, and which from a multi-table parent?
answer
- two different rules
- abstract Meta is handed down
- abstract reset to False
- only ordering and get_latest_by
basics
~20 sA child of an abstract base inherits the whole Meta if it declares none, with abstract reset to False, and can extend it by subclassing Base.Meta; a multi-table or proxy child inherits only ordering and get_latest_by.
solid answer
~40 sFor an abstract base, Django keeps the base's `Meta` available; a child that declares no `Meta` inherits it, and a child that wants to add options writes `class Meta(FleetRecord.Meta):`. Django sets `abstract=False` on the inherited `Meta`, so children are concrete unless they re-declare `abstract = True`. Options that must be unique per table, such as `db_table`, do not belong in an abstract `Meta`, and index or constraint names there need the `%(app_label)s`/`%(class)s` placeholders. With several abstract bases only the first base's `Meta` is inherited unless you subclass them all. A multi-table child has no access to its parent's `Meta`: only `ordering` and `get_latest_by` carry over, and only when the child does not set them; `ordering = []` drops the parent's. Proxy models follow the multi-table rule.
go deeper
Recall that children of an abstract base get its Meta, and that this is not true of a concrete parent.
State both rules precisely: abstract Meta handed down with abstract reset, multi-table and proxy children inheriting only ordering and get_latest_by.
Spot the production traps: a child Meta that silently replaces the base's, a surprise inherited ordering, and index or constraint names that collide across children.
Set conventions for shared abstract bases, such as always subclassing Base.Meta and placeholder names, so a base change cannot silently alter every child table.
## Two rules, because two different things are inherited A Django model's inner **`Meta`** class holds table-level options: `ordering`, `db_table`, `indexes`, `constraints`, `get_latest_by`, `verbose_name` and others. When models inherit, Django applies **different rules** depending on whether the parent exists in the database: | Parent kind | Does the child get the parent's `Meta`? | What carries over | |---|---|---| | **Abstract base** (`abstract = True`) | yes, if the child declares no `Meta` | every option, with `abstract` reset to `False` | | **Multi-table parent** (concrete) | no | only `ordering` and `get_latest_by`, if the child sets neither | | **Proxy's parent** | no | same as multi-table: `ordering` and `get_latest_by` | The reason: an abstract base never became a table, so its options have not been applied anywhere yet and are meant for the children. A concrete parent's options have already been applied to the parent's own table; re-applying them to the child would usually contradict them. ## Abstract bases: handed down and extensible If a child of an abstract base declares no `Meta`, it simply gets the base's. To add to it, the child subclasses it: ```python class FleetRecord(models.Model): created_at = models.DateTimeField(auto_now_add=True) class Meta: abstract = True ordering = ["-created_at"] get_latest_by = "created_at" class Car(FleetRecord): plate = models.CharField(max_length=12, unique=True) class Meta(FleetRecord.Meta): db_table = "fleet_cars" ``` Points that trip people up: - **`abstract` is reset.** Before installing the inherited `Meta`, Django sets `abstract=False`, so `Car` is concrete. An abstract chain needs `abstract = True` re-declared on each abstract level. - **A child `Meta` that does not subclass replaces.** If `Car` wrote a plain `class Meta:` with only `db_table`, it would lose the base's `ordering` and `get_latest_by`. - **Multiple abstract bases.** Python's attribute lookup finds `Meta` on the first base only; to combine two, write `class Meta(FirstBase.Meta, SecondBase.Meta):`. ## Options that misbehave when inherited from an abstract base Some options name something that must be unique per table: 1. **`db_table`** — every child that inherits it would map to the same table, which is almost never intended. 2. **`indexes` and `constraints`** — index and constraint names must be unique, so a fixed name repeats in every child; write `name="%(app_label)s_%(class)s_plate_idx"`, which Django fills with the child's lowercased app label and class name. ## Multi-table and proxy children: only two options travel A multi-table child cannot see its parent's `Meta` at all. The only behaviour it inherits is: - **`ordering`**, when the child does not declare its own; - **`get_latest_by`**, when the child does not declare its own. So a `Vehicle` ordered by `plate` makes `Van.objects.all()` ordered by `plate` too, which can surprise someone reading `Van` alone. To remove it, the child declares `ordering = []`. Everything else — `verbose_name`, `indexes`, `constraints`, `permissions`, `db_table` — must be declared on the child if the child needs it. Proxy models inherit `Meta` the same way, which is why `ElectricCar(Car)` can set its own `ordering` while silently keeping `Car`'s `get_latest_by`, if `Car` declares one. ## Checking what a model actually got When a hierarchy is several levels deep, reading the source is slower than asking Django. The model `_meta` API exposes the resolved options on every class: - `Van._meta.ordering` — the ordering in force, whether declared or inherited from `Vehicle`; - `Van._meta.get_latest_by` — the field `latest()` and `earliest()` use when called without arguments; - `Car._meta.db_table` — the table name, which exposes a `db_table` accidentally inherited from an abstract base; - `Car._meta.abstract` — `False` on every concrete child, confirming the automatic reset. A short test that asserts these values for each model in a hierarchy catches the silent cases: a child `Meta` that replaced the base's instead of extending it, or a parent ordering that started applying to every child query after someone added it to the parent. Such a test fails at the moment of the change instead of months later in a slow, unexpectedly sorted admin list. ## How to answer in an interview State the two rules and the reason: abstract options are meant for children; concrete options already belong to a table. Then name the traps — the `abstract` reset, replacing instead of subclassing, the first-base-wins lookup, and placeholder names for indexes and constraints.
- Vehicle has Meta.ordering = ['plate'] and Van is a multi-table child with no Meta; how is Van.objects.all() ordered, and how do you stop it?It is ordered by `plate`, because a multi-table child inherits `ordering` when it declares none. Declare `class Meta: ordering = []` on `Van` to remove the default ordering, or set a different list.
- Why must an index declared in an abstract base's Meta use the %(class)s placeholder in its name?The `indexes` option is copied to every child with identical arguments, including `name`, and index names must be unique. Writing `name="%(app_label)s_%(class)s_plate_idx"` lets Django substitute each child's app label and class name so the names differ.
saying these in an interview costs you the question
- A multi-table child inherits every Meta option from its parent.
- A child of an abstract base is abstract too unless you set abstract = False.
- Putting db_table in an abstract base's Meta gives each child its own table.
- A plain class Meta on the child merges with the abstract base's Meta.
- With two abstract bases, Django merges both Meta classes automatically.