请考虑以下三种模式:
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)
现在我想通过过滤特定电影对象的所有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:名称'movie_id'未定义
如何在django rest框架中使用viewset进行过滤?或者,如果有任何其他优先方式,请列出来。谢谢。
答案 0 :(得分:15)
ModelViewSet
假定您要实现CRUD(创建,更新,删除)
还有一个ReadOnlyModelViewSet
仅使用GET
方法来只读取端点
对于Movie
和Show
模型,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)
此终结点将由movie-list/:id/date_list
网址提供
Docs about extra routes
答案 1 :(得分:6)
将您的路线注册为
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)
使用检索方法,该方法将匹配任何对端点 /date-list/<id>/
的GET请求。
优点是你不必显式处理序列化和返回响应,你使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
它的实现包括以下方法(括号上的HTTP动词和端点)
list()
(获取/date-list/
) create()
(POST /date-list/
) retrieve()
(获取date-list/<id>/
) update()
(PUT /date-list/<id>/
) partial_update()
(PATCH,/date-list/<id>/
destroy()
(删除/date-list/<id>/
) 如果您只想实现retrieve()
(对端点date-list/<id>/
的 GET请求),则可以执行此操作而不是“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)
答案 2 :(得分:5)
错误
class DateListViewSet(viewsets.ModelViewSet,movie_id):NameError:未定义名称'movie_id'
发生是因为movie_id
作为DataListViewSet的父类传递而不是您想象的参数
documentation中的这个示例应该是您正在寻找的。 p>
调整您的网址:
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
答案 3 :(得分:1)
Ivan Semochkin的答案正确,但是详细信息装饰器已弃用。它由action decorator代替。
from rest_framework.decorators import action
class MovieViewSet(viewsets.ModelViewset):
@action(detail=True)
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)
答案 4 :(得分:0)
To improve @all-is-vanity answer, you can explicitly use movie_id
as a parameter in the retrieve
function since you are overriding the lookup_field
class property:
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)
You can also call self.get_object()
to get the 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)