2010-06-27 6 views
2

Supposons que j'ai quelques modèles représentant des objets de la vie réelle: "personne", "président", "Chambre"requête Tricky Django GenericRelation

j'ai aussi une "Collection" modèle, ce qui représente une collection d'enregistrements de ces modèles. Chaque modèle peut être un membre de plus que sur la collection - par conséquent, j'ai également créé un modèle "Membership", qui représente un objet est un membre d'une collection. Il est défini comme suit:

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

Je veux être en mesure de créer un QuerySet, qui donne une collection, représente tous ses membres d'un modèle donné. Je sais que je peux le faire par programme, mais je dois dans un QuerySet, qui peut être filtré, commandé etc.

EDIT:

Évidemment, cela peut être fait en utilisant SQL brute:

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

Mais peut-il être représenté en utilisant le langage de requête Django?

Répondre

2

Il semble que j'ai trouvé la solution, en utilisant la méthode QuerySet « s extra:

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]) 

Ce retourne un QuerySet valide d'un modèle spécifique, qui détient tous les documents qui sont membres d'une collection spécifique.

0

Non, ce n'est pas possible. Les Querysets ne peuvent être que d'un seul type de modèle. Vous pouvez donc obtenir un ensemble d'objets Membership et faire référence à la propriété content_object de chaque personne, ce qui vous donnera l'objet associé, mais vous ne pouvez pas obtenir tous les objets associés directement dans un même jeu de requête.

+1

Je veux seulement le queryset de tenir les dossiers d'un modèle ... je pourrais reformuler la question: Puis-je interroger tous les enregistrements du modèle X, de sorte qu'il existe une adhésion qui a le dossier X comme son contenu objet et une collection spécifique dans son champ "collection"? – adamk

1

Je mis en œuvre exactement ce au moyen d'une méthode with_model sur un gestionnaire personnalisé pour le modèle d'adhésion:

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 est mon équivalent à votre modèle Membership. Pour plus de contexte, voir the code in its entirety.

+0

Ceci est une réponse valide, seulement il ya deux problèmes: (1) Il nécessite cette requête supplémentaire & (2) Utiliser pk__in avec de très grands ensembles ne fonctionnera pas toujours - pour les grandes collections (~ 1000 membres), le les requêtes générées sont trop longues. – adamk