I have the following conceptual models:
class GenericAbstractBase(models.Model):
name = models.CharField(max_length=255)
staff = generic.GenericRelation(
"Staff",
content_type_field="content_type",
object_id_field="object_id",
)
class Meta:
abstract = True
class GenericModelA(GenericAbstractBase):
extra_a = models.CharField(max_length=255)
class GenericModelB(GenericAbstractBase):
extra_b = models.CharField(max_lenth=10)
class Staff(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
active = models.CharField(max_length=10, choices = ACTIVE_CHOICES)
limit = models.Q(app_label='staff', model='genericmodela') | models.Q(app_label='staff', model='genericmodelb')
content_type = models.ForeignKey(ContentType, limit_choices_to=limit)
object_id = models.PositiveIntegerField()
generic_object = generic.GenericForeignKey("content_type", "object_id")
In Django v1.4 & Django v1.5 the following query works fine:
>>> ctype = ContentType.objects.get_for_model(GenericModelA)
>>> Staff.objects.filter(
content_type=ctype,
genericmodela__name__icontains="a"
)
>>> [<Staff: Abbott, Kaylee>, <Staff: Adams, Kay>, ... ]
and the SQL (sqlite) it produces looks like:
SELECT
"staff_staff"."id", "staff_staff"."first_name","staff_staff"."last_name",
"staff_staff"."active","staff_staff"."content_type_id" ,"staff_staff"."object_id"
FROM "staff_staff"
INNER JOIN "staff_staff" T2 ON ("staff_staff"."id" = T2."id")
INNER JOIN "staff_genericmodela" ON (T2."object_id" = "staff_genericmodela"."id")
WHERE (
"staff_genericmodela"."name" LIKE % a % ESCAPE \ '\\\'
AND "staff_staff"."content_type_id" = 11
)
However in Django 1.6 the query fails with a FieldError:
FieldError: Cannot resolve keyword 'genericmodela' into field. Choices are: active, content_type, department, first_name, id, last_name, object_id, position
The following statement in the release notes may be relevant:
Django 1.6 contains many changes to the ORM. These changes fall mostly in three categories:
- Bug fixes (e.g. proper join clauses for generic relations, query combining, join promotion, and join trimming fixes)
My question is, what changed in Django 1.6 that caused this to break? Am I stuck with using extra
or doing this type of filtering in Python?
I've found some interesting information here.
As a workaround you can do something like this:
ctype = ContentType.objects.get_for_model(GenericModelA)
pk_list = Staff.objects.filter(
content_type=ctype
).values_list('object_id', flat=True)
GenericModelA.objects.filter(pk__in=pk_list, name__icontains="a")
It appears that this only worked in Django 1.4 due to undocumented behaviour so I decided to use a CASE
statement in an extra
queries to do the queries I wanted. For example:
cta = ContentType.objects.get_for_model(models.GenericModelA)
ctb = ContentType.objects.get_for_model(models.GenericModelB)
extraq = """
CASE
WHEN content_type_id = {0}
THEN (SELECT extra_a from staff_genericmodela WHERE object_id = staff_genericmodela.id)
WHEN content_type_id = {1}
THEN (SELECT extra_b from staff_genericmodelb WHERE object_id = staff_genericmodelb.id)
END
""".format(cta.pk, ctb.pk)
Staff.objects.extra(select={'genericname': extraq}).extra(where=["genericname LIKE %s", params=["%{0}%".format("foobar")])
This has worked well for me so far and should be easily extendable to other similar cases.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With