Python 从单个视图到不同端点的方法:将HTML和JSON的API分开(Django Rest框架)

Python 从单个视图到不同端点的方法:将HTML和JSON的API分开(Django Rest框架),python,django,api,templates,django-rest-framework,Python,Django,Api,Templates,Django Rest Framework,在Django Rest框架中,您能否帮助建议如何保持编码风格的组织,以使端点与HTML和JSON分开 在Flask中,我习惯于将用于服务Json的端点和用于服务HTML的端点分开,如: @application.route('/api/') def api_root(): #... return jsonify({'data' : data}) 及 在Django RF中,我了解到ModelViewSet可以同时提供这两种功能 所以我可以把所有的东西都放在一个地方 然而,当我

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

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

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

在Django RF中,我了解到ModelViewSet可以同时提供这两种功能

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

然而,当我在路由器上映射视图时,我会让所有端点都按照与我的模型相关的路径服务,它们都是
/api

您是否可以帮助建议一种好的编码实践,以使用ModelViewSet,并为html路由与API分离的端点

这是我正在做的例子,我的疑问在评论中:

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
            }
        )  
另外,请注意我是如何提供方法
问候语的
:这里我重复了queryset和serialising部分。我想做:

def greetings(self, request):

        person = self.last_person(request)
        return render(
            request,
            'myapi/greetings.html',
            {
                'person': person
            }
        )  
但是它会给出错误,因为
person
将是
响应
对象,并且无法找到将其传递给
渲染
的方法

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

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

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

app_name = 'myapi'
urlpatterns = [
    path('', include(router.urls)),
]
在main
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
标题上,以所需的格式提供响应

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

  • Accept:application/json
    它应该返回json格式的响应
  • Accept:text/html
    它应该返回html响应
在开始实施之前,让我们首先讨论DRF如何处理渲染器:

  • 渲染器可以在
    settings.py
    集合中全局定义为
    DEFAULT\u RENDERER\u CLASSES
    集合,也可以基于每个视图(从技术上讲,视图集是具有方法动作映射和关联逻辑的视图)定义为
    RENDERER\u CLASSES
    类属性

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

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

因此,基于上述情况,从API使用者,您需要传递正确的
Accept
头,如果使用
DefaultRouter
,最好添加
格式
扩展来对渲染器进行预过滤

在API上,执行以下操作:

  • 按照前面提到的正确顺序定义渲染器类
  • 如果使用者传递了
    格式
    ,请确保渲染器具有格式名称作为属性
  • 如果您自己发送响应,请确保使用
    response
    rest\u framework.response.response
    )类,该类传递正确的渲染器上下文,并调用渲染器的
    render
    方法以发回正确的响应
如果您想要发送JSON响应,实际上可以利用
JSONRenderer
rest\u framework.renderers.JSONRenderer
)来实现这一目的。如果只想定制一些东西,可以创建自己的子类化
JSONRenderer

在发送HTTP响应的情况下,您可以从
templatehtmlender
rest\u framework.renders.templatehtmlender
)中获得灵感,或者扩展它以满足您的需要。它有一个样板:

media_type = 'text/html'
format = 'html'
template_name = None
exception_template_names = [
    '%(status_code)s.html',
    'api_exception.html'
]
charset = 'utf-8'
序列化程序传入的数据已经作为模板上下文可用。因此,您可以在上面设置
template\u name
(或者在覆盖时使用
Response
传入),并在那里添加所有HTML表示。如果需要,还可以覆盖
渲染
,以便在那里进行更多自定义


最后,如果你想自己做一个定制的

嗨@heemayl,很抱歉没有早点回来,我在最后期限内:|。我想再次感谢你的回答和清晰,你擅长解释事情!我感觉到了同理心,这对我来说是一件非常重要的事情,尤其是当我把人际关系“虚拟化”时,而不是经常看到笔记本电脑的同一个“视域”。。啊哈……)我看了你的个人资料:恭喜你!(p.s.YNWA+PinkFloyed真是太棒了)| | |/@user305883非常感谢你的客气话,这意味着很多!YNWA和PF——是的:)
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 ??
]
media_type = 'text/html'
format = 'html'
template_name = None
exception_template_names = [
    '%(status_code)s.html',
    'api_exception.html'
]
charset = 'utf-8'