Wie filtern Sie in Django Rest Framework einen Serializer, wenn er in einem anderen Serializer verschachtelt ist?
Meine Filter werden in den DRF-Ansichtssätzen festgelegt. Wenn Sie jedoch einen Serializer aus einem anderen Serializer heraus aufrufen, wird das Viewset des verschachtelten Serializers nie aufgerufen, sodass die verschachtelten Ergebnisse ungefiltert angezeigt werden.
Ich habe versucht, einen Filter für das ursprüngliche Ansichtsset hinzuzufügen, aber es scheint die verschachtelten Ergebnisse nicht zu filtern, da die verschachtelten Ergebnisse als separate vorab abgerufene Abfrage aufgerufen werden. (Der verschachtelte Serializer ist eine umgekehrte Suche.)
Ist es möglich, eine get_queryset () - Überschreibung im verschachtelten Serializer selbst hinzuzufügen (aus dem Viewset zu verschieben), um den Filter dort hinzuzufügen? Ich habe das auch ohne Glück versucht.
Dies ist, was ich versucht habe, aber es scheint nicht einmal genannt zu werden:
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
get_queryset
ist eine Klasse aufModelViewSet
, nicht auf dem Serializer, weshalb es nicht aufgerufen wirdAntworten:
Sie können den ListSerializer in Unterklassen unterteilen und die
to_representation
Methode überschreiben .Standardmäßig
to_representation
ruft die Methodedata.all()
das verschachtelte Abfrageset auf. Sie müssen also effektiv machen,data = data.filter(**your_filters)
bevor die Methode aufgerufen wird. Anschließend müssen Sie Ihren untergeordneten ListSerializer als list_serializer_class zum Meta des verschachtelten Serializers hinzufügen.to_representation
und ruft dann super auflist_serializer_class
zum verschachtelten Serializer hinzuHier ist der relevante Code für Ihr Beispiel.
class FilteredListSerializer(serializers.ListSerializer): def to_representation(self, data): data = data.filter(user=self.context['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
quelle
QuestionnaireSerializer
Variable von an den ListSerializer übergeben? Zur Annäherung muss ich sowohl nach der ID der Edition als auch nach der ID des Fragebogens filtern.'FilteredListSerializer' object has no attribute 'request'
jemand anderes das gleiche?Testete viele Lösungen von SO und anderen Orten.
Es wurde nur eine funktionierende Lösung für Django 2.0 + DRF 3.7.7 gefunden.
Definieren Sie eine Methode in einem Modell mit verschachtelter Klasse. Stellen Sie einen Filter her, der Ihren Anforderungen entspricht.
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',)
Achten Sie darauf
source="current_epg"
und Sie werden den Punkt bekommen.quelle
Während alle oben genannten Antworten funktionieren, finde ich die Verwendung von Djangos
Prefetch
Objekt der einfachste Weg von allen.Restaurant
Angenommen, ein Objekt hat vieleMenuItem
s, von denen einige sindis_remove == True
, und Sie möchten nur diejenigen, die nicht entfernt werden.In
RestaurantViewSet
, so etwas wiefrom 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') )
In
RestaurantSerializer
, so etwas wieclass RestaurantSerializer(serializers.ModelSerializer): menu_items = MenuItemSerializer(source='filtered_menu_items', many=True, read_only=True)
quelle
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', )
Im Hinblick auf:
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)
quelle
Ich finde es einfacher und unkomplizierter, ein
SerializerMethodField
Feld für den Serializer zu verwenden, das Sie filtern möchten.Sie würden also so etwas tun.
class CarTypesSerializer(serializers.ModelSerializer): class Meta: model = CarType fields = '__all__' class CarSerializer(serializers.ModelSerializer): car_types = serializers.SerializerMethodField() class Meta: model = Car fields = '__all__' def get_car_types(self, instance): # Filter using the Car model instance and the CarType's related_name # (which in this case defaults to car_types_set) car_types_instances = instance.car_types_set.filter(brand="Toyota") return CarTypesSerializer(car_types_instances, many=True).data
Dies erspart Ihnen das Erstellen vieler Überschreibungen von,
serializers.ListSerializer
wenn Sie unterschiedliche Filterkriterien für verschiedene Serialisierer benötigen.Es hat auch den zusätzlichen Vorteil, genau zu sehen, was der Filter im Serializer tut, anstatt in eine Unterklassendefinition einzutauchen.
Der Nachteil ist natürlich, wenn Sie einen Serializer mit vielen verschachtelten Objekten haben, die alle auf irgendeine Weise gefiltert werden müssen. Dies kann dazu führen, dass der Serializer-Code stark zunimmt. Es liegt an Ihnen, wie Sie filtern möchten.
Hoffe das hilft!
quelle