OneToOneField vs ForeignKey in Django
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
OneToOneField creates a one-to-one relationship where each row in one table links to exactly one row in another table, and vice versa. ForeignKey creates a many-to-one relationship where multiple rows in one table can link to the same row in another table. Under the hood, OneToOneField is a ForeignKey with unique=True, but it changes the reverse accessor from a QuerySet (manager) to a single object. Use OneToOneField for profile extensions, settings, or any "has exactly one" relationship. Use ForeignKey for "belongs to" relationships where many records share the same parent.
OneToOneField Example
The reverse accessor returns a single object directly. Accessing user.profile when no profile exists raises UserProfile.DoesNotExist (a subclass of RelatedObjectDoesNotExist).
ForeignKey Example
The reverse accessor is a RelatedManager that returns a QuerySet. Multiple books can belong to the same author.
Key Differences
| Feature | OneToOneField | ForeignKey |
| Relationship | One-to-one | Many-to-one |
| Reverse accessor | Single object | QuerySet (manager) |
| Database constraint | UNIQUE + FOREIGN KEY | FOREIGN KEY only |
| Reverse syntax | user.profile | author.books.all() |
| DoesNotExist on reverse | Raises exception | Returns empty QuerySet |
When to Use Each
Handling the Reverse Accessor
on_delete Options
Common Pitfalls
- Using
ForeignKeywhereOneToOneFieldis needed: If a profile should be unique per user, aForeignKeyallows multiple profiles per user (violating the design intent). UseOneToOneFieldto enforce the constraint at the database level, not just in application logic. - Not handling
DoesNotExiston reverse OneToOneField access:user.profileraisesRelatedObjectDoesNotExistif no profile exists. Always wrap intry/exceptor usehasattr(user, 'profile'). ForeignKey reverse access (author.books.all()) never raises — it returns an empty QuerySet. - Forgetting
select_relatedfor OneToOneField queries: Eachuser.profileaccess executes a separate SQL query. UseUser.objects.select_related('profile')to fetch both in a single JOIN query, especially in loops or templates. - Using
related_nameinconsistently: Withoutrelated_name, Django generates a default name (userprofileforOneToOneField,book_setforForeignKey). Specifyrelated_nameexplicitly for clarity and to avoid clashes when multiple foreign keys point to the same model. - Confusing
on_deletebehavior:on_delete=models.CASCADEon aOneToOneFielddeletes the profile when the user is deleted. If you want profiles to survive user deletion, useSET_NULLwithnull=True. Theon_deleteargument is required and has no default — Django forces you to choose.
Summary
OneToOneFieldenforces a unique one-to-one relationship;ForeignKeyallows many-to-one- Reverse access differs:
user.profilereturns a single object;author.books.all()returns a QuerySet OneToOneFieldis equivalent toForeignKey(unique=True)at the database level- Use
select_relatedforOneToOneFieldandprefetch_relatedforForeignKeyto optimize queries - Handle
DoesNotExiston reverseOneToOneFieldaccess — it raises an exception if the related object is missing - Always specify
on_deleteandrelated_nameexplicitly for clarity
Related reading
- opensource tool for Oracle Change data capture - Alternative to GoldenGate
- Opentelemetry traceid for Couchbase Database Change Protocol
- Optimal JVM settings for Cassandra
- Optimal settings for Cassandra Java driver to write to the local data centre only
- Only Add Unique Item To List
- Open file in a relative location in Python
- Optimistic concurrency control clarification
- Optimistic Offline Lock Achieve this in database offering Serializability without Linearizability? (i.e., DB does not provide strict serializability)

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.