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

时间:2018-02-09 16:04:00

标签: 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()

在我的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'}
        ]
     }
]

我的序列化工具看起来像:

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

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

在上面的示例数据中,两个位置都会返回,位置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

这将过滤掉两个日期之间没有任何事件的所有位置,但是这些返回的位置会显示所有事件,而不仅仅是min_datemax_date之间的事件。我假设我必须以某种方式将查询参数传递给EventSerializer中嵌入的LocationSerializer,但我不确定如何执行此操作。

1 个答案:

答案 0 :(得分:2)

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

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

您还可以使用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

使用self.context['request'],您可以获取请求数据,但是系列化程序。