52

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?

anonDuck
  • 1,337
  • 1
  • 9
  • 15
  • 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')` – Anentropic Feb 20 '16 at 14:07
  • 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')))` – Todor Feb 20 '16 at 14:14
  • 3
    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? – anonDuck Feb 20 '16 at 14:48
  • 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. – anonDuck Apr 16 '16 at 18:00

3 Answers3

75

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')
T. Opletal
  • 2,124
  • 1
  • 15
  • 23
  • 1
    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? – anonDuck Feb 21 '16 at 20:56
  • @RA123 I edited my answer. Tried it and this way it works for me – T. Opletal Feb 22 '16 at 13:37
  • 1
    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. – anonDuck Feb 23 '16 at 15:15
  • It's working on 1.10, I'm using it with `QuerySet.defer()`, thanks! – lechup Mar 07 '17 at 12:26
  • 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 :/ ). – dfrankow Feb 16 '23 at 19:23
  • I verified that in my case `only` was 50% slower than `values`, with exactly the same fields. If you really need performance, consider `values`. – dfrankow Feb 16 '23 at 19:39
26

You can use annotate() for this.

>>> a = Articles.objects.annotate(blog_name=F('blog__name')).first()
>>> a.title
>>> a.blog_name
brki
  • 2,672
  • 1
  • 17
  • 12
0

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

tarasinf
  • 796
  • 5
  • 17