QuerySet values() or values_list() with some RelatedObjectDoesNotExist records returns only those records with valid related intances

60 views
Skip to first unread message

Steve W

unread,
Nov 18, 2015, 7:31:44 PM11/18/15
to Django users
Condition:  A model with a ForeignKey or OneToOne relationship to another model, where the related model instance may be null.

Desired Behavior: queryset.values() or values_list() returns all records from queryset, not just those with valid related model instance.

Observed Behavior: queryset.values() or values_list() returns records for only those members of the queryset where the related model instance exists 

Django 1.8.6 Postgres 9.4.1

#!
class Release(Model):
   release = TextField()

class Case(Model):
    reference = TextField()
    release = OneToOneField(Release, blank=True, null=True,
..........related_name='cases')


from models import Case
cases = Case.objects.all()
cases.count()
228

fieldnames = ['reference']
records = cases.values(*fieldnames)
records.count()
228
#So far, so good

fieldnames = ('reference','release__release')
records = cases.values(*fieldnames)
records.count()
63

#No, not good.  Just lost a whole bunch of records, why?
cases = Case.objects.filter(release__isnull=False)
cases.count()
63
records = cases.values(*fieldnames)
records.count()
63
#Clearly this is because there is no related release for each missing case.

#As we see here:
case_without_release = Case.objects.filter(release__isnull=True)[0]
case.release
ReleatedObjectDoesNotExist: Case has no release

#How then to make queryset.values() or queryset.values_list() behave in the expected manner, by failing gracefully when it finds a member of a queryset to have a RelatedObjectDoesNotExist exception, and thus writing out the record to values() or values_list() the exception notwithstanding.


Thank you,
-Steve 

Steve W

unread,
Nov 18, 2015, 8:39:43 PM11/18/15
to Django users
Solved or Work-Around:

#Include a 'select_related()' invocation in the queryset chain, against the related model(s):

cases = Case.objects.select_related('release').all()
cases.count()
228

fieldnames = ('reference','release__release')
records = cases.values(*fieldnames)
records.count()
228

#OK, so that is the desired result. I implemented it in my (real) code and it works.

#So select_related() appears to be safe with respect to RelatedObjectDoesNotExist, returning None as desired, unlike queryset.values() which by default seems to remove the record.

#Perhaps a modification of queryset.values() to include the select_related() invocation on each related model would return all values as expected.

#Somewhere in the django.db.models.query.ValuesQuerySet I would imagine.

thanks.


 

On Thursday, November 19, 2015 at 12:31:44 AM UTC, Steve W wrote:
Condition:  A model with a ForeignKey or OneToOne relationship to another model...
 
...How then to make queryset.values() or queryset.values_list() behave in the expected manner, by failing gracefully when it finds a member of a queryset to have a RelatedObjectDoesNotExist exception, and thus writing out the record to values() or values_list() the exception notwithstanding.


Reply all
Reply to author
Forward
0 new messages