Tricky Django GenericRelation query
Suppose I have a few models representing real life objects: "Person", "Chair", "Room"
I also have a "Collection" model, which represents some collection of records of these models.
Each model can be a member of more than on collection - therefore, I have also created a "Membership" model, which represents an object is a member of a collection. It is defined as follows:
c开发者_StackOverflow社区lass Membership(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
collection = models.ForeignKey('Collection', related_name="members")
I want to be able to create a QuerySet, which given a collection, represents all its members of a given model. I know I can do it programmatically, but I need it in a QuerySet, which can be filtered, ordered etc.
EDIT:
Obviously this can be done using raw SQL:
SELECT * FROM
( modelx INNER JOIN membership ON modelx.id = membership.object_id)
WHERE
( membership.collection_id=<my-collection-id> AND
membership.content_type_id=<modelx-type-id> )
But can it be represented using the Django query language?
It seems I have found the solution, by using QuerySet
's extra
method:
def members_of_model(collection,cls):
cls_type = ContentType.objects.get_for_model(cls)
cm_tablename = CollectionMembership._meta.db_table
cls_tablename = cls._meta.db_table
return cls.objects.all().extra(tables=[cm_tablename],
where=[ '%s.content_type_id=%%s' % cm_tablename,
'%s.collection_id=%%s' % cm_tablename,
'%s.object_id=%s.id' % (cm_tablename, cls_tablename) ],
params=[cls_type.id,collection.id] )
This returns a valid QuerySet of a specific model, which holds all records which are members of a specific collection.
I implemented exactly this by way of a with_model
method on a custom manager for the membership model:
class CollectionMemberManager(models.Manager):
use_for_related_fields = True
def with_model(self, model):
return model._default_manager.filter(pk__in=self.filter(member_content_type=ContentType.objects.get_for_model(model)).values_list('member_object_id', flat=True))
CollectionMember
is my equivalent to your Membership
model. For more context, see the code in its entirety.
No, this isn't possible. Querysets can only ever be of one single model type. So you can get a queryset of Membership
objects and refer to each one's content_object
property, which will give you the related object, but you can't get all the related objects directly in one queryset.
精彩评论