Opened 4 years ago

Closed 4 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 4 years ago by aaugustin

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Triage Stage changed from Unreviewed to Accepted

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 4 years ago by desh

  • Owner changed from nobody to desh
  • Status changed from new to assigned
  • UI/UX unset

comment:3 Changed 4 years ago by desh

  • 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 4 years ago by desh

  • Owner desh deleted
  • Status changed from assigned to new

comment:5 Changed 4 months ago by timgraham

  • Resolution set to fixed
  • Status changed from new to closed

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