Search code examples
djangogeneric-foreign-keygeneric-relations

Django Generic Foreign Key Filtering (difference between v1.5 & v1.6)


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:

  1. 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?


Solution

  • 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")