Clear Model Relationship API Usage
Django ORM could seem complex and daunting when different kinds of model relationships are combined together. To keep sanity, I devised a basic schema in differentiating different kind of model relationships.
Many-to-many relationships
class Publication(models.Model):
title = models.CharField(max_length=30)
class Meta:
ordering = ["title"]
def __str__(self):
return self.title
class Article(models.Model):
headline = models.CharField(max_length=100)
publication_set = models.ManyToManyField(Publication, related_query_name='article_set')
# By naming many to many field with _set suffix and setting 'related_query_name' with _set suffix, I know both article_obj.publication_set.all() and publication_obj.article_set.all() work and Article and Publication are ManyToMany-related to each other. Both Article.object.filter(publication_set__tile__istartswith='Science') and Publication.objects.filter(article_set__pk=1) work, too.
class Meta:
ordering = ["headline"]
def __str__(self):
return self.headline
Many-to-one relationships
class Reporter(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
def __str__(self):
return f"{self.first_name} {self.last_name}"
class Report(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, related_name='reports', on_delete=models.CASCADE)
# By set 'related_name' with 's' suffix or other plural form, It is clear to me this is a one to many relation.
# Now both reporter_obj.reports.all() and Reporter.objects.filter(reports__headline__icontains='This') work.
def __str__(self):
return self.headline
class Meta:
ordering = ["headline"]
One-to-one relationships
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
def __str__(self):
return f"{self.name} the place"
class Restaurant(models.Model):
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
primary_key=True,
)
# Queries such as restaurant_obj.place, place_obj.restaurant, Restaurant.objects.filter(place__name__startswith='Demon) and Place.objects.filter(restaurant__serves_pizza=True) will work.
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
def __str__(self):
return "%s the restaurant" % self.place.name
class Waiter(models.Model):
restaurant = models.ForeignKey(Restaurant, related_name='waiters', on_delete=models.CASCADE)
name = models.CharField(max_length=50)
def __str__(self):
return "%s the waiter at %s" % (self.name, self.restaurant)
Please refer to Django Documentation: Examples of model relationship API usage for detailed query examples and try out yourself.