Joh*_*ohn 68 django django-rest-framework
在Django Rest Framework中,如何在串行器嵌套在另一个串行器中时对其进行过滤?
我的过滤器强加在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
Run Code Online (Sandbox Code Playgroud)
inp*_*ive 76
您可以继承ListSerializer并覆盖该to_representation方法.
默认情况下,该to_representation方法调用data.all()嵌套查询集.因此,data = data.filter(**your_filters)在调用方法之前,您需要进行有效的操作.然后,您需要在嵌套序列化程序的元数据上添加子类ListSerializer作为list_serializer_class.
to_representation然后调用superlist_serializer_class在嵌套的Serializer上添加子类ListSerializer作为元以下是您的样本的相关代码.
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
Run Code Online (Sandbox Code Playgroud)
Den*_*Lau 29
虽然上述所有答案都有效,但我发现使用了 Django 的 Prefetch对象是最简单的方法。
假设一个Restaurantobj 有很多MenuItems,其中一些是is_remove == 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')
)
Run Code Online (Sandbox Code Playgroud)
在RestaurantSerializer,做类似的事情
class RestaurantSerializer(serializers.ModelSerializer):
menu_items = MenuItemSerializer(source='filtered_menu_items', many=True, read_only=True)
Run Code Online (Sandbox Code Playgroud)
小智 15
测试了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)
Run Code Online (Sandbox Code Playgroud)
.
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',)
Run Code Online (Sandbox Code Playgroud)
注意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',
)
Run Code Online (Sandbox Code Playgroud)
鉴于:
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)
Run Code Online (Sandbox Code Playgroud)
我发现SerializerMethodField在要过滤的序列化程序字段上使用 a 更容易也更直接。
所以你会做这样的事情。
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
Run Code Online (Sandbox Code Playgroud)
serializers.ListSerializer如果您需要为不同的序列化程序使用不同的过滤条件,这将使您不必创建许多覆盖。
它还具有额外的好处,可以准确查看过滤器在序列化程序中的作用,而不是深入到子类定义中。
当然,缺点是如果您有一个带有许多嵌套对象的序列化程序,所有这些对象都需要以某种方式进行过滤。它可能会导致序列化程序代码大大增加。您想如何过滤取决于您。
希望这可以帮助!
| 归档时间: |
|
| 查看次数: |
17638 次 |
| 最近记录: |