过滤使用viewsets Django的REST框架

问题描述:

请考虑以下三种模式:过滤使用viewsets Django的REST框架

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

而且他们每个人都有自己的序列化:

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__' 

而且他们的路由器

router.register(r'movie-list', views.MovieViewSet) 
router.register(r'time-table', views.TimeTableViewSet) 
router.register(r'show-list', views.ShowViewSet)   

现在我想通过特定的mov过滤所有的Show对象来获得所有的TimeTable对象(即日期列表)即对象。此代码似乎是工作,并得到像我想要的名单

m = Movie.objects.get(id=request_id) 
TimeTable.objects.filter(show__movie=m).distinct() 

但我不知道如何在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 

urls.py:

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

错误:

class DateListViewSet(viewsets.ModelViewSet, movie_id): NameError: name 'movie_id' is not defined

如何使用django rest框架中的viewsets进行过滤?或者如果有其他任何优先方式,请将其列出。谢谢。

+0

@一切都是空虚对不起,这是一个错字。正确更新。 –

ModelViewSet设计假设你想实现一个CRUD(创建,更新,删除)
还有一个ReadOnlyModelViewSet其仅实现GET方法只读端点。
对于MovieShow型号,ModelViewSetReadOnlyModelViewSet是一个不错的选择,无论您是否要实施CRUD。
但是对于描述Movie模型的日程安排的TimeTable的相关询问的单独的ViewSet看起来并不太好。
更好的方法是直接将该端点置于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) 

该终端将可通过movie-list/:id/date_list网址
Docs about extra routes

错误

class DateListViewSet(viewsets.ModelViewSet, movie_id): NameError: name 'movie_id' is not defined

是因为movie_id被作为父类DataListViewSet的传递,而不是作为参数,你在documentation想象

这个例子应该是你在找什么。

调整你的网址:

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

调整型号:

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

您的看法是这样的:

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() 

另一种方式来做到这一点是:

调整你的网址:

router.register(r'date-list', views.DateListViewSet) 

调整模型:

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

您的看法是这样的:

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

,这将使你提出要求,如:

http://example.com/api/date-list?show__movie_id=1 

documentation

+0

这似乎并不奏效。 –

+0

我认为现在更有意义。 –

注册您的路线

router.register(r'date-list', views.DateListViewSet) 

现在改变你的视图集,如下图所示,

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) 

使用一个retr ieve方法,它将匹配任何GET请求到端点/date-list/<id>/

优点是您不必显式处理序列化并返回响应您使ViewSet能够完成这一难题。我们只是将queryset更新为序列化,其余部分完成。

由于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 

它的实现包括以下方法(HTTP动词和端点上支架)

  • list()(GET /date-list/
  • create()( POST /date-list/
  • retrieve()(GET date-list/<id>/
  • update()(PUT /date-list/<id>/
  • partial_update()(PATCH,/date-list/<id>/
  • destroy()(DELETE /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) 

为了提高@全是 - 虚荣的回答,您可以明确使用movie_id作为参数在retrieve功能,因为你正在重写lookup_field类属性:

def retrieve(self, request, 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) 

您也可以拨打self.get_object()获取对象:

def retrieve(self, request, movie_id=None): 
    movie = self.get_object() 
    self.queryset = TimeTable.objects.filter(show__movie=movie).distinct() 
    return super(DateListViewSet, self).retrieve(request, *args, **kwargs)