Duplicate items when ordering by Generic Relation in Django
When the optional ordering = ...
attribute of a Django Model’s Meta class contains a GenericRelation
from the Content Types framework, there is no way to eliminate duplicate items being returned, even when using .distinct()
(since .order_by(...)
is applied by the ORM only after the SQL SELECT DISTINCT
clause is built).
This problem is current as of Django 1.2 and has no generic solution as far as I know.
Problem
This problem will manifest when two or more different Models instances are attached to instances of the Model with the GenericForeignKey
. In the following example, we have Model X
which has a GenericForeignKey
, and two Models, Y
and Z
, which have GenericRelations
to X
and ordering based on these relations.
Imports
from django.db.models import CharField
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.generic import \
GenericForeignKey, GenericRelation
Model X
|
|
Model Y
|
|
Model Z
|
|
REPL Demonstration
The following sequence of statements demonstrates this problem:
x1 = X.objects.create(name = 'first x')
x2 = X.objects.create(name = 'second x')
x3 = X.objects.create(name = 'third x')
x4 = X.objects.create(name = 'fourth x')
y1 = Y.objects.create(name = 'first y')
y2 = Y.objects.create(name = 'second y')
z1 = Z.objects.create(name = 'first z')
z2 = Z.objects.create(name = 'second z')
x1.content = y1
x1.save()
x2.content = z1
x2.save()
x3.content = y2
x3.save()
x4.content = z2
x4.save()
X.objects.all()
[<X: first x>, <X: fourth x>, <X: second x>, <X: third x>]
Y.objects.all()
[<Y: first y>, <Y: first y>, <Y: second y>]
Notice that ‘first y’ appears twice. The SQL COUNT statement will not have any ordering applied, and so returns the correct value:
Y.objects.count()
2
However, when ordering is applied, the duplicate instance is present:
len(Y.objects.all())
3