gpt4 book ai didi

python - 从单个 View 到不同端点的方法 : keep APIs for HTML and for JSON separated (Django Rest Framework)

转载 作者:太空宇宙 更新时间:2023-11-04 01:46:23 27 4
gpt4 key购买 nike

在 Django Rest Framework 中,您能否帮助建议如何保持编码风格的组织以保持端点与 HTML 和 JSON 分离?

在 Flask 中,我习惯于将用于服务 Json 的端点和用于服务 HTML 的端点分开,例如:

@application.route('/api/')
def api_root():
#...
return jsonify({'data' : data})

@application.route('/home/<string:page>/', endpoint='page_template')
#...
return render_template(template, page)

因此我可以像这样提供 API:

/api/page => serve the json for the page, say for AJAX etc.
/page => serve the corresponding html page

在 Django RF 中,我读到 ModelViewSet 可以为两者提供服务。

所以我可以把所有东西都放在一个地方。

但是,当我在路由器上映射 View 时,我会让所有端点服务都遵循与我的模型相关的路径,它们都是 /api 的子路径

您能否提供一个良好的编码实践建议,以利用 ModelViewSet,并为与 API 分离的 html 路由端点?

这是我正在处理的示例,我的疑问在评论中:

from rest_framework import viewsets
from rest_framework import generics
from rest_framework.decorators import action
from rest_framework.response import Response

from .serializers import PersonSerializer
from .models import Person

class PersonViewSet( viewsets.ModelViewSet):
queryset = Person.objects.all().order_by('name')
serializer_class = PersonSerializer

# this will return last person
# I can see it registered at: 127.0.0.1:8000/api/people/last_person/
@action(detail=False)
def last_person(self, request):
queryset = Person.objects.all().order_by('timestamp').reverse()[0]
serializer = self.get_serializer(queryset)
return Response(serializer.data)

# this will return a template:
# I can see it registered at: ../api/people/greetings : I wanted at /greetings
@action(detail=False)
def greetings(self, request):

queryset = Person.objects.all().order_by('timestamp').reverse()[0]
serializer = self.get_serializer(queryset)

return render(
request,
'myapi/greetings.html',
{
'person': serializer.data
}
)

另外,请注意我是如何提供方法greetings的:在这里我重复了查询集和序列化部分。我想这样做:

def greetings(self, request):

person = self.last_person(request)
return render(
request,
'myapi/greetings.html',
{
'person': person
}
)

但它会报错,因为 person 将是一个 Response 对象,并且找不到将其传递给 render 的方法.

哪种编码风格可以避免重复,并保持 API 和模板分离?

/myapi/url.py 中,我注册了如下端点:

router = routers.DefaultRouter()
router.register(r'people', views.PersonViewSet)

app_name = 'myapi'
urlpatterns = [
path('', include(router.urls)),
]

在主 url.py 中,像这样:

from django.urls import include

urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('myapi.urls')),
path('', include('myapi.urls')) # How to keep views for templates and for Json separated ??
]

最佳答案

如果响应阶段之前的一切都是一样的,那么除了渲染器之外,你不应该接触任何东西。您可以根据用户的请求准确地根据媒体类型 -- Accept header 选择正确的渲染器,以所需格式提供响应。

例如,假设您要根据媒体类型(Accept header )发送 JSON 和 HTML 响应。因此,当您传递 (仅传递一种媒体类型以使示例简单)时:

  • Accept: application/json 它应该以 JSON 格式返回响应
  • Accept: text/html 它应该返回 HTML 响应

在继续实现之前,让我们先讨论一下 DRF 如何处理渲染器:

  • 渲染器可以在 settings.py 中全局定义为 DEFAULT_RENDERER_CLASSES 集合或在每个 View 上定义( View 集在技术上是具有方法- Action 映射和相关联的 View 逻辑)作为 renderer_classes 类属性的基础。

  • 渲染器的顺序非常重要。 DRF 根据 Accept 值选择最具体的渲染器。对于更通用的渲染器或包罗万象的 (*/*),将选择第一个满足媒体类型的渲染器。

  • 如果您使用 DRF 的 DefaultRouter 进行 URL 映射,您还可以使用 format 扩展来过滤掉任何不支持所传递格式的渲染器。例如,如果您有一个端点 /foo/1/,您可以添加类似 /foo/1.json/ 的格式。然后只会选择具有 format = json 作为属性的渲染器类(然后前面提到的最终选择将仅在这些渲染器中发生)。

因此,基于上述,从 API 消费者,您需要传递正确的 Accept header ,如果使用 DefaultRouter 也最好添加 format 在渲染器上进行预过滤的扩展。

在 API 上,执行:

  • 按照前面提到的正确顺序定义渲染器类
  • 如果消费者传递format,确保渲染器将格式名称作为属性
  • 如果您自己发送响应,请确保您使用传递正确渲染器的Response (rest_framework.response.Response) 类上下文并调用渲染器的render方法返回正确的响应

如果您想发送一个 JSON 响应,您实际上可以利用 JSONRenderer (rest_framework.renderers.JSONRenderer) 来完美地达到目的。如果您只想自定义一些东西,您可以创建自己的子类 JSONRenderer

在发送 HTTP 响应的情况下,您可以从 TemplateHTMLRenderer (rest_framework.renderers.TemplateHTMLRenderer) 中获取灵感或对其进行扩展以满足您的需求。它有样板:

media_type = 'text/html'
format = 'html'
template_name = None
exception_template_names = [
'%(status_code)s.html',
'api_exception.html'
]
charset = 'utf-8'

并且序列化器传入的数据已经可以作为模板上下文使用了。因此,您可以设置上面的 template_name(或者如果您正在覆盖,则使用 Response 传入)并在其中添加所有 HTML 表示形式。如果需要,您还可以覆盖 render 以获得更多自定义。

最后,如果您想自己定制一个,the DRF doc is pretty awesome in explaining what you need to do .

关于python - 从单个 View 到不同端点的方法 : keep APIs for HTML and for JSON separated (Django Rest Framework),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59009504/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com