开发者

Tricky Django GenericRelation query

开发者 https://www.devze.com 2023-01-04 17:37 出处:网络
Suppose I have a fewmodels representing real life objects: \"Person\", \"Chair\", \"Room\" I also have a \"Collection\" model, which represents some collection of records of these models.

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消