Hi, I tried creating a minimal Django project that only implements a blog app with the models you described. I then used makemigrations
+migrate
and jumped into the shell to test things out:
from blog.models import Blog, Author, Entry
from datetime import date
Author.objects.create(name='Jane')
Author.objects.create(name='Shantay')
Author.objects.create(name='Jean')
Blog.objects.create(name='Progruni', tagline='Where programmers unite')
e1 = Entry.objects.create(blog=Blog.objects.get(name='Progruni'),
headline='Foo',
body_text='Fizzbuzz',
pub_date=date(year=2020, month=2, day=5),
mod_date=date(year=2020, month=2, day=6),
number_of_comments=0,
number_of_pingbacks=0,
rating=6)
e2 = Entry.objects.create(blog=Blog.objects.get(name='Progruni'),
headline='Bar',
body_text='Fazzbazz',
pub_date=date(year=2020, month=3, day=5),
mod_date=date(year=2020, month=3, day=6),
number_of_comments=0,
number_of_pingbacks=0,
rating=6)
e1.authors.set([Author.objects.get(name='Shantay')])
e2.authors.set([Author.objects.get(name='Shantay')])
Blog.objects.filter(entry__id=1)
# <QuerySet [<Blog: Progruni>]>
Blog.objects.filter(entry__authors__entry__id=1)
# <QuerySet [<Blog: Progruni>, <Blog: Progruni>]>
Blog.objects.filter(entry__authors__entry__id=1).distinct()
# <QuerySet [<Blog: Progruni>]>
Looking at the SQL queries produced, as @KenWhitesell suggested:
print(Blog.objects.filter(entry__id=1).query)
# SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline"
# FROM "blog_blog" INNER JOIN "blog_entry"
# ON ("blog_blog"."id" = "blog_entry"."blog_id")
# WHERE "blog_entry"."id" = 1
print(Blog.objects.filter(entry__authors__entry__id=1).query)
# SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline"
# FROM "blog_blog" INNER JOIN "blog_entry"
# ON ("blog_blog"."id" = "blog_entry"."blog_id") INNER JOIN "blog_entry_authors"
# ON ("blog_entry"."id" = "blog_entry_authors"."entry_id") INNER JOIN "blog_author"
# ON ("blog_entry_authors"."author_id" = "blog_author"."id") INNER JOIN "blog_entry_authors" T5
# ON ("blog_author"."id" = T5."author_id")
# WHERE T5."entry_id" = 1
print(Blog.objects.filter(entry__authors__entry__id=1).distinct().query)
# SELECT DISTINCT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline"
# FROM "blog_blog" INNER JOIN "blog_entry"
# ON ("blog_blog"."id" = "blog_entry"."blog_id") INNER JOIN "blog_entry_authors"
# ON ("blog_entry"."id" = "blog_entry_authors"."entry_id") INNER JOIN "blog_author"
# ON ("blog_entry_authors"."author_id" = "blog_author"."id") INNER JOIN "blog_entry_authors" T5
# ON ("blog_author"."id" = T5."author_id")
# WHERE T5."entry_id" = 1
As you can see:
-
Blog.objects.filter(entry__authors__entry__id=1)
“duplicates” the blog name in the produced queryset. This is because two authors have ‘Shantay’ as the author, who in turn is the author of the entry with ID 1.
-
Blog.objects.filter(entry__authors__entry__id=1).distinct()
ensures that no blog objects will be repeated in the produced queryset, simply by adding DISTINCT directly after SELECT in the SQL statement used to fetch data from the database.
If it’s a good idea to use .distinct()/DISTINCT for what you want to do, I don’t know. I’m guessing that if there is a ‘cleaner’ method that does what you want while avoiding ‘duplicates’ being produced to begin with, that might be preferable.
I hope this helps