use*_*883 5 python generics django routes django-rest-framework
我想了解如何使用Django DRF进行注册:
请说明使用 ViewSet 和泛型的区别。
这些是我的尝试。
我来自Flask,发现使用装饰器定义多个端点非常清楚。
@application.route('/people/', endpoint='people')
def people():
# return a list of people
pass
@application.route('/last/', endpoint='last_person')
def last_person():
# return last person
pass
Run Code Online (Sandbox Code Playgroud)
@application.route('/people/', endpoint='people')
def people():
field = request.args.get('last', None)
if field:
# return last person from list of people
else:
# return list of people
Run Code Online (Sandbox Code Playgroud)
我理解DRF的好处可能是一致性和阅读文档,但发现很麻烦,希望更清楚地了解如何使用ModelsViewSets和Generics来查看VSflask的好处。
请帮忙举个例子来获取用户列表和最后一个用户。
# serializer.py
from rest_framework import serializers
from .models import Person
class PersonSerializer( serializers.HyperlinkedModelSerializer):
class Meta:
model = Person
fields = ('name', 'nickname', 'timestamp')
Run Code Online (Sandbox Code Playgroud)
# views.py
class PersonViewSet( viewsets.ModelViewSet):
queryset = Person.objects.all().order_by('name')
serializer_class = PersonSerializer
Run Code Online (Sandbox Code Playgroud)
我知道该类PersonViewSet应该准备好公开获取列表和检索项目的方法,因为ModelViewSet:https :
//www.django-rest-framework.org/api-guide/viewsets/#modelviewset
但是如何在注册端点时明确地看到这一点?
例子:
# urls.py
router = routers.DefaultRouter()
router.register(r'people', views.PersonViewSet)
app_name = 'myapi'
urlpatterns = [
path('', include(router.urls)),
]
Run Code Online (Sandbox Code Playgroud)
当我浏览时http://127.0.0.1:8000/api/,我只能看到一个端点。是的,如果我尝试http://127.0.0.1:8000/api/1/它会自行检索用户,但是如何在上面的代码中读取它?如何从PersonViewSet(类似PersonViewSet.get_last_person())映射方法以指示使用相同的端点获取最后一个条目?
我读过Generics模型公开适合检索单个项目的 API 模型,而不是列表:
https://www.django-rest-framework.org/api-guide/generic-views/#retrieveapiview
我试图添加,在views.py:
# I make use of RetrieveAPIView now
class LastPersonView( generics.RetrieveAPIView):
queryset = Person.objects.all()
serializer_class = PersonSerializer
Run Code Online (Sandbox Code Playgroud)
并在urls.py:
router.register(r'people', views.PersonViewSet)
router.register(r'last-person', views.LastPersonView)
Run Code Online (Sandbox Code Playgroud)
但这会产生错误: AttributeError: type object 'LastPersonView' has no attribute 'get_extra_actions 因为泛型没有 get_extra_actions ,而不是 ViewSet
在第二个示例中,如何在我的路由器中注册两个视图?
我还可以urls.py通过分配基本名称来指示 ViewSet 在我的 中注册不同的端点:
router.register(r'people', views.PersonViewSet)
router.register(r'last-person', views.PersonViewSet, basename='lastperson')
Run Code Online (Sandbox Code Playgroud)
并使用相同的ModelViewSet:
class PersonViewSet( viewsets.ModelViewSet):
queryset = Person.objects.all().order_by('name')
serializer_class = PersonSerializer
Run Code Online (Sandbox Code Playgroud)
我明白这种方法的好处是更“简单”,因为查询集总是相同的。
但是我可以注册一个方法来检索最后一个对象,并将该方法映射到我的路由器 ( include(router.urls)) 中吗?
能否提供使用 ModelViewSet、泛型和更明确的方法的示例,这些方法在视图中声明方法并在端点中调用这些方法?
您能否说明哪种方法可能更好:
我将介绍您采用的每种方法,并将提供一个解决方案来满足您基于该方法的需求。所以让我们滚...
你的第一种方法:
您的第一种方法使用非常典型的 DRF 设置,并为PersonViewSetDRF 本身的各种操作生成路由。
现在,您需要添加一个新的 URL 端点,该端点将解析为 的最后一个对象queryset:
Person.objects.all().order_by('name')
Run Code Online (Sandbox Code Playgroud)
据推测,端点应该位于person基本名称下。
我们可以利用action这里的装饰器将特定 URL 上的 HTTP GET 映射到要映射到视图集中的方法,从该方法我们可以将kwargs视图集实例的属性设置pk为最后一个对象的属性,最后发送对retrieve方法本身的请求,例如:
from rest_framework.decorators import action
class PersonViewSet(viewsets.ModelViewSet):
queryset = Person.objects.all().order_by('name')
serializer_class = PersonSerializer
@action(
methods=['get'],
detail=False,
url_path='last',
url_name='last',
)
def get_last_person(self, request, *args, **kwargs):
last_pk = self.queryset.all().last().pk
self.kwargs.update(pk=last_pk)
return self.retrieve(request, *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
现在,如果您在/people/last/端点上发出请求,您将获得最后检索到的对象的响应。
请注意,如果您有lookup_url_kwargs并且lookup_field不同于pk,则需要kwargs像您想象的那样使用 set 。此外,您可以将操作retrieve留给自己而不是委托给,retrieve但让我们保持它DRY。
FWIW,如果您也想为 PUT/PATCH/DELETE 使用此端点,则需要添加方法action并将它们委托给基于方法名称的相应操作。
你的第二种方法:
视图不是视图集(查看 :rest_framework.views.View和的基类rest_framework.viewsets.ViewSet);DRF 路由器为视图集创建端点,而不是视图。有很多方法可以将视图转换为视图集,基本上只是通过继承自viewsets.GenericViewSet- 它转而继承自viewsets.ViewSetMixin和generics.GenericAPIView。
viewsets.ViewSetMixin通过在classmethod 中提供所有必要的 action-method 映射,具有将视图转换为视图集的实际魔力as_view。
为了使这种方法起作用,我们需要定义一个retrieve方法来发送来自序列化程序的响应:
from rest_framework import generics
from rest_framework.response import Response
class LastPersonView(generics.RetrieveAPIView):
serializer_class = PersonSerializer
queryset = Person.objects.all().order_by('name')
def retrieve(self, request, *args, **kwargs):
instance = self.queryset.all().last()
serializer = self.get_serializer(instance)
return Response(serializer.data)
Run Code Online (Sandbox Code Playgroud)
generics.RetrieveAPIView的get方法将请求委托给retrieve方法,因此我们在retrieve此处进行了覆盖。
现在,我们需要将路由定义为常规端点,而不是在 DRF 路由器中:
urlpatterns = router.urls + [
path('last-person/', LastPersonView.as_view()),
]
Run Code Online (Sandbox Code Playgroud)
你的第三种方法:
您已经为同一个视图集注册了两个不同的前缀(在这里再次使用视图不起作用,必须是一个视图集),因此两个不同的 URL 映射集具有所有相同的 CRUD 操作。根据您的期望,我认为这不是您想要的,所以我不会进一步讨论这种方法,但我认为您从上面得到了为什么它不相关的想法。
现在,如果我必须选择,我更喜欢第一种方法,因为所有内容都在相同的视图集、前缀和基名下,并且您不需要与 URLConf 混在一起。
我希望以上能澄清一两件事。