2010-12-10 18 views
18

J'ai un modèle Django avec deux méthodes de gestion personnalisées. Chacun renvoie un sous-ensemble différent des objets du modèle, basé sur une propriété différente de l'objet.Comment puis-je trouver l'intersection de deux sous-ensembles de Django?

class FeatureManager(models.Manager): 

    def without_test_cases(self): 
     return self.get_query_set().annotate(num_test_cases=models.Count('testcase_set')).filter(num_test_cases=0) 

    def standardised(self): 
     return self.get_query_set().annotate(standardised=Count('documentation_set__standard')).filter(standardised__gt=0) 

(Les deux testcase_set et documentation_set se réfèrent à ManyToManyField s sur d'autres modèles.)

Est-il possible d'obtenir un queryset, ou tout simplement une liste d'objets, qui est le intersectiond des QuerySets retourné par chaque méthode de gestion

+0

Qu'est-ce qui vous empêche de combiner les deux fonctions de filtrage de chaque gestionnaire? –

+0

Vous voulez dire: 'Model.objects.managerMethodOne(). ManagerMethodTwo()'? Cela n'a pas semblé fonctionner. Peut-être que je n'ai pas écrit ma méthode de gestionnaire correctement? –

+3

Le filtre fonctionne lui-même. 'Model.objects.filter (this = cela) .filter (that = somethingelse)'. Pourquoi ne faites-vous pas ça? –

Répondre

3

Refactor

class FeatureManager(models.Manager): 

    @staticmethod 
    def _test_cases_eq_0(qs): 
     return qs.annotate(num_test_cases=models.Count('testcase_set')).filter(num_test_cases=0) 

    @staticmethod 
    def _standardized_gt_0(qs): 
     return qs.annotate(standardised=Count('documentation_set__standard')).filter(standardised__gt=0) 

    def without_test_cases(self): 
     return self._test_cases_eq_0(self.get_query_set()) 

    def standardised(self): 
     return self._standardized_gt_0(self.get_query_set()) 

    def intersection(self): 
     return self._test_cases_eq_0(self._standardized_gt_0(self.get_query_set())) 
+0

Ah! Ouais c'est intelligent, je pensais que mon design pourrait être le problème. –

+7

Qu'il répare ou non son problème, il ne répond toujours pas comment trouver l'intersection de deux jeux de requête, pour lesquels c'était le premier lien que google renvoyait à la recherche de "intersection django de jeux de requête" – johannestaas

0

Une façon peut être d'utiliser le module jeux de python et juste faire une intersection:

faire deux ou trois ensembles de requête qui se chevauchent à id = 5:

In [42]: first = Location.objects.filter(id__lt=6) 
In [43]: last = Location.objects.filter(id__gt=4) 

« ensembles d'importation » premier (obtient un avertissement de dépréciation ... euh ... oh bien). Maintenant, construire et les recouper - nous obtenons un élément dans l'ensemble:

In [44]: sets.Set(first).intersection(sets.Set(last)) 
Out[44]: Set([<Location: Location object>]) 

maintenant obtenir l'identifiant des éléments d'intersection pour vérifier qu'il est vraiment 5:

In [48]: [s.id for s in sets.Set(first).intersection(sets.Set(last))] 
Out[48]: [5] 

Cette frappe évidemment deux fois la base de données renvoie tous les éléments de l'ensemble de requêtes - mieux serait d'enchaîner les filtres sur vos gestionnaires et qui devrait être en mesure de le faire en un seul coup de DB et au niveau SQL. Je ne peux pas voir une méthode QuerySet.and/ou (QuerySet).

+1

Ne jamais utiliser 'sets'; c'est obsolète, le 'set' intégré (' frozenset' pour immutable) est meilleur. –

40

Dans la plupart des cas, vous pouvez simplement écrire (en exploitant la « Set » une partie de QuerySet):

intersection = Model.objects.filter(...) & Model.objects.filter(...) 

Ce n'est pas très bien documenté, mais devrait se comporter presque exactement comme l'utilisation et les conditions dans des conditions à la fois requêtes. Code pertinent: https://github.com/django/django/blob/1.8c1/django/db/models/query.py#L203

+0

Ouais, j'ai essayé, mais ça n'a pas l'air de marcher. J'ai juste semblé obtenir un jeu de requête avec tous les objets du jeu de requête plus petit, y compris ceux qui n'étaient pas dans le jeu de requête plus grand. –

+0

Pouvez-vous faire ce qui suit: 'intersection = Model.objects.filter (...) & Model.objects.filter (...)' et puis 'retourner HttpResponse ("% s "% intersection.query)' Ce sera Il est plus facile de comprendre ce que fait Django quand il combine les deux requêtes en une seule. –

+0

c'est bien mais je ne pouvais pas obtenir les lignes uniques. –

2

Si vous voulez le faire en python, pas dans la base de données:

intersection = set(queryset1) & set(queryset2) 

Les problèmes est que si vous utilisez des annotations dans le queriesdue aux annotations ajoutées les objets peuvent sembler différentes ...

0

Si vous utilisez juste vraiment annotation à filtrer en fonction du fait que le nombre est égal à zéro ou non, cette Shoul d travail à la place:

class FeatureManager(models.Manager): 

    def without_test_cases(self): 
     return self.get_query_set().filter(testcase__pk__isnull=True) 

    def standardised(self): 
     return self.get_query_set().filter(documentation_set__standard__isnull=False) 

Puisque vous ne vous souciez plus de l'annotation, les deux requêtes doivent se croiser très facilement.

+0

Ah, voyez, je ne pense pas que la requête pour 'standardized' fonctionne. Cela sélectionne n'importe quelle fonction qui a * une * documentation liée qui * n'est pas * une norme - alors que je veux qu'elle sélectionne n'importe quelle caractéristique qui a * des * documentations * qui * sont * des normes. –

4

Je crois que qs1.filter (pk__in = qs2) devrait fonctionner (habituellement). Il semble fonctionner pour un cas similaire pour moi, il est logique que cela fonctionne, et la requête générée semble saine. (Si l'un de vos jeux de requête utilise values ​​() pour ne pas sélectionner la colonne de la clé primaire ou quelque chose de bizarre, je peux croire que ça casserait, bien que ...)

19

Vous pouvez juste faire quelque chose comme ceci:

intersection = queryset1 & queryset2 

Pour faire une union juste remplacer & par |

+0

Merci, ça marche! mais cela ne fonctionne pas dans un jeu de queues tranché –

4

Comme par Django 1.11, maintenant il est disponible la fonction intersection()

>>> qs1.intersection(qs2, qs3)