~2 min read • Updated Mar 10, 2026
1. Defining a Many‑to‑One Relationship
A many‑to‑one relationship is defined using ForeignKey.
In the example below, a Reporter can have many Article objects, but each Article has exactly one Reporter.
class Reporter(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
2. Creating Data
Creating Reporter instances:
r = Reporter(first_name="John", last_name="Smith", email="[email protected]")
r.save()
r2 = Reporter(first_name="Paul", last_name="Jones", email="[email protected]")
r2.save()
Creating an Article:
from datetime import date
a = Article(headline="This is a test", pub_date=date(2005, 7, 27), reporter=r)
a.save()
Accessing the related Reporter:
a.reporter
a.reporter.id
ForeignKey requires saved objects
Assigning an unsaved Reporter raises an error:
r3 = Reporter(first_name="John", last_name="Smith", email="[email protected]")
Article.objects.create(..., reporter=r3) # ValueError
3. Reverse Access: Reporter → Article
Django automatically creates a reverse manager named article_set:
r.article_set.all()
Creating an Article from the reverse side:
new_article = r.article_set.create(
headline="John's second story",
pub_date=date(2005, 7, 29)
)
4. Moving an Article Between Reporters
You can reassign an Article to another Reporter using add() on the reverse manager:
r2.article_set.add(new_article2)
new_article2.reporter # Now Paul Jones
Adding the wrong type raises TypeError:
r.article_set.add(r2) # TypeError
5. Querying Across ForeignKey Relationships
Filtering Articles by Reporter fields:
Article.objects.filter(reporter__first_name="John")
Article.objects.filter(reporter__first_name="John", reporter__last_name="Smith")
Filtering by primary key or object:
Article.objects.filter(reporter__pk=1)
Article.objects.filter(reporter=1)
Article.objects.filter(reporter=r)
Using lists or querysets:
Article.objects.filter(reporter__in=[r, r2]).distinct()
Article.objects.filter(
reporter__in=Reporter.objects.filter(first_name="John")
).distinct()
6. Reverse Queries: Article → Reporter
Reporter.objects.filter(article__pk=1)
Reporter.objects.filter(article=a)
Reporter.objects.filter(article__headline__startswith="This").distinct()
Counting with distinct:
Reporter.objects.filter(article__headline__startswith="This").count()
Reporter.objects.filter(article__headline__startswith="This").distinct().count()
7. Circular Relationship Queries
You can chain lookups indefinitely:
Reporter.objects.filter(article__reporter__first_name__startswith="John").distinct()
8. Deletion Behavior (CASCADE)
Because on_delete=models.CASCADE is used, deleting a Reporter deletes all their Articles:
r2.delete()
Article.objects.all() # Articles belonging to r2 are gone
Deleting via JOIN:
Reporter.objects.filter(article__headline__startswith="This").delete()
Reporter.objects.all() # Empty
Article.objects.all() # Empty
Conclusion
Many‑to‑one relationships in Django are simple yet powerful.
Using ForeignKey, you can easily navigate between related objects, create new objects from either side, move relationships, and perform complex queries across multiple levels.
Django’s automatic reverse managers and expressive lookup syntax make working with relational data intuitive and efficient.
Written & researched by Dr. Shahin Siami