select_related queryset query example cual create python django django-models

python - example - ¿Cómo puedo encontrar la intersección de dos querysets de Django?



django queryset to list (8)

Creo que qs1.filter (pk__in = qs2) debería funcionar (generalmente). Parece funcionar para un caso similar para mí, tiene sentido que funcionaría, y la consulta generada parece sensata. (Si uno de sus querysets usa valores () para no seleccionar la columna de clave principal o algo extraño, puedo creer que se rompería, sin embargo ...)

Tengo un modelo Django con dos métodos de administrador personalizados. Cada uno devuelve un subconjunto diferente de los objetos del modelo, basado en una propiedad diferente del objeto.

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)

(Tanto testcase_set como documentation_set refieren a ManyToManyField s en otros modelos).

¿Hay alguna forma de obtener un conjunto de consultas, o simplemente una lista de objetos, esa es la intersección de los conjuntos de consultas devueltos por cada método de administrador?


En la mayoría de los casos, solo puede escribir (explotando la parte "Establecer" de QuerySet):

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

Esto no está muy bien documentado, pero debería comportarse casi exactamente como usar las condiciones AND en las condiciones de ambas consultas. Código relevante: https://github.com/django/django/blob/1.8c1/django/db/models/query.py#L203


Puedes hacer algo como esto:

intersection = queryset1 & queryset2

Para hacer un sindicato simplemente reemplazar & por |


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


Según Django 1.11, ahora está disponible la función intersection()

>>> qs1.intersection(qs2, qs3)


Si quieres hacerlo en python, no en la base de datos:

intersection = set(queryset1) & set(queryset2)

El problema es que si usa diferentes anotaciones en las consultas debido a las anotaciones agregadas, los objetos podrían verse diferentes ...


Si realmente solo estás usando la anotación para filtrar en función de si el recuento es cero o no, entonces esto debería funcionar en su lugar:

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)

Ya que ya no te preocupas por la anotación, las dos consultas deberían cruzarse muy bien.


Una forma puede ser usar el módulo de conjuntos de python y simplemente hacer una intersección:

haga un par de conjuntos de consultas que se superpongan en id = 5:

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

"importar conjuntos" primero (recibe una advertencia de desaprobación ... ummm ... oh bueno) Ahora constrúyelos e interséctelos, obtenemos un elemento en el conjunto:

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

Ahora obtenga el id de los elementos de la intersección para verificar que realmente es 5:

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

Obviamente, esto afecta a la base de datos dos veces y devuelve todos los elementos del conjunto de consultas; una mejor manera sería encadenar los filtros a sus administradores y eso debería poder hacerlo en un solo hit de base de datos y en el nivel SQL. No puedo ver un método QuerySet.and / or (QuerySet).