如何在 Django RestFramework 中筛选嵌套的序列化程序?

在 Django RestFramework 中,当一个序列化程序嵌套在另一个序列化程序中时,如何筛选它?

我的过滤器强加在 DRF 视图集中,但是当您从另一个序列化程序内部调用序列化程序时,嵌套序列化程序的视图集从未被调用,因此嵌套结果显示为未过滤的。

我试过在原始视图集上添加一个过滤器,但是它似乎没有过滤嵌套的结果,因为嵌套的结果被调用为一个单独的预抓取查询。(您可以看到,嵌套序列化程序是一个反向查找。)

是否可以在嵌套序列化程序本身中添加 get _ queryset ()覆盖(将其移出视图集) ,在那里添加过滤器?我也试过了,但没用。

这是我试过的,但好像都没人叫它:

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
42438 次浏览

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

默认情况下,to_representation方法调用嵌套查询集上的 data.all()。因此,实际上需要在调用该方法之前创建 data = data.filter(**your_filters)。然后,您需要将子类 ListSerializer 添加到嵌套序列化器的 meta 上,作为 list _ serializer _ class。

  1. 子类 ListSerializer,覆盖 to_representation,然后调用 super
  2. 添加子类 ListSerializer 作为嵌套 Serializer 上的元 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.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

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

在 Django 2.0 + DRF 3.7.7中只找到了一个可行的解决方案。

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

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

注意 source="current_epg",你会得分的。

在实例化序列化程序并传递 many = True 时,将引发 将创建 ListSerializer 实例 成为父 ListSerializer 的子级

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

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

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)

While all the above answers work, I find the use of Django's Prefetch object the easiest way of all.

假设一个 Restaurant obj 有很多 MenuItem,其中一些是 is_removed == True,您只需要那些没有删除的。

RestaurantViewSet中,做类似于

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

RestaurantSerializer中,做类似于

class RestaurantSerializer(serializers.ModelSerializer):
menu_items = MenuItemSerializer(source='filtered_menu_items', many=True, read_only=True)


我发现在要筛选的序列化器字段上使用 SerializerMethodField更容易,也更直接。

所以你会这么做。

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

如果您需要针对不同序列化程序的不同筛选条件,这可以节省您创建许多 serializers.ListSerializer重写的时间。

它还有一个额外的好处,就是可以准确地看到过滤器在序列化程序中的作用,而不必深入到子类定义中。

当然,缺点是如果您有一个包含许多嵌套对象的序列化程序,这些对象都需要以某种方式进行筛选。它可能导致序列化程序代码大大增加。怎么过滤取决于你。

希望这个能帮上忙!

下面的工作对我来说,从 自我上下文[‘ view’],你可以得到序列化程序内的过滤器参数,并使用它的任何你想要的。

class ShipmentDocumentSerializer(serializers.ModelSerializer):


class Meta:
model = Document
fields = ['id', 'created_date', 'consignment', 'document', 'org', 'title' ]


class ShipmentDocumentTypeSerializer(serializers.ModelSerializer):
documents = serializers.SerializerMethodField()


class Meta:
model = DocumentType
fields = ['id', 'type', 'documents']


def get_documents(self, instance):
consignment_id=self.context['view'].kwargs['consignment_id']
queryset = Document.objects.filter(consignment__id=consignment_id)
return ShipmentDocumentSerializer(queryset, many=True).data