Django Rest Framework에서 다른 serializer에 중첩되어있을 때 serializer를 어떻게 필터링합니까?
내 필터는 DRF 뷰셋에 적용되지만 다른 시리얼 라이저 내부에서 시리얼 라이저를 호출하면 중첩 된 시리얼 라이저의 뷰셋이 호출되지 않으므로 중첩 된 결과가 필터링되지 않은 것처럼 보입니다.
원래 뷰셋에 필터를 추가하려고 시도했지만 중첩 된 결과가 별도의 미리 분할 된 쿼리로 호출되기 때문에 중첩 된 결과를 필터링하지 않는 것 같습니다. (중첩 된 직렬 변환기는 역방향 조회입니다.)
중첩 된 serializer 자체에 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
답변
ListSerializer 를 하위 클래스로 만들고to_representation
메서드를 덮어 쓸 수 있습니다 .
기본적으로 to_representation
메서드 data.all()
는 중첩 된 쿼리 집합을 호출 합니다. 따라서 data = data.filter(**your_filters)
메서드가 호출되기 전에 효과적으로 만들어야 합니다. 그런 다음 하위 클래스 화 된 ListSerializer를 중첩 된 serializer의 메타에 list_serializer_class로 추가해야합니다.
- ListSerializer 하위 클래스, 덮어 쓰기
to_representation
후 super 호출 list_serializer_class
중첩 된 Serializer에 메타로 하위 클래스 ListSerializer 추가
다음은 샘플 관련 코드입니다.
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"
이면 요점을 알 수 있습니다.
답변
위의 모든 답변이 작동하는 동안 Django의 Prefetch
개체를 사용하는 것이 가장 쉬운 방법이라는 것을 알았 습니다.
발언권 Restaurant
obj가 많이 가지고 MenuItem
있는 일부들, 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')
)
에서 RestaurantSerializer
다음과 같이하십시오.
class RestaurantSerializer(serializers.ModelSerializer):
menu_items = MenuItemSerializer(source='filtered_menu_items', many=True, read_only=True)
답변
serializer가 인스턴스화되고 many = True가 전달되면 ListSerializer 인스턴스가 생성됩니다. 그러면 serializer 클래스가 부모 ListSerializer의 자식이됩니다.
이 메서드는 필드의 대상을 값 인수로 사용하고 대상을 직렬화하는 데 사용해야하는 표현을 반환해야합니다. 일반적으로 값 인수는 모델 인스턴스입니다.
다음은 중첩 된 직렬 변환기의 예입니다.
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',
)
보기 :
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)
답변
SerializerMethodField
필터링하려는 serializer 필드에서 를 사용하는 것이 더 쉽고 간단합니다 .
그래서 당신은 이렇게 할 것입니다.
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
서로 다른 serializer에 대해 서로 다른 필터링 기준이 필요한 경우의 재정의를 많이 만들지 않아도됩니다.
또한 하위 클래스 정의로 들어가는 대신 직렬 변환기 내에서 필터가 수행하는 작업을 정확히 확인할 수있는 추가 이점도 있습니다.
물론 어떤 식 으로든 필터링해야하는 중첩 된 개체가 많은 serializer가있는 경우 단점이 있습니다. 직렬 변환기 코드가 크게 증가 할 수 있습니다. 필터링 방법은 귀하에게 달려 있습니다.
도움이 되었기를 바랍니다!