Django Rest Framework 2:创建完整的函数视图

前面我们将重点放在是序列化器的简单实现和简单使用上,这里就着重说一下在函数视图中如何进一步使用序列化器。

一,请求对象Request

REST framework 引入了一个扩展自 django HttpRequest 对象rest_framework.request.Request 对象,增加了对 REST framework 请求的灵活解析和请求认证的支持。

  • 由于实现的原因, Request 类并不会继承 HttpRequest 类, 而是使用合成来进行扩展。

(一)请求解析(Request parsing)

REST框架的请求对象提供了灵活的请求解析,允许以与处理表单数据相同的方式处理 JSON 数据或其他媒体类型的请求。

1,data

Request 对象的核心就是 .data 属性,它与 request.POST 类似,它返回请求体解析后的内容,这与标准的 request.POSTrequest.FILES 属性类似,但也有不同:

  • 它包括所有解析的内容,文件和非文件输入。
  • 它还支持解析 POST 以外的一些 HTTP 方法的内容,即 PUT 和 PATCH 请求的内容。
  • 它支持对 REST Framework 请求的灵活解析,而不仅仅是支持表单数据(例如可以像处理传入的表单数据那样处理传入的JSON数据)。
  • 更过解析详情请参考官方API:解析器

2,query_params

request.query_paramsrequest.GET 更正确的命名同义词。

  • 为了使代码内部更清晰,官方推荐使用 request.query_params 而不是 Django
    的标准 request.GET。这样做有助于让代码更加正确和明显——能处理任何 HTTP 方法类型所包含查询参数。

3,parsers

APIView类或@api_view装饰器将确保根据视图上设置的解析器类配置或默认的解析器类配置,自动将request.parsers 属性设置为解析器实例列表。

  • 如果客户端发送格式错误的内容,则访问 request.data 可能会引发 ParseError 错误。默认情况下,REST framework 的 APIView 类或 @api_view 装饰器将捕获错误并返回 400 Bad Request 响应。
  • 如果客户端发送的请求的内容类型无法解析,则会引发 UnsupportedMediaType 异常,默认情况下会被捕获并返回 415 Unsupported Media Type 响应。

(二)内容协商 (Content negotiation)

请求会公开一些属性,以允许确定内容协商阶段的结果。这就允许实现具体的行为,例如为不同的媒体类型选择不用的序列化方案。

1,accepted_renderer

由内容协商阶段选择的 render 实例。

2,accepted_media_type

由内容协商阶段接受的媒体类型的字符串。

(三)认证(Authentication)

REST framework 为每次请求提供了灵活的验证,让你能够:

  • 对API的不同部分使用不同的身份验证策略。
  • 支持使用多种身份验证策略。
  • 提供与传入请求相关联的用户和令牌信息。

1,user

request.user 通常会返回 django.contrib.auth.models.User 模型的实例,但其行为取决于正在使用的身份验证策略。

2,auth

request.auth 返回任何附加的认证上下文,它的确切行为取决于正在使用的身份验证策略,但它通常可能是请求通过身份验证的令牌实例。

  • 如果请求未经身份验证,或者没有附加上下文,则 request.auth 的默认值为 None

3,authenticators

APIView 类或 @api_view 装饰器将根据视图上配置的 authentication_classes 或根据 DEFAULT_AUTHENTICATORS 配置确保将此属性自动设置为 Authentication 实例列表。

更多详情请参考官方API:认证

(四)浏览器增强(Browser enhancements)

REST framework 支持一些浏览器增强功能,例如基于浏览器的 PUT,PATCH 和 DELETE 表单。

1,method

request.method 返回大写字符串表示的 HTTP 请求方法。

  • 透明地支持基于浏览器的 PUT,PATCH 和 DELETE 表单。

2,content_type

request.content_type 返回表示 HTTP 请求正文的媒体类型的字符串对象,

  • 如果没有被提供媒体类型,则返回空字符串。
  • 如果需要访问请求的内容类型,则应该优先使用 .content_type 属性,而不是使用 request.META.get('HTTP_CONTENT_TYPE'),因为前者为基于浏览器的非表单内容提供了透明支持。

3,stream

request.stream 返回一个代表请求主体内容的流。

更多详情请参考官方API:浏览器增强

二,响应对象Response

REST framework 还提供了一个继承了 Django SimpleTemplateResponse 对象rest_framework.response.Response对象来支持HTTP内容协商,它允许根据客户端的请求返回可以呈现为多种内容类型的内容。

一般不需要直接使用Response类,如果需要,你也可以从视图中返回常规的HttpResponseStreamingHttpResponse对象。使用Response类只是为返回经过内容协商的Web API响应提供了一个更好的接口,它可以呈现为多种格式。

除非你出于某种原因想要大量定制REST framework,否则你应该总是使用一个APIView类或@api_view函数来处理返回Response对象的视图。这样做可以确保视图在返回响应之前能够执行内容协商,并为内容选择适当的渲染器器。

(一)创建响应

与常规的HttpResponse对象不同,你不应该用已被渲染的内容实例化Response对象,而应该传入未被渲染的内容,这些内容可以包含任何 Python 基本数据类型。

  • Response类所使用的渲染器不能处理诸如 Django 模型实例这样的复杂数据类型,所以在创建Response对象之前,需要将实例序列化为 Python 基本数据类型。
>>> from rest_framework.response import Response
>>> response = Response(data)

参数:

  • data :响应的序列化数据。
  • status :响应的状态码。默认为200。
  • template_name :选择 HTMLRenderer 时使用的模板名称。
  • headers :响应中使用的 HTTP headers 的字典。
  • content_type :响应的内容类型。通常情况下,渲染器会根据内容协商的结果自动设置,但有些情况下需要明确指定内容类型。

(二)属性

1,data

需要被渲染的、序列化的或为 Python 基本数据类型的响应数据。

2,status_code

HTTP 响应的数字状态码。

3,content

已被渲染的响应内容。

  • 在访问 content 之前,必须先调用 .render() 方法。

4,template_name

template_name 只有在使用 HTMLRenderer 或者其他自定义模板作为响应的渲染器时才需要被提供。

5,accepted_renderer

用于渲染响应的渲染器实例。

  • 自动通过 APIView 或者 @api_view 在视图返回响应之前设置。

6,accepted_media_type

内容协商阶段选择的媒体类型。

  • 自动通过 APIView 或者 @api_view 在视图返回响应之前设置。

7,renderer_context

一个将传递给渲染器的.render()方法的附加上下文信息的字典。

  • 自动通过 APIView 或者 @api_view 在视图返回响应之前设置。

8,标准的 HttpResponse 属性

Response 类扩展了 SimpleTemplateResponse,并且所有常用的属性和方法都是提供的。比如你可以使用标准的方法设置响应的header信息:

>>> response = Response()
>>> response['Cache-Control'] = 'no-cache'

三,状态码

在视图中使用数字 HTTP 状态码并不总是利于代码的阅读,如果写错代码,很容易被忽略。REST framework包含一组可以使代码更加明显和可读的状态码常量。

  • 官方也不建议在响应中使用裸状态码。
from rest_framework import status
from rest_framework.response import Response

def empty_view(self):
    content = {'please move along': 'nothing to see here'}
    return Response(content, status=status.HTTP_404_NOT_FOUND)

下面列出了 state 模块中包含的全套HTTP状态码:

  • 该模块还包括一组辅助函数,用于测试状态码是否在给定范围内。
from rest_framework import status
from rest_framework.test import APITestCase

class ExampleTestCase(APITestCase):
    def test_url_root(self):
        url = reverse('index')
        response = self.client.get(url)
        self.assertTrue(status.is_success(response.status_code))

有关正确使用HTTP状态代码的更多信息请参考 RFC 2616RFC 6585.

1,Informational - 1xx

此类状态码表示临时响应。默认情况下,REST framework 中没有使用 1xx 状态码。

TTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS

2,Successful - 2xx

此类状态码表明客户端的请求已被成功接收、理解和接受。

HTTP_200_OK
HTTP_201_CREATED
HTTP_202_ACCEPTED
HTTP_203_NON_AUTHORITATIVE_INFORMATION
HTTP_204_NO_CONTENT
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
HTTP_207_MULTI_STATUS

3,Redirection - 3xx

此类状态码表明用户代理需要采取进一步行动来满足请求。

HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY
HTTP_302_FOUND
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT

4,Client Error - 4xx

4xx类的状态代码用于客户机似乎出错的情况。

  • 除了在响应 HEAD 请求时,服务器应该包含一个实体,其中包含错误情况的解释,以及它是临时的还是永久的条件。
HTTP_400_BAD_REQUEST
HTTP_401_UNAUTHORIZED
HTTP_402_PAYMENT_REQUIRED
HTTP_403_FORBIDDEN
HTTP_404_NOT_FOUND
HTTP_405_METHOD_NOT_ALLOWED
HTTP_406_NOT_ACCEPTABLE
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
HTTP_408_REQUEST_TIMEOUT
HTTP_409_CONFLICT
HTTP_410_GONE
HTTP_411_LENGTH_REQUIRED
HTTP_412_PRECONDITION_FAILED
HTTP_413_REQUEST_ENTITY_TOO_LARGE
HTTP_414_REQUEST_URI_TOO_LONG
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_422_UNPROCESSABLE_ENTITY
HTTP_423_LOCKED
HTTP_424_FAILED_DEPENDENCY
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS

5,Server Error - 5xx

5xx类的响应状态码表明服务器意识到它已经错误或无法执行请求的情况。

  • 除了在响应 HEAD 请求时,服务器应该包括一个实体,其中包含错误情况的解释,以及它是临时的还是永久的。
HTTP_500_INTERNAL_SERVER_ERROR
HTTP_501_NOT_IMPLEMENTED
HTTP_502_BAD_GATEWAY
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_507_INSUFFICIENT_STORAGE
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED

6,帮助函数 (Helper functions)

以下帮助函数可用于识别响应代码的类别。

is_informational()  # 1xx
is_success()        # 2xx
is_redirect()       # 3xx
is_client_error()   # 4xx
is_server_error()   # 5xx

四,装饰 API 视图 (Wrapping API views)

REST框架提供了两个可以用于编写API视图的装饰器:
1,用于函数视图的 @api_view 装饰器。若用视图函数开发API接口,则必须对视图函数使用@api_view装饰器。

  • 源码在 rest_framework\decorators.py。

2,用于类视图的 APIView 类即若用视图类开发API接口,则视图类继承APIView类。

  • 源码在 rest_framework\views.py。

这些装饰都提供了一些自动化的功能,例如确保你能在视图中接收到Request实例,并能将上下文添加到Response以便执行内容协商。

装饰器还提供了诸如在适当时候返回 405 Method Not Allowed 响应,并处理在使用格式错误的输入来访问request.data时引发的任何ParseError异常。

五,函数视图

在REST framework中,你可以使用常规的函数视图,也可以使用它提供的一组简单的装饰器装饰你的视图函数,以确保视图函数会收到Request(而不是Django一般的HttpRequest)对象,并且返回Response(而不是Django的HttpResponse)对象,同时允许你设置这个请求的处理方式。

(一)@api_view

@api_view(http_method_names=['GET'], exclude_from_schema=False)处理请求并返回响应。

  • http_method_names:视图应该响应的 HTTP 方法列表。默认情况下,只有 GET 方法会被接受。其他方法将以 405 Method Not Allowed 进行响应,但可以改变。
  • exclude_from_schema:标记 API 视图来忽略任何自动生成的视图
@api_view(['GET', 'POST'])
def hello_world(request):
    if request.method == 'POST':
        return Response({"message": "Got some data!", "data": request.data})
    return Response({"message": "Hello, world!"})

@api_view(['GET'], exclude_from_schema=True)
def api_docs(request):
    ...
  • 该视图将使用 settings 中指定的默认渲染器,解析器,认证类等。

(二)API 策略装饰器 (API policy decorators)

REST framework 还提供了一组可以附加到视图上的装饰器来重写默认设置。这些装饰器必须放在 @api_view 的后面。

比如,要创建一个使用限制器确保特定用户每天只能调用一次的视图,可以用 @throttle_classes 装饰器并给它传递一个限制器类的列表:

from rest_framework.decorators import api_view, throttle_classes
from rest_framework.throttling import UserRateThrottle

class OncePerDayUserThrottle(UserRateThrottle):
    rate = '1/day'

@api_view(['GET'])
@throttle_classes([OncePerDayUserThrottle])
def view(request):
    return Response({"message": "Hello for today! See you tomorrow!"})

rest_framework/decorators.py中了提供这些可用的装饰器:

  • 这些装饰器中的每一个都接受一个必须是类的列表或元组的参数。
  • @renderer_classes(...):覆盖默认的渲染器功能
  • @parser_classes(...):覆盖默认的解析器功能
  • @authentication_classes(...):覆盖默认的认证功能
  • @throttle_classes(...):覆盖默认的限流功能
  • @permission_classes(...):覆盖默认的权限功能

rest_framework/throttling.py则提供了可在上面那些装饰器中使用的类,其具体作用请参考源码中的注释。

六,创建完整的 API 视图

Django Rest Framework 1:序列化与反序列化中的视图实现是简单且粗糙的。
为了能处理 JSON 格式的数据,我们还实现了一个现在看来就有些多余的 JSONResponse 类,因为使用 Response 会更加高效,还能简化代码。

现在我们所需要的基础组件都差不多介绍完了,接下来就是用视图装饰器、函数视图、请求对象、响应对象和响应状态码来创建较完整的 API 视图了。

重构我们的视图:

from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from snippet.models import Snippet
from snippet.serializers import SnippetSerializer


# Create your views here.
@api_view(['GET', 'POST'])
def snippet_list(request):
    """
    List all code snippets, or create a new snippet.
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  • 我们的实例视图是对前一个示例的改进。它更简洁了一些,现在的代码感觉与我们使用 Forms API
    时非常相似。我们还使用了状态码,这使得响应的含义更加明显。
@api_view(['GET', 'PUT', 'DELETE'])
def snippet_detail(request, pk):
    """
    Retrieve, update or delete a code snippet.
    """
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)

    elif request.method == 'PUT':
        serializer = SnippetSerializer(snippet, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        snippet.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
  • 这应该很熟悉了——它与使用常规的 Django 视图没有太大的不同。不再显式地将请求或响应绑定到给定的内容类型。请求。data 可以自动处理传入的 JSON 请求,但它也可以处理其他格式。类似地,我们返回带有数据的响应对象,但让REST框架自动将响应呈现为正确的内容类型。

七,给网址添加可选的格式后缀

为了利用我们的响应不再硬连接到单一内容类型这一事实,我们可以向 API 端点添加对格式后缀的支持。这种机制为我们提供了能显式地引用的给定格式的 url,这意味着我们的 API 将能够处理像http://example.com/api/items/4.json 这样的 url。

  • 我们不一定非要添加这些额外的 url 模式,但它确实为我们提供了一种简单、干净的方式来引用特定的格式。
  • 官方 API :Format suffixes

1,对两个视图函数都追加一个参数,format=None

def snippet_list(request, format=None):def snippet_detail(request, pk, format=None):

2,在snippet/urls.py 文件的 urls 基础上追加format_suffix_patterns

from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views

urlpatterns = [
    url(r'^snippets/$', views.snippet_list),
    url(r'^snippets/(?P<pk>[0-9]+)$', views.snippet_detail),
]

urlpatterns = format_suffix_patterns(urlpatterns)

3,运行:
运行服务器后,先用 HTTPie 发送用于测试的请求,看看结果如何:

>>> http http://127.0.0.1:8000/snippets/
HTTP/1.1 200 OK
Allow: OPTIONS, GET, POST
Content-Length: 411
Content-Type: application/json
Date: Tue, 08 Feb 2022 06:30:16 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.6
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

[
    {
        "code": "foo = \"bar\"\n",
        "id": 1,
        "language": "python",
        "linenos": false,
        "style": "friendly",
        "title": ""
    },
    {
        "code": "print \"hello, world\"\n",
        "id": 2,
        "language": "python",
        "linenos": false,
        "style": "friendly",
        "title": ""
    },
    {
        "code": "print \"hello, world\"",
        "id": 3,
        "language": "python",
        "linenos": false,
        "style": "friendly",
        "title": ""
    },
    {
        "code": "print 123",
        "id": 4,
        "language": "python",
        "linenos": false,
        "style": "friendly",
        "title": ""
    }
]

尝试通过指定请求头Accept选项控制接收的响应的格式:

>>> http http://127.0.0.1:8000/snippets/ Accept:application/json  # 请求JSON>>> http http://127.0.0.1:8000/snippets/ Accept:text/html         # 请求HTML

或者通过附加格式后缀控制接收的响应的格式:

>>> http http://127.0.0.1:8000/snippets.json  # JSON后缀
>>> http http://127.0.0.1:8000/snippets.api   # 浏览器可浏览API后缀

同样可以使请求头Content-Type选项用控制我们发送的请求的格式:

>>> http --form POST http://127.0.0.1:8000/snippets/ code="print 123"
HTTP/1.1 201 Created
Allow: OPTIONS, GET, POST
Content-Length: 93
Content-Type: application/json
Date: Tue, 08 Feb 2022 06:40:40 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.6
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "code": "print 123",
    "id": 5,
    "language": "python",
    "linenos": false,
    "style": "friendly",
    "title": ""
}

具体可以向请求命令中添加 --debug 查看请求类型:

>>> http --debug http://127.0.0.1:8000/snippets.json
HTTPie 2.4.0
Requests 2.25.1
Pygments 2.9.0
Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)]
c:\software_point\python\python.exe
Windows 10

<Environment {'colors': 256,
 'config': {'default_options': []},
 'config_dir': WindowsPath('C:/Users/PC/AppData/Roaming/httpie'),
 'devnull': <property object at 0x0000025C8EB6A958>,
 'is_windows': True,
 'log_error': <function Environment.log_error at 0x0000025C8EB62DC8>,
 'program_name': 'http',
 'stderr': <colorama.ansitowin32.StreamWrapper object at 0x0000025C8EB56C48>,
 'stderr_isatty': True,
 'stdin': <_io.TextIOWrapper name='<stdin>' mode='r' encoding='utf-8'>,
 'stdin_encoding': 'utf-8',
 'stdin_isatty': True,
 'stdout': <colorama.ansitowin32.StreamWrapper object at 0x0000025C8EB45408>,
 'stdout_encoding': 'utf-8',
 'stdout_isatty': True}>

>>> requests.request(**{'auth': None,
 'data': RequestJSONDataDict(),
 'headers': {'User-Agent': b'HTTPie/2.4.0'},
 'method': 'get',
 'params': <generator object MultiValueOrderedDict.items at 0x0000025C8EBEBC48>,
 'url': 'http://127.0.0.1:8000/snippets.json'})

HTTP/1.1 200 OK
Allow: OPTIONS, GET, POST
Content-Length: 505
Content-Type: application/json
Date: Tue, 08 Feb 2022 06:41:40 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.6
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

[
    {
        "code": "foo = \"bar\"\n",
        "id": 1,
        "language": "python",
        "linenos": false,
        "style": "friendly",
        "title": ""
    },
    {
        "code": "print \"hello, world\"\n",
        "id": 2,
        "language": "python",
        "linenos": false,
        "style": "friendly",
        "title": ""
    },
    {
        "code": "print \"hello, world\"",
        "id": 3,
        "language": "python",
        "linenos": false,
        "style": "friendly",
        "title": ""
    },
    {
        "code": "print 123",
        "id": 4,
        "language": "python",
        "linenos": false,
        "style": "friendly",
        "title": ""
    },
    {
        "code": "print 123",
        "id": 5,
        "language": "python",
        "linenos": false,
        "style": "friendly",
        "title": ""
    }
]

访问 http://127.0.0.1:8000/snippets.json
在这里插入图片描述

八,可浏览的 API

API 即代表应用程序编程接口,但人类也必须能够阅读 API。Django REST Framework 支持在请求指定格式时为每个资源生成人性化的 HTML 输出。这些页面允许轻松地浏览资源,以及使用 POST、PUT 和向资源提交数据的表单 DELETE,比如直接访问 http://127.0.0.1:8000/snippets/

在这里插入图片描述
这与之前的显示效果完全不同了!此时的页面显示了更多的内容信息,甚至有一些可供填充的表单!这一切都是 Request 对象 、 Response 对象以及幕后各个组件相互支持的结果!

这个效果被称为可浏览的 API (The Browsable API),这是 Django REST Framework 地撒手锏之一!请参考官方主题:The Browsable API

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值