Opened 7 years ago
Closed 7 years ago
#28988 closed Bug (fixed)
Multi-table inheritance breaks GenericRelation querying
Reported by: | robwa | Owned by: | |
---|---|---|---|
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 , 7 years ago
Component: | Uncategorized → contrib.contenttypes |
---|---|
Triage Stage: | Unreviewed → Accepted |
Type: | Uncategorized → Bug |
comment:2 by , 7 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:3 by , 7 years ago
comment:4 by , 7 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 , 7 years ago
Maybe the reverse relationship should be removed from GenericRelation
s when copying them to child classes.
comment:6 by , 7 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 , 7 years ago
Owner: | changed from | to
---|
@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:9 by , 7 years ago
Patch needs improvement: | set |
---|
comment:10 by , 7 years ago
Owner: | removed |
---|---|
Status: | assigned → new |
comment:12 by , 7 years ago
Patch needs improvement: | unset |
---|---|
Triage Stage: | Accepted → Ready for checkin |
Updated changes look good.
I'm not sure if this is helpful, but I found