Opened 6 years ago

Closed 7 months ago

#12885 closed Bug (fixed)

GenericRelation fails to join the related table from a inherited model

Reported by: semenov Owned by: nobody
Component: contrib.contenttypes Version: 1.1
Severity: Normal Keywords:
Cc: carljm 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

A minimalistic crashing example is worth thousands words:

class Car(models.Model):
        content_type = models.ForeignKey(ContentType)
        object_id = models.PositiveIntegerField()
        object = generic.GenericForeignKey()

class CarDriver(models.Model):
        cars = generic.GenericRelation(Car)

class Truck(Car):
        pass

class TruckDriver(models.Model):
        trucks = generic.GenericRelation(Truck)

# Selecting cars for a car drivers works fine

>>> john = CarDriver.objects.create()
>>> Car.objects.filter(cardriver=john)
[]

# Selecting trucks for a truck driver crashes

>>> pete = TruckDriver.objects.create()
>>> Truck.objects.filter(truckdriver=pete)
# OperationalError: (1054, "Unknown column 'T2.object_id' in 'where clause'")

Attachments (1)

12885-test.diff (729 bytes) - added by timgraham 10 months ago.

Download all attachments as: .zip

Change History (17)

comment:1 Changed 6 years ago by carljm

  • Cc carljm added
  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

comment:2 Changed 6 years ago by ramiro

  • Component changed from ORM aggregation to Database layer (models, ORM)
  • Owner set to nobody

Can you exercise the same test case you've created against SVN trunk and report back the results please?

comment:3 Changed 6 years ago by semenov

The SQL query is a bit different in SVN trunk, but still results in the same error ("Unknown column 'T2.object_id' in 'where clause'").

SQL query in Django 1.1.1:

SELECT `tracker_car`.`id`, `tracker_car`.`content_type_id`, `tracker_car`.`object_id`, `tracker_truck`.`car_ptr_id`
FROM `tracker_truck`
INNER JOIN `tracker_truck` T2 ON (`tracker_truck`.`car_ptr_id` = T2.`id`)
INNER JOIN `tracker_car` ON (`tracker_truck`.`car_ptr_id` = `tracker_car`.`id`)
WHERE T2.`object_id` = 1 LIMIT 21

SQL query in Django-SVN r12453:

SELECT `tracker_car`.`id`, `tracker_car`.`content_type_id`, `tracker_car`.`object_id`, `tracker_truck`.`car_ptr_id`
FROM `tracker_truck`
INNER JOIN `tracker_truck` T2 ON (`tracker_truck`.`car_ptr_id` = T2.`car_ptr_id`)
INNER JOIN `tracker_car` ON (`tracker_truck`.`car_ptr_id` = `tracker_car`.`id`)
WHERE T2.`object_id` = 1 LIMIT 21

(By the way, I am also confused by the magic number 21, but that's irrelevant to the topic.)

comment:4 Changed 6 years ago by russellm

  • Triage Stage changed from Unreviewed to Accepted

comment:5 Changed 6 years ago by semenov

Just in case, the expected query is:

SELECT `tracker_car`.`id`, `tracker_car`.`content_type_id`, `tracker_car`.`object_id`, `tracker_truck`.`car_ptr_id`
FROM `tracker_truck`
INNER JOIN `tracker_car` ON (`tracker_truck`.`car_ptr_id` = `tracker_car`.`id`)
WHERE `tracker_car`.`object_id` = 1
  • the redundant JOIN to itself removed along with its alias, and there's no "LIMIT 21".

I will probably look into the related ORM code on the weekend.

comment:6 Changed 6 years ago by carljm

The magic "LIMIT 21" is added because the QuerySet is evaluated via repr. This is a safety feature to prevent careless use of "manage.py shell" from bringing your database to its knees.

comment:7 Changed 5 years ago by lukeplant

  • Type set to Bug

comment:8 Changed 5 years ago by lukeplant

  • Severity set to Normal

comment:9 Changed 5 years ago by anonymous

  • Component changed from Database layer (models, ORM) to contrib.contenttypes

comment:10 Changed 4 years ago by aaugustin

  • UI/UX unset

Change UI/UX from NULL to False.

comment:11 Changed 4 years ago by aaugustin

  • Easy pickings unset

Change Easy pickings from NULL to False.

comment:12 Changed 3 years ago by ramiro

See also #13203.

comment:13 Changed 10 months ago by timgraham

This remains an issue as of 58c7ff39fb265754fb17ab8d7f8a1401b355777b (Django 1.10 dev).

Here's the query for the attached failing test:

SELECT "generic_relations_mineral"."id", "generic_relations_mineral"."name", "generic_relations_mineral"."hardness", "generic_relations_valuablerock"."mineral_ptr_id"
FROM "generic_relations_valuablerock"
INNER JOIN "generic_relations_valuabletaggeditem" ON ("generic_relations_valuablerock"."mineral_ptr_id" = "generic_relations_valuabletaggeditem"."object_id" AND ("generic_relations_valuabletaggeditem"."content_type_id" = 13)) 
INNER JOIN "generic_relations_taggeditem" ON ("generic_relations_valuabletaggeditem"."taggeditem_ptr_id" = "generic_relations_taggeditem"."id")
INNER JOIN "generic_relations_mineral" ON ("generic_relations_valuablerock"."mineral_ptr_id" = "generic_relations_mineral"."id")
WHERE "generic_relations_taggeditem"."tag" = countertop

Traceback:

File "/home/tim/code/django/django/db/backends/sqlite3/base.py", line 323, in execute
    return Database.Cursor.execute(self, query, params)
django.db.utils.OperationalError: no such column: generic_relations_valuabletaggeditem.object_id

Changed 10 months ago by timgraham

comment:14 Changed 9 months ago by akaariai

  • Has patch set

I've created a PR for this (see https://github.com/django/django/pull/5487). Luckily the changes needed here were only to contrib.contenttypes. This is always a good sign, we likely don't have a more fundamental problem. Lets see what CI things of my PR...

comment:15 Changed 9 months ago by timgraham

  • Triage Stage changed from Accepted to Ready for checkin

comment:16 Changed 7 months ago by Tim Graham <timograham@…>

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

In cd0ba805:

Fixed #12885 -- Fixed queries with GenericRelations to multi-table inheritance child models.

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