One of the basic concepts of making connections through tables in the database using Django is a ForeignKey. This concept is crucial to master to create large, scalable applications supported by relational databases.
What is ForeignKey in Django?
ForeignKey is a Field (which represents a column in a database table), and it’s used to create many-to-one relationships within tables. It’s a standard practice in relational databases to connect data using ForeignKeys.
What’s the difference between foreign key and primary key?
The primary key defines the unique value for a row in a table. Foreign key connects tables using the primary key value from another table.
ForeignKey Syntax
ForeignKey syntax in Django is as follows:
ForeignKey(to, on_delete, **options)
ForeignKey requires two arguments:
to
- class of connected Model
on_delete
- Defines the behavior of instance after the referenced instance is deleted
- 7 possible behaviors explained in Django on_delete Explained article
**options
- Default Field options explained in official Django documentation
WARNING - Don’t forget to add on_delete parameter, it’s required! Possible options: CASCADE, PROTECT, RESTRICT, SET_NULL, SET_DEFAULT, SET(), DO_NOTHING
Django ForeignKey Example
Now, when we understand the theoretical background, we will give some practical examples.
We will define two models in the following example, Book and Author. We’ll also assume that Book can have only one Author (ManyToOne Relationship) to reduce complexity.
Our models look like this:
class Author(models.Model): name = models.CharField(max_length=512) class Book(models.Model): title = models.CharField(max_length=512) author = models.ForeignKey(Author, on_delete=models.CASCADE)
Now you can see the actual purpose of the ForeignKey Field in practice. It connects the Book model with the Author model.

Where to put ForeignKey?
To define a relationship between two models, you need to define the ForeignKey field in the model from the Many side of the relationship. In other words, ForeignKey should be placed in the Child table, referencing the Parent table.
NOTE - Without an Author, there can't be Book (Author is parent, Book is child model)
In given example – One Author can write multiple books, and one Book can be written only by one Author. That’s why we placed ForeignKey in the Book model.
Author | Book |
William Shakespeare | Hamlet |
William Shakespeare | Macbeth |
William Shakespeare | Romeo and Juliet |
Miguel de Cervantes | Don Quixote |
When to use ForeignKey instead of choices?
As you may already know, the Django Field class by default has a choices option. The choices option allows us to define already known data (choice) that could be inserted into the database.
So when should you use ForeignKey and when other Fields with choices?
Consider using the choices parameter if your database table doesn’t change and remains practically the same.
Why would you choose choices over ForeignKey?
Without having an additional table in the database, you may save time on database queries (as they are some of the most time-consuming operations in web development).
Example of using choices parameter knowing our application will have only two authors:
class Book(models.Model): class Author(models.TextChoices): WS = 'William Shakespeare' MC = 'Miguel de Cervantes' title = models.CharField(max_length=512) author = models.CharField(max_length=512, choices=Author.choices)
Choosing choices or ForeignKey is highly dependent on the use case, the selection between optimization and scalability. But even if the wrong option is selected in some early development stages, it can always be corrected by writing custom migration files.
How is Django ForeignKey saved to the database?
ForeignKey saves, as the name suggests, the foreign key to the database. The foreign key represents the primary key of the other instance.
In standard practice, BigInt almost always references other instances as ForeignKey because the primary key of other instances is BigInt.
Let’s have a look at the database structure of the Book model from the first example:
id | title | author_id |
bigint | character varying (255) | bigint |
NOTE - Primary key in the referenced table doesn't have to be BigInt. ForeignKey can represent other data structures as well. (CharField, DatetimeField, …)
Another important thing to say is how is a column named in the database? Standard Django behavior adds _id
suffix to the name written in the model. So, for example, the author is added _id
suffix making it author_id
as a database column name.
Django ForeignKey ORM
Object-relational mapping (ORM) is a Django feature that helps us write queries in Django/python way.
In the following example, I will show you what can we do with ORM and ForeignKey field:
# create author >>> author = Author.objects.create(name="Miguel de Cervantes") <Author: Author object (1)> # create book with given author >>> Book.objects.create(title="Don Quixote", author=author) <Book: Book object (1)> # get author object >>> Book.objects.get(id=1).author <Author: Author object (1)> # get author id >>> Book.objects.get(id=1).author.id 1 # get author name >>> Book.objects.get(id=1).author.name 'Miguel de Cervantes’
Django ORM can help us retrieve all books connected to a given Author, working with the reverse relationship. For example, we have 3 books related to Miguel de Cervantes (representing Author with id=1) in the database. With book_set
, we can retrieve all books that are connected to him.
>>> Author.objects.get(id=1).book_set.all() <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>]>
ORM queries might change if we use built-in parameters in models, so let’s check it out.
Additional ForeignKey Parameters
In this article, we’ll cover some of the most used parameters such as:
related_name
related_query_name
to_field
related_name
To modify _set name, we can use the related_name
parameter in ForeignKey Field. Default behavior sets related_name
to column_name + _set
suffix.
class Author(models.Model): name = models.CharField(max_length=512) class Book(models.Model): title = models.CharField(max_length=512) author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
As you can see, the author column in the book model now has related_name=’books’
. We can now fetch ‘book_set’ using ‘books’ as a lookup field.
>>> Author.objects.get(id=1).books.all() <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>]>
Code now looks a bit cleaner and more intuitive.
NOTE - If you dont want backwards relationship, you can define related_name = ‘+’ in your model.
WARNING - If all() parameter is not added to the book_set, Django ORM returns RelatedManager object that is not iterable!
>>> Author.objects.get(id=1).book_set <django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager object at 0x7faa0941fd90>
related_query_name
When using ORM, we can perform queries on the connected instance. related_query_name
can be changed in Django models using the related_query_name
parameter, changing the syntax queries are performed.
First, let’s try to fetch queryset of Authors that wrote Book with the title “Hamlet” without related_query_name
:
>>> Author.objects.filter(books__title__icontains="Hamlet") <QuerySet [<Author: Author object (1)>]>
Using related_query_name
changes lookup field for querysets. It might sound a bit abstract but let’s see this in action. First, we need to change our Book model:
class Author(models.Model): name = models.CharField(max_length=512) class Book(models.Model): title = models.CharField(max_length=512) author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books', related_query_name="book")
Now we can perform queries using the book as a lookup field.
>>> Author.objects.filter(book__title__icontains="Hamlet") <QuerySet [<Author: Author object (1)>]>
to_field
to_field
changes column referenced in table, by default Django uses the primary key of the related object.
class Author(models.Model): name = models.CharField(max_length=512, unique=True) class Book(models.Model): title = models.CharField(max_length=512) author = models.ForeignKey(Author, on_delete=models.CASCADE, to_field='name')
We set to_field='name'
, which means the Book model saves the name of Author as author_id in the Book table.
WARNING - The referenced field has to have unique=True.
More parameters
You can find a few additional parameters not covered in this article in official Django documentation because they are rarely used.
db_constraint
- Controls whether constraint should be created in the database
- Defaults True – and should stay True 🙂
swappable
- Defaults True
WARNING - It is recommended to leave the default settings of the specified parameters.
Summary
For any kind of working with relational databases in Django, you should feel comfortable working with ForeignKeys. In this article, we covered the basics. Soon, we’ll cover more advanced stuff covering optimization with powerful tools such as prefetch_related
and select_related
optimizing our queries.