如何过滤Django Rest框架中的嵌套序列化器?

时间:2021-08-16 19:37:54

In Django Rest Framework, how do you filter a serializer when it's nested in another serializer?

在Django Rest框架中,当在另一个序列化器中嵌套时,如何过滤序列化器?

My filters are imposed in the DRF viewsets, but when you call a serializer from inside another serializer, the viewset of the nested serializer never gets called, so the nested results appear unfiltered.

我的过滤器是在DRF视图集中设置的,但是当您从另一个序列化器内部调用序列化器时,嵌套序列化器的viewset将不会被调用,因此嵌套的结果将不会被过滤。

I have tried adding a filter on originating viewset, but it doesn't seem to filter the nested results because the nested results get called as a separate pre-fretched query. (The nested serializer is a reverse lookup, you see.)

我尝试过在原始viewset上添加一个过滤器,但是它似乎没有过滤嵌套的结果,因为嵌套的结果被作为一个单独的预刷新查询调用。(嵌套序列化器是反向查找。)

Is it possible to add a get_queryset() override in the nested serializer itself (moving it out of the viewset), to add the filter there? I've tried that, too, with no luck.

是否有可能在嵌套序列化器本身(将其移出viewset)中添加get_queryset()覆盖,以添加过滤器?我也试过了,运气不好。

This is what I tried, but it doesn't even seem to get called:

这就是我所尝试过的,但它甚至没有被叫做:

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

Any help appreciated

任何帮助表示赞赏

John

约翰

3 个解决方案

#1


47  

You can subclass the ListSerializer and overwrite the to_representation method.

您可以子类化ListSerializer并覆盖to_representation方法。

By default the to_representation method calls data.all() on the nested queryset. So you effectively need to make data = data.filter(**your_filters) before the method is called. Then you need to add your subclassed ListSerializer as the list_serializer_class on the meta of the nested serializer.

默认情况下,to_representation方法调用嵌套的queryset上的data.all()。因此,在调用该方法之前,您需要有效地使data = data.filter(* your_filter)。然后,您需要在嵌套序列化器的元数据上添加您的子类ListSerializer作为list_serializer_class。

  1. subclass ListSerializer, overwriting to_representation and then calling super
  2. 子类ListSerializer,覆盖to_representation,然后调用super
  3. add subclassed ListSerializer as the meta list_serializer_class on the nested Serializer
  4. 添加子类ListSerializer作为嵌套序列化器上的meta list_serializer_class

Here is the relevant code for your sample.

这是您的示例的相关代码。

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

#2


4  

Tested many solutions from SO and other places.

测试了来自SO和其他地方的许多解决方案。

Found only one working solution for Django 2.0 + DRF 3.7.7.

为Django 2.0 + DRF 3.7.7找到了一个有效的解决方案。

Define a method in model which has nested class. Craft a filter that will fit your needs.

在具有嵌套类的模型中定义一个方法。制作一个适合你需要的过滤器。

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',)

Pay attention to source="current_epg" and you'll get the point.

注意source="current_epg",你就会明白了。

#3


0  

When a serializer is instantiated and many=True is passed, a ListSerializer instance will be created. The serializer class then becomes a child of the parent ListSerializer

当实例化一个序列化器并传递了许多=True时,将创建一个ListSerializer实例。然后,序列化器类成为父ListSerializer的子类

This method takes the target of the field as the value argument, and should return the representation that should be used to serialize the target. The value argument will typically be a model instance.

该方法以字段的目标作为值参数,并应该返回应该用于序列化目标的表示。值参数通常是一个模型实例。

Below is the example of the nested serializer

下面是嵌套序列化器的示例

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', 
        )

In view:

在视图:

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)

#1


47  

You can subclass the ListSerializer and overwrite the to_representation method.

您可以子类化ListSerializer并覆盖to_representation方法。

By default the to_representation method calls data.all() on the nested queryset. So you effectively need to make data = data.filter(**your_filters) before the method is called. Then you need to add your subclassed ListSerializer as the list_serializer_class on the meta of the nested serializer.

默认情况下,to_representation方法调用嵌套的queryset上的data.all()。因此,在调用该方法之前,您需要有效地使data = data.filter(* your_filter)。然后,您需要在嵌套序列化器的元数据上添加您的子类ListSerializer作为list_serializer_class。

  1. subclass ListSerializer, overwriting to_representation and then calling super
  2. 子类ListSerializer,覆盖to_representation,然后调用super
  3. add subclassed ListSerializer as the meta list_serializer_class on the nested Serializer
  4. 添加子类ListSerializer作为嵌套序列化器上的meta list_serializer_class

Here is the relevant code for your sample.

这是您的示例的相关代码。

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

#2


4  

Tested many solutions from SO and other places.

测试了来自SO和其他地方的许多解决方案。

Found only one working solution for Django 2.0 + DRF 3.7.7.

为Django 2.0 + DRF 3.7.7找到了一个有效的解决方案。

Define a method in model which has nested class. Craft a filter that will fit your needs.

在具有嵌套类的模型中定义一个方法。制作一个适合你需要的过滤器。

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',)

Pay attention to source="current_epg" and you'll get the point.

注意source="current_epg",你就会明白了。

#3


0  

When a serializer is instantiated and many=True is passed, a ListSerializer instance will be created. The serializer class then becomes a child of the parent ListSerializer

当实例化一个序列化器并传递了许多=True时,将创建一个ListSerializer实例。然后,序列化器类成为父ListSerializer的子类

This method takes the target of the field as the value argument, and should return the representation that should be used to serialize the target. The value argument will typically be a model instance.

该方法以字段的目标作为值参数,并应该返回应该用于序列化目标的表示。值参数通常是一个模型实例。

Below is the example of the nested serializer

下面是嵌套序列化器的示例

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', 
        )

In view:

在视图:

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)