Opened 5 years ago

Closed 15 months ago

#16135 closed Bug (fixed)

Model 'Child' is inherited from 'Parent', expecting to get only Child results with Parent.objects.filter(child__isnull=False), but does not work.

Reported by: Robin Owned by:
Component: Database layer (models, ORM) Version: 1.3
Severity: Normal Keywords:
Cc: nikolay@… Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


Example model:

   class Parent(models.Model): 
        name = models.CharField(max_length='20',blank=True) 
    class Child(Parent): 

I want to get the result of Child.objects.all() but I do not want to
use Child.
I want to use Parent instead, which I tried to do with:

    Parent.objects.filter(child__isnull=False) # not work, but should

Which doesn't work and gives the result of Product.objects.all() instead.

However If I do the following, it works:

    Parent.objects.exclude(child__isnull=True) # work

If I insist on using:

    Parent.objects.filter(child__isnull=False) # will work if do below

Then it will work if I use the OneToOneField for Child.

    class Child(models.Model): 
        parent = models.OneToOneField(Parent) 

I expect the query to also work with Model Inheritance as well.

Also discussed in

Change History (5)

comment:1 Changed 5 years ago by Aymeric Augustin

Needs documentation: unset
Needs tests: unset
Patch needs improvement: unset
Triage Stage: UnreviewedAccepted

I'm accepting the bug for two reasons:

  • Parent.objects.filter(child__isnull=False) and Parent.objects.exclude(child__isnull=True) should always give the same results.
  • Model inheritance and OneToOneField should give the same results — at least that's what the docs say:

    The inheritance relationship introduces links between the child model and each of its parents (via an automatically-created OneToOneField).

It sounds like the wrong kind of join is used.

comment:2 Changed 5 years ago by Nikolay Zakharov

Owner: changed from nobody to Nikolay Zakharov
Status: newassigned
UI/UX: unset

comment:3 Changed 5 years ago by Nikolay Zakharov

Cc: nikolay@… added

The problem is in trim_joins() method of the Query class. This method just trims inner join because the condition is "pk of Child is not null", "pk of Child" is actually a foreign key to parent so trimmer reduces join chain and we finally gets "pk of Parent is not null".

We can avoid it by skipping trimming for the isnull lookup. We also can avoid it by not trimming at all. I think that this problem need better recognition and thinkwork, as it is not only about model inheritance. This behaviour is also reproducible if one has ForeignKey(SomeModel, primary_key=True).

comment:4 Changed 5 years ago by Nikolay Zakharov

Owner: Nikolay Zakharov deleted
Status: assignednew

comment:5 Changed 15 months ago by Tim Graham

Resolution: fixed
Status: newclosed

Fixed by #15316 / 2e56066a5b93b528fbce37285bac591b44bc6ed7 which is in Django 1.4+.

The test I used to bisect is:

self.assertEqual(Parent.objects.filter(child__isnull=False).count(), 0)
self.assertEqual(Parent.objects.filter(child__isnull=True).count(), 1)
Note: See TracTickets for help on using tickets.
Back to Top