fields - django rest framework response
¿Cómo se filtra un serializador anidado en Django Rest Framework? (4)
En Django Rest Framework, ¿cómo filtra un serializador cuando está anidado en otro serializador?
Mis filtros se imponen en las vistas DRF, pero cuando llama a un serializador desde otro serializador, la vista del serializador anidado nunca se llama, por lo que los resultados anidados aparecen sin filtrar.
Intenté agregar un filtro en la vista de origen, pero no parece filtrar los resultados anidados porque los resultados anidados se llaman como una consulta pretrapada separada. (El serializador anidado es una búsqueda inversa, ya ves).
¿Es posible agregar una anulación get_queryset () en el serializador anidado (moviéndolo fuera del conjunto de visualización), para agregar el filtro allí? También lo intenté sin suerte.
Esto es lo que probé, pero parece que ni siquiera se llama:
class QuestionnaireSerializer(serializers.ModelSerializer):
edition = EditionSerializer(read_only=True)
company = serializers.StringRelatedField(read_only=True)
class Meta:
model = Questionnaire
def get_queryset(self):
query = super(QuestionnaireSerializer, self).get_queryset(instance)
if not self.request.user.is_staff:
query = query.filter(user=self.request.user, edition__hide=False)
return query
Cuando se crea una instancia de un serializador y se pasa many = True, se creará una instancia de ListSerializer. La clase de serializador se convierte en un elemento secundario del ListSerializer primario
Este método toma el objetivo del campo como argumento de valor y debe devolver la representación que se debe usar para serializar el objetivo. El argumento del valor normalmente será una instancia de modelo.
A continuación se muestra el ejemplo del serializador anidado
class UserSerializer(serializers.ModelSerializer):
""" Here many=True is passed, So a ListSerializer instance will be
created"""
system = SystemSerializer(many=True, read_only=True)
class Meta:
model = UserProfile
fields = (''system'', ''name'')
class FilteredListSerializer(serializers.ListSerializer):
"""Serializer to filter the active system, which is a boolen field in
System Model. The value argument to to_representation() method is
the model instance"""
def to_representation(self, data):
data = data.filter(system_active=True)
return super(FilteredListSerializer, self).to_representation(data)
class SystemSerializer(serializers.ModelSerializer):
mac_id = serializers.CharField(source=''id'')
system_name = serializers.CharField(source=''name'')
serial_number = serializers.CharField(source=''serial'')
class Meta:
model = System
list_serializer_class = FilteredListSerializer
fields = (
''mac_id'', ''serial_number'', ''system_name'', ''system_active'',
)
En vista:
class SystemView(viewsets.GenericViewSet, viewsets.ViewSet):
def retrieve(self, request, email=None):
data = get_object_or_404(UserProfile.objects.all(), email=email)
serializer = UserSerializer(data)
return Response(serializer.data)
Probado muchas soluciones de SO y otros lugares.
Encontró una sola solución de trabajo para Django 2.0 + DRF 3.7.7.
Defina un método en el modelo que tenga una clase anidada. Crea un filtro que se ajuste a tus necesidades.
class Channel(models.Model):
name = models.CharField(max_length=40)
number = models.IntegerField(unique=True)
active = models.BooleanField(default=True)
def current_epg(self):
return Epg.objects.filter(channel=self, end__gt=datetime.now()).order_by("end")[:6]
class Epg(models.Model):
start = models.DateTimeField()
end = models.DateTimeField(db_index=True)
title = models.CharField(max_length=300)
description = models.CharField(max_length=800)
channel = models.ForeignKey(Channel, related_name=''onair'', on_delete=models.CASCADE)
.
class EpgSerializer(serializers.ModelSerializer):
class Meta:
model = Epg
fields = (''channel'', ''start'', ''end'', ''title'', ''description'',)
class ChannelSerializer(serializers.ModelSerializer):
onair = EpgSerializer(many=True, read_only=True, source="current_epg")
class Meta:
model = Channel
fields = (''number'', ''name'', ''onair'',)
Presta atención a
source="current_epg"
y obtendrás el punto.
Puede subclasificar el
ListSerializer
y sobrescribir el método
to_representation
.
Por defecto, el método
to_representation
llama a
data.all()
en el conjunto de consultas anidado.
Por lo tanto, efectivamente debe hacer
data = data.filter(**your_filters)
antes de que se llame al método.
Luego debe agregar su ListSerializer subclasificado como list_serializer_class en el meta del serializador anidado.
-
subclase ListSerializer, sobrescribiendo
to_representation
y luego llamando super -
agregue ListSerializer subclases como meta
list_serializer_class
en el serializador anidado
Aquí está el código relevante para su muestra.
class FilteredListSerializer(serializers.ListSerializer):
def to_representation(self, data):
data = data.filter(user=self.request.user, edition__hide=False)
return super(FilteredListSerializer, self).to_representation(data)
class EditionSerializer(serializers.ModelSerializer):
class Meta:
list_serializer_class = FilteredListSerializer
model = Edition
class QuestionnaireSerializer(serializers.ModelSerializer):
edition = EditionSerializer(read_only=True)
company = serializers.StringRelatedField(read_only=True)
class Meta:
model = Questionnaire
Si bien todas las respuestas anteriores funcionan, creo que el uso del objeto
Prefetch
de Django es la forma más fácil de todas.
Digamos que un obj de
Restaurant
tiene muchos
MenuItem
s, algunos de los cuales son
is_remove == True
, y solo desea aquellos que no se eliminan.
En
RestaurantViewSet
, haga algo como
from django.db.models import Prefetch queryset = Restaurant.objects.prefetch_related( Prefetch(''menu_items'', queryset=MenuItem.objects.filter(is_removed=False), to_attr=''filtered_menu_items'') )
En
RestaurantSerializer
, haga algo como
class RestaurantSerializer(serializers.ModelSerializer): menu_items = MenuItemSerializer(source=''filtered_menu_items'', many=True, read_only=True)