使用django rest框架中的视图集进行过滤

Ben*_*Max 15 django django-rest-framework

请考虑以下三种型号:

class Movie(models.Model):
    name = models.CharField(max_length=254, unique=True)
    language = models.CharField(max_length=14)
    synopsis = models.TextField()

class TimeTable(models.Model):
    date = models.DateField()

class Show(models.Model):
    day = models.ForeignKey(TimeTable)
    time = models.TimeField(choices=CHOICE_TIME)
    movie = models.ForeignKey(Movie)

    class Meta:
        unique_together = ('day', 'time')
Run Code Online (Sandbox Code Playgroud)

每个人都有他们的序列化器:

class MovieSerializer(serializers.HyperlinkedModelSerializer):
    movie_id = serializers.IntegerField(read_only=True, source="id")

    class Meta:
        model = Movie
        fields = '__all__'    

class TimeTableSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = TimeTable
        fields = '__all__'


class ShowSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Show
        fields = '__all__'
Run Code Online (Sandbox Code Playgroud)

和他们的路由器

router.register(r'movie-list', views.MovieViewSet)
router.register(r'time-table', views.TimeTableViewSet)
router.register(r'show-list', views.ShowViewSet)        
Run Code Online (Sandbox Code Playgroud)

现在我想通过过滤特定电影对象的所有Show对象来获取所有TimeTable对象(即日期列表).这段代码似乎是工作和获取列表,就像我想要的那样

m = Movie.objects.get(id=request_id)
TimeTable.objects.filter(show__movie=m).distinct()
Run Code Online (Sandbox Code Playgroud)

但我不知道如何在django rest框架中使用它?我尝试这样做(我很确定它是错的),我收到错误:

views.py:

class DateListViewSet(viewsets.ModelViewSet, movie_id):
    movie = Movie.objects.get(id=movie_id)
    queryset = TimeTable.objects.filter(show__movie=movie).distinct()
    serializer_class = TimeTableSerializer
Run Code Online (Sandbox Code Playgroud)

urls.py:

router.register(r'date-list/(?P<movie_id>.+)/', views.DateListViewSet)
Run Code Online (Sandbox Code Playgroud)

错误:

class DateListViewSet(viewsets.ModelViewSet,movie_id):NameError:名称'movie_id'未定义

如何使用django rest框架中的视图集进行过滤?或者,如果有任何其他优先方式,请列出来.谢谢.

Iva*_*kin 19

ModelViewSet按设计假设您要实现CRUD(创建,更新,删除)
还有一个ReadOnlyModelViewSet只实现GET只读端点的方法.
对于MovieShow模型,a ModelViewSet或者ReadOnlyModelViewSet是一个很好的选择,无论你是否想要实现CRUD.
但是ViewSet对于TimeTable描述Movie模型计划的相关查询的单独看起来并不那么好.
更好的方法是将该端点MovieViewSet直接放入.DRF提供它@detail_route@list_route装饰.

from rest_framework.response import Response
from rest_framework.decorators import detail_route

class MovieViewSet(viewsets.ModelViewset):

    queryset = Movie.objects.all()
    serializer_class = MovieSerializer

    @detail_route()
    def date_list(self, request, pk=None):
        movie = self.get_object() # retrieve an object by pk provided
        schedule = TimeTable.objects.filter(show__movie=movie).distinct()
        schedule_json = TimeTableSerializer(schedule, many=True)
        return Response(schedule_json.data)
Run Code Online (Sandbox Code Playgroud)

该端点将由movie-list/:id/date_listURL
文档提供有关额外路由的信息

  • 我知道这是一篇旧帖子(可能对某些人有用,就像今天对我一样),但由于 DRF 3.9 最近的更新,`@detail_route()` 已被替换。装饰器已被替换为“action”,因此根据 DRF 公告,在本例中它将是“@action(detail=True)”,可以在此处找到:[Django Rest Framework 3.9 公告](https://www .django-rest-framework.org/community/3.9-announcement/#action-decorator-replaces-list_route-and-detail_route) (4认同)

All*_*ітy 6

将您的路线注册为

router.register(r'date-list', views.DateListViewSet)
Run Code Online (Sandbox Code Playgroud)

现在,如下所示更改视图集,

class DateListViewSet(viewsets.ModelViewSet):
    queryset = TimeTable.objects.all()
    serializer_class = TimeTableSerializer
    lookup_field = 'movie_id'

    def retrieve(self, request, *args, **kwargs):
        movie_id = kwargs.get('movie_id', None)
        movie = Movie.objects.get(id=movie_id)
        self.queryset = TimeTable.objects.filter(show__movie=movie).distinct()
        return super(DateListViewSet, self).retrieve(request, *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

使用检索方法,该方法会将所有GET请求与端点匹配/date-list/<id>/

优点是您不必显式处理序列化和返回使ViewSet完成此困难部分的响应。我们仅更新要序列化的查询集,其余框架完成其余工作。

由于ModelViewSet被实现为

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    pass
Run Code Online (Sandbox Code Playgroud)

其实现包括以下方法(括号中的HTTP动词和端点)

  • list()(获取/date-list/
  • create()(开机自检/date-list/
  • retrieve()(获取date-list/<id>/
  • update()(输入/date-list/<id>/
  • partial_update() (补丁, /date-list/<id>/
  • destroy()(删除/date-list/<id>/

如果您只想实现retrieve()对端点的GET请求date-list/<id>/),则可以执行此操作,而不要执行`ModelViewSet),

from rest_framework import mixins, views

class DateListViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    queryset = TimeTable.objects.all()
    serializer_class = TimeTableSerializer
    lookup_field = 'movie_id'

    def retrieve(self, request, *args, **kwargs):
        movie_id = kwargs.get('movie_id', None)
        movie = Movie.objects.get(id=movie_id)
        self.queryset = TimeTable.objects.filter(show__movie=movie).distinct()
        return super(DateListViewSet, self).retrieve(request, *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)


Hug*_*nte 5

错误

class DateListViewSet(viewsets.ModelViewSet,movie_id):NameError:名称'movie_id'未定义

因为movie_id作为DataListViewSet的父类传递而不是您想象的参数

文档中的这个示例应该是您正在寻找的.

调整您的网址:

url(r'date-list/(?P<movie_id>.+)/', views.DateListView.as_view())
Run Code Online (Sandbox Code Playgroud)

调整你的型号:

class Show(models.Model):
   day = models.ForeignKey(TimeTable, related_name='show')
   time = models.TimeField(choices=CHOICE_TIME)
   movie = models.ForeignKey(Movie)

class Meta:
    unique_together = ('day', 'time')
Run Code Online (Sandbox Code Playgroud)

您的视图将如下所示:

class DateListView(generics.ListAPIView):
     serializer_class = TimeTableSerializer

     def get_queryset(self):

         movie = Movie.objects.get(id=self.kwargs['movie_id'])

         return TimeTable.objects.filter(show__movie=movie).distinct()
Run Code Online (Sandbox Code Playgroud)

另一种方法是:

调整您的网址:

router.register(r'date-list', views.DateListViewSet)
Run Code Online (Sandbox Code Playgroud)

调整你的型号:

class Show(models.Model):
   day = models.ForeignKey(TimeTable, related_name='show')
   time = models.TimeField(choices=CHOICE_TIME)
   movie = models.ForeignKey(Movie)

class Meta:
    unique_together = ('day', 'time')
Run Code Online (Sandbox Code Playgroud)

您的视图将如下所示:

class DateListViewSet(viewsets.ModelViewSet):
     serializer_class = TimeTableSerializer
     queryset = TimeTable.objects.all()
     filter_backends = (filters.DjangoFilterBackend,)
     filter_fields = ('show__movie_id')
Run Code Online (Sandbox Code Playgroud)

这将允许您提出以下请求:

http://example.com/api/date-list?show__movie_id=1
Run Code Online (Sandbox Code Playgroud)

文档