2017-03-03 110 views
10

請考慮以下三種模式:過濾使用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

@一切都是空虛對不起,這是一個錯字。正確更新。 –

回答

7

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

3

錯誤

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

我認爲現在更有意義。 –

2

註冊您的路線

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

爲了提高@全是 - 虛榮的回答,您可以明確使用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)