django queryset union not working with only

Viewed 273

I have 2 querysets and I am applying union on them but the strange thing is when I use only, it is selecting all fields from DB, and when I use values, it is selecting only given fields.

This is selecting all fields from the Physician table

doctors_models.Physician.objects.filter(
    appointments__member=self.context['member']
).union(doctors_models.Physician.objects.filter(
    appointments__booked_by=self.context['member']
)).only('id', 'name_prefix', 'first_name', 'middle_name', 'last_name', 'name_suffix')

BUT This is selecting only specified fields from the Physician table

doctors_models.Physician.objects.filter(
    appointments__member=self.context['member']
).union(doctors_models.Physician.objects.filter(
    appointments__booked_by=self.context['member']
)).values('id', 'name_prefix', 'first_name', 'middle_name', 'last_name', 'name_suffix')

However, this is also selecting only specified fields but why should I define fields 2 times.

doctors_models.Physician.objects.filter(
    appointments__member=self.context['member']
).only('id', 'name_prefix', 'first_name', 'middle_name', 'last_name', 'name_suffix').union(
    doctors_models.Physician.objects.filter(
        appointments__booked_by=self.context['member']
).only('id', 'name_prefix', 'first_name', 'middle_name', 'last_name', 'name_suffix'))
1 Answers

However, this is also selecting only specified fields but why should I define fields 2 times?

Because the number of columns and their types of both SELECT statements should match, as is specified in the documentation on .union(…):

Passing different models works as long as the SELECT list is the same in all QuerySets (at least the types, the names don’t matter as long as the types are in the same order). In such cases, you must use the column names from the first QuerySet in QuerySet methods applied to the resulting QuerySet.

That being said, please don't use .union(…) in the first place for this. You can merge the two conditions by wrapping these in Q objects and combining these with an "or":

from django.db.models import Q

doctors_models.Physician.objects.filter(
    Q(appointments__member=self.context['member']) |
    Q(appointments__booked_by=self.context['member'])
).only('id', 'name_prefix', 'first_name', 'middle_name', 'last_name', 'name_suffix')

Furthermore using .only(…) [Django-doc] or .defer(…) [Django-doc] is only advisable for columns that contain a large amount of data. For "small" columns, it will often not have a significant impact, and in case you later need these fields, it can even result in N+1 problems.

Related