ForeignKey, OneToOneField, and ManyToManyField
coreintermediateForeignKey is many-to-one (many Orders, one Customer). OneToOneField enforces uniqueness on top of the same idea — exactly one match each way (one User, one Profile). ManyToManyField lets both sides have many matches (many Students, many Courses). related_name renames the reverse accessor (customer.order_set becomes customer.orders); related_query_name renames the reverse lookup keyword used in filter().
Think of it as
Picture the three fields as the same underlying idea — a link between two tables — with a different UNIQUE constraint applied on top. ForeignKey adds none: any number of rows on the "many" side can point at the same row on the "one" side. OneToOneField is a ForeignKey with a UNIQUE constraint bolted onto the FK column — so at most one row can ever point at each target. ManyToManyField is not a column at all; it is a whole separate join table with two foreign keys, one to each side, because a single column cannot hold "many" references.
What we're doing: Give an Order's ForeignKey to Customer a readable reverse accessor and a matching query keyword.
- 2
- related_name="orders" replaces the default customer.order_set with customer.orders — the plural, model-name-based default is functional but rarely the name a reader would reach for first.
- 3
- related_query_name="order" is independent of related_name — it controls the SINGULAR keyword used inside filter(), separately from the plural accessor used for direct traversal.
Why this works: The default order_set works but reads as generated code; related_name gives every reverse relationship a name that matches how the codebase actually talks about it, and related_query_name does the same for the filter() keyword, which defaults to the lowercased model name and can diverge in style from related_name if left unset.
Reusing the same related_name on two ForeignKeys to the same target model
Wrong
Better
What you see: django.core.exceptions.FieldError: Reverse accessor for 'Order.shipping_customer' clashes with reverse accessor for 'Order.billing_customer' — raised at startup (system check), before any request is even handled.
Why: Both ForeignKeys point at the same target model (Customer), so both would try to create the exact same reverse accessor name on Customer unless each gets its own distinct related_name — one column pointing at Customer is fine with the default name, but two or more from the same model require explicit, distinct names.
- ForeignKey: plain column, unconstrained — many-to-one, a column on the "many" side
- OneToOneField: plain column, unique — ForeignKey + unique=True
- ManyToManyField: separate join table, between unconstrained and unique — a real join table, two foreign keys
The three relationship fields
Together
Remember: ForeignKey (many-to-one), OneToOneField (ForeignKey + unique=True), ManyToManyField (a separate join table, no on_delete needed) — related_name renames the reverse accessor, related_query_name renames the filter() keyword, independently.
See also: reverse relations and through models · the seven on delete options · primary keys and field options

