django rest 框架 - 如何将查询参数传递给外键序列化程序?

Mar*_*rkD 6 django django-rest-framework

对于我的应用程序,我有以下(简化的)模型给出的位置和事件:

class Location(models.Model):
    name = models.CharField(max_length=64)

class Event(models.Model):
    location = models.ForeignKey(Location)
    date = models.DateField()
Run Code Online (Sandbox Code Playgroud)

在我的 API 中,我有一个端点:/api/locations/它返回所有位置,每个位置都嵌入了它的事件,例如:

[
    {
        'id': 1,
        'name': 'Location 1',
        'events': [
            {'id': 1, 'date': '2018-01-01'},
            {'id': 2, 'date': '2018-01-14'}
        ]
     },
     {
        'id': 2,
        'name': 'Location 2',
        'events': [
            {'id': 3, 'date': '2017-12-28'},
            {'id': 4, 'date': '2018-01-10'}
        ]
     }
]
Run Code Online (Sandbox Code Playgroud)

我的序列化程序看起来像:

class EventSerializer(serializers.ModelSerializer):
    class Meta:
        model = Event
        fields = ('id', 'date',)
        read_only_fields = ('id',)

class LocationSerializer(serializers.ModelSerializer):
    events = EventSerializer(many=True)
    class Meta:
        model = Location
        fields = ('id', 'name', 'events',)
        read_only_fields = ('id',)
Run Code Online (Sandbox Code Playgroud)

使用 ViewSets,我想向这个调用添加一个过滤器,带有一个max_date, 和min_date参数,例如:/api/locations/?min_date=2018-01-01&max_date=2018-01-15。哪个应该只返回在这两个日期之间有事件的位置,并且应该只返回这两个日期之间每个位置的事件。

在上面的示例数据中,两个位置都会返回,位置 1 会列出两个事件,但位置 2 的列表中只会有第二个事件。在我的 ViewSet 中,我可以采用这些参数并将它们附加为查询集过滤器:

class LocationViewSet(mixins.RetrieveModelMixin,
                      mixins.ListModelMixin,
                      viewsets.GenericViewSet):
    serializer_class = LocationSerializer

    def get_queryset(self):
        queryset = Location.objects.all()
        min_date = self.request.query_params.get('min_date', None)
        max_date = self.request.query_params.get('max_date', None)
        if min_date is not None and max_date is not None:
            queryset = queryset.filter(event__date__lte=max_date, event__date__gte=min_date)
        return queryset
Run Code Online (Sandbox Code Playgroud)

这将过滤掉那些没有这两个日期之间的任何事件的任何位置,但是那些返回位置展示其所有的事件,不只是之间的那些min_date和max_date。我假设我必须以某种方式将我的查询参数传递给EventSerializer嵌入在LocationSerializer.

nev*_*ner 4

尝试在视图中预取过滤事件:

from django.db.models import Prefetch
class LocationViewSet(mixins.RetrieveModelMixin,
                  mixins.ListModelMixin,
                  viewsets.GenericViewSet):
serializer_class = LocationSerializer

    def get_queryset(self):
        queryset = Location.objects.all()
        min_date = self.request.query_params.get('min_date', None)
        max_date = self.request.query_params.get('max_date', None)
        if min_date is not None and max_date is not None:
            queryset = queryset.filter(event__date__lte=max_date, event__date__gte=min_date).prefetch_related(Prefetch('events', queryset=Event.objects.filter(date__lte=max_date, date__gte=min_date))
        return queryset
Run Code Online (Sandbox Code Playgroud)

您还可以使用serializerMethodField过滤事件:

class LocationSerializer(serializers.ModelSerializer):
    events = SerializerMethodField()
    class Meta:
        model = Location
        fields = ('id', 'name', 'events',)
        read_only_fields = ('id',)

    get_events(self, obj):
        min_date = self.context['request'].query_params.get('min_date', None)
        max_date = self.context['request'].query_params.get('max_date', None)
        events = obj.events.filter(date__lte=max_date, date__gte=min_date) 
        return LocationSerializer(events, many=True).data
Run Code Online (Sandbox Code Playgroud)

您可以在self.context['request']序列化器中获取请求数据。