Opened 6 years ago

Closed 6 years ago

#28988 closed Bug (fixed)

Multi-table inheritance breaks GenericRelation querying

Reported by: robwa Owned by: Tim Graham <timograham@…>
Component: contrib.contenttypes Version: 1.11
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Minimal example to reproduce. Create two models:

class Generic(models.Model):                                                                   
    content_type = models.ForeignKey('contenttypes.ContentType')                               
    object_id = models.PositiveIntegerField()                                                  
    obj = GenericForeignKey()                                                                  
                                                                                               
class Vehicle(models.Model):                                                                   
    associations = GenericRelation('Generic', related_query_name='vehicles')

Create a Vehicle and a Generic object pointing to the Vehicle. The following query returns the expected result:

In [2]: Generic.objects.filter(vehicles__id=1)
Out[2]: <QuerySet [<Generic: Generic object>]>

Add a third model. Don't change any data. Now the query returns an empty query set:

class Bike(Vehicle):                                                                          
    pass
In [2]: Generic.objects.filter(vehicles__id=1)
Out[2]: <QuerySet []>

Change History (13)

comment:1 by Tim Graham, 6 years ago

Component: Uncategorizedcontrib.contenttypes
Triage Stage: UnreviewedAccepted
Type: UncategorizedBug

comment:2 by Paulo, 6 years ago

Owner: changed from nobody to Paulo
Status: newassigned

comment:3 by robwa, 6 years ago

I'm not sure if this is helpful, but I found

In [44]: Generic._meta.get_field('vehicles').field.model
Out[44]: foo.models.Bike

comment:4 by robwa, 6 years ago

Now I can see why this happens:

  • GenericRelation fields are created as private fields.
  • Private fields are explicitly inherited by child classes (db.models.base).
  • When constructing the relation tree, private fields are included (db.models.options).
  • When constructing the fields map, relations to private fields of child classes simply overwrite the relations to the parents (db.models.options).

Where is the point to fix this?

comment:5 by robwa, 6 years ago

Maybe the reverse relationship should be removed from GenericRelations when copying them to child classes.

comment:6 by Paulo, 6 years ago

I think the behavior should be consistent with how a normal ForeignKey field behaves in multi-table inheritance.
In the following scenario:

class Car(models.Model):
    owner = models.ForeignKey(Owner, related_name='cars')
    some_id = models.CharField(max_length=100)

class Car2(Car):
    pass

Running

Owner.objects.filter(cars__isnull=True)

Will output:

SELECT "paulotest_owner"."id",
       "paulotest_owner"."nick"
FROM "paulotest_owner"
LEFT OUTER JOIN "paulotest_car" ON ("paulotest_owner"."id" = "paulotest_car"."owner_id")
WHERE "paulotest_car"."id" IS NULL

So we should prevent the reverse relationship from being set.

@Robert, if you would like to work on this, I can un-assign it, I'm planning to tackle it within the next two weeks.

comment:7 by robwa, 6 years ago

Owner: changed from Paulo to robwa

@Paulo, I will try to work on this. If I come up with a patch, I'd like to discuss it with you, as I'm not very familiar with the django model internals.

comment:8 by robwa, 6 years ago

Has patch: set

Added a pull request.

comment:9 by Carlton Gibson, 6 years ago

Patch needs improvement: set

comment:10 by robwa, 6 years ago

Owner: robwa removed
Status: assignednew

comment:11 by robwa, 6 years ago

Updated the PR.

comment:12 by Carlton Gibson, 6 years ago

Patch needs improvement: unset
Triage Stage: AcceptedReady for checkin

Updated changes look good.

comment:13 by Tim Graham <timograham@…>, 6 years ago

Owner: set to Tim Graham <timograham@…>
Resolution: fixed
Status: newclosed

In 4ab027b:

Fixed #28988 -- Fixed queries when a GenericRelation is used with multi-table inheritance.

Note: See TracTickets for help on using tickets.
Back to Top