Selecting specific fields using select_related in Django
Asked Answered
L

3

58

I have two models Article and Blog related using a foreign key. I want to select only blog name while extracting the article.

articles = Articles.objects.all().select_related('blog__name')

The query generated shows that it selected all the fields from the Blog model. I tried using only() and defer() with select_related but both didn't work out.

articles = Articles.objects.all().select_related('blog__name').only('blog__name', 'title', 'create_time')

The above query resulted in error: Invalid field name(s) given in select_related: Choices are: blog

How do i generate a query so that only article fields and blog name is selected?

Lemniscus answered 20/2, 2016 at 13:57 Comment(4)
I don't think it's possible - the docs for only don't show any examples of traversing relations, while the docs for select_related only show examples of traversing multiple relations (i.e. rel__rel, not rel__field). seems like the best you can do is articles = Articles.objects.all().select_related('blog').only('blog', 'title', 'create_time')Earp
Whats the purpose of doing this? Performance optimization? However, you can make this using prefetch_related, but this way you will end up with 2 queries instead of one. Articles.objects.all().prefetch_related(Prefetch('blog', queryset=Blog.objects.all().only('name')))Simferopol
The sole purpose was to optimize performance. I'm already using the select_related, but it gives all the attributes which consumes a lot of memory. Is there any other way to do this?Lemniscus
As of this date, for django 1.8.9, i am not able to find the solution to use 'defer' or 'only' with 'select_related' or anything similar to this returning a queryset.Lemniscus
H
27

You can use annotate() for this.

>>> a = Articles.objects.annotate(blog_name=F('blog__name')).first()
>>> a.title
>>> a.blog_name
Highup answered 11/8, 2016 at 10:37 Comment(3)
This is not the expected answer. The below one solves the purpose.Thursday
Sometimes, this serves the purpose. anyway both are significantGenaro
original question was: "How do i generate a query so that only article fields and blog name is selected?" This is fetching all fields from Blog, right? If so, it wouldn't answer the question properly. Correct?Wan
S
82

select_related should be use on the whole model, and then you can filter it more. This will work:

Articles.objects.select_related('blog').only('blog__name', 'title', 'create_time')
Sniffy answered 20/2, 2016 at 18:44 Comment(6)
Using .values returns dictionary instead of queryset object, which makes it useless to use the relations like 'file.url' or 'file.name' in the templates or view. Is there any other way possible?Lemniscus
@RA123 I edited my answer. Tried it and this way it works for meSniffy
This is also not working. I'm on Django 1.8. It gives the following error - "Invalid field name(s) given in select_related: 'blog'. Choices are: blog" When i remove '__name' from 'blog__name' in 'only', it works fine but fetches all the fields.Lemniscus
It's working on 1.10, I'm using it with QuerySet.defer(), thanks!Alvaalvan
FYI: the answer here is appropriate (for "file.url" etc.), but I found values a better fit for me, and changing a bit of code, in order to not pay the cost of making all the Django objects. I think it made a difference for tens of thousands of rows (although I narrowed the columns at the same time :/ ).Intramolecular
I verified that in my case only was 50% slower than values, with exactly the same fields. If you really need performance, consider values.Intramolecular
H
27

You can use annotate() for this.

>>> a = Articles.objects.annotate(blog_name=F('blog__name')).first()
>>> a.title
>>> a.blog_name
Highup answered 11/8, 2016 at 10:37 Comment(3)
This is not the expected answer. The below one solves the purpose.Thursday
Sometimes, this serves the purpose. anyway both are significantGenaro
original question was: "How do i generate a query so that only article fields and blog name is selected?" This is fetching all fields from Blog, right? If so, it wouldn't answer the question properly. Correct?Wan
J
2

It can be done by adding one more field into only section, it's blog (I assume it helps Django to keep relation between objects (Article & Blog):

articles = Articles.objects.select_related(
    'blog',
).only(
    'blog',
    'blog__name',
    'title',
    'create_time',
)

Tested on Django==2.2.19

Jahnke answered 21/5, 2021 at 19:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.