fghfgg
fghfgg

Reputation: 21

Accessing one model from within another in Django many-to-one using ForeignKey

Lets imagine we have two models (many-to-one model). Code below shows that a reporter can have multiple articles

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 "%s %s" % (self.first_name, self.last_name)

class Article(models.Model):
    headline = models.CharField(max_length=100)
    pub_date = models.DateField(null=True)
    reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE, null=True)

    def __str__(self):
        return self.headline

Let's see what I have in my database on this model.

# Reporter.objects.all().values()
# <QuerySet [
# {'id': 1, 'first_name': 'John', 'last_name': 'Smith', 'email': '[email protected]'},
# {'id': 2, 'first_name': 'Paul', 'last_name': 'Jones', 'email': '[email protected]'}
# ]>
# Article.objects.all().values()
# <QuerySet [
# {'id': 5, 'headline': "1st headline", 'pub_date': datetime.date(2005, 7, 29), 
# 'reporter_id': 1},
# {'id': 6, 'headline': "2nd headline", 'pub_date': datetime.date(2006, 1, 17), 
# 'reporter_id': 2},
# {'id': 7, 'headline': '3rd headline', 'pub_date': datetime.date(2005, 7, 27), 
# 'reporter_id': 1}
# ]>

The first reporter has two publications and second has the only. I need to get the list of all articles for each reporter. I tried this way (according to django docs):

Article.objects.filter(reporter__first_name='John')

It's okay. It works. I also tried to instantiate the first reporter as 'r1' and then do this:

r1.article_set.all()

And this piece of code works too. But as I'm new to django, I think that instantiating the first reporter as 'r1' and then making a query is a bit slow. It is because django makes me run r1.save() and then r1.article_set.all(). It looks like django makes 2 query into database (first query - to save an instance, the second query to run r1.article_set.all)

Is my point of view correct? And how to query all the reporter's articles as fast as Article.objects.filter(reporter__first_name='John') but using the Reporter object? Thanks

Upvotes: 1

Views: 202

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476594

I also tried to instantiate the first reporter as 'r1' and then do this:

r1.article_set.all()

And this piece of code works too. But as I'm new to django, I think that instantiating the first reporter as 'r1' and then making a query is a bit slow.

Yes, but Django can load the related articles all with a second query in bulk. We do this with .prefetch_related(…) [Django-doc]:

reporters = Reporter.objects.prefetch_related('article_set')

for reporter in reporters:
    print(reporter.first_name)
    print(reporter.article_set.all())

Instead of the N+1 queries that your implementations make (one query to fetch all reporters, and one query per reporter to fetch the related articles), this will make two queries: one to fetch all the reporters, and one to fetch all the articles related to one of these reporters. Django will then do some joining such that the articles related to the first reporter r1 end up in r1.article_set.all()

Upvotes: 0

Related Questions