El título puede resultar un poco confuso. Sería bueno tener un método filter
genérico que se llama cada vez que se recupera el conjunto de consultas y se aplica el filtrado por campos. Sí, puedo anular la propiedad qs
, primero obtener un conjunto de consultas filtrado y luego aplicar capturas previas. Pero _no está claro si esto puede tener algunos efectos secundarios y es seguro acceder a los datos que se pasan al filtro.
Ese es mi caso de uso.
Estoy filtrando un conjunto de consultas por una relación M2M (como en queryset.filter(items__field="foo")
). Sin embargo, también quiero que los elementos precargados se filtren por ese campo.
En otras palabras, mi filtro se ve así:
# In my filterset
def filter_items_by_field(self, queryset, name, value):
queryset = queryset.all().filter(
items__field=value,
)
prefetched_items = Prefetch(
'items',
Item.objects..filter(
field=value,
),
)
return queryset.prefetch_related(prefetched_items)
# In my vieset
def get_queryset(self):
queryset = super().get_queryset()
# This should be applied everytime
prefetched_items = Prefetch(
'items',
Item.objects..filter(
another_field=some_value,
),
)
return queryset.prefetch_related(prefetched_items)
Un solo filtro como este funciona como se esperaba. Sin embargo, es posible que tenga varios filtros filter_items_by_*
+ Necesito aplicar el mismo filtrado para el conjunto de consultas desde la captación previa para todos los filtros. Y múltiples filtros no son un problema, porque son mutuamente excluyentes en mi caso. Pero un filtro + genérico provocará una excepción.
El punto es que puedo aplicar solo una captación previa para la misma búsqueda. Entonces, en mi caso, simplemente obtendré una excepción.
Tengo una solución temporal para este problema: todos los filter_items_by_*
realizan un filtrado obligatorio en la captación previa.
Y en filter_queryset
(después de aplicar el filtrado), si items
no se obtienen previamente (lo que es malo ya que estoy inspeccionando los atributos protegidos de queryset), aplico la captura previa con el filtrado obligatorio. Pero debería haber una mejor manera de lidiar con situaciones como esta.
Espero que mi ejemplo sea lo suficientemente claro.
Hola @ Alexx-G. Creo que esto podría ser posible con cierta coordinación entre el conjunto de filtros y las clases de filtros relevantes. Básicamente, el filtro agregaría una instancia Prefetch
a su conjunto de filtros parent
. FilterSet.qs
simplemente llamaría prefetch_related
cuando sea apropiado.
Dicho esto, ¿cuál es el caso de uso para esto? Yo diría que probablemente haya lugares más apropiados para manejar la captación previa.
Creo que esto podría ser posible con cierta coordinación entre el conjunto de filtros y las clases de filtros relevantes.
Ya resolví mi problema anulando la propiedad qs
, pero nuevamente, no es obvio y no está documentado. En otras palabras, no puedo estar seguro de que algunos cambios internos en la clase base no afecten a mi conjunto de filtros (de acuerdo, he fijado la versión, pero de todos modos ...)
Dicho esto, ¿cuál es el caso de uso para esto? Yo diría que probablemente haya lugares más apropiados para manejar la captación previa.
Creo que describí arriba mi caso de uso. No tiene sentido separar el filtrado y la captación previa, porque la captación previa depende de los filtros. Y si los separo, tendré que duplicar las mismas comprobaciones para los filtros.
En otras palabras, si hubiera una descripción del contrato público de la propiedad qs o un gancho documentado para ello, esto mejoraría la personalización del conjunto de filtros.
Hola @ Alexx-G. Una versión 2.0 todavía es una salida, pero habrá algunos cambios en la API que abordarán específicamente esta cuestión. En breve,
.filter_queryset()
. El atributo .qs
será básicamente responsable de llamar a .filter_queryset()
y almacenar en caché su resultado.self.form.cleaned_data
.¡Muchas gracias por su esfuerzo!
Comentario más útil
Hola @ Alexx-G. Una versión 2.0 todavía es una salida, pero habrá algunos cambios en la API que abordarán específicamente esta cuestión. En breve,
.filter_queryset()
. El atributo.qs
será básicamente responsable de llamar a.filter_queryset()
y almacenar en caché su resultado.self.form.cleaned_data
.