├── .gitignore ├── API-Guide ├── Authentication.md ├── Content-negotiation.md ├── Exceptions.md ├── Filtering.md ├── Format-suffixes.md ├── Generic-views.md ├── Metadata.md ├── Pagination.md ├── Parsers.md ├── Permissions.md ├── Renderers.md ├── Requests.md ├── Responses.md ├── Returning-URLs.md ├── Routers.md ├── Schemas.md ├── Serializer-fields.md ├── Serializer-relations.md ├── Serializers.md ├── Settings.md ├── Status-Codes.md ├── Testing.md ├── Throttling.md ├── Validators.md ├── Versioning.md ├── ViewSets.md └── Views.md ├── Community ├── 3.0_Announcement.md ├── 3.1_Announcement.md ├── 3.2_Announcement.md ├── 3.3_Announcement.md ├── 3.4_Announcement.md ├── 3.5_Announcement.md ├── 3.6_Announcement.md ├── 3.7_Announcement.md ├── 3.8_Announcement.md ├── 3.9_Announcement.md ├── Contributing_to_REST_framework.md ├── Funding.md ├── Jobs.md ├── Kickstarter_Announcement.md ├── Mozilla_Grant.md ├── Project_management.md ├── Release_Notes.md ├── Third_Party_Packages.md └── Tutorials_and_Resources.md ├── Image └── api-docs.gif ├── README.md ├── Topics ├── AJAX,_CSRF_&_CORS.md ├── API_Clients.md ├── Browser_enhancements.md ├── Documenting_your_API.md ├── HTML_&_Forms.md ├── Internationalization.md ├── REST,_Hypermedia_&_HATEOAS.md └── The_Browsable_API.md └── Tutorial ├── Authentication-and-Permissions.md ├── Class-based-views.md ├── Quickstart.md ├── Relationships-and-Hyperlinked-APIs.md ├── Requests-and-Responses.md ├── Schemas-and-client_libraries.md ├── Serialization.md └── ViewSets-and-Routers.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /API-Guide/Content-negotiation.md: -------------------------------------------------------------------------------- 1 | # 内容协商 (Content negotiation) 2 | HTTP 具有用于 “内容协商” 的若干机制的规定—当有多个可用表示时,为给定响应选择最佳表示的过程。—— [RFC 2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html), Fielding et al. 3 | 4 | 内容协商是基于客户端或服务器首选项选择多个可能表示之一以返回到客户端的过程。 5 | 6 | ## 确定接受的渲染器 (Determining the accepted renderer) 7 | REST framework 根据可用的渲染器,每个渲染器的优先级以及客户端的 `Accept:` 标头,使用简单的内容协商样式来确定应将哪些媒体类型返回给客户端。所使用的样式部分由客户端驱动,部分由服务器驱动。 8 | 9 | 1. 更具体的媒体类型优先于较不特定的媒体类型。 10 | 2. 如果多个媒体类型具有相同的特异性,则优先根据为给定视图配置的渲染器排序。 11 | 12 | 例如,给定以下 `Accept` 标头: 13 | ```python 14 | application/json; indent=4, application/json, application/yaml, text/html, */* 15 | ``` 16 | 17 | 每种给定媒体类型的优先级将是: 18 | 19 | - `application/json; indent=4` 20 | - `application/json`, `application/yaml` 和 `text/html` 21 | - `*/*` 22 | 23 | 如果请求的视图仅配置了 `YAML` 和 `HTML` 的渲染器,则 REST framework 将选择`renderer_classes` 列表或 `DEFAULT_RENDERER_CLASSES` 设置中首先列出的渲染器。 24 | 25 | 有关 `HTTP Accept` 标头的更多信息,请参阅 [RFC 2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html) 26 | 27 | *** 28 | **注意**:确定首选项时,REST framework 不会考虑 “q” 值。使用 “q” 值会对缓存产生负面影响,并且在作者看来,这是一种不必要且过于复杂的内容协商方法。 29 | 30 | 这是一种有效的方法,因为 HTTP 规范故意未指定服务器应如何根据基于客户端的首选项对基于服务器的首选项进行加权。 31 | *** 32 | 33 | # 自定义内容协商 (Custom content negotiation) 34 | 您不太可能希望为 REST framework 提供自定义内容协商方案,但如果需要,您可以这样做。要实现自定义内容协商方案,请覆盖 `BaseContentNegotiation`。 35 | 36 | REST framework 的内容协商类处理选择适当的请求解析器和适当的响应渲染器,因此您应实现 `.select_parser(request, parsers)` 和 `.select_renderer(request, renderers, format_suffix)` 方法。 37 | 38 | `select_parser()` 方法应从可用解析器列表中返回一个解析器实例,如果没有解析器可以处理传入请求,则返回 `None`。 39 | 40 | `select_renderer()` 方法应返回 (渲染器实例,媒体类型) 的二元组,或引发 `NotAcceptable` 异常。 41 | 42 | ## 举个栗子 43 | 以下是自定义内容协商类,它在选择适当的解析器或渲染器时会忽略客户端请求。 44 | ```python 45 | from rest_framework.negotiation import BaseContentNegotiation 46 | 47 | class IgnoreClientContentNegotiation(BaseContentNegotiation): 48 | def select_parser(self, request, parsers): 49 | """ 50 | 选择 `.parser_classes` 列表中的第一个解析器。 51 | """ 52 | return parsers[0] 53 | 54 | def select_renderer(self, request, renderers, format_suffix): 55 | """ 56 | 选择 `.renderer_classes` 列表中的第一个渲染器。 57 | """ 58 | return (renderers[0], renderers[0].media_type) 59 | ``` 60 | 61 | ## 设置内容协商 (Setting the content negotiation) 62 | 使用 `DEFAULT_CONTENT_NEGOTIATION_CLASS` 设置可以全局设置默认内容协商类。例如,以下设置将使用我们的示例 `IgnoreClientContentNegotiation` 类。 63 | ```python 64 | REST_FRAMEWORK = { 65 | 'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'myapp.negotiation.IgnoreClientContentNegotiation', 66 | } 67 | ``` 68 | 69 | 您还可以使用 `APIView` 基于类的视图设置用于单个视图或视图集的内容协商。 70 | ```python 71 | from myapp.negotiation import IgnoreClientContentNegotiation 72 | from rest_framework.response import Response 73 | from rest_framework.views import APIView 74 | 75 | class NoNegotiationView(APIView): 76 | """ 77 | An example view that does not perform content negotiation. 78 | """ 79 | content_negotiation_class = IgnoreClientContentNegotiation 80 | 81 | def get(self, request, format=None): 82 | return Response({ 83 | 'accepted media type': request.accepted_renderer.media_type 84 | }) 85 | ``` 86 | -------------------------------------------------------------------------------- /API-Guide/Exceptions.md: -------------------------------------------------------------------------------- 1 | # 异常 (Exceptions) 2 | 异常...允许错误处理在程序结构的中心或者高级的地方被清晰有条理的组织起来。—— Doug Hellmann, [Python 异常处理技术](https://doughellmann.com/blog/2009/06/19/python-exception-handling-techniques/) 3 | 4 | ## REST framework 视图中的异常处理 (Exception handling in REST framework views) 5 | REST framework 的视图处理各种异常,并处理返回适当的错误响应。 6 | 7 | 处理的异常有: 8 | 9 | - 在 REST framework 内引发的 `APIException` 的子类。 10 | - Django 的 `Http404` 异常。 11 | - Django 的 `PermissionDenied` 异常。 12 | 13 | 针对每种情况,REST framework 将返回具有适当的状态码和内容类型的响应。响应的主体将包含有关错误性质的任何其他细节。 14 | 15 | 大多数错误响应将包括响应正文中的 `detail` 键。 16 | 17 | 例如,以下请求: 18 | ```python 19 | DELETE http://api.example.com/foo/bar HTTP/1.1 20 | Accept: application/json 21 | ``` 22 | 23 | 可能会收到一个错误响应,指示在该资源上不允许 `DELETE` 方法: 24 | ```python 25 | HTTP/1.1 405 Method Not Allowed 26 | Content-Type: application/json 27 | Content-Length: 42 28 | 29 | {"detail": "Method 'DELETE' not allowed."} 30 | ``` 31 | 32 | 验证错误的处理方式稍有不同,并将字段名称作为响应中的键。如果验证错误没有指定特定字段,那么它将使用 “non_field_errors” 键,或者为 `NON_FIELD_ERRORS_KEY` setting 中设置的任何字符串值。 33 | 34 | 任何示例验证错误可能如下所示: 35 | ```python 36 | HTTP/1.1 400 Bad Request 37 | Content-Type: application/json 38 | Content-Length: 94 39 | 40 | {"amount": ["A valid integer is required."], "description": ["This field may not be blank."]} 41 | ``` 42 | 43 | ## 自定义异常处理 (Custom exception handling) 44 | 您可以通过创建处理程序函数来实现自定义异常处理,该函数将 API 视图中引发的异常转换为响应对象。这允许您控制 API 使用的错误响应的样式。 45 | 46 | 该函数必须带有一对参数,第一个是要处理的异常,第二个是包含任何额外上下文的字典,例如当前正在处理的视图。异常处理程序函数应返回 `Response` 对象,或者如果无法处理异常,则返回 `None`。如果处理程序返回 `None`,那么异常将被重新引发,Django 将返回一个标准的 HTTP 500 'server error' 响应。 47 | 48 | 例如,您可能希望确保所有错误响应都包含响应正文中的 HTTP 状态代码,如下所示: 49 | ```python 50 | HTTP/1.1 405 Method Not Allowed 51 | Content-Type: application/json 52 | Content-Length: 62 53 | 54 | {"status_code": 405, "detail": "Method 'DELETE' not allowed."} 55 | ``` 56 | 57 | 为了改变响应的样式,您可以编写以下自定义异常处理程序: 58 | ```python 59 | from rest_framework.views import exception_handler 60 | 61 | def custom_exception_handler(exc, context): 62 | # Call REST framework's default exception handler first, 63 | # to get the standard error response. 64 | response = exception_handler(exc, context) 65 | 66 | # Now add the HTTP status code to the response. 67 | if response is not None: 68 | response.data['status_code'] = response.status_code 69 | 70 | return response 71 | ``` 72 | 73 | 默认处理程序不使用上下文参数,但如果异常处理程序需要诸如当前正在处理的视图之类的进一步信息,这可能很有用,这可以作为 `context['view']` 访问。 74 | 75 | 异常处理程序还必须使用 `EXCEPTION_HANDLER` 设置键在你的设置中进行配置。例如: 76 | ```python 77 | REST_FRAMEWORK = { 78 | 'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler' 79 | } 80 | ``` 81 | 82 | 如果未指定,则 `'EXCEPTION_HANDLER'` 设置默认为由 REST framework 提供的标准异常处理程序: 83 | ```python 84 | REST_FRAMEWORK = { 85 | 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler' 86 | } 87 | ``` 88 | 89 | 请注意,异常处理程序仅由异常引发生成的响应调用。它不会用于视图直接返回的任何响应,例如当序列化器验证失败时通用视图返回的 `HTTP_400_BAD_REQUEST` 响应。 90 | 91 | *** 92 | 93 | # API 参考 (API Reference) 94 | ## APIException 95 | **签名**:`APIException()` 96 | 97 | `APIView` 类或 `@api_view` 中引发的所有异常的**基类**。 98 | 99 | 要提供自定义异常,请继承 `APIException`,并在该类上设置 `.status_code`,`.default_detail` 和 `default_code` 属性。 100 | 101 | 例如,如果您的 API 依赖于有时可能无法访问的第三方服务,则可能希望为 "503 Service Unavailable" HTTP 响应码实现异常。您可以这样做: 102 | ```python 103 | from rest_framework.exceptions import APIException 104 | 105 | class ServiceUnavailable(APIException): 106 | status_code = 503 107 | default_detail = 'Service temporarily unavailable, try again later.' 108 | default_code = 'service_unavailable' 109 | ``` 110 | 111 | #### 检查 API 异常 (Inspecting API exceptions) 112 | 有许多不同的属性可用于检查API异常的状态。您可以使用它们为项目构建自定义异常处理。 113 | 114 | 可用的属性和方法是: 115 | 116 | - `.detail` - 返回错误的文本描述。 117 | - `.get_codes()` - 返回错误的代码标识符。 118 | - `.get_full_details()` - 返回文本描述和代码标识符。 119 | 120 | 在大多数情况下,错误细节将是一个简单的项: 121 | ```python 122 | >>> print(exc.detail) 123 | You do not have permission to perform this action. 124 | >>> print(exc.get_codes()) 125 | permission_denied 126 | >>> print(exc.get_full_details()) 127 | {'message':'You do not have permission to perform this action.','code':'permission_denied'} 128 | ``` 129 | 130 | 在验证错误的情况下,错误详情将是项列表或字典: 131 | ```python 132 | >>> print(exc.detail) 133 | {"name":"This field is required.","age":"A valid integer is required."} 134 | >>> print(exc.get_codes()) 135 | {"name":"required","age":"invalid"} 136 | >>> print(exc.get_full_details()) 137 | {"name":{"message":"This field is required.","code":"required"},"age":{"message":"A valid integer is required.","code":"invalid"}} 138 | ``` 139 | 140 | ## ParseError 141 | **签名**:`ParseError(detail=None, code=None)` 142 | 143 | 如果请求在访问 `request.data` 时包含格式错误的数据,则引发此异常。 144 | 145 | 默认情况下,此异常会导致 HTTP 状态码 "400 Bad Request" 的响应。 146 | 147 | ## AuthenticationFailed 148 | **签名**:`AuthenticationFailed(detail=None, code=None)` 149 | 150 | 当传入请求包含不正确的身份验证时引发。 151 | 152 | 默认情况下,此异常会导致 HTTP 状态码 “401 Unauthenticated” 的响应,但它也可能导致 “403 Forbidden” 的响应,具体取决于所使用的身份验证方案。有关详细信息,请参阅[身份验证文档](http://www.django-rest-framework.org/api-guide/authentication/)。 153 | 154 | ## NotAuthenticated 155 | **签名**:`NotAuthenticated(detail=None, code=None)` 156 | 157 | 未经身份验证的请求未通过权限检查时引发。 158 | 159 | 默认情况下,此异常会导致 HTTP 状态码 “401 Unauthenticated” 的响应,但它也可能导致 “403 Forbidden” 的响应,具体取决于所使用的身份验证方案。有关详细信息,请参阅[身份验证文档](http://www.django-rest-framework.org/api-guide/authentication/)。 160 | 161 | ## PermissionDenied 162 | **签名**:`PermissionDenied(detail=None, code=None)` 163 | 164 | 当经过身份验证的请求未通过权限检查时引发。 165 | 166 | 默认情况下,此异常会导致 HTTP 状态码为 “403 Forbidden” 的响应。 167 | 168 | ## NotFound 169 | **签名**:`NotFound(detail=None, code=None)` 170 | 171 | 当资源不存在于给定的 URL 时引发。该异常相当于标准的 `Http404` Django 异常。 172 | 173 | 默认情况下,此异常会导致 HTTP 状态码 “404 Not Found” 的响应。 174 | 175 | ## MethodNotAllowed 176 | **签名**:`MethodNotAllowed(method, detail=None, code=None)` 177 | 178 | 当传入请求发生,但未映射到视图上的处理程序方法时引发。 179 | 180 | 默认情况下,此异常会导致 HTTP 状态代码 “405 Method Not Allowed” 的响应。 181 | 182 | ## NotAcceptable 183 | **签名**:`NotAcceptable(detail=None, code=None)` 184 | 185 | 当传入请求的 `Accept` 标头不能被任何可用的渲染器满足时引发。 186 | 187 | 默认情况下,此异常会导致 HTTP 状态码为 “406 Not Acceptable” 的响应。 188 | 189 | ## UnsupportedMediaType 190 | **签名**:`UnsupportedMediaType(media_type, detail=None, code=None)` 191 | 192 | 如果在访问 `request.data` 时没有可以处理请求数据的内容类型的解析器,则引发。 193 | 194 | 默认情况下,此异常会导致 HTTP 状态码为 “415 Unsupported Media Type” 的响应。 195 | 196 | ## Throttled 197 | **签名**:`Throttled(wait=None, detail=None, code=None)` 198 | 199 | 当传入请求未通过限制检查时引发。 200 | 201 | 默认情况下,此异常会导致 HTTP 状态码 “429 Too Many Requests” 的响应。 202 | 203 | ## ValidationError 204 | **签名**:`ValidationError(detail, code=None)` 205 | 206 | `ValidationError` 异常与其他 `APIException` 类略有不同: 207 | 208 | - `detail` 参数是必需的,不是可选的。 209 | - `detail` 参数可以是错误详情列表或字典,也可以是嵌套的数据结构。 210 | - 按照惯例,您应该导入 serializers 模块并使用完全限定的 `ValidationError` 样式,用以区别 Django 的内置验证错误。例如:`raise serializers.ValidationError('This field must be an integer value.')` 211 | 212 | `ValidationError` 类应用于序列化器和字段验证以及验证器类。在调用 `serializer.is_valid` 时使用 `raise_exception` 关键字参数也会引发: 213 | ```python 214 | serializer.is_valid(raise_exception=True) 215 | ``` 216 | 217 | 通用视图使用 `raise_exception=True` 标志,这意味着您可以在 API 中全局覆盖验证错误响应的样式。为此,请使用自定义异常处理程序,如上所述。 218 | 219 | 默认情况下,此异常会导致 HTTP 状态码为 “400 Bad Request” 的响应。 220 | 221 | *** 222 | 223 | # 通用错误视图 (Generic Error Views) 224 | Django REST Framework 提供了两个错误视图,适用于提供通用 JSON `500` Server Error 和 `400` Bad Request 响应。( Django 的默认错误视图提供 HTML 响应,这可能不适合仅限 API 应用程序。) 225 | 226 | 按照 [Django 的自定义错误视图文档](https://docs.djangoproject.com/en/dev/topics/http/views/#customizing-error-views)使用它们。 227 | 228 | ## `rest_framework.exceptions.server_error` 229 | 返回状态码为 `500` 且 `application/json` 内容类型的响应。 230 | 231 | 设为 `handler500`: 232 | ```python 233 | handler500 = 'rest_framework.exceptions.server_error' 234 | ``` 235 | 236 | ## `rest_framework.exceptions.server_error` 237 | 返回状态码为 `400` 且 `application/json` 内容类型的响应。 238 | 239 | 设为 `handler400`: 240 | ```python 241 | handler400 = 'rest_framework.exceptions.bad_request' 242 | ``` 243 | -------------------------------------------------------------------------------- /API-Guide/Format-suffixes.md: -------------------------------------------------------------------------------- 1 | # 格式后缀 (Format suffixes) 2 | 第 6.2.1 节并未说明应始终使用内容协商。—— Roy Fielding, [REST 讨论邮件列表](http://tech.groups.yahoo.com/group/rest-discuss/message/5857) 3 | 4 | Web API 的常见模式是在 URL 上使用文件扩展名来为给定的媒体类型提供端点。例如,'http://example.com/api/users.json' 用于提供 JSON 表示。 5 | 6 | 在 URLconf 中为您的 API 添加格式后缀模式到每个条目是容易出错和非 DRY 的,因此 REST framework 提供了将这些模式添加到 URLconf 的快捷方式。 7 | 8 | ## format_suffix_patterns 9 | **签名**:format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None) 10 | 11 | 返回一个 URL 模式列表,其中包括附加到提供的每个 URL 模式的格式后缀模式。 12 | 13 | 参数: 14 | 15 | - **urlpatterns**:必需。URL 模式列表。 16 | - **suffix_required**:可选。布尔值,指示 URL 中的后缀是可选的还是强制的。默认为 `False`,意味着默认情况下后缀是可选的。 17 | - **allowed**:可选。有效格式后缀的列表或元组。如果没有提供,将使用通配符格式后缀模式。 18 | 19 | 举个栗子: 20 | ```python 21 | from rest_framework.urlpatterns import format_suffix_patterns 22 | from blog import views 23 | 24 | urlpatterns = [ 25 | url(r'^/$', views.apt_root), 26 | url(r'^comments/$', views.comment_list), 27 | url(r'^comments/(?P[0-9]+)/$', views.comment_detail) 28 | ] 29 | 30 | urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html']) 31 | ``` 32 | 33 | 在使用 `format_suffix_patterns` 时,您必须确保将 `'format'` 关键字参数添加到相应的视图。例如: 34 | ```python 35 | @api_view(('GET', 'POST')) 36 | def comment_list(request, format=None): 37 | # do stuff... 38 | ``` 39 | 40 | 或者使用基于类的视图: 41 | ```python 42 | class CommentList(APIView): 43 | def get(self, request, format=None): 44 | # do stuff... 45 | 46 | def post(self, request, format=None): 47 | # do stuff... 48 | ``` 49 | 50 | 可以使用 `FORMAT_SUFFIX_KWARG` 设置来修改使用的 kwarg 的名称。 51 | 52 | 另请注意,`format_suffix_patterns` 不支持陷入到 `include` URL 模式。 53 | 54 | ### 使用 `i18n_patterns` (Using with `i18n_patterns`) 55 | 如果使用 Django 提供的 `i18n_patterns` 函数以及 `format_suffix_patterns`,则应确保将 `i18n_patterns` 函数应用到最终或最外层函数。例如: 56 | ```python 57 | url patterns = [ 58 | … 59 | ] 60 | 61 | urlpatterns = i18n_patterns( 62 | format_suffix_patterns(urlpatterns, allowed=['json', 'html']) 63 | ) 64 | ``` 65 | 66 | *** 67 | 68 | ## 查询参数格式 (Query parameter formats) 69 | 格式后缀的替代方法是在查询参数中包含所请求的格式。REST framework 默认提供此选项,并且它在可浏览的 API 中用于在不同的可用表示之间切换。 70 | 71 | 要使用其短格式的选择表示,请使用 `format` 查询参数。例如: `http://example.com/organizations/?format=csv`。 72 | 73 | 此查询参数的名称可以使用 `URL_FORMAT_OVERRIDE` 设置进行修改。设置该值为 `None` 以禁用此行为。 74 | 75 | *** 76 | 77 | ## 接受标头和格式后缀 (Accept headers vs. format suffixes) 78 | 在一些 Web 社区中似乎有这样一种观点,即文件名扩展不是 RESTful 模式,应始终使用 `HTTP Accept` 标头。 79 | 80 | 这实际上是一种误解。例如,引用 Roy Fielding 的以下引文,讨论查询参数媒体类型指示符的相对优点。文件扩展媒体类型指示符: 81 | 82 | “这就是为什么我总是喜欢扩展。这两种选择都与 REST 无关。” - Roy Fielding,[REST 讨论邮件列表](http://tech.groups.yahoo.com/group/rest-discuss/message/14844) 83 | 84 | 引用中没有提到 Accept 标头,但是它清楚地表明格式后缀应该被认为是可接受的模式。 85 | -------------------------------------------------------------------------------- /API-Guide/Metadata.md: -------------------------------------------------------------------------------- 1 | # 元数据 (Metadata) 2 | [`OPTIONS`] 方法允许客户端在无暗示资源动作或发起资源检索的情况下确定与资源相关的选项和/或要求,或服务器的能力。—— [RFC7231, Section 4.3.7.](https://tools.ietf.org/html/rfc7231#section-4.3.7) 3 | 4 | REST framework 包括一个用于确定您的 API 应该如何响应 `OPTIONS` 请求可配置的机制。这允许您返回 API 模式或其他资源信息。 5 | 6 | 目前还没有任何广泛采用的约定来确定对于 HTTP `OPTIONS` 请求应该返回什么类型的响应,因此我们提供了一个特别的样式,它返回一些有用的信息。 7 | 8 | 下面是一个演示默认情况下返回的信息的示例响应。 9 | ```python 10 | HTTP 200 OK 11 | Allow: GET, POST, HEAD, OPTIONS 12 | Content-Type: application/json 13 | 14 | { 15 | "name": "To Do List", 16 | "description": "List existing 'To Do' items, or create a new item.", 17 | "renders": [ 18 | "application/json", 19 | "text/html" 20 | ], 21 | "parses": [ 22 | "application/json", 23 | "application/x-www-form-urlencoded", 24 | "multipart/form-data" 25 | ], 26 | "actions": { 27 | "POST": { 28 | "note": { 29 | "type": "string", 30 | "required": false, 31 | "read_only": false, 32 | "label": "title", 33 | "max_length": 100 34 | } 35 | } 36 | } 37 | } 38 | ``` 39 | 40 | ## 设置元数据方案 (Setting the metadata scheme) 41 | 您可以使用 `'DEFAULT_METADATA_CLASS'` 设置键全局设置元数据类: 42 | ```python 43 | REST_FRAMEWORK = { 44 | 'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata' 45 | } 46 | ``` 47 | 48 | 或者,您可以为视图单独设置元数据类: 49 | ```python 50 | class APIRoot(APIView): 51 | metadata_class = APIRootMetadata 52 | 53 | def get(self, request, format=None): 54 | return Response({ 55 | ... 56 | }) 57 | ``` 58 | 59 | REST framework 包只包含一个名为 `SimpleMetadata` 的元数据类实现。如果您想使用另一种样式,则需要实现一个自定义的元数据类。 60 | 61 | ## 创建模式端点 (Creating schema endpoints) 62 | 如果您对创建使用常规 `GET` 请求访问的模式端点有特定的要求,您可考虑重新使用元数据 API 来进行这样的操作。 63 | 64 | 例如,可以在视图集上使用以下附加路由来提供可链接的模式端点。 65 | ```python 66 | @action(methods=['GET'], detail=False) 67 | def schema(self, request): 68 | meta = self.metadata_class() 69 | data = meta.determine_metadata(request, self) 70 | return Response(data) 71 | ``` 72 | 73 | 这有几个您可能会选择采用此方法的原因,包括 `OPTIONS` 响应[不可缓存](https://www.mnot.net/blog/2012/10/29/NO_OPTIONS)。 74 | 75 | *** 76 | 77 | # 自定义元数据类 (Custom metadata classes) 78 | 如果您想提供一个自定义的元数据类,您应该继承 `BaseMetadata` 并且实现 `determine_metadata(self, request, view)` 方法。 79 | 80 | 您可能想要做的有用的事情包括返回模式信息,使用 [JSON 模式](http://json-schema.org/)等格式,或将调试信息返回给管理员用户。 81 | 82 | ## 举个栗子 83 | 以下类可用于限制返回到 `OPTIONS` 请求的信息。 84 | ```python 85 | class MinimalMetadata(BaseMetadata): 86 | """ 87 | 不要为 “OPTIONS” 请求包含字段和其他信息。 88 | 只需返回名称和说明即可。 89 | """ 90 | def determine_metadata(self, request, view): 91 | return { 92 | 'name': view.get_view_name(), 93 | 'description': view.get_view_description() 94 | } 95 | ``` 96 | 97 | 然后配置您的设置以使用此自定义类: 98 | ```python 99 | REST_FRAMEWORK = { 100 | 'DEFAULT_METADATA_CLASS': 'myproject.apps.core.MinimalMetadata' 101 | } 102 | ``` 103 | 104 | # 第三方包 (Third party packages) 105 | 以下第三方包提供其他元数据实现。 106 | 107 | ## DRF-schema-adapter 108 | [drf-schema-adapter](https://github.com/drf-forms/drf-schema-adapter) 是可以更轻松地为前端框架和库提供模式信息一组工具。它提供了元数据混合以及 2 个元数据类和适合于生成 [JSON 模式](http://json-schema.org/)的多个适配器以及各种库可读的模式信息。 109 | 110 | 您也可以编写自己的适配器来处理特定的前端。如果您希望这样做,它还提供了一个可以将这些模式信息导出到 json 文件的导出器。 111 | -------------------------------------------------------------------------------- /API-Guide/Parsers.md: -------------------------------------------------------------------------------- 1 | # 解析器 (Parsers) 2 | 与表单编码相比,机器交互 web 服务更倾向于使用更结构化的格式来发送数据,因为它们发送的数据比简单表单更复杂。—— Malcom Tredinnick, Django developers group 3 | 4 | REST framework 包含许多内置的 Parser 类,允许您接受各种媒体类型的请求。还支持自定义解析器,这使您可以灵活地设计API接受的媒体类型。 5 | 6 | ## 如何确定解析器 (How the parser is determined) 7 | 视图的有效解析器集始终定义为类列表。访问 `request.data` 时,REST framework 将检查传入请求的 `Content-Type` 标头,并确定用于解析请求内容的解析器。 8 | 9 | *** 10 | 11 | **注意**:在开发客户端应用程序时,请始终记住确保在 HTTP 请求中发送数据时设置 `Content-Type` 标头。 12 | 13 | 如果您没有设置内容类型,大多数客户端将默认使用`'application/x-www-form-urlencoded'`,这可能不是您想要的。 14 | 15 | 例如,如果使用带有[.ajax() 方法](https://api.jquery.com/jQuery.ajax/)的 jQuery 发送 `json` 编码数据,您应确保包含 `contentType: 'application/json'` 设置。 16 | *** 17 | 18 | ## 设置解析器 (Setting the parsers) 19 | 可以使用 `DEFAULT_PARSER_CLASSES` 设置默认的全局解析器。例如,以下设置将只允许带有 `JSON` 内容的请求,而不是默认的 JSON 或表单数据。 20 | ```python 21 | REST_FRAMEWORK = { 22 | 'DEFAULT_PARSER_CLASSES': ( 23 | 'rest_framework.parsers.JSONParser', 24 | ) 25 | } 26 | ``` 27 | 您还可以使用基于 `APIView` 类的视图设置用于单个视图或视图集的解析器。 28 | ```python 29 | from rest_framework.parsers import JSONParser 30 | from rest_framework.response import Response 31 | from rest_framework.views import APIView 32 | 33 | class ExampleView(APIView): 34 | """ 35 | 接受带有 JSON 内容的 POST 请求的视图。 36 | """ 37 | parser_classes = (JSONParser,) 38 | 39 | def post(self, request, format=None): 40 | return Response({'received data': request.data}) 41 | ``` 42 | 或者,如果您使用带有基于函数的视图的 `@api_view` 装饰器。 43 | ```python 44 | from rest_framework.decorators import api_view 45 | from rest_framework.decorators import parser_classes 46 | from rest_framework.parsers import JSONParser 47 | 48 | @api_view(['POST']) 49 | @parser_classes((JSONParser,)) 50 | def example_view(request, format=None): 51 | """ 52 | 接受带有 JSON 内容的 POST 请求的视图。 53 | """ 54 | return Response({'received data': request.data}) 55 | ``` 56 | 57 | *** 58 | 59 | # API 参考 (API Reference) 60 | ## JSONParser 61 | 解析 `JSON` 请求内容。 62 | 63 | **.media_type**: `application/json` 64 | 65 | ## FormParser 66 | 解析 HTML 表单内容。`request.data` 将用 `QueryDict` 数据填充。 67 | 68 | 您通常希望同时使用 `FormParser` 和 `MultiPartParser`,以便完全支持 HTML 表单数据。 69 | 70 | **.media_type**: `application/x-www-form-urlencoded` 71 | 72 | ## MultiPartParser 73 | 解析多部分 HTML 表单内容,支持文件上传。`request.data` 将被 `QueryDict` 填充。 74 | 75 | 您通常希望同时使用 `FormParser` 和 `MultiPartParser`,以便完全支持 HTML 表单数据。 76 | 77 | **.media_type**: `multipart/form-data` 78 | 79 | ## FileUploadParser 80 | 解析原始文件上传内容。`request.data` 属性将是一个包含上传文件的单个键 `'file'` 的字典。 81 | 82 | 如果使用 `FileUploadParser` 调用了 `filename` URL 关键字参数的视图,则该参数将用作文件名。 83 | 84 | 如果没有 `filename` URL 关键字参数被调用,那么客户端必须在 HTTP 报头 `Content-Disposition` 中设置文件名。例如 `Content-Disposition: attachment; filename=upload.jpg`。 85 | 86 | **.media_type**: `*/*` 87 | 88 | ##### 注意: 89 | - `FileUploadParser` 适用于可以将文件作为原始数据请求上传的本地客户端。对于基于 Web 的上传,或者对于具有分段上传支持的本地客户端,您应该使用 `MultiPartParser` 解析器。 90 | - 由于该解析器的 `media_type` 匹配任何内容类型,因此 `FileUploadParser` 通常应该是 API 视图上唯一的解析器集。 91 | - `FileUploadParser` 遵循 Django 的标准 `FILE_UPLOAD_HANDLERS` 设置,和 `request.upload_handlers` 属性。有关更多详细信息,请参阅 [Django 文档](https://docs.djangoproject.com/en/stable/topics/http/file-uploads/#upload-handlers)。 92 | 93 | ##### 基本用法示例: 94 | ```python 95 | # views.py 96 | class FileUploadView(views.APIView): 97 | parser_classes = (FileUploadParser,) 98 | 99 | def put(self, request, filename, format=None): 100 | file_obj = request.data['file'] 101 | # ... 102 | # 用上传的文件做一些事情 103 | # ... 104 | return Response(status=204) 105 | 106 | # urls.py 107 | urlpatterns = [ 108 | # ... 109 | url(r'^upload/(?P[^/]+)$', FileUploadView.as_view()) 110 | ] 111 | ``` 112 | 113 | *** 114 | 115 | # 自定义解析器 (Custom parsers) 116 | 要实现一个自定义解析器,你应该重写 `BaseParser`,设置 `.media_type` 属性,并实现 `.parse(self,stream,media_type,parser_context)` 方法。 117 | 118 | 该方法应返回将用于填充 `request.data` 属性的数据。 119 | 120 | 传递给 `.parse()` 的参数是: 121 | 122 | ### stream 123 | 表示请求主体的流状对象。 124 | 125 | ### media_type 126 | 可选的。如果提供,这是传入请求内容的媒体类型。 127 | 128 | 根据请求的 `Content-Type:` 标头,这可能比渲染器的 `media_type` 属性更具体,可能包括媒体类型参数。例如 `"text/plain; charset=utf-8"`。 129 | 130 | ### parser_context 131 | 可选的。如果提供,该参数将是包含解析请求内容可能需要的任何附加上下文的字典。 132 | 133 | 默认情况下,将包含以下键:`view`, `request`, `args`, `kwargs`。 134 | 135 | ## 举个栗子 136 | 以下是一个纯文本解析器示例,它将使用表示请求主体的字符串填充 `request.data` 属性。 137 | ```python 138 | class PlainTextParser(BaseParser): 139 | """ 140 | 纯文本解析器。 141 | """ 142 | media_type = 'text/plain' 143 | 144 | def parse(self, stream, media_type=None, parser_context=None): 145 | """ 146 | 只需返回表示请求主体的字符串。 147 | """ 148 | return stream.read() 149 | ``` 150 | 151 | *** 152 | 153 | # 第三方包 (Third party packages) 154 | 以下是可用的第三方包。 155 | 156 | ## YAML 157 | [REST framework YAML](https://jpadilla.github.io/django-rest-framework-yaml/) 提供 [YAML](http://www.yaml.org/) 解析和渲染支持。它以前直接包含在REST framework 包中,现在被替代为第三方包支持。 158 | 159 | #### 安装和配置 (Installation & configuration) 160 | 使用 pip 安装。 161 | ```python 162 | $ pip install djangorestframework-yaml 163 | ``` 164 | 修改您的 REST framework 设置。 165 | ```python 166 | REST_FRAMEWORK = { 167 | 'DEFAULT_PARSER_CLASSES': ( 168 | 'rest_framework_yaml.parsers.YAMLParser', 169 | ), 170 | 'DEFAULT_RENDERER_CLASSES': ( 171 | 'rest_framework_yaml.renderers.YAMLRenderer', 172 | ), 173 | } 174 | ``` 175 | 176 | ## XML 177 | [REST Framework XML](https://jpadilla.github.io/django-rest-framework-xml/) 提供了一个简单的非正式 XML 格式。它以前直接包含在 REST framework 包中,现在被替代为第三方包支持。 178 | 179 | #### 安装和配置 (Installation & configuration) 180 | 使用 pip 安装。 181 | ```python 182 | $ pip install djangorestframework-xml 183 | ``` 184 | 修改您的 REST framework 设置。 185 | ```python 186 | REST_FRAMEWORK = { 187 | 'DEFAULT_PARSER_CLASSES': ( 188 | 'rest_framework_xml.parsers.XMLParser', 189 | ), 190 | 'DEFAULT_RENDERER_CLASSES': ( 191 | 'rest_framework_xml.renderers.XMLRenderer', 192 | ), 193 | } 194 | ``` 195 | 196 | ## MessagePack 197 | [MessagePack](https://msgpack.org/) 是一种快速,高效的二进制序列化格式。[Juan Riaza](https://github.com/juanriaza) 维护着 [djangorestframework-msgpack](https://github.com/juanriaza/django-rest-framework-msgpack) 包,它为 REST framework 提供 MessagePack 渲染器和解析器支持。 198 | 199 | ## CamelCase JSON 200 | [djangorestframework-camel-case](https://github.com/vbabiy/djangorestframework-camel-case) 为 REST framework 提供了驼峰式 JSON 渲染器和解析器。这使序列化程序可以使用 Python 风格的下划线字段名,但是在 API 中显示成 Javascript 样式的驼峰字段名。它由 [Vitaly Babiy](https://github.com/vbabiy) 维护着。 201 | -------------------------------------------------------------------------------- /API-Guide/Permissions.md: -------------------------------------------------------------------------------- 1 | # 权限 (Permissions) 2 | 认证或识别本身通常不足以获得对信息或代码的访问。为此,请求访问的实体必须具有授权。—— [Apple 开发人员文档](https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html) 3 | 4 | 与[身份验证](http://www.django-rest-framework.org/api-guide/authentication/)和[限流](http://www.django-rest-framework.org/api-guide/throttling/)一起,权限确定是否应该授予或拒绝访问请求。 5 | 6 | 在允许任何其他代码继续之前,权限检查始终在视图的最开始运行。权限检查通常会使用 `request.user` 和 `request.auth` 属性中的认证信息来确定是否允许传入请求。 7 | 8 | 权限用于授予或拒绝不同类别的用户访问 API 的不同部分。 9 | 10 | 最简单的权限类型是允许访问任何经过身份验证的用户,并拒绝访问任何未经身份验证的用户。这对应于 REST framework 中的 `IsAuthenticated` 类。 11 | 12 | 稍微宽松的权限会允许通过身份验证的用户完全访问,而未经身份验证的用户只能进行只读访问。这对应于 REST framework 中的 `IsAuthenticatedOrReadOnly` 类。 13 | 14 | ## 如何确定权限 (How permissions are determined) 15 | REST framework 中的权限始终被定义为权限类的列表。 16 | 17 | 在运行视图的主体之前,列表中的每个权限都会被检查。如果任何权限检查失败,则会引发 `exceptions.PermissionDenied` 或 `exceptions.NotAuthenticated` 异常,并且视图的主体不会再运行。 18 | 19 | 当权限检查失败时,根据以下规则,将返回 “403 Forbidden” 或 “401 Unauthorized” 响应: 20 | 21 | - 请求成功通过身份验证,但权限被拒绝。 —— 将返回 403 Forbidden 响应。 22 | - 请求未成功通过身份验证,并且最高优先级身份验证类未使用 `WWW-Authenticate` 标头。—— 将返回 403 Forbidden 响应。 23 | - 请求未成功通过身份验证,并且最高优先级身份验证类使用 `WWW-Authenticate` 标头。—— 将返回 HTTP 401 Unauthorized 响应,并附带适当的 `WWW-Authenticate` 标头。 24 | 25 | ## 对象级权限 (Object level permissions) 26 | REST framework 权限还支持对象级权限。对象级权限用于确定是否允许用户对特定对象进行操作,该特定对象通常是指模型实例。 27 | 28 | 当 `.get_object()` 被调用时,对象级权限由 REST framework 的通用视图运行。与视图级权限一样,如果用户不被允许对给定对象进行操作,则会引发 `exceptions.PermissionDenied` 异常。 29 | 30 | 如果您正在编写自己的视图并希望强制执行对象级权限,或者在通用视图上重写 `get_object` 方法,那么您需要在检索对象时显式地调用视图上的 `.check_object_permissions(request, obj)` 方法。 31 | 32 | 这将引发 `PermissionDenied` 或 `NotAuthenticated` 异常,或者只是在视图具有适当的权限时才返回。 33 | 34 | 举个栗子: 35 | ```python 36 | def get_object(self): 37 | obj = get_object_or_404(self.get_queryset(), pk=self.kwargs["pk"]) 38 | self.check_object_permissions(self.request, obj) 39 | return obj 40 | ``` 41 | 42 | #### 对象级权限的限制 (Limitations of object level permissions) 43 | 出于性能原因,在返回对象列表时,通用视图不会自动将对象级权限应用于查询集中的每个实例。 44 | 45 | 通常,当您使用对象级权限时,您还需要适当地[过滤查询集](http://www.django-rest-framework.org/api-guide/filtering/),以确保用户只能看到他们被允许查看的实例。 46 | 47 | ## 设置权限策略 (Setting the permission policy) 48 | 可以使用 `DEFAULT_PERMISSION_CLASSES` setting 全局设置默认权限策略。例如: 49 | ```python 50 | REST_FRAMEWORK = { 51 | 'DEFAULT_PERMISSION_CLASSES': ( 52 | 'rest_framework.permissions.IsAuthenticated', 53 | ) 54 | } 55 | ``` 56 | 如果未指定,则此设置默认为允许无限制访问: 57 | ```python 58 | 'DEFAULT_PERMISSION_CLASSES': ( 59 | 'rest_framework.permissions.AllowAny', 60 | ) 61 | ``` 62 | 您还可以使用基于类的视图 (APIView) 在每个视图或每个视图集的基础上设置身份验证策略。 63 | ```python 64 | from rest_framework.permissions import IsAuthenticated 65 | from rest_framework.response import Response 66 | from rest_framework.views import APIView 67 | 68 | class ExampleView(APIView): 69 | permission_classes = (IsAuthenticated,) 70 | 71 | def get(self, request, format=None): 72 | content = { 73 | 'status': 'request was permitted' 74 | } 75 | return Response(content) 76 | ``` 77 | 或者,使用基于函数的视图的 `@api_view` 装饰器。 78 | ```python 79 | from rest_framework.decorators import api_view, permission_classes 80 | from rest_framework.permissions import IsAuthenticated 81 | from rest_framework.response import Response 82 | 83 | @api_view(['GET']) 84 | @permission_classes((IsAuthenticated, )) 85 | def example_view(request, format=None): 86 | content = { 87 | 'status': 'request was permitted' 88 | } 89 | return Response(content) 90 | ``` 91 | **注意**:当通过类属性或装饰器设置新的权限类时,您会告诉视图忽略 **settings.py** 文件上设置的默认列表。 92 | 93 | *** 94 | 95 | # API 参考 96 | ## AllowAny 97 | `AllowAny` 权限类将允许不受限制的访问,**不管请求是经过身份验证还是未经身份验证**。 98 | 99 | 此权限不是严格要求的,因为您可以通过使用空列表或元组进行权限设置来获得相同的结果,但您可能会发现指定此类很有用,因为它使意图明确。 100 | 101 | ## IsAuthenticated 102 | `IsAuthenticated` 权限类将拒绝任何未经身份验证的用户的权限,否则允许权限。 103 | 104 | 如果您希望 API 仅供注册用户访问,则此权限适用。 105 | 106 | ## IsAdminUser 107 | `IsAdminUser` 权限类将拒绝任何用户的权限,除非在 `user.is_staff` 为 `True` 的情况下权限将被允许。 108 | 109 | 如果您希望 API 仅对受信任的管理员子集进行访问,则此权限是合适的。 110 | 111 | ## IsAuthenticatedOrReadOnly 112 | `IsAuthenticatedOrReadOnly` 将允许经过身份验证的用户执行任何请求。如果请求方法是 “安全” 的方法之一 (`GET`、`HEAD`、`OPTIONS`),则只允许未授权用户的请求。 113 | 114 | 如果您希望 API 允许匿名用户拥有读取权限,并且只允许对经过身份验证的用户拥有写入权限,则此权限是合适的。 115 | 116 | ## DjangoModelPermissions 117 | 此权限类与 Django 的标准 `django.contrib.auth` [模型权限](https://docs.djangoproject.com/en/stable/topics/auth/customizing/#custom-permissions)绑定。此权限只能应用于具有 `.queryset` 属性集的视图。只有在用户经过身份验证并分配了相关模型权限时,才会获得授权。 118 | 119 | - `POST` 请求要求用户在模型上具有 `add` 权限。 120 | - `PUT` 和 `PATCH` 请求要求用户在模型上具有 `change` 权限。 121 | - `DELETE` 请求要求用户在模型上具有 `delete` 权限。 122 | 123 | 还可以重写默认行为以支持自定义模型权限。例如,你可能想要包含 `GET` 请求的 `view` 模型权限。 124 | 125 | 要使用自定义模型权限,请重写 `DjangoModelPermissions` 并设置 `.perms_map` 属性。有关详细信息,请参阅源代码。 126 | 127 | #### 使用不包含 `queryset` 属性的视图 (Using with views that do not include a `queryset` attribute.) 128 | 如果您将此权限与重写的 `get_queryset()` 方法的视图一起使用,则视图上可能没有 `queryset` 属性。在这种情况下,我们建议还用前哨查询集标记视图,以便该类可以确定所需的权限。例如: 129 | ```python 130 | queryset = User.objects.none() # Required for DjangoModelPermissions 131 | ``` 132 | 133 | ## DjangoModelPermissionsOrAnonReadOnly 134 | 与 `DjangoModelPermissions` 类似,但也允许未经身份验证的用户对 API 的进行只读访问。 135 | 136 | ## DjangoObjectPermissions 137 | 该权限类与 Django 的标准[对象权限框架](https://docs.djangoproject.com/en/stable/topics/auth/customizing/#handling-object-permissions)绑定,该框架允许模型上的每个对象权限。要使用此权限类,您还需要添加支持对象级权限的权限后端,例如 [django-guardian](https://github.com/lukaszb/django-guardian)。 138 | 139 | 与 `DjangoModelPermissions` 一样,此权限只能应用于具有 `.queryset` 属性或 `.get_queryset()` 方法的视图。只有在用户经过身份验证且分配了相关的每个对象权限和相关的模型权限时,才会获得授权。 140 | 141 | - `POST` 请求要求用户在模型实例上具有 `add` 权限。 142 | - `PUT` 和 `PATCH` 请求要求用户在模型实例上具有 `change` 权限。 143 | - `DELETE` 请求要求用户在模型实例上具有 `delete` 权限。 144 | 145 | 请注意,`DjangoObjectPermissions` **不需要** `django-guardian` 软件包,并且应同样支持其他对象级后端。 146 | 147 | 与 `DjangoModelPermissions` 一样,您可以通过重写 `DjangoObjectPermissions` 并设置 `.perms_map` 属性来使用自定义模型权限。有关详细信息,请参阅源代码。 148 | 149 | *** 150 | 151 | **注意**:如果您需要 `GET`,`HEAD` 和 `OPTIONS` 请求的对象级 `view` 权限,则还需要考虑添加 `DjangoObjectPermissionsFilter` 类,以确保列表端点只返回包含用户具有适当查看权限的对象的结果。 152 | 153 | *** 154 | *** 155 | 156 | # 自定义权限 (Custom permissions) 157 | 要实现自定义权限,请重写 `BasePermission` 并实现以下方法中的一个或两个: 158 | 159 | - `.has_permission(self, request, view)` 160 | - `.has_object_permission(self, request, view, obj)` 161 | 162 | 如果请求被授予访问权限,则方法应返回 `True`,否则返回 `False`。 163 | 164 | 如果您需要测试请求是读操作还是写操作,则应该根据常量 `SAFE_METHODS` 检查请求方法,该常量是包含 `'GET'`,`'OPTIONS'` 和 `'HEAD'` 的元组。例如: 165 | ```python 166 | if request.method in permissions.SAFE_METHODS: 167 | # Check permissions for read-only request 168 | else: 169 | # Check permissions for write request 170 | ``` 171 | *** 172 | **注意**:只有在视图级别 `has_permission` 检查通过时才会调用实例级别的 `has_object_permission` 方法。还要注意,为了运行实例级检查,视图代码应显式调用 `.check_object_permissions(request, obj)`。如果您使用的是通用视图,则默认情况下将为您处理。(基于函数的视图需要显式检查对象权限,在失败时引发 `PermissionDenied`。) 173 | *** 174 | 175 | 如果测试失败,自定义权限将引发 `PermissionDenied` 异常。要更改与异常相关的错误消息,请直接在自定义权限上实现 `message` 属性。否则,将使用 `PermissionDenied` 中的 `default_detail` 属性。 176 | ```python 177 | from rest_framework import permissions 178 | 179 | class CustomerAccessPermission(permissions.BasePermission): 180 | message = 'Adding customers not allowed.' 181 | 182 | def has_permission(self, request, view): 183 | ... 184 | ``` 185 | 186 | ## 举个栗子 187 | 以下是根据黑名单检查传入请求的 IP 地址的权限类的示例,并且如果 IP 已被列入黑名单,则拒绝该请求。 188 | ```python 189 | from rest_framework import permissions 190 | 191 | class BlacklistPermission(permissions.BasePermission): 192 | """ 193 | 对列入黑名单的IP进行全局权限检查。 194 | """ 195 | 196 | def has_permission(self, request, view): 197 | ip_addr = request.META['REMOTE_ADDR'] 198 | blacklisted = Blacklist.objects.filter(ip_addr=ip_addr).exists() 199 | return not blacklisted 200 | ``` 201 | 除了针对所有传入请求运行的全局权限之外,您还可以创建对象级权限,仅运行针对影响特定对象实例的操作。例如: 202 | ```python 203 | class IsOwnerOrReadOnly(permissions.BasePermission): 204 | """ 205 | 对象级权限,仅允许对象的所有者编辑它。 206 | 假设模型实例具有 `owner` 属性。 207 | """ 208 | 209 | def has_object_permission(self, request, view, obj): 210 | # 任何请求都允许读取权限, 211 | # 所以我们总是允许 GET,HEAD 或 OPTIONS 请求。 212 | if request.method in permissions.SAFE_METHODS: 213 | return True 214 | 215 | # 实例必须具有名为 `owner` 的属性。 216 | return obj.owner == request.user 217 | ``` 218 | 请注意,通用视图将检查适当的对象级权限,但如果您正在编写自己的自定义视图,则需要确保检查自己的对象级权限。您可以通过在拥有对象实例后从视图中调用 `self.check_object_permissions(request, obj)` 来完成此操作。如果任何对象级权限检查失败,此调用将引发适当的 `APIException`,否则将简单地返回。 219 | 220 | 还要注意,通用视图仅检查检索单个模型实例的视图的对象级权限。如果需要对列表视图进行对象级别过滤,则需要单独过滤查询集。有关详细信息,请参阅[过滤文档](http://www.django-rest-framework.org/api-guide/filtering/)。 221 | *** 222 | 223 | # 第三方包 (Third party packages) 224 | 以下是可用的第三方包。 225 | 226 | ## Composed Permissions 227 | [Composed Permissions](https://github.com/niwibe/djangorestframework-composed-permissions) 包使用小的和可重用组件来提供一种定义复杂和多深度 (带逻辑运算符) 权限对象的简单方法。 228 | 229 | ## REST Condition 230 | [REST Condition](https://github.com/caxap/rest_condition) 包是以简单方便的方式构建复杂权限的另一种扩展。该扩展允许您将权限与逻辑运算符组合在一起。 231 | 232 | ## DRY Rest Permissions 233 | [DRY Rest Permissions](https://github.com/Helioscene/dry-rest-permissions) 包提供了为单个默认和自定义操作定义不同权限的能力。此包适用于具有从应用程序数据模型中定义的关系派生的权限的应用程序。它还支持通过 API 的序列化器将权限检查返回给客户端应用程序。此外,它还支持向默认和自定义列表操作添加权限,以限制每个用户检索的数据。 234 | 235 | ## Django Rest Framework Roles 236 | [Django Rest Framework Roles](https://github.com/computer-lab/django-rest-framework-roles) 包使您在多种类型的用户上参数化 API 更轻松。 237 | 238 | ## Django Rest Framework API Key 239 | [Django Rest Framework API Key](https://github.com/manosim/django-rest-framework-api-key) 包允许您确保对服务器发出的每个请求都需要 API 密钥头。您可以从 django 管理界面生成一个。 240 | 241 | ## Django Rest Framework Role Filters 242 | [Django Rest Framework Role Filters](https://github.com/allisson/django-rest-framework-role-filters) 包提供对多种类型角色的简单过滤。 243 | -------------------------------------------------------------------------------- /API-Guide/Requests.md: -------------------------------------------------------------------------------- 1 | # Requests 2 | 如果你正在做基于 REST 的 Web 服务的东西...你应该忽略 request.POST。—— Malcom Tredinnick,Django 开发人员小组 3 | 4 | REST framework 的 `Request` 类扩展了标准的 `HttpRequest`,增加了对 REST framework 灵活的请求解析和请求认证的支持。 5 | *** 6 | 7 | # 请求解析 8 | REST framework 的 Request 对象提供了灵活的请求解析,允许您使用 JSON 数据或其他媒体类型像通常处理表单数据相同的方式处理请求。 9 | 10 | ## .data 11 | `request.data` 返回请求主体的解析内容。这与标准的 `request.POST` 和 `request.FILES` 属性类似,除了: 12 | 13 | - 它包括所有解析的内容,包括文件和非文件输入。 14 | - 它支持解析 `POST` 以外的 HTTP 方法的内容,这意味着您可以访问 `PUT` 和 `PATCH` 请求的内容。 15 | - 它支持 REST framework 的灵活请求解析,而不仅仅是支持表单数据。例如,您可以像处理传入表单数据一样处理传入的 JSON 数据。 16 | 17 | 有关更多详细信息,请参阅[解析器文档](http://www.django-rest-framework.org/api-guide/parsers/)。 18 | 19 | ## .query_params 20 | `request.query_params` 是 `request.GET` 更准确的命名同义词。 21 | 22 | 为了使代码内部更清晰,我们推荐使用 `request.query_params` 而不是 Django 的标准`request.GET`。这样做有助于保持您的代码库更加正确和明显 —— 任何 HTTP 方法类型都可能包含查询参数,而不仅仅是 `GET` 请求。 23 | 24 | ## .parsers 25 | `APIView` 类或 `@api_view` 装饰器将根据视图上设置的 `parser_classes` 或根据 `DEFAULT_PARSER_CLASSES` 设置确保将此属性自动设置为 `Parser` 实例列表。 26 | 27 | 您通常不需要访问此属性。 28 | 29 | *** 30 | 注意:如果客户端发送格式错误的内容,则访问 `request.data` 可能会引发 `ParseError`。默认情况下,REST framework 的 `APIView` 类或 `@api_view` 装饰器将捕获错误并返回 `400 Bad Request` 响应。 31 | 32 | 如果客户端发送的请求的内容类型无法解析,则会引发 `UnsupportedMediaType` 异常,默认情况下会被捕获并返回 `415 Unsupported Media Type` 响应。 33 | *** 34 | 35 | # 内容协商 (Content negotiation) 36 | 请求公开了一些属性,这些属性允许您确定内容协商阶段的结果。这允许您实现一些行为,例如为不同的媒体类型选择不同的序列化方案。 37 | 38 | ## .accepted_renderer 39 | 渲染器实例是由内容协商阶段选择的。 40 | 41 | ## .accepted_media_type 42 | 内容协商阶段被接受的字符串表示的媒体类型。 43 | *** 44 | 45 | # 认证 (Authentication) 46 | REST framework 提供灵活的每个请求认证,使您能够: 47 | 48 | - 为您的 API 的不同部分使用不同的身份验证策略。 49 | - 支持使用多个身份验证策略。 50 | - 提供与传入请求关联的用户和令牌信息。 51 | 52 | ## .user 53 | `request.user` 通常会返回 `django.contrib.auth.models.User` 的一个实例,但其行为取决于正在使用的身份验证策略。 54 | 55 | 如果请求未经身份验证,则 `request.user` 的默认值是 `django.contrib.auth.models.AnonymousUser` 的实例。 56 | 57 | 有关详细信息,请参阅[身份验证文档](http://www.django-rest-framework.org/api-guide/authentication/)。 58 | 59 | ## .auth 60 | `request.auth` 返回任何附加的认证上下文。`request.auth` 的确切行为取决于正在使用的身份验证策略,但它通常可能是请求通过身份验证的令牌实例。 61 | 62 | 如果请求未经身份验证,或者没有附加上下文,则 `request.auth` 的默认值为 `None`。 63 | 64 | 有关详细信息,请参阅[身份验证文档](http://www.django-rest-framework.org/api-guide/authentication/)。 65 | 66 | ## .authenticators 67 | `APIView` 类或 `@api_view` 装饰器将根据视图上设置的 `authentication_classes` 或根据 `DEFAULT_AUTHENTICATORS` 设置确保将此属性自动设置为 `Authentication` 实例列表。 68 | 69 | 您通常不需要访问此属性。 70 | 71 | *** 72 | 注意:调用 `.user` 或 `.auth` 属性时可能会引发 `WrappedAttributeError` 异常。这些错误源自认证器 (authenticator) 作为标准 `AttributeError`,但是为了防止它们被外部属性访问修改,有必要重新设置为不同的异常类型。Python 将无法识别来自认证器 (authenticator) 的 `AttributeError`,并会立即假定请求对象没有 `.user` 或 `.auth` 属性。认证器 (authenticator) 需要修复。 73 | *** 74 | 75 | # 浏览器增强 76 | REST framework 支持一些浏览器增强功能,例如基于浏览器的 `PUT`,`PATCH` 和 `DELETE` 表单。 77 | 78 | ## .method 79 | `request.method` 返回大写字符串表示的请求 HTTP 方法。 80 | 81 | 透明地支持基于浏览器的 `PUT`,`PATCH` 和 `DELETE` 表单。 82 | 83 | 有关更多信息,请参阅[浏览器增强功能文档](http://www.django-rest-framework.org/topics/browser-enhancements/)。 84 | 85 | ## .content_type 86 | `request.content_type` 返回表示 HTTP 请求正文的媒体类型的字符串对象,如果没有提供媒体类型,则返回空字符串。 87 | 88 | 您通常不需要直接访问请求的内容类型,因为您通常会依赖 REST framework 的默认请求解析行为。 89 | 90 | 如果确实需要访问请求的内容类型,则应该优先使用 `.content_type` 属性,而不是使用 `request.META.get('HTTP_CONTENT_TYPE')`,因为它为基于浏览器的非表单内容提供了透明支持。 91 | 92 | 有关更多信息,请参阅[浏览器增强功能文档](http://www.django-rest-framework.org/topics/browser-enhancements/)。 93 | 94 | ## .stream 95 | `request.stream` 返回一个代表请求主体内容的流。 96 | 97 | 您通常不需要直接访问请求的内容,因为您通常会依赖 REST framework 的默认请求解析行为。 98 | *** 99 | 100 | # 标准的 HttpRequest 属性 101 | 由于 REST framework 的 `Request` 扩展了 Django 的 `HttpRequest`,所有其他标准属性和方法也可用。例如 `request.META` 和 `request.session` 字典可以正常使用。 102 | 103 | 请注意,由于实现原因,`Request` 类不会从 `HttpRequest` 类继承,而是使用组合扩展类。 104 | -------------------------------------------------------------------------------- /API-Guide/Responses.md: -------------------------------------------------------------------------------- 1 | # 响应 (Responses) 2 | 与基本的 HttpResponse 对象不同,TemplateResponse 对象保留了视图提供的用于计算响应的上下文的详细信息。在响应过程中,直到需要时才会计算最终的响应输出。—— Django 文档 3 | 4 | REST framework 通过提供一个 `Response` 类来支持 HTTP 内容协商,该类允许您根据客户端请求返回可渲染为多种内容类型的内容。 5 | 6 | `Response` 类的子类是 Django 的 `SimpleTemplateResponse`。响应对象使用数据进行初始化,数据应由本地 Python 基元组成。REST framework 然后使用标准的 HTTP 内容协商来确定它如何渲染最终响应内容。 7 | 8 | 您不需要使用 `Response` 类,如果需要,也可以从视图中返回常规的 `HttpResponse` 或 `StreamingHttpResponse` 对象。使用 `Response` 类只是为返回内容协商的 Web API 响应提供更好的接口,这些响应可以渲染为多种格式。 9 | 10 | 除非出于某种原因需要大量定制 REST framework,否则应始终对返回 `Response` 对象的视图使用 `API​​View` 类或 `@api_view` 函数。这样做可以确保视图执行内容协商,并在视图返回之前为响应选择适当的渲染器。 11 | 12 | # 创建 responses 13 | ## Response() 14 | 语法:`Response(data, status=None, template_name=None, headers=None, content_type=None)` 15 | 16 | 与常规 `HttpResponse` 对象不同,您不会使用渲染的内容实例化 `Response` 对象。相反,您传递的是未渲染的数据,可能由任何 Python 基元组成。 17 | 18 | 由于 `Response` 类使用的渲染器不能处理复杂的数据类型,例如 Django 模型实例,所以需要在创建 `Response` 对象之前将数据序列化为基本数据类型。 19 | 20 | 您可以使用 REST framework 的 `Serializer` 类来执行数据序列化,或者使用您自己的自定义序列化。 21 | 22 | 参数: 23 | 24 | - `data` :响应的序列化数据。 25 | - `status` :响应的状态代码。默认为200。另请参阅[状态代码](http://www.django-rest-framework.org/api-guide/status-codes/)。 26 | - `template_name` :选择 `HTMLRenderer` 时使用的模板名称。 27 | - `headers` :响应中使用的 HTTP headers 的字典。 28 | - `content_type` :响应的内容类型。通常情况下,渲染器会根据内容协商的结果自动设置,但有些情况下需要明确指定内容类型。 29 | 30 | *** 31 | 32 | # Attributes 33 | ## .data 34 | 未渲染的、序列化的响应数据。 35 | 36 | ## .status_code 37 | HTTP 响应的数字状态码。 38 | 39 | ## .content 40 | 响应的渲染内容。在访问 `.content` 之前,必须先调用 `.render()` 方法。 41 | 42 | ## .template_name 43 | `template_name` (如果提供)。只有当 `HTMLRenderer` 或其他自定义模板渲染器是响应的渲染器时才需要。 44 | 45 | ## .accepted_renderer 46 | 用于渲染响应的渲染器实例。 47 | 48 | 从视图返回响应之前由 `APIView` 或 `@api_view` 自动设置。 49 | 50 | ## .accepted_media_type 51 | 内容协商阶段选择的媒体类型。 52 | 53 | 从视图返回响应之前由 `APIView` 或 `@api_view` 自动设置。 54 | 55 | ## .renderer_context 56 | 将传递给渲染器的 `.render()` 方法的附加的上下文信息的字典。 57 | 58 | 从视图返回响应之前由 `APIView` 或 `@api_view` 自动设置。 59 | 60 | *** 61 | 62 | # 标准 HttpResponse 属性 63 | `Response` 类扩展了 `SimpleTemplateResponse`,并且响应中也提供了所有常用的属性和方法。例如,您可以用标准方式在响应中设置 headers: 64 | ```python 65 | response = Response() 66 | response['Cache-Control'] = 'no-cache' 67 | ``` 68 | 69 | ## .render() 70 | 语法:`.render()` 71 | 72 | 与其他任何 `TemplateResponse` 一样,调用此方法将响应的序列化数据渲染为最终响应内容。调用 `.render()` 时,响应内容将设置为在 `accepted_renderer` 实例上调用 `.render(data,accepted_media_type,renderer_context)` 方法的结果。 73 | 74 | 您通常不需要自己调用 `.render()`,因为它是由 Django 的标准响应循环处理的。 75 | -------------------------------------------------------------------------------- /API-Guide/Returning-URLs.md: -------------------------------------------------------------------------------- 1 | # 返回 URL (Returning URLs) 2 | 区别 REST 架构风格与其他基于网络风格的中心特征是它强调组件之间的统一接口。—— Roy Fielding,[架构风格与基于网络的软件体系结构设计](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5) 3 | 4 | 通常,从 Web API 返回绝对 URI (例如 `http://example.com/foobar`) 可能是比返回相对 URI (`/foobar`) 更好。 5 | 6 | 这样做的好处是: 7 | 8 | - 它更明确。 9 | - 它为 API 客户端减少了工作量。 10 | - 当在诸如 JSON 的表示中没有本地 URI 类型时,字符串的含义是没有歧义的。 11 | - 它使得使用超链接标记 HTML 表示等事情变得很容易。 12 | 13 | REST framework 提供了两个实用函数,使从 Web API 返回绝对 URI 更加简单。 14 | 15 | 您无需使用它们,但如果您这样做,那么自描述 API 将能够自动为您超链接其输出,这使得浏览 API 更加容易。 16 | 17 | ## reverse 18 | **签名**:`reverse(viewname, *args, **kwargs)` 19 | 20 | 具有与 `django.urls.reverse` 相同的行为,除了它返回一个完全限定的 URL,使用请求来确定主机和端口。 21 | 22 | 你应该将**请求作为关键字参数**包含在该函数中,例如: 23 | ```python 24 | from rest_framework.reverse import reverse 25 | from rest_framework.views import APIView 26 | from django.utils.timezone import now 27 | 28 | class APIRootView(APIView): 29 | def get(self, request): 30 | year = now().year 31 | data = { 32 | ... 33 | 'year-summary-url': reverse('year-summary', args=[year], request=request) 34 | } 35 | return Response(data) 36 | ``` 37 | 38 | ## reverse_lazy 39 | **签名**:`reverse_lazy(viewname, *args, **kwargs)` 40 | 41 | 具有与 `django.urls.reverse_lazy` 相同的行为,除了它返回一个完全限定的 URL,使用请求来确定主机和端口。 42 | 43 | 与 `reverse` 函数一样,你应该将**请求作为关键字参数**包含在函数中,例如: 44 | ```python 45 | api_root = reverse_lazy('api-root', request=request) 46 | ``` 47 | -------------------------------------------------------------------------------- /API-Guide/Status-Codes.md: -------------------------------------------------------------------------------- 1 | # 状态码 (Status Codes) 2 | 418 我是茶壶 — 任何用茶壶泡咖啡的尝试都会导致错误代码 “418 我是茶壶”。产生的实体主体可能短而粗。—— [RFC 2324](https://www.ietf.org/rfc/rfc2324.txt), 超文本咖啡壶控制协议 3 | 4 | 不建议在您的响应中使用裸露 (直接使用数字) 的状态码。REST framework 包含一组命名常量,您可以使用来使代码更加清晰易读。 5 | ```python 6 | from rest_framework import status 7 | from rest_framework.response import Response 8 | 9 | def empty_view(self): 10 | content = {'please move along': 'nothing to see here'} 11 | return Response(content, status=status.HTTP_404_NOT_FOUND) 12 | ``` 13 | 下面列出了 `status` 模块中包含的全套 HTTP 状态码。 14 | 15 | 该模块还包含一组帮助函数,用于测试状态码是否在给定范围内。 16 | ```python 17 | from rest_framework import status 18 | from rest_framework.test import APITestCase 19 | 20 | class ExampleTestCase(APITestCase): 21 | def test_url_root(self): 22 | url = reverse('index') 23 | response = self.client.get(url) 24 | self.assertTrue(status.is_success(response.status_code)) 25 | ``` 26 | 有关正确使用 HTTP 状态码的详细信息,请参阅 [RFC 2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) 和 [RFC 6585](https://tools.ietf.org/html/rfc6585)。 27 | 28 | ## 信息 - 1xx (Informational - 1xx) 29 | 此类状态码表示临时响应。默认情况下,REST framework 中没有使用 1xx 状态码。 30 | ```python 31 | HTTP_100_CONTINUE 32 | HTTP_101_SWITCHING_PROTOCOLS 33 | ``` 34 | 35 | ## 成功 - 2xx (Successful - 2xx) 36 | 此类状态码表明客户端的请求已被成功接收,理解和接受。 37 | ```python 38 | HTTP_200_OK 39 | HTTP_201_CREATED 40 | HTTP_202_ACCEPTED 41 | HTTP_203_NON_AUTHORITATIVE_INFORMATION 42 | HTTP_204_NO_CONTENT 43 | HTTP_205_RESET_CONTENT 44 | HTTP_206_PARTIAL_CONTENT 45 | HTTP_207_MULTI_STATUS 46 | ``` 47 | 48 | ## 重定向 - 3xx (Redirection - 3xx) 49 | 此类状态码表明用户代理需要采取进一步行动来满足请求。 50 | ```python 51 | HTTP_300_MULTIPLE_CHOICES 52 | HTTP_301_MOVED_PERMANENTLY 53 | HTTP_302_FOUND 54 | HTTP_303_SEE_OTHER 55 | HTTP_304_NOT_MODIFIED 56 | HTTP_305_USE_PROXY 57 | HTTP_306_RESERVED 58 | HTTP_307_TEMPORARY_REDIRECT 59 | ``` 60 | 61 | ## 客户端错误 - 4xx (Client Error - 4xx) 62 | 4xx 类的状态码是针对客户端似乎已经出错的情况。除了在响应 HEAD 请求时,服务器应该包括一个实体,其中包含错误情况的解释,以及它是临时的还是永久的。 63 | ```python 64 | HTTP_400_BAD_REQUEST 65 | HTTP_401_UNAUTHORIZED 66 | HTTP_402_PAYMENT_REQUIRED 67 | HTTP_403_FORBIDDEN 68 | HTTP_404_NOT_FOUND 69 | HTTP_405_METHOD_NOT_ALLOWED 70 | HTTP_406_NOT_ACCEPTABLE 71 | HTTP_407_PROXY_AUTHENTICATION_REQUIRED 72 | HTTP_408_REQUEST_TIMEOUT 73 | HTTP_409_CONFLICT 74 | HTTP_410_GONE 75 | HTTP_411_LENGTH_REQUIRED 76 | HTTP_412_PRECONDITION_FAILED 77 | HTTP_413_REQUEST_ENTITY_TOO_LARGE 78 | HTTP_414_REQUEST_URI_TOO_LONG 79 | HTTP_415_UNSUPPORTED_MEDIA_TYPE 80 | HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE 81 | HTTP_417_EXPECTATION_FAILED 82 | HTTP_422_UNPROCESSABLE_ENTITY 83 | HTTP_423_LOCKED 84 | HTTP_424_FAILED_DEPENDENCY 85 | HTTP_428_PRECONDITION_REQUIRED 86 | HTTP_429_TOO_MANY_REQUESTS 87 | HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE 88 | HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS 89 | ``` 90 | 91 | ## 服务器错误 - 5xx (Server Error - 5xx) 92 | 以数字 “5” 开头的响应状态码表明服务器意识到它已经错误或无法执行请求的情况。除了在响应 HEAD 请求时,服务器应该包括一个实体,其中包含错误情况的解释,以及它是临时的还是永久的。 93 | ```python 94 | HTTP_500_INTERNAL_SERVER_ERROR 95 | HTTP_501_NOT_IMPLEMENTED 96 | HTTP_502_BAD_GATEWAY 97 | HTTP_503_SERVICE_UNAVAILABLE 98 | HTTP_504_GATEWAY_TIMEOUT 99 | HTTP_505_HTTP_VERSION_NOT_SUPPORTED 100 | HTTP_507_INSUFFICIENT_STORAGE 101 | HTTP_511_NETWORK_AUTHENTICATION_REQUIRED 102 | ``` 103 | 104 | ## 帮助函数 (Helper functions) 105 | 以下帮助函数可用于识别响应代码的类别。 106 | ```python 107 | is_informational() # 1xx 108 | is_success() # 2xx 109 | is_redirect() # 3xx 110 | is_client_error() # 4xx 111 | is_server_error() # 5xx 112 | ``` 113 | -------------------------------------------------------------------------------- /API-Guide/Throttling.md: -------------------------------------------------------------------------------- 1 | # 限流 (Throttling) 2 | 限流类似于[权限](http://www.django-rest-framework.org/api-guide/permissions/),因为它确定是否应该授权请求。限流阀表示临时状态,并用来控制客户端对 API 的请求速率。 3 | 4 | 与权限一样,可能会使用多种限流方式。您的 API 可能对未经身份验证的请求进行限流,对经过身份验证的请求限流较少。 5 | 6 | 您可能想要使用多种限流的另一种方案是,如果您需要对 API 的不同部分施加不同的约束,由于某些服务特别是资源密集型。 7 | 8 | 如果您想同时施加突发节流速率和持续节流速率,还可以使用多个限流阀。例如,您可能希望将用户限制为每分钟最多 60 个请求,每天 1000 个请求。 9 | 10 | 限流阀不一定只指限速请求。例如,存储服务可能还需要限制带宽,而付费数据服务可能希望限制访问的记录的特定数量。 11 | 12 | ## 如何确定限流 (How throttling is determined) 13 | 与权限和身份验证一样,REST framework 中的限流始终定义为类列表。 14 | 15 | 在运行视图的主体之前,检查列表中的每个限流阀。如果任何限流检查失败,将引发 `exceptions.Throttled` 异常,并且该视图的主体将不会再执行。 16 | 17 | ## 设置限流策略 (Setting the throttling policy) 18 | 使用 `DEFAULT_THROTTLE_CLASSES` 和 `DEFAULT_THROTTLE_RATES` settings 可以设置全局的默认限流策略。例如: 19 | ```python 20 | REST_FRAMEWORK = { 21 | 'DEFAULT_THROTTLE_CLASSES': ( 22 | 'rest_framework.throttling.AnonRateThrottle', 23 | 'rest_framework.throttling.UserRateThrottle' 24 | ), 25 | 'DEFAULT_THROTTLE_RATES': { 26 | 'anon': '100/day', 27 | 'user': '1000/day' 28 | } 29 | } 30 | ``` 31 | `DEFAULT_THROTTLE_RATES` 中使用的频率描述可能包括 `second`,`minute`,`hour` 或 `day` 作为限流周期。 32 | 33 | 您还可以使用基于 `APIView` 类的视图,在每个视图或每个视图集的基础上设置限流策略。 34 | ```python 35 | from rest_framework.response import Response 36 | from rest_framework.throttling import UserRateThrottle 37 | from rest_framework.views import APIView 38 | 39 | class ExampleView(APIView): 40 | throttle_classes = (UserRateThrottle,) 41 | 42 | def get(self, request, format=None): 43 | content = { 44 | 'status': 'request was permitted' 45 | } 46 | return Response(content) 47 | ``` 48 | 或者使用基于函数的视图的 `@api_view` 装饰器。 49 | ```python 50 | @api_view(['GET']) 51 | @throttle_classes([UserRateThrottle]) 52 | def example_view(request, format=None): 53 | content = { 54 | 'status': 'request was permitted' 55 | } 56 | return Response(content) 57 | ``` 58 | 59 | ## 如何识别客户端 (How clients are identified) 60 | `X-Forwarded-For` HTTP 标头和 `REMOTE_ADDR` WSGI 变量用于为限流的唯一标识的客户端 IP 地址。如果存在 `X-Forwarded-For` 标头,则将使用它,否则将使用 WSGI 环境中的 `REMOTE_ADDR` 变量的值。 61 | 62 | 如果您需要严格标识唯一的客户端 IP 地址,则需要首先通过设置 `NUM_PROXIES` 设置来配置 API 运行的应用程序代理的数量。此设置应为零或更大的整数。如果设置为非零,则一旦任何应用程序代理 IP 地址首先被排除,客户端 IP 将被标识为 `X-Forwarded-For` 标头中的最后一个 IP 地址。如果设置为零,则 `REMOTE_ADDR` 值将始终用作识别 IP 地址。 63 | 64 | 重要的是要理解,如果配置 `NUM_PROXIES` 设置,那么唯一的 [NAT](https://en.wikipedia.org/wiki/Network_address_translation) 网关后面的所有客户端将被视为单个客户端。 65 | 66 | 关于 `X-Forwarded-For` 标头如何工作以及识别远程客户端 IP 的更多上下文可以[在这里找到](http://oxpedia.org/wiki/index.php?title=AppSuite:Grizzly#Multiple_Proxies_in_front_of_the_cluster)。 67 | 68 | ## 设置缓存 (Setting up the cache) 69 | EST framework 提供的限流类使用 Django 的缓存后端。你应该确保你已经设置了适当的[缓存设置](https://docs.djangoproject.com/en/stable/ref/settings/#caches)。对于简单的设置,`LocMemCache` 后端的默认值应该没问题。有关更多详细信息,请参阅 Django 的[缓存文档](https://docs.djangoproject.com/en/stable/topics/cache/#setting-up-the-cache)。 70 | 71 | 如果您需要使用 `'default'` 以外的缓存,则可以通过创建自定义限流类并设置 `cache` 属性来实现。例如: 72 | ```python 73 | class CustomAnonRateThrottle(AnonRateThrottle): 74 | cache = get_cache('alternate') 75 | ``` 76 | 您还需要记住在 `'DEFAULT_THROTTLE_CLASSES'` settings 键中设置自定义的限流类,或者使用 `throttle_classes` 视图属性。 77 | 78 | *** 79 | 80 | # API 参考 81 | ## AnonRateThrottle 82 | `AnonRateThrottle` 只会限制未经认证的用户。传入请求的 IP 地址用于生成唯一的键来限流。 83 | 84 | 允许的请求率由以下之一决定 (按优先顺序)。 85 | 86 | - 类上的 `rate` 属性,可以通过重写 `AnonRateThrottle` 并设置其属性来提供。 87 | - `DEFAULT_THROTTLE_RATES['anon']` 设置。 88 | 89 | 如果您想限制未知来源的请求率,`AnonRateThrottle` 是合适的。 90 | 91 | ## UserRateThrottle 92 | `UserRateThrottle` 将通过 API 限制用户的给定请求率。用户 id 用于生成唯一的密钥来加以限制。未经身份验证的请求将回退到使用传入请求的 IP 地址生成唯一的密钥来进行限制。 93 | 94 | 允许的请求率由以下之一决定 (按优先顺序)。 95 | 96 | - 类上的 `rate` 属性,可以通过重写 `UserRateThrottle` 并设置其属性来提供。 97 | - `DEFAULT_THROTTLE_RATES['user']` 设置。 98 | 99 | API 可能同时具有多个 `UserRateThrottles`。为此,请重写 `UserRateThrottle` 并为每个类设置唯一的 “范围”。 100 | 101 | 例如,可以通过使用以下类来实现多个用户限流率... 102 | ```python 103 | class BurstRateThrottle(UserRateThrottle): 104 | scope = 'burst' 105 | 106 | class SustainedRateThrottle(UserRateThrottle): 107 | scope = 'sustained' 108 | ``` 109 | ...和以下设置。 110 | ```python 111 | REST_FRAMEWORK = { 112 | 'DEFAULT_THROTTLE_CLASSES': ( 113 | 'example.throttles.BurstRateThrottle', 114 | 'example.throttles.SustainedRateThrottle' 115 | ), 116 | 'DEFAULT_THROTTLE_RATES': { 117 | 'burst': '60/min', 118 | 'sustained': '1000/day' 119 | } 120 | } 121 | ``` 122 | 如果希望对每个用户进行简单的全局速率限制,那么 `UserRateThrottle` 是合适的。 123 | 124 | ## ScopedRateThrottle 125 | `ScopedRateThrottle` 类可用于限制对 API 特定部分的访问。只有当正在访问的视图包含 `.throttle_scope` 属性时才会应用此限制。然后通过将请求的 “范围” 与唯一的用户 id 或 IP 地址连接起来形成唯一的限流密钥。 126 | 127 | 允许的请求率由 `DEFAULT_THROTTLE_RATES` setting 使用请求 “范围” 中的键确定。 128 | 129 | 例如,给出以下视图... 130 | ```python 131 | class ContactListView(APIView): 132 | throttle_scope = 'contacts' 133 | ... 134 | 135 | class ContactDetailView(APIView): 136 | throttle_scope = 'contacts' 137 | ... 138 | 139 | class UploadView(APIView): 140 | throttle_scope = 'uploads' 141 | ... 142 | ``` 143 | ...和以下设置。 144 | ```python 145 | REST_FRAMEWORK = { 146 | 'DEFAULT_THROTTLE_CLASSES': ( 147 | 'rest_framework.throttling.ScopedRateThrottle', 148 | ), 149 | 'DEFAULT_THROTTLE_RATES': { 150 | 'contacts': '1000/day', 151 | 'uploads': '20/day' 152 | } 153 | } 154 | ``` 155 | 用户对 `ContactListView` 或 `ContactDetailView` 的请求将被限制为每天 1000 次。用户对 `UploadView` 的请求将被限制为每天 20 次。 156 | 157 | *** 158 | 159 | # 自定义限流 (Custom throttles) 160 | 要创建自定义限流,请重写 `BaseThrottle` 类并实现 `.allow_request(self, request, view)`。如果请求被允许,该方法应返回 `True`,否则返回 `False`。 161 | 162 | 还可以选择重写 `.wait()` 方法。如果实现,`.wait()` 应返回在尝试下一次请求之前等待的推荐秒数,或者返回 `None`。如果 `.allow_request()` 先前已经返回 `False`,则只会调用 `.wait()` 方法。 163 | 164 | 如果 `.wait()` 方法被实现并且请求受到限制,那么 `Retry-After` 标头将包含在响应中。 165 | 166 | ## 举个栗子 167 | 以下是速率限制的示例,它将随机地限制 10 次请求中的 1 次。 168 | ```python 169 | import random 170 | 171 | class RandomRateThrottle(throttling.BaseThrottle): 172 | def allow_request(self, request, view): 173 | return random.randint(1, 10) != 1 174 | ``` 175 | -------------------------------------------------------------------------------- /API-Guide/Validators.md: -------------------------------------------------------------------------------- 1 | # 验证器 (Validators) 2 | 验证器对于在不同类型的字段之间重用验证逻辑非常有用。—— [Django 文档](https://docs.djangoproject.com/en/stable/ref/validators/) 3 | 4 | 大多数情况下,您在 REST framework 中处理验证时,只需依赖默认的字段验证,或者在序列化器或字段类上编写显式的验证方法。 5 | 6 | 但是,有时您需要将验证逻辑放入可重用的组件中,以便可以在整个代码库中轻松地重用它。这可以通过使用验证器函数和验证器类来实现。 7 | 8 | # REST framework 中的验证 (Validation in REST framework) 9 | Django REST framework 序列化器中的验证与 Django 的 `ModelForm` 类中的验证工作方式略有不同。 10 | 11 | 使用 `ModelForm`,验证一部分在表单上执行,一部分在模型实例上执行。使用 REST framework ,验证完全在序列化器上执行。这是有利的,原因如下: 12 | 13 | - 它引入了适当的关注点分离,使您的代码行为更加明显。 14 | - 使用快捷的 `ModelSerializer` 类和使用显式的 `Serializer` 类可以轻松切换。任何用于 `ModelSerializer` 的验证行为都很容易复制。 15 | - 打印序列化器实例的 `repr` 将准确显示它应用的验证规则。在模型实例上没有额外的隐藏验证行为被调用。 16 | 17 | 当您使用 `ModelSerializer` 时,所有这些都会自动为您处理。如果您想转而使用 `Serializer` 类,那么需要显式地定义验证规则。 18 | 19 | #### 举个栗子 20 | 作为 REST framework 如何使用显式验证的一个示例,我们将使用一个具有唯一性约束的字段的简单模型类。 21 | ```python 22 | class CustomerReportRecord(models.Model): 23 | time_raised = models.DateTimeField(default=timezone.now, editable=False) 24 | reference = models.CharField(unique=True, max_length=20) 25 | description = models.TextField() 26 | ``` 27 | 这是一个基本的 `ModelSerializer`,我们可以用它来创建或更新 `CustomerReportRecord` 实例: 28 | ```python 29 | class CustomerReportSerializer(serializers.ModelSerializer): 30 | class Meta: 31 | model = CustomerReportRecord 32 | ``` 33 | 如果我们使用 `manage.py shell` 打开 Django shell,我们现在可以 34 | ```python 35 | >>> from project.example.serializers import CustomerReportSerializer 36 | >>> serializer = CustomerReportSerializer() 37 | >>> print(repr(serializer)) 38 | CustomerReportSerializer(): 39 | id = IntegerField(label='ID', read_only=True) 40 | time_raised = DateTimeField(read_only=True) 41 | reference = CharField(max_length=20, validators=[]) 42 | description = CharField(style={'type': 'textarea'}) 43 | ``` 44 | 这里有趣的一点是 `reference` 字段。我们可以看到,序列化器字段上的验证器显式强制执行唯一性约束。 45 | 46 | 由于这种更显式的样式,REST framework 包含了一些在核心 Django 中不可用的验证器类。下面详细介绍这些类。 47 | 48 | *** 49 | 50 | ## UniqueValidator 51 | 该验证器可用于在模型字段上强制实施 `unique=True` 约束。它需要一个必需的参数和一个可选的 `messages` 参数: 52 | 53 | - `queryset` 必须 - 这是强制执行唯一性的查询集。 54 | - `message` - 验证失败时使用的错误消息。 55 | - `lookup` - lookup 用于查找具有验证值的现有实例。默认为 `'exact'`。 56 | 57 | 此验证器应该应用于序列化器字段,如下所示: 58 | ```python 59 | from rest_framework.validators import UniqueValidator 60 | 61 | slug = SlugField( 62 | max_length=100, 63 | validators=[UniqueValidator(queryset=BlogPost.objects.all())] 64 | ) 65 | ``` 66 | 67 | ## UniqueTogetherValidator 68 | 此验证器可用于在模型实例上强制执行 `unique_together` 约束。它有两个必需的参数和一个可选的 `messages` 参数: 69 | 70 | - `queryset` 必须 - 这是强制执行唯一性的查询集。 71 | - `fields` 必须 - 生成唯一集合的字段名称的列表或元组。这些必须作为序列化器类的字段存在。 72 | - `message` - 验证失败时使用的错误消息。 73 | 74 | 此验证器应该应用于序列化器类,如下所示: 75 | ```python 76 | from rest_framework.validators import UniqueTogetherValidator 77 | 78 | class ExampleSerializer(serializers.Serializer): 79 | # ... 80 | class Meta: 81 | # ToDo 项属于父列表,并具有由 'position' 字段定义的排序。给定列表中没有两个项目可以共享相同的位置。 82 | validators = [ 83 | UniqueTogetherValidator( 84 | queryset=ToDoItem.objects.all(), 85 | fields=('list', 'position') 86 | ) 87 | ] 88 | ``` 89 | 90 | *** 91 | 92 | **注意**:`UniqueTogetherValidation` 类始终施加一个隐式约束,即它所应用的所有字段都是按必须处理的。具有 `default` 值的字段是个例外,因为它们总是提供一个值,即使在用户输入中省略。 93 | 94 | *** 95 | 96 | ## UniqueForDateValidator 97 | ## UniqueForMonthValidator 98 | ## UniqueForYearValidator 99 | 这些验证器可用于在模型实例上强制执行 `unique_for_date`,`unique_for_month` 和 `unique_for_year` 约束。他们有以下参数: 100 | 101 | - `queryset` 必须 - 这是强制执行唯一性的查询集。 102 | - `fields` 必须 - 将验证给定日期范围中唯一性的字段名。这必须作为序列化类中的字段存在。 103 | - `date_field` 必须 - 将用于确定唯一性约束的日期范围的字段名称。这必须作为序列化器类中的字段存在。 104 | - `message` - 验证失败时使用的错误消息。 105 | 106 | 此验证器应该应用于序列化器类,如下所示: 107 | ```python 108 | from rest_framework.validators import UniqueForYearValidator 109 | 110 | class ExampleSerializer(serializers.Serializer): 111 | # ... 112 | class Meta: 113 | # Blog posts should have a slug that is unique for the current year. 114 | validators = [ 115 | UniqueForYearValidator( 116 | queryset=BlogPostItem.objects.all(), 117 | field='slug', 118 | date_field='published' 119 | ) 120 | ] 121 | ``` 122 | 用于验证的日期字段始终需要出现在序列化器类中。不能只依赖模型类的 `default=…`,因为要用于默认值的值在验证运行之后才会生成。 123 | 124 | 你可能需要使用几种样式,具体取决于您希望 API 如何展现。如果您使用的是 `ModelSerializer` ,可能只需依赖 REST framework 为您生成的默认值,但如果你使用的是 `Serializer` 或只想更明确的控制,请使用下面演示的样式。 125 | 126 | #### 使用可写日期字段 (Using with a writable date field.) 127 | 如果您希望日期字段可写,唯一值得注意的是您应确保输入数据始终可用,或者通过设置 `default` 参数,或者设置 `required = True`。 128 | ```python 129 | published = serializers.DateTimeField(required=True) 130 | ``` 131 | 132 | #### 使用只读日期字段 (Using with a read-only date field.) 133 | 如果你希望日期字段可见,但用户无法编辑,请设置 `read_only=True` 并另外设置 `default=...` 参数。 134 | ```python 135 | published = serializers.DateTimeField(read_only=True, default=timezone.now) 136 | ``` 137 | 该字段对用户不可写,但默认值仍将传递给 `validated_data`。 138 | 139 | #### 使用隐藏日期字段 (Using with a hidden date field.) 140 | 如果您希望日期字段对用户完全隐藏,请使用 `HiddenField`。该字段类型不接受用户输入,而是始终将其默认值返回到序列化器中的 `validated_data`。 141 | ```python 142 | published = serializers.HiddenField(default=timezone.now) 143 | ``` 144 | 145 | *** 146 | **注意**:`UniqueForValidation` 类强加一个隐式约束,即它所应用的字段都按必须处理的。具有 `default` 值的字段是个例外,因为它们总是提供一个值,即使在用户输入中省略。 147 | *** 148 | 149 | # 高级字段默认值 (Advanced field defaults) 150 | 在序列化器中跨多个字段应用的验证器有时需要 API 客户端不应提供的字段输入,但可用作验证器的输入。 151 | 152 | 您可能希望用于此类验证的两种模式包括: 153 | 154 | - 使用 `HiddenField`。该字段将出现在 `validated_data` 中,但不会在序列化器输出表示中使用。 155 | - 使用 `read_only=True` 的标准字段,但也包含 `default=...` 参数。该字段将用于序列化器输出表示,但不能由用户直接设置。 156 | 157 | REST framework 包含一些在此上下文中可能有用的默认值。 158 | 159 | #### CurrentUserDefault 160 | 可用于表示当前用户的默认类。为了使用它,在实例化序列化器时,'request' 必须作为上下文字典的一部分提供。 161 | ```python 162 | owner = serializers.HiddenField( 163 | default=serializers.CurrentUserDefault() 164 | ) 165 | ``` 166 | 167 | #### CreateOnlyDefault 168 | 可用于在创建操作期间仅设置默认参数的默认类。在更新期间,该字段被省略。 169 | 170 | 它接受一个参数,这是在创建操作期间应该使用的默认值或可调用参数。 171 | ```python 172 | created_at = serializers.DateTimeField( 173 | default=serializers.CreateOnlyDefault(timezone.now) 174 | ) 175 | ``` 176 | 177 | *** 178 | 179 | # 验证器的限制 (Limitations of validators) 180 | 有一些模棱两可的情况,您需要显式处理验证,而不是依赖于 `ModelSerializer` 生成的默认序列化器类。 181 | 182 | 在这些情况下,您可能希望通过为序列化器 `Meta.validators` 属性指定一个空列表来禁用自动生成的验证器。 183 | 184 | ## 可选字段 (Optional fields) 185 | 默认情况下 "unique together" 验证强制所有字段都是 `required=True`。在某些情况下,您可能希望显式的将 `required=False` 应用到其中一个字段,在这种情况下,期望的验证行为是不明确的。 186 | 187 | 在这种情况下,您通常需要从序列化器类中排除验证器,而不是显式地编写任何验证逻辑,无论是在 `.validate()` 方法中,还是在视图中。 188 | 189 | 举个栗子: 190 | ```python 191 | class BillingRecordSerializer(serializers.ModelSerializer): 192 | def validate(self, data): 193 | # 在这里或视图中应用自定义验证。 194 | 195 | class Meta: 196 | fields = ('client', 'date', 'amount') 197 | extra_kwargs = {'client': {'required': False}} 198 | validators = [] # 删除默认的 "unique together" 约束。 199 | ``` 200 | 201 | ## 更新嵌套的序列化器 (Updating nested serializers) 202 | 将更新应用于现有实例时,唯一性验证器将从唯一性检查中排除当前实例。当前实例在唯一性检查的上下文中可用,因为它作为序列化器中的属性存在,最初在实例化序列化器时使用 `instance=...` 传递。 203 | 204 | 在嵌套序列化器上进行更新操作时,无法应用此排除,因为该实例不可用。 205 | 206 | 同样,您可能希望从序列化器类中显式删除验证器,并在 `.validate()` 方法或视图中显式地为验证约束编写代码。 207 | 208 | ## 调试复杂案例 (Debugging complex cases) 209 | 如果您不确定 `ModelSerializer` 类将生成什么行为,那么运行 `manage.py shell` 通常是个好主意,并打印序列化器的实例,以便您可以检查自动生成的字段和验证器。 210 | ```python 211 | >>> serializer = MyComplexModelSerializer() 212 | >>> print(serializer) 213 | class MyComplexModelSerializer: 214 | my_fields = ... 215 | ``` 216 | 还要记住,对于复杂的情况,通常最好显式地定义序列化器类,而不是依赖于默认的 `ModelSerializer` 行为。这涉及更多代码,但确保结果行为更透明。 217 | 218 | *** 219 | 220 | # 编写自定义验证器 (Writing custom validators) 221 | 您可以使用任何 Django 现有的验证器,也可以编写自己的自定义验证器。 222 | 223 | ## 基于函数 (Function based) 224 | 验证器可以是任何可调用函数,在失败时引发 `serializers.ValidationError`。 225 | ```python 226 | def even_number(value): 227 | if value % 2 != 0: 228 | raise serializers.ValidationError('This field must be an even number.') 229 | ``` 230 | 231 | #### 字段级验证 (Field-level validation) 232 | 您可以通过将 `.validate_` 方法添加到 `Serializer` 子类来指定自定义字段级验证。这在 [Serializer 文档](http://www.django-rest-framework.org/api-guide/serializers/#field-level-validation)中有记录 233 | 234 | ## 基于类 (Class-based) 235 | 要编写基于类的验证器,请使用 `__call__` 方法。基于类的验证器非常有用,因为它们允许您参数化和重用行为。 236 | ```python 237 | class MultipleOf(object): 238 | def __init__(self, base): 239 | self.base = base 240 | 241 | def __call__(self, value): 242 | if value % self.base != 0: 243 | message = 'This field must be a multiple of %d.' % self.base 244 | raise serializers.ValidationError(message) 245 | ``` 246 | 247 | #### 使用 `set_context()` (Using `set_context()`) 248 | 在某些高级情况下,您可能希望将验证器传递给作为附加上下文使用的序列化器字段。您可以通过在基于类的验证器上声明 `set_context` 方法来实现。 249 | ```python 250 | def set_context(self, serializer_field): 251 | # 确定这是更新还是创建操作。 252 | # 在 `__call__` 中,我们可以使用该信息来修改验证行为。 253 | self.is_update = serializer_field.parent.instance is not None 254 | ``` 255 | -------------------------------------------------------------------------------- /API-Guide/Versioning.md: -------------------------------------------------------------------------------- 1 | # 版本控制 (Versioning) 2 | 对接口进行版本控制只是一种杀死已部署客户端的 “礼貌” 方式。—— [Roy Fielding](https://www.slideshare.net/evolve_conference/201308-fielding-evolve/31) 3 | 4 | API 版本控制允许您更改不同客户端之间的行为。REST framework 提供了许多不同的版本控制方案。 5 | 6 | 版本控制由传入的客户端请求决定,并且可以基于请求 URL,也可以基于请求头。 7 | 8 | 有许多有效的方法来处理版本控制。[非版本化的系统也可能是合适的](https://www.infoq.com/articles/roy-fielding-on-versioning),特别是如果您正在为超出您控制之外的多个客户端的非常长期的系统进行工程设计。 9 | 10 | ## REST framework 的版本控制 (Versioning with REST framework) 11 | 当启用 API 版本控制时,`request.version` 属性将包含对应于传入客户端请求中请求的版本的字符串。 12 | 13 | 默认情况下,版本控制未启用,`request.version` 将始终返回 `None`。 14 | 15 | #### 基于版本改变行为 (Varying behavior based on the version) 16 | 如何改变 API 行为取决于您,但您可能通常需要的一个示例是在新版本中切换到不同的序列化样式。例如: 17 | ```python 18 | def get_serializer_class(self): 19 | if self.request.version == 'v1': 20 | return AccountSerializerVersion1 21 | return AccountSerializer 22 | ``` 23 | 24 | #### 版本化 API 的反向 URL (Reversing URLs for versioned APIs) 25 | REST framework 包含的 `reverse` 函数与版本控制方案相关联。您需要确保将当前 `request` 包含为关键字参数,如下所示。 26 | ```python 27 | from rest_framework.reverse import reverse 28 | 29 | reverse('bookings-list', request=request) 30 | ``` 31 | 32 | 上述函数将应用适用于请求版本的任何 URL 转换。例如: 33 | 34 | - 如果正在使用 `NamespacedVersioning`,并且 API 版本为 'v1',那么使用的 URL 查找将是 `'v1:bookings-list'`,它可能解析为像 `http://example.org/v1/bookings/` 这样的 URL。 35 | - 如果正在使用 `QueryParameterVersioning`,并且 API 版本为 `1.0`,那么返回的 URL 可能类似于 `http://example.org/bookings/?version=1.0` 36 | 37 | #### 版本化 API 和超链接序列化器 (Versioned APIs and hyperlinked serializers) 38 | 将超链接序列化样式与基于 URL 的版本控制方案一起使用时,请确保将请求作为上下文包含在序列化器中。 39 | ```python 40 | def get(self, request): 41 | queryset = Booking.objects.all() 42 | serializer = BookingsSerializer(queryset, many=True, context={'request': request}) 43 | return Response({'all_bookings': serializer.data}) 44 | ``` 45 | 46 | 这样做将允许任何返回的 URL 包含适当的版本控制。 47 | 48 | ## 配置版本控制方案 (Configuring the versioning scheme) 49 | 版本控制方案由 `DEFAULT_VERSIONING_CLASS` 设置键定义。 50 | ```python 51 | REST_FRAMEWORK = { 52 | 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning' 53 | } 54 | ``` 55 | 56 | 除非显式设置,否则 `DEFAULT_VERSIONING_CLASS` 的值将为 `None`。在这种情况下,`request.version` 属性将始终返回 `None`。 57 | 58 | 您还可以在单个视图上设置版本控制方案。通常,您不需要这样做,因为全局使用单个版本控制方案更有意义。如果您确实需要这样做,请使用 `versioning_class` 属性。 59 | ```python 60 | class ProfileList(APIView): 61 | versioning_class = versioning.QueryParameterVersioning 62 | ``` 63 | 64 | #### 其他版本设置 (Other versioning settings) 65 | 以下设置键也用于控制版本控制: 66 | 67 | - `DEFAULT_VERSION`。当没有版本控制信息存在时,用于 `request.version` 的值。默认为 `None`。 68 | - `ALLOWED_VERSIONS`。如果设置,则该值将限制版本控制方案可能返回的版本集,并且如果提供的版本不在此集合中,则会引发错误。请注意,用于 `DEFAULT_VERSION` 设置的值始终被认为是 `ALLOWED_VERSIONS` 集的一部分 (除非它是 `None`)。默认为 `None`。 69 | - `VERSION_PARAM`。应用于任何版本控制参数的字符串,例如媒体类型或 URL 查询参数。默认为 `'version'`。 70 | 71 | 您还可以通过定义自己的版本控制方案并使用 `default_version`,`allowed_versions` 和 `version_param` 类变量,在每个视图或每个视图集的基础上设置版本控制类以及这三个值。例如,如果您想使用 `URLPathVersioning`: 72 | ```python 73 | from rest_framework.versioning import URLPathVersioning 74 | from rest_framework.views import APIView 75 | 76 | class ExampleVersioning(URLPathVersioning): 77 | default_version = ... 78 | allowed_versions = ... 79 | version_param = ... 80 | 81 | class ExampleView(APIVIew): 82 | versioning_class = ExampleVersioning 83 | ``` 84 | 85 | *** 86 | 87 | # API 参考 (API Reference) 88 | ## AcceptHeaderVersioning 89 | 此方案要求客户端在 `Accept` 标头中将版本指定为媒体类型的一部分。该版本作为媒体类型参数包含在内,它补充了主要媒体类型。 90 | 91 | 以下是使用 accept 标头版本控制样式的 HTTP 请求示例。 92 | ```python 93 | GET /bookings/ HTTP/1.1 94 | Host: example.com 95 | Accept: application/json; version=1.0 96 | ``` 97 | 98 | 在上面的示例请求中 `request.version` 属性将返回字符串 `'1.0'`。 99 | 100 | 基于 Accept header 的版本控制[通常被认为](http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http#i_want_my_api_to_be_versioned)是[最佳实践](https://github.com/interagent/http-api-design/blob/master/en/foundations/require-versioning-in-the-accepts-header.md),尽管其他样式可能更适合您的客户端需求。 101 | 102 | #### 使用带有供应商媒体类型的 accept 标头 (Using accept headers with vendor media types) 103 | 严格地说,`json` 媒体类型未指定为[包含额外参数](https://tools.ietf.org/html/rfc4627#section-6)。如果您正在构建一个明确指定的公共 API,您可以考虑使用[供应商媒体类型](https://en.wikipedia.org/wiki/Internet_media_type#Vendor_tree)。为此,使用具有自定义媒体类型的基于 JSON 的渲染器配置您的渲染器: 104 | ```python 105 | class BookingsAPIRenderer(JSONRenderer): 106 | media_type = 'application/vnd.megacorp.bookings+json' 107 | ``` 108 | 109 | 您的客户端请求现在看起来像这样: 110 | ```python 111 | GET /bookings/ HTTP/1.1 112 | Host: example.com 113 | Accept: application/vnd.megacorp.bookings+json; version=1.0 114 | ``` 115 | 116 | ## URLPathVersioning 117 | 此方案要求客户端将版本指定为 URL 路径的一部分。 118 | ```python 119 | GET /v1/bookings/ HTTP/1.1 120 | Host: example.com 121 | Accept: application/json 122 | ``` 123 | 124 | 您的 URL conf 必须包含一个与 `'version'` 关键字参数相匹配的模式,以便版本控制方案可以使用此信息。 125 | ```python 126 | urlpatterns = [ 127 | url( 128 | r'^(?P(v1|v2))/bookings/$', 129 | bookings_list, 130 | name='bookings-list' 131 | ), 132 | url( 133 | r'^(?P(v1|v2))/bookings/(?P[0-9]+)/$', 134 | bookings_detail, 135 | name='bookings-detail' 136 | ) 137 | ] 138 | ``` 139 | 140 | ## NamespaceVersioning 141 | 对于客户端,此方案与 `URLPathVersioning` 相同。唯一的区别是,它是如何在 Django 应用程序中配置的,因为它使用 URL 命名空间而不是 URL 关键字参数。 142 | ```python 143 | GET /v1/something/ HTTP/1.1 144 | Host: example.com 145 | Accept: application/json 146 | ``` 147 | 148 | 使用此方案,`request.version` 属性是根据与传入请求路径匹配的 `namespace` 确定的。 149 | 150 | 在下面的示例中,我们给出了一组视图,其中有两个不同的可能的 URL 前缀,每个前缀都位于不同的名称空间中: 151 | ```python 152 | # bookings/urls.py 153 | urlpatterns = [ 154 | url(r'^$', bookings_list, name='bookings-list'), 155 | url(r'^(?P[0-9]+)/$', bookings_detail, name='bookings-detail') 156 | ] 157 | 158 | # urls.py 159 | urlpatterns = [ 160 | url(r'^v1/bookings/', include('bookings.urls', namespace='v1')), 161 | url(r'^v2/bookings/', include('bookings.urls', namespace='v2')) 162 | ] 163 | ``` 164 | 165 | 如果您只需要简单的版本控制方案,则 `URLPathVersioning` 和 `NamespaceVersioning` 都是合理的。`URLPathVersioning` 方法可能更适合小型临时项目,而 `NamespaceVersioning` 可能更容易管理大型项目。 166 | 167 | ## HostNameVersioning 168 | 主机名版本控制方案要求客户端将请求的版本指定为URL中主机名的一部分。 169 | 170 | 例如,以下是对 `http://v1.example.com/bookings/` URL 的 HTTP 请求: 171 | ```python 172 | GET /bookings/ HTTP/1.1 173 | Host: v1.example.com 174 | Accept: application/json 175 | ``` 176 | 177 | 默认情况下,此实现要求主机名与此简单正则表达式匹配: 178 | ```python 179 | ^([a-zA-Z0-9]+)\.[a-zA-Z0-9]+\.[a-zA-Z0-9]+$ 180 | ``` 181 | 182 | 请注意,第一个组用括号括起来,表示这是主机名的匹配部分。 183 | 184 | 在调试模式中,使用 `HostNameVersioning` 方案可能会很尴尬,因为您通常会访问原始的 IP 地址 (如 127.0.0.1)。有关如何[使用自定义子域访问本地主机](https://reinteractive.net/posts/199-developing-and-testing-rails-applications-with-subdomains)的各种在线教程,在这种情况下您可能会发现有用的子域。 185 | 186 | 如果您有基于版本将传入请求路由到不同服务器的要求,则基于主机名的版本控制特别有用,因为您可以为不同的 API 版本配置不同的 DNS 记录。 187 | 188 | ## QueryParameterVersioning 189 | 此方案是一种简单样式,其中包含版本作为 URL 中的查询参数。例如: 190 | ```python 191 | GET /something/?version=0.1 HTTP/1.1 192 | Host: example.com 193 | Accept: application/json 194 | ``` 195 | 196 | *** 197 | 198 | # 自定义版本控制方案 (Custom versioning schemes) 199 | 要实现自定义版本控制方案,请继承 `BaseVersioning` 并覆盖 `.determine_version` 方法。 200 | 201 | ## 举个栗子 202 | 以下示例使用自定义 `X-API-Version` 标头来确定所请求的版本。 203 | ```python 204 | class XAPIVersionScheme(versioning.BaseVersioning): 205 | def determine_version(self, request, *args, **kwargs): 206 | return request.META.get('HTTP_X_API_VERSION', None) 207 | ``` 208 | 209 | 如果您的版本控制方案基于请求 URL,您还需要更改版本化 URL 的确定方式。为了做到这一点,您应重写类的 `.reverse()` 方法。有关示例,请参阅源代码。 210 | -------------------------------------------------------------------------------- /API-Guide/ViewSets.md: -------------------------------------------------------------------------------- 1 | # ViewSets 2 | 在路由确定了用于请求的控制器之后,控制器负责了解请求并生成适当的输出。—— [Ruby on Rails 文档](http://guides.rubyonrails.org/routing.html) 3 | 4 | Django REST framework 允许您将一组相关视图的逻辑组合在一个单独的类中,称为 `ViewSet`。在其他框架中,您可能会发现概念上类似的实现,名为 “Resources” 或 “Controllers” 。 5 | 6 | `ViewSet` 类只是一种基于类的视图类型,它不提供任何方法处理程序,如 `.get()` 或 `.post()`,而是提供诸如 `.list()` 和 `.create()` 之类的操作。 7 | 8 | `ViewSet` 的方法处理程序使用 `.as_view()` 方法在最终确定视图时绑定相应操作。 9 | 10 | 通常,与其在 urlconf 中显式地注册视图集中的视图,倒不如用路由器类注册视图集,它自动为您确定 urlconf。 11 | 12 | ## 举个栗子 13 | 让我们定义一个简单的视图集,可用于列出或检索系统中的所有用户。 14 | ```python 15 | from django.contrib.auth.models import User 16 | from django.shortcuts import get_object_or_404 17 | from myapps.serializers import UserSerializer 18 | from rest_framework import viewsets 19 | from rest_framework.response import Response 20 | 21 | class UserViewSet(viewsets.ViewSet): 22 | """ 23 | A simple ViewSet for listing or retrieving users. 24 | """ 25 | def list(self, request): 26 | queryset = User.objects.all() 27 | serializer = UserSerializer(queryset, many=True) 28 | return Response(serializer.data) 29 | 30 | def retrieve(self, request, pk=None): 31 | queryset = User.objects.all() 32 | user = get_object_or_404(queryset, pk=pk) 33 | serializer = UserSerializer(user) 34 | return Response(serializer.data) 35 | ``` 36 | 如果需要,我们可以将此视图集绑定到两个单独的视图中,如下所示: 37 | ```python 38 | user_list = UserViewSet.as_view({'get': 'list'}) 39 | user_detail = UserViewSet.as_view({'get': 'retrieve'}) 40 | ``` 41 | 通常我们不会这样做,而是用一个路由器注册视图集,并允许自动生成 urlconf。 42 | ```python 43 | from myapp.views import UserViewSet 44 | from rest_framework.routers import DefaultRouter 45 | 46 | router = DefaultRouter() 47 | router.register(r'users', UserViewSet, base_name='user') 48 | urlpatterns = router.urls 49 | ``` 50 | 您通常希望使用提供默认行为集合的现有基类,而不是编写自己的视图集。举个栗子: 51 | ```python 52 | class UserViewSet(viewsets.ModelViewSet): 53 | """ 54 | 用于查看和编辑用户实例的视图集。 55 | """ 56 | serializer_class = UserSerializer 57 | queryset = User.objects.all() 58 | ``` 59 | 使用 `ViewSet` 类比使用 `View` 类有两个主要优点。 60 | 61 | - 重复的逻辑可以组合成一个类。在上面的示例中,我们只需要指定一次 `queryset`,它将在多个视图中使用。 62 | - 通过使用 routers,我们不再需要处理自己的 URL 连接。 63 | 64 | 这两者各有优缺点。使用常规视图和 URL 配置文件更加明确,并为您提供更多控制。如果您想快速启动和运行,或者当您拥有大型 API 并希望始终执行一致的 URL 配置时, ViewSets 非常有用。 65 | 66 | ## 视图集操作 67 | REST framework 中包含的默认路由器将为一套标准的创建/检索/更新/销毁样式操作提供路由,如下所示: 68 | ```python 69 | class UserViewSet(viewsets.ViewSet): 70 | """ 71 | 示例空视图集演示由路由器类处理的标准操作。 72 | 73 | 如果您使用的是格式后缀,请确保每个操作还包括 `format=None` 关键字参数。 74 | """ 75 | 76 | def list(self, request): 77 | pass 78 | 79 | def create(self, request): 80 | pass 81 | 82 | def retrieve(self, request, pk=None): 83 | pass 84 | 85 | def update(self, request, pk=None): 86 | pass 87 | 88 | def partial_update(self, request, pk=None): 89 | pass 90 | 91 | def destroy(self, request, pk=None): 92 | pass 93 | ``` 94 | 95 | ## 反思视图集操作 96 | 在调度期间,`ViewSet` 上提供了以下属性。 97 | 98 | - `basename` —— 用于创建的 URL 名称的基础。 99 | - `action` —— 当前操作的名称 (例如,list,create)。 100 | - `detail` —— 布尔值指示当前动作是否配置为列表或详细视图。 101 | - `suffix` —— 视图集类型的显示后缀 - 反射详细属性。 102 | 103 | 您可以检查这些属性以根据当前操作调整行为。例如,您可以限制除了与 `list` 类似的所有权限: 104 | ```python 105 | def get_permissions(self): 106 | """ 107 | 实例化并返回该视图所需的权限列表。 108 | """ 109 | if self.action == 'list': 110 | permission_classes = [IsAuthenticated] 111 | else: 112 | permission_classes = [IsAdmin] 113 | return [permission() for permission in permission_classes] 114 | ``` 115 | 116 | ## 标记路由的额外操作 117 | 如果你有可路由的特殊方法,你可以用 `@action` 装饰器来标记它们。与常规操作一样,额外的操作可以用于对象列表或单个实例。要指出这一点,设置 `detail` 参数为 `True` 或 `False`。路由器将相应地配置其 URL 模式。例如,`DefaultRouter` 将在其 URL 模式中配置包含 `pk` 的详细操作。 118 | 119 | 更完整的额外操作例子: 120 | ```python 121 | from django.contrib.auth.models import User 122 | from rest_framework import status, viewsets 123 | from rest_framework.decorators import action 124 | from rest_framework.response import Response 125 | from myapp.serializers import UserSerializer, PasswordSerializer 126 | 127 | class UserViewSet(viewsets.ModelViewSet): 128 | """ 129 | 提供标准操作的视图集 130 | """ 131 | queryset = User.objects.all() 132 | serializer_class = UserSerializer 133 | 134 | @action(methods=['post'], detail=True) 135 | def set_password(self, request, pk=None): 136 | user = self.get_object() 137 | serializer = PasswordSerializer(data=request.data) 138 | if serializer.is_valid(): 139 | user.set_password(serializer.data['password']) 140 | user.save() 141 | return Response({'status': 'password set'}) 142 | else: 143 | return Response(serializer.errors, 144 | status=status.HTTP_400_BAD_REQUEST) 145 | 146 | @action(detail=False) 147 | def recent_users(self, request): 148 | recent_users = User.objects.all().order('-last_login') 149 | 150 | page = self.paginate_queryset(recent_users) 151 | if page is not None: 152 | serializer = self.get_serializer(page, many=True) 153 | return self.get_paginated_response(serializer.data) 154 | 155 | serializer = self.get_serializer(recent_users, many=True) 156 | return Response(serializer.data) 157 | ``` 158 | 另外,装饰器可以为路由视图设置额外的参数。举个栗子: 159 | ```python 160 | @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf]) 161 | def set_password(self, request, pk=None): 162 | ... 163 | ``` 164 | 这些装饰器将默认路由 `GET` 请求,但也可以通过设置 `methods` 参数来接受其他 HTTP 方法。例如: 165 | ```python 166 | @action(methods=['post', 'delete'], detail=True) 167 | def unset_password(self, request, pk=None): 168 | ... 169 | ``` 170 | 两个新的操作将在 url `^users/{pk}/set_password/$` 和 `^users/{pk}/unset_password/$` 上可用。 171 | 172 | 要查看所有额外操作,请调用 `.get_extra_actions()` 方法。 173 | 174 | ## 反转操作 urls 175 | 如果你需要获取操作的 URL,请使用 `.reverse_action()` 方法。这是 `Reverse()` 的便利封装,它自动传递视图的 `request` 对象,并在 `url_name` 前加上 `.basename` 属性。 176 | 177 | 请注意,`basename` 是路由器在 `ViewSet` 注册期间提供的。如果不使用路由器,则必须向 `.as_view()` 方法提供 `basename` 参数。 178 | 179 | 使用上一节中的示例: 180 | ```python 181 | >>> view.reverse_action('set-password', args=['1']) 182 | 'http://localhost:8000/api/users/1/set_password' 183 | ``` 184 | 或者,您可以使用 `@action` 装饰器设置的 `url_name` 属性。 185 | ```python 186 | >>> view.reverse_action(view.set_password.url_name, args=['1']) 187 | 'http://localhost:8000/api/users/1/set_password' 188 | ``` 189 | `.reverse_action()` 的 `url_name` 参数应该与 `@action` 装饰器匹配相同的参数。此外,此方法可用于反转默认操作,例如 `list` 和 `create`。 190 | 191 | *** 192 | 193 | # API 参考 194 | ## ViewSet 195 | `ViewSet` 类继承自 `APIView`。您可以使用任何标准属性 (例如 `permission_classes`,`authentication_classes`) 来控制视图集上的 API 策略。 196 | 197 | `ViewSet` 类不提供任何操作的实现。为了使用 `ViewSet` 类,您将重写该类并显式地定义操作的实现。 198 | 199 | ## GenericViewSet 200 | `GenericViewSet` 类继承自 `GenericAPIView` ,并提供默认的 `get_object`,`get_queryset` 方法和其他通用视图基本行为,但默认情况下不包含任何操作。 201 | 202 | 为了使用 `GenericViewSet` 类,您将重写该类,或混合所需的 mixin 类,或者显式定义操作实现。 203 | 204 | ## ModelViewSet 205 | `ModelViewSet` 类继承自 `GenericAPIView`,并通过混合各种 mixin 类的行为来包含各种操作的实现。 206 | 207 | `ModelViewSet` 类提供的操作有 `.list()`,`.retrieve()`,`.create()`,`.update()`,`.partial_update()` 和 `.destroy()`。 208 | 209 | #### 举个栗子 210 | 因为 `ModelViewSet` 扩展了 `GenericAPIView` ,所以通常需要至少提供 `queryset` 和 `serializer_class` 属性。例如: 211 | ```python 212 | class AccountViewSet(viewsets.ModelViewSet): 213 | """ 214 | 用于查看和编辑帐户的简单 ViewSet。 215 | """ 216 | queryset = Account.objects.all() 217 | serializer_class = AccountSerializer 218 | permission_classes = [IsAccountAdminOrReadOnly] 219 | ``` 220 | 注意,您可以使用 `GenericAPIView` 提供的任何标准属性或方法重写。例如,要动态确定它应该操作的查询集的 `ViewSet`,可以这样做: 221 | ```python 222 | class AccountViewSet(viewsets.ModelViewSet): 223 | """ 224 | 一个简单的视图集,用于查看和编辑与用户相关的帐户。 225 | """ 226 | serializer_class = AccountSerializer 227 | permission_classes = [IsAccountAdminOrReadOnly] 228 | 229 | def get_queryset(self): 230 | return self.request.user.accounts.all() 231 | ``` 232 | 然而注意,当从您的 `ViewSet` 中移除 `queryset` 属性,任何关联的[路由器](http://www.django-rest-framework.org/api-guide/routers/)将无法自动导出模型的 base_name,因此您必须指定 `base_name` kwarg 作为[路由器注册](http://www.django-rest-framework.org/api-guide/routers/)的一部分。 233 | 234 | 还要注意,尽管这个类默认情况下提供了完整的创建/列表/检索/更新/销毁操作集,但是可以使用标准权限类来限制可用操作。 235 | 236 | ## ReadOnlyModelViewSet 237 | `ReadOnlyModelViewSet` 类也继承自 `GenericAPIView`。与 `ModelViewSet` 一样,它还包括各种操作的实现,但与 `ModelViewSet` 不同,它只提供“只读”操作,`.list()` 和 `.retrieve()`。 238 | 239 | #### 举个栗子 240 | 与 `ModelViewSet` 一样,您通常需要至少提供 `queryset` 和 `serializer_class` 属性。例如: 241 | ```python 242 | class AccountViewSet(viewsets.ReadOnlyModelViewSet): 243 | """ 244 | 用于查看帐户的简单 ViewSet。 245 | """ 246 | queryset = Account.objects.all() 247 | serializer_class = AccountSerializer 248 | ``` 249 | 同样,与 `ModelViewSet` 一样,您可以使用 `GenericAPIView` 可用的任何标准属性和方法重写。 250 | 251 | # 自定义视图集基类 252 | 您可能需要提供自定义 `ViewSet` 类,这些类没有完整的 `ModelViewSet` 操作集,或者以其他方式自定义行为。 253 | 254 | ## 举个栗子 255 | 要创建提供 `create`,`list` 和 `retrieve` 操作的基本视图集类,请继承 `GenericViewSet`,并混合所需的操作: 256 | ```python 257 | from rest_framework import mixins 258 | 259 | class CreateListRetrieveViewSet(mixins.CreateModelMixin, 260 | mixins.ListModelMixin, 261 | mixins.RetrieveModelMixin, 262 | viewsets.GenericViewSet): 263 | """ 264 | 提供“检索”、“创建”和“列表”操作的视图集。 265 | 266 | 若要使用它,请重写该类并设置 “.queryset” 和 “.serializer_class” 属性。 267 | """ 268 | pass 269 | ``` 270 | 通过创建自己的基本 `ViewSet` 类,可以提供在 API 中的多个视图集中重用的通用行为。 271 | -------------------------------------------------------------------------------- /API-Guide/Views.md: -------------------------------------------------------------------------------- 1 | # 基于类的视图 2 | Django 的基于类的视图是一个受欢迎的背离旧风格的视图。 —— Reinout van Rees 3 | 4 | REST framework 提供了一个 `APIView` 类,它是 Django 的 `View` 类的子类。 5 | 6 | `APIView` 类与常规 `View` 类不同,有以下几种方法: 7 | 8 | - 传递给处理程序方法的请求将是 REST framework 的 `Request` 实例,而不是 Django 的 `HttpRequest` 实例。 9 | - 处理程序方法可能会返回 REST framework 的 `Response`,而不是 Django 的 `HttpResponse`。该视图将管理内容协商并在响应中设置正确的渲染器。 10 | - 任何 `APIException` 异常都会被捕获并调解为适当的响应。 11 | - 传入的请求将被认证,并且在将请求分派给处理程序方法之前将运行适当的的权限和/或限流检查。 12 | 13 | 使用 `APIView` 类与使用常规 `View` 类几乎是一样的,像往常一样,传入的请求被分派到适当的处理程序方法,如 `.get()` 或 `.post()` 。另外,可以在控制 API 策略的各个方面的类上设置多个属性。 14 | 15 | 例如: 16 | ```python 17 | from rest_framework.views import APIView 18 | from rest_framework.response import Response 19 | from rest_framework import authentication, permissions 20 | from django.contrib.auth.models import User 21 | 22 | class ListUsers(APIView): 23 | """ 24 | 查看以列出系统中的所有用户。 25 | 26 | * 需要令牌认证。 27 | * 只有管理员用户才能访问此视图。 28 | """ 29 | authentication_classes = (authentication.TokenAuthentication,) 30 | permission_classes = (permissions.IsAdminUser,) 31 | 32 | def get(self, request, format=None): 33 | """ 34 | 返回所有用户的列表。 35 | """ 36 | usernames = [user.username for user in User.objects.all()] 37 | return Response(usernames) 38 | ``` 39 | 40 | *** 41 | **注意**:Django REST framework 的 `APIView`,`GenericAPIView`,各种 `Mixins` 和 `Viewsets` 之间的完整方法,属性和关系初始(理解)很复杂。除了这里的文档之外,[Classy Django REST Framework](http://www.cdrf.co/) 资源还为每个 Django REST Framework 的基于类的视图提供了一个可浏览的参考,包含完整的方法和属性。 42 | *** 43 | 44 | ## API 策略属性 45 | 以下属性控制 API 视图的可插入方面。 46 | 47 | ### .renderer_classes 48 | ### .parser_classes 49 | ### .authentication_classes 50 | ### .throttle_classes 51 | ### .permission_classes 52 | ### .content_negotiation_class 53 | 54 | ## API 策略实例化方法 55 | REST framework 使用以下方法来实例化各种可插入 API 策略。您通常不需要重写这些方法。 56 | 57 | ### .get_renderers(self) 58 | ### .get_parsers(self) 59 | ### .get_authenticators(self) 60 | ### .get_throttles(self) 61 | ### .get_permissions(self) 62 | ### .get_content_negotiator(self) 63 | ### .get_exception_handler(self) 64 | 65 | ## API 策略实现方法 66 | 在分派到处理程序方法之前调用以下方法。 67 | 68 | ### .check_permissions(self, request) 69 | ### .check_throttles(self, request) 70 | ### .perform_content_negotiation(self, request, force=False) 71 | 72 | ## 调度方法 73 | 以下方法由视图的 `.dispatch()` 方法直接调用。它们执行调用处理程序方法之前或之后的任何操作,例如 `.get()`,`.post()`,`put()`,`patch()` 和 `.delete()`。 74 | 75 | ### .initial(self, request, *args, **kwargs) 76 | 执行在调用处理程序方法之前需要执行的任何操作。该方法用于强制执行权限和限流,并执行内容协商。 77 | 78 | 您通常不需要重写此方法。 79 | 80 | ### .handle_exception(self, exc) 81 | 处理程序方法抛出的任何异常都将传递给此方法,该方法要么返回 `Response` 实例,要么重新引发异常。 82 | 83 | 默认实现处理 `rest_framework.exceptions.APIException` 的任何子类,以及 Django 的 `Http404` 和 `PermissionDenied` 异常,并返回相应的错误响应。 84 | 85 | 如果您需要自定义 API 返回的错误响应,您应该子类化此方法。 86 | 87 | ### .initialize_request(self, request, *args, **kwargs) 88 | 确保传递给处理程序方法的请求对象是 `Request` 的一个实例,而不是通常的 Django ` HttpRequest`。 89 | 90 | 您通常不需要重写此方法。 91 | 92 | ### .finalize_response(self, request, response, *args, **kwargs) 93 | 确保从处理程序方法返回的任何 `Response` 对象都被渲染成正确的内容类型,这是由内容协商决定的。 94 | 95 | 您通常不需要重写此方法。 96 | *** 97 | 98 | # 基于函数的视图 (Function Based Views) 99 | 说 [基于类的视图] 永远是最好的解决方案是一个错误。——Nick Coghlan 100 | 101 | REST framework 还允许您使用常规的基于函数的视图。它提供了一套简单的装饰器来包装你的基于函数的视图,以确保它们接收 `Request` (而不是通常的 Django `HttpRequest`)实例并允许它们返回 `Response` (而不是 Django `HttpResponse` ),并允许你配置该请求的处理方式。 102 | 103 | ## @api_view() 104 | 语法:`@api_view(http_method_names=['GET'])` 105 | 106 | 这个功能的核心是 `api_view` 装饰器,它接受你的视图应该响应的 HTTP 方法列表。例如,这就是如何编写一个非常简单的视图,只需手动返回一些数据: 107 | ```python 108 | from rest_framework.decorators import api_view 109 | 110 | @api_view() 111 | def hello_world(request): 112 | return Response({"message": "Hello, world!"}) 113 | ``` 114 | 该视图将使用[设置](http://www.django-rest-framework.org/api-guide/settings/)中指定的默认渲染器,解析器,认证类等。 115 | 116 | 默认情况下,只有 `GET` 方法会被接受。其他方法将以“405 Method Not Allowed”进行响应。要改变这种行为,请指定视图允许的方法,如下所示: 117 | ```python 118 | @api_view(['GET', 'POST']) 119 | def hello_world(request): 120 | if request.method == 'POST': 121 | return Response({"message": "Got some data!", "data": request.data}) 122 | return Response({"message": "Hello, world!"}) 123 | ``` 124 | 125 | ## API 策略装饰器 (API policy decorators) 126 | 为了覆盖默认设置,REST framework 提供了一系列可以添加到视图中的附加装饰器。这些必须在 `@api_view` 装饰器之后(下方)。例如,要创建一个使用[限流](http://www.django-rest-framework.org/api-guide/throttling/)来确保它每天只能由特定用户调用一次的视图,请使用 `@throttle_classes` 装饰器,传递一个限流类列表: 127 | ```python 128 | from rest_framework.decorators import api_view, throttle_classes 129 | from rest_framework.throttling import UserRateThrottle 130 | 131 | class OncePerDayUserThrottle(UserRateThrottle): 132 | rate = '1/day' 133 | 134 | @api_view(['GET']) 135 | @throttle_classes([OncePerDayUserThrottle]) 136 | def view(request): 137 | return Response({"message": "Hello for today! See you tomorrow!"}) 138 | ``` 139 | 这些装饰器对应于上面描述的 `APIView` 子类上设置的属性。 140 | 141 | 可用的装饰者有: 142 | 143 | - `@renderer_classes(...)` 144 | - `@parser_classes(...)` 145 | - `@authentication_classes(...)` 146 | - `@throttle_classes(...)` 147 | - `@permission_classes(...)` 148 | 149 | 每个装饰器都有一个参数,它必须是一个类列表或者类元组。 150 | 151 | ## 视图模式装饰器 (View schema decorator) 152 | 要覆盖基于函数的视图的默认模式生成,您可以使用 `@schema` 装饰器。这必须在 `@api_view` 装饰器之后(下方)。例如: 153 | ```python 154 | from rest_framework.decorators import api_view, schema 155 | from rest_framework.schemas import AutoSchema 156 | 157 | class CustomAutoSchema(AutoSchema): 158 | def get_link(self, path, method, base_url): 159 | # override view introspection here... 160 | 161 | @api_view(['GET']) 162 | @schema(CustomAutoSchema()) 163 | def view(request): 164 | return Response({"message": "Hello for today! See you tomorrow!"}) 165 | ``` 166 | 167 | 该装饰器将接受一个 `AutoSchema` 实例,一个 `AutoSchema` 子类实例或 `ManualSchema` 实例,如[Schemas 文档](http://www.django-rest-framework.org/api-guide/schemas/)中所述。为了从模式生成中排除视图,您可以传递 `None`。 168 | -------------------------------------------------------------------------------- /Community/3.1_Announcement.md: -------------------------------------------------------------------------------- 1 | # Django REST framework 3.1 2 | 3.1 版本是 Kickstarter 项目发行版中的一个中间步骤,包含了一系列新功能。 3 | 4 | 一些亮点包括: 5 | 6 | - 超级智能光标分页方案。 7 | - 改进的分页 API,支持标头或主体分页样式。 8 | - 分页控制可浏览API中的渲染。 9 | - 更好地支持 API 版本控制。 10 | - 内置国际化支持。 11 | - 支持 Django 1.8 的 `HStoreField` 和 `ArrayField`。 12 | 13 | *** 14 | 15 | ## 分页 (Pagination) 16 | pagination API 经过了改进,使其更易于使用,功能更强大。 17 | 18 | 以下是标题功能的指南。有关完整详细信息,请参阅[分页文档](https://www.django-rest-framework.org/api-guide/pagination/)。 19 | 20 | 注意,作为此工作的结果,许多设置键和通用视图属性现在被移到挂起的弃用状态。控制分页样式现在主要通过覆盖分页类并修改其配置属性来处理。 21 | 22 | - `PAGINATE_BY` 设置键将继续工作,但现在正在等待弃用。现在应该使用更明显命名的 `PAGE_SIZE` 设置键。 23 | - `PAGINATE_BY_PARAM`、`MAX_PAGINATE_BY` 设置键将继续工作,但现在正在等待弃用,以便在配置的分页类上设置配置属性。 24 | - `paginate_by`、`page_query_param`、`paginate_by_param` 和 `max_paginate_by` 通用视图属性将继续工作,但现在正在等待弃用,以便在配置的分页类上设置配置属性。 25 | - `pagination_serializer_class` 视图属性和 `DEFAULT_PAGINATION_SERIALIZER_CLASS` 设置键**不再有效**。分页 API 不使用序列化器来确定输出格式,您需要在分页类上覆盖 `get_paginated_response` 方法,以便指定如何控制输出格式。 26 | 27 | #### 新的分页方案 (New pagination schemes.) 28 | 到目前为止,REST framework 中只有一个内置的分页样式。我们现在有了默认的基于页面、限制/偏移和游标的方案。 29 | 30 | 基于游标的分页方案特别智能,对于客户端迭代大型或频繁更改的结果集是一种更好的方法。该方案通过使用游标和限制/偏移信息支持对非唯一的索引分页。它还允许正向和反向游标分页。David Cramer 对这个主题的[博客文章](http://cramer.io/2011/03/08/building-cursors-for-the-disqus-api)赞不绝口。 31 | 32 | #### 可浏览 API 中的分页控件 (Pagination controls in the browsable API.) 33 | 分页结果现在包括直接在可浏览 API 中渲染的控件。如果您正在使用页面或限制/偏移样式,那么您将在可浏览的 API 中看到基于页面的控件: 34 | 35 |
36 | 37 | 基于游标的分页渲染出更简单的控件样式: 38 | 39 |
40 | 41 | #### 支持基于标头的分页 (Support for header-based pagination.) 42 | 分页 API 以前只能更改响应正文中的分页样式。API 现在支持能够在响应标头中写入分页信息,从而可以使用使用 `Link` 或 `Content-Range` 标头的分页方案。 43 | 44 | 有关更多信息,请参阅[自定义分页样式](https://www.django-rest-framework.org/api-guide/pagination/#custom-pagination-styles)文档。 45 | 46 | *** 47 | 48 | ## 版本控制 (Versioning) 49 | 我们使[构建版本化 API 变得更容易](https://www.django-rest-framework.org/api-guide/versioning/)。内置版本方案包括基于 URL 和基于 Accept 标头的变体。 50 | 51 | 当使用基于 URL 的方案时,超链接序列化器将解决与传入请求相同的 API 版本的关系。 52 | 53 | 例如,当使用 `NamespaceVersioning`,以下超链接序列化器: 54 | ```python 55 | class AccountsSerializer(serializer.HyperlinkedModelSerializer): 56 | class Meta: 57 | model = Accounts 58 | fields = ('account_name', 'users') 59 | ``` 60 | 61 | 输出表示将与传入请求上使用的版本匹配。像这样: 62 | ```python 63 | GET http://example.org/v2/accounts/10 # Version 'v2' 64 | 65 | { 66 | "account_name": "europa", 67 | "users": [ 68 | "http://example.org/v2/users/12", # Version 'v2' 69 | "http://example.org/v2/users/54", 70 | "http://example.org/v2/users/87" 71 | ] 72 | } 73 | ``` 74 | 75 | *** 76 | 77 | ## 国际化 (Internationalization) 78 | REST framework 现在包含一组内置的翻译,并[支持国际化的错误响应](https://www.django-rest-framework.org/topics/internationalization/)。这允许您更改默认语言,或者允许客户端通过 `Accept-Language` 标头指定语言。 79 | 80 | 您可以使用标准 Django `LANGUAGE_CODE` 设置更改默认语言: 81 | ```python 82 | LANGUAGE_CODE = "es-es" 83 | ``` 84 | 85 | 您可以通过将 `LocalMiddleware` 添加到 `MIDDLEWARE_CLASSES` 设置来打开每个请求的语言请求: 86 | ```python 87 | MIDDLEWARE_CLASSES = [ 88 | ... 89 | 'django.middleware.locale.LocaleMiddleware' 90 | ] 91 | ``` 92 | 93 | 当启用每个请求的国际化时,客户端请求将尽可能尊重 `Accept-Language` 标头。例如,让我们请求不受支持的媒体类型: 94 | 95 | **Request** 96 | ```python 97 | GET /api/users HTTP/1.1 98 | Accept: application/xml 99 | Accept-Language: es-es 100 | Host: example.org 101 | ``` 102 | 103 | **Response** 104 | ```python 105 | HTTP/1.0 406 NOT ACCEPTABLE 106 | 107 | { 108 | "detail": "No se ha podido satisfacer la solicitud de cabecera de Accept." 109 | } 110 | ``` 111 | 112 | 注意,错误响应的结构仍然相同。我们在响应中仍然有一个 `detail` 键。如果需要,您也可以通过使用自定义异常处理程序修改此行为。 113 | 114 | 我们包含了针对标准异常情况和序列化器验证错误的内置翻译。 115 | 116 | 支持的语言的完整列表可以在我们的 [Transifex 项目页面](https://www.transifex.com/projects/p/django-rest-framework/)上找到。 117 | 118 | 如果您只想支持一部分受支持的语言,请使用 Django 的标准 `LANGUAGES` 设置: 119 | ```python 120 | LANGUAGES = [ 121 | ('de', _('German')), 122 | ('en', _('English')), 123 | ] 124 | ``` 125 | 126 | 有关更多详细信息,请参阅[国际化文档](https://www.django-rest-framework.org/topics/internationalization/)。 127 | 128 | 非常感谢 [Craig Blaszczyk](https://github.com/jakul) 帮助推动这一过程。 129 | 130 | *** 131 | 132 | ## 新字段类型 (New field types) 133 | Django 1.8 的新 `ArrayField`,`HStoreField` 和 `UUIDField` 现在都完全支持。 134 | 135 | 这项工作还意味着我们现在有了 `serializers.DictField()` 和 `serializers.ListField()` 类型,允许您表达和验证更广泛的表示集。 136 | 137 | 如果您正在构建一个新的 1.8 项目,那么您应该考虑使用 `UUIDField` 作为所有模型的主键。此样式将自动与超链接序列化器一起工作,返回以下样式的 URL: 138 | ```python 139 | http://example.org/api/purchases/9b1a433f-e90d-4948-848b-300fdc26365d 140 | ``` 141 | 142 | *** 143 | 144 | ## ModelSerializer API 145 | 3.0 中的序列化器重新设计不包含任何公共 API,用于修改 `ModelSerializer` 类如何从给定的模式类自动生成一组字段。我们现在为此重新引入了一个 API,允许您创建行为不同的新 `ModelSerializer` 基类,例如为关系使用不同的默认样式。 146 | 147 | 有关更多信息,请参阅有关为 ModelSerializer 类[自定义字段映射](https://www.django-rest-framework.org/api-guide/serializers/#customizing-field-mappings)的文档。 148 | 149 | *** 150 | 151 | ## Moving packages out of core 152 | 我们现在已经将许多软件包从 REST framework 的核心移到了独立的可安装包中。如果您当前正在使用这些,则无需担心,您只需要 `pip install` 新软件包,并更改任何导入路径。 153 | 154 | 我们进行此更改是为了帮助分配维护工作负载,并更好地关注框架的核心要素。 155 | 156 | 这一变化也意味着我们可以更灵活地使用我们推荐的外部软件包。例如,出色维护的 [Django OAuth 工具包](https://github.com/evonove/django-oauth-toolkit)现在已经被提升为集成 OAuth 支持的推荐选项。 157 | 158 | 以下包现在已移出核心,应单独安装: 159 | 160 | - OAuth - [djangorestframework-oauth](https://jpadilla.github.io/django-rest-framework-oauth/) 161 | - XML - [djangorestframework-xml](https://jpadilla.github.io/django-rest-framework-xml) 162 | - YAML - [djangorestframework-yaml](https://jpadilla.github.io/django-rest-framework-yaml) 163 | - JSONP - [djangorestframework-jsonp](https://jpadilla.github.io/django-rest-framework-jsonp) 164 | 165 | 值得重申的是,除了添加新要求和修改某些导入路径之外,策略的更改不应该意味着您的代码库中的任何工作。例如,要安装 XML 渲染,您现在可以: 166 | ```python 167 | pip install djangorestframework-xml 168 | ``` 169 | 170 | 并修改您的设置,如下所示: 171 | ```python 172 | REST_FRAMEWORK = { 173 | 'DEFAULT_RENDERER_CLASSES': [ 174 | 'rest_framework.renderers.JSONRenderer', 175 | 'rest_framework.renderers.BrowsableAPIRenderer', 176 | 'rest_framework_xml.renderers.XMLRenderer' 177 | ] 178 | } 179 | ``` 180 | 181 | 感谢我们维护团队的最新成员 [JoséPadilla](https://github.com/jpadilla/) 处理这项工作并承担这些包的所有权。 182 | 183 | *** 184 | 185 | ## 弃用 (Deprecations) 186 | `request.DATA`,`request.FILES` 和 `request.QUERY_PARAMS` 属性从等待弃用变为已弃用。请使用 `request.data` 和 `request.query_params`,正如 3.0 版本注释中所讨论的那样。 187 | 188 | `write_only_fields`、`view_name` 和 `lookup_field` 的 ModelSerializer 元选项也从等待弃用改为弃用。使用 `extra_kwargs`,正如 3.0 版本注释中所讨论的那样。 189 | 190 | 所有这些属性和选项在 3.1 中仍然有效,但是它们的使用将引起警告。它们将在 3.2 中被完全删除。 191 | 192 | *** 193 | 194 | ## What's next? 195 | 下一个重点是 API 输出的 HTML 渲染,包括: 196 | 197 | - 序列化器的 HTML 表单渲染。 198 | - 可浏览 API 内置的过滤控件。 199 | - 另一种管理样式的接口。 200 | 201 | 这将作为单个 3.2 版本发布,或者分为两个单独的版本,HTML 表单和过滤器控件将在 3.2 中出现,管理样式界面将在 3.3 版本中发布。 202 | -------------------------------------------------------------------------------- /Community/3.2_Announcement.md: -------------------------------------------------------------------------------- 1 | # Django REST framework 3.2 2 | 3.2 版本是第一个包含可浏览 API 的管理界面的版本。 3 | 4 | ![](https://www.django-rest-framework.org/img/admin.png) 5 | 6 | 此接口旨在充当 API 的更用户友好的接口。它既可以作为现有 `BrowsableAPIRenderer` 的替代品,也可以与它一起使用,允许您根据需要在两种样式之间切换。 7 | 8 | 我们还修复了大量的问题,并进行了大量的清理和改进。 9 | 10 | 在 3.1.x 系列的过程中,我们在 GitHub 问题跟踪器上[解决了近 600 票](https://github.com/encode/django-rest-framework/issues?utf8=%E2%9C%93&q=closed%3A%3E2015-03-05)。这意味着我们目前以**每月约 100 个问题关闭或拉取请求**的速度运行。 11 | 12 | 没有我们优秀的 Kickstarter 支持者的支持,这一切都不可能实现。如果您正在寻找 Django 开发的工作,我们强烈建议您[查看我们的赞助商](https://www.django-rest-framework.org/topics/kickstarter-announcement/#sponsors)并了解谁在招聘。 13 | 14 | ## AdminRenderer 15 | 要包含 `AdminRenderer`,只需将其添加到您的设置: 16 | ```python 17 | REST_FRAMEWORK = { 18 | 'DEFAULT_RENDERER_CLASSES': [ 19 | 'rest_framework.renderers.JSONRenderer', 20 | 'rest_framework.renderers.AdminRenderer', 21 | 'rest_framework.renderers.BrowsableAPIRenderer' 22 | ], 23 | 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 24 | 'PAGE_SIZE': 100 25 | } 26 | ``` 27 | 28 | `AdminRenderer` 存在一些限制,特别是它还不能处理列表或字典输入,因为我们没有任何支持这些的 HTML 表单字段。 29 | 30 | 另请注意,这是初始版本,我们还没有用于修改覆盖模板的行为或文档的公共 API。 31 | 32 | 我们的想法是尽早将这个版本发布给用户,这样我们就可以开始得到反馈,并在 3.3 版本中发布功能更加完善的版本。 33 | 34 | ## 支持的版本 (Supported versions) 35 | 此版本放弃了对 Django 1.4 的支持。 36 | 37 | 我们支持的 Django 版本现在是 1.5.6 +,1.6.3 +,1.7 和 1.8。 38 | 39 | ## 弃用 (Deprecations) 40 | 3.2 中没有新的弃用,尽管一些现有的弃用已经随着我们的弃用策略而升级。 41 | 42 | - `request.DATA` 被放在 3.0 的弃用路径中。它现在已被删除,其使用将导致错误。请使用更 pythonic 样式的 `request.data`。 43 | - `request.QUERY_PARAMS` 被放在 3.0 的弃用路径中。它现在已被删除,其使用将导致错误。请使用更 pythonic 样式的 `request.query_params`。 44 | - 现在已删除以下 `ModelSerializer.Meta` 选项:`write_only_fields`,`view_name`,`lookup_field`。请使用更通用的 `extra_kwargs` 选项。 45 | 46 | 自 3.1 以来,以下分页视图属性和设置已移至分页类的属性中。它们的使用以前处于 “待定弃用” 状态,现在已升级为 “弃用”。它们将继续行使职责但会引发错误。 47 | 48 | - `view.paginate_by` - 使用 `paginator.page_size` 代替。 49 | - `view.page_query_param` - 使用 `paginator.page_query_param` 代替。 50 | - `view.paginate_by_param` - 使用 `paginator.page_size_query_param` 代替。 51 | - `view.max_paginate_by` - 使用 `paginator.max_page_size` 代替。 52 | - `settings.PAGINATE_BY` - 使用 `paginator.page_size` 代替。 53 | - `settings.PAGINATE_BY_PARAM` - 使用 `paginator.page_size_query_param` 代替。 54 | - `settings.MAX_PAGINATE_BY` - 使用 `paginator.max_page_size` 代替。 55 | 56 | ## 列出行为的修改 (Modifications to list behaviors) 57 | 当他们引入不同的行为时,有一些 bug 修复值得一提。 58 | 59 | 这些有点微妙,可能不会影响到大多数用户,但是在升级项目之前值得了解。 60 | 61 | ### ManyToMany fields and blank=True 62 | 我们现在添加了 `allow_empty` 参数,它可以与 `ListSerializer` 或 `many=True` 关系一起使用。默认情况下为 `True`,但是如果您不允许空列表作为有效输入,则可以将其设置为 `False`。 63 | 64 | 作为这方面的后续工作,我们现在能够正确地反映 Django 的 `ModelForm` 在验证多对多字段方面的行为。 65 | 66 | 以前,模型上的多对多字段会映射到允许空或非空列表输入的序列化器字段。现在,多对多字段将映射到需要至少一个输入的序列化器字段,除非模型字段具有 `blank=True` 设置。 67 | 68 | 这是映射在实践中的样子: 69 | 70 | - `models.ManyToManyField()` → `serializers.PrimaryKeyRelatedField(many=True, allow_empty=False)` 71 | - `models.ManyToManyField(blank=True)` → `serializers.PrimaryKeyRelatedField(many=True)` 72 | 73 | 结果是这样的:如果模型中有多对多字段,那么如果要在等效的 `ModelSerializer` 字段中允许空输入,请确保包含参数 `blank=True`。 74 | 75 | ### List fields and allow_null 76 | 当使用带有 `ListField` 的 `allow_null` 或嵌套的 `many=True` 序列化器时,前面的行为是允许 `null` 值作为列表中的项。现在的行为是允许 `null` 值而不是列表。 77 | 78 | 例如,请使用以下字段: 79 | ```python 80 | NestedSerializer(many=True, allow_null=True) 81 | ``` 82 | 83 | 以前的验证行为是: 84 | 85 | - `[{…}, null, {…}]` **有效**。 86 | - `null` **无效**。 87 | 88 | 我们从 3.2.0 开始的验证行为现在是: 89 | 90 | - `[{…}, null, {…}]` **无效**。 91 | - `null` **有效**。 92 | 93 | 如果希望允许 `null` 子项,则需要在子类上指定 `allow_null`,使用显式 `ListField` 而不是 `many=True`。例如: 94 | ```python 95 | ListField(child=NestedSerializer(allow_null=True)) 96 | ``` 97 | 98 | ## What's next? 99 | 3.3 版本目前计划于 10 月初发布,这将是 kickstarter 资助的最后一个版本。 100 | 101 | 此版本计划包括: 102 | 103 | - 在可浏览的 API 和管理界面中搜索和过滤控件。 104 | - 管理界面的改进和公共 API。 105 | - 我们模板化的 HTML 表单和字段的改进和公共 API。 106 | - HTML 表单中的嵌套对象和列表支持。 107 | 108 | 再次感谢所有赞助商和支持者。 109 | -------------------------------------------------------------------------------- /Community/3.3_Announcement.md: -------------------------------------------------------------------------------- 1 | # Django REST framework 3.3 2 | 3.3 版本标志着 Kickstarter 资助系列的最终工作。我们想向所有精彩的赞助商和支持者致以响亮的**谢意**。 3 | 4 | 由于资金直接结果而实现的工作量是巨大的。我们添加了大量的新功能,解决了近 2000 票,并且重新设计和完善了项目的大部分内容。 5 | 6 | 为了继续推动 REST framework 向前发展,我们将很快宣布一套新的资助计划。关注@_tomchristie,以及时更新这些公告,并成为首批注册会员。 7 | 8 | 我们坚信,合作资助的软件开发能够以相对较低的人均投资获得出色的成果。如果您或您的公司在商业上使用 REST framework,那么我们将强烈敦促您参与这一最新的资金驱动,并帮助我们继续构建一个日益完善和专业的产品。 9 | 10 | *** 11 | 12 | ## 发行说明 (Release notes) 13 | 3.3 版本中的重要新功能包括: 14 | 15 | - 过滤器在可浏览的 API 中显示为 HTML 控件。 16 | - [表单 API](https://www.django-rest-framework.org/topics/html-and-forms/),允许将序列化器渲染为 HTML 表单。 17 | - Django 1.9 支持。 18 | - [`JSONField` 序列化器字段](https://www.django-rest-framework.org/api-guide/fields#jsonfield),对应于 Django 1.9 的 Postgres `JSONField` 模型字段。 19 | - [通过 AJAX](https://github.com/encode/ajax-form) 提供可浏览的 API 支持,而不是服务器端请求重载。 20 | 21 | ![](https://www.django-rest-framework.org/img/filter-controls.png) 22 | 23 | *新过滤器控件的示例* 24 | 25 | *** 26 | 27 | ## 支持的版本 (Supported versions) 28 | 此版本不再支持 Django 1.5 和 1.6。Django 1.7,1.8 或 1.9 现在是必需的。 29 | 30 | 这使我们支持的版本与 Django [目前支持的版本](https://www.djangoproject.com/download/#supported-versions)一致。 31 | 32 | ## 弃用 (Deprecations) 33 | 基于 AJAX 的可浏览 API 支持意味着请求类中有许多内部清理。对于绝大多数开发人员来说,这应该基本上保持透明: 34 | 35 | - 要支持基于表单的 `PUT` 和 `DELETE`,或者支持表单内容类型 (比如 JSON),您现在应该使用 AJAX 表单 javascript 库。这替换了以前的 “方法和内容类型重载”,这要求请求类具有显著的内部复杂性。 36 | - 默认内容协商类不再支持 `accept` 查询参数。如果您需要,那么您将需要[使用自定义内容协商类](https://www.django-rest-framework.org/topics/browser-enhancements/#url-based-accept-headers)。 37 | - 默认不再支持自定义 `HTTP_X_HTTP_METHOD_OVERRIDE` 标头。如果您需要它,那么您将需要[使用自定义中间件](https://www.django-rest-framework.org/topics/browser-enhancements/#http-header-based-method-overriding)。 38 | 39 | 自 3.1 以来,以下分页视图属性和设置已移至分页类的属性中。它们的使用以前已被弃用,现已完全删除,符合弃用政策。 40 | 41 | - `view.paginate_by` - 使用 `paginator.page_size` 代替。 42 | - `view.page_query_param` - 使用 `paginator.page_query_param` 代替。 43 | - `view.paginate_by_param` - 使用 `paginator.page_size_query_param` 代替。 44 | - `view.max_paginate_by` - 使用 `paginator.max_page_size` 代替。 45 | - `settings.PAGINATE_BY` - 使用 `paginator.page_size` 代替。 46 | - `settings.PAGINATE_BY_PARAM` - 使用 `paginator.page_size_query_param` 代替。 47 | - `settings.MAX_PAGINATE_BY` - 使用 `paginator.max_page_size` 代替。 48 | 49 | `ModelSerializer` 和 `HyperlinkedModelSerializer` 类现在应该包含 `fields` 或 `exclude` 选项,尽管可以使用 `fields = '__all__'` 快捷方式。没有包括这两个选项中的任何一个,目前正在等待弃用,并将在 3.5 版本中完全删除。这种行为使 `ModelSerializer` 更加符合 Django 的 `ModelForm` 行为。 50 | -------------------------------------------------------------------------------- /Community/3.4_Announcement.md: -------------------------------------------------------------------------------- 1 | # Django REST framework 3.4 2 | 3.4 发行版是计划系列中的第一个版本,它将处理模式生成、超媒体支持、API 客户端以及最后的实时支持。 3 | 4 | *** 5 | 6 | ## 资金 (Funding) 7 | 3.4 版本是通过我们的[协作资助模型](https://www.django-rest-framework.org/community/funding/)在最近的 [Mozilla 拨款](https://www.django-rest-framework.org/community/mozilla-grant/)中实现的。如果您在商业上使用 REST framework,并且希望看到这项工作继续进行,我们强烈鼓励您通过[**注册付费计划**](https://www.django-rest-framework.org/community/funding/)来投资其持续开发。 8 | 9 | 最初的目标是在 REST framework 上提供一个单一的全职职位。现在,我们已达到实现这一目标的 60% 以上。每一次注册都会产生重大影响。 10 | 11 | [![](https://fund-rest-framework.s3.amazonaws.com/rover_130x130.png)](http://jobs.rover.com/) [![](https://fund-rest-framework.s3.amazonaws.com/sentry130.png)](https://getsentry.com/welcome/) [![](https://fund-rest-framework.s3.amazonaws.com/stream-130.png)](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf) 12 | 13 | *非常感谢我们所有[出色的赞助商](https://fund.django-rest-framework.org/topics/funding/#our-sponsors),特别是我们的高级赞助商 [Rover](http://jobs.rover.com/),[Sentry](https://getsentry.com/welcome/) 和 [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf)。* 14 | 15 | *** 16 | 17 | ## 模式和客户端库 (Schemas & client libraries) 18 | REST framework 3.4 为生成 API 模式提供了内置支持。 19 | 20 | 我们通过使用 [Core API](https://www.coreapi.org/) (用于描述 API 的文档对象模型) 来提供此支持。 21 | 22 | 由于 Core API 以独立于格式的方式表示 API 模式,因此通过允许渲染器类确定内部表示如何映射到外部模式格式,我们能够将 Core API `Document` 对象渲染为许多不同的模式格式。 23 | 24 | 通过将 `Document` 对象渲染为 HTML 文档页面,此方法还将为将来的一系列自动生成的 API 文档选项打开大门。 25 | 26 | 除了内置模式支持外,我们现在还提供了以下功能: 27 | 28 | - 用于与 API 交互的[命令行工具](https://www.django-rest-framework.org/community/api-clients#command-line-client)。 29 | - 用于与 API 交互的 [Python 客户端库](https://www.django-rest-framework.org/community/api-clients#python-client-library)。 30 | 31 | 这些 API 客户端是动态驱动的,并且能够与任何公开支持的模式格式的 API 交互。 32 | 33 | 动态驱动客户端允许您在应用层接口而不是网络层接口上与 API 交互,同时还提供了 RESTful Web API 设计的好处。 34 | 35 | 我们期望在未来几个月内扩大我们提供客户库的语言范围。 36 | 37 | 还计划进一步完善 API 模式支持,包括支持文件上传和下载的文档,以及对文档生成和参数注释的改进支持。 38 | 39 | *** 40 | 41 | 当前对模式格式的支持如下: 42 | 43 | | 名称 | 支持 | PyPI 包 | 44 | | ------ | ------ | ------ | 45 | | [Core JSON](https://www.coreapi.org/specification/encoding/#core-json-encoding) | 模式生成和客户端支持。 | `coreapi` 内置支持。 | 46 | | [Swagger / OpenAPI](https://openapis.org/specification) | 模式生成和客户端支持。 | `openapi-codec` 包。 | 47 | | [JSON Hyper-Schema](https://json-schema.org/latest/json-schema-hypermedia.html) | 目前仅支持客户端。 | `hyperschema-codec` 包。 | 48 | | [API Blueprint](https://apiblueprint.org/) | 尚不可用。 | 尚不可用。 | 49 | 50 | *** 51 | 52 | 你可以阅读更多关于任何的新功能如下: 53 | 54 | - 关于[模式和客户端库](https://www.django-rest-framework.org/tutorial/7-schemas-and-client-libraries/)的新教程部分。 55 | - 关于[模式生成](https://www.django-rest-framework.org/api-guide/schemas/)的文档页。 56 | - 关于 [API 客户端](https://www.django-rest-framework.org/topics/api-clients/)的主题页面。 57 | 58 | 同样值得注意的是,Marc Gibbons 目前正在为流行的 Django REST Swagger 包的 2.0 版本努力,它将与我们新的内置支持相结合。 59 | 60 | *** 61 | 62 | ## 支持的版本 (Supported versions) 63 | 3.4.0 版本增加了对 Django 1.10 的支持。 64 | 65 | 现在支持以下版本的 Python 和 Django: 66 | 67 | - Django 版本 1.8,1.9 和 1.10。 68 | - Python 版本 2.7,3.2 (\*),3.3 (\*),3.4,3.5。 69 | 70 | (\*) 请注意,Django 1.9 以后不支持 Python 3.2 和 3.3。 71 | 72 | *** 73 | 74 | ## 弃用和变化 (Deprecations and changes) 75 | 3.4 版本包括非常有限的弃用或行为更改,并应提供简单的升级。 76 | 77 | ### 在序列化器类上使用字段或排除 (Use fields or exclude on serializer classes.) 78 | 3.3.0 中的以下更改现在已从 “等待弃用” 升级为 “已弃用”。它的使用将继续发挥作用,但会引起警告: 79 | 80 | `ModelSerializer` 和 `HyperlinkedModelSerializer` 应包含 `fields` 选项或 `exclude` 选项。`fields = '__ all__'` 快捷方式可用于显式包含所有字段。 81 | 82 | ### 返回时间或日期时间时的微秒精度 (Microsecond precision when returning time or datetime.) 83 | 使用默认的 JSON 渲染器并直接返回 `datetime` 或 `time` 实例,现在将以微秒精度 (6 位数) 呈现,而不是毫秒精度 (3 位数)。这使得输出格式与 `serializers.DateTimeField` 和 `serializers.TimeField` 的默认字符串输出一致。 84 | 85 | 此更改*不会影响使用序列化器时的默认行为*,而是将 `datetime` 和 `time` 实例以微秒精度序列化为字符串。 86 | 87 | 如果需要,可以使用 `DATETIME_FORMAT` 和 `TIME_FORMAT` 设置修改序列化器行为。 88 | 89 | 可以通过在 `JSONRenderer` 子类上设置自定义 `encoder_class` 属性来修改渲染器行为。 90 | 91 | ### 关系选项不再显示在 OPTIONS 请求中 (Relational choices no longer displayed in OPTIONS requests.) 92 | 向具有序列化器选择字段的视图发出 `OPTIONS` 请求将导致响应中返回可用选项列表。 93 | 94 | 在存在关系字段的情况下,先前的行为是返回可用实例的列表以供该关系字段选择。 95 | 96 | 为了尽量减少暴露的信息,现在的行为是不返回关系字段的选择信息。 97 | 98 | 如果要覆盖此新行为,则需要[实现自定义元数据类](https://www.django-rest-framework.org/api-guide/metadata/#custom-metadata-classes)。 99 | 100 | 有关此行为更改的详细信息,请参阅[问题 #3751](https://github.com/encode/django-rest-framework/issues/3751)。 101 | 102 | ## 其他改进 (Other improvements) 103 | 此版本包括来自大量[拉取请求和问题](https://github.com/encode/django-rest-framework/milestone/35)的进一步工作。 104 | 105 | 非常感谢所有参与发布的贡献者,无论是通过提出问题、提供反馈、改进文档、建议和实现代码更改。 106 | 107 | 完整的逐项发布说明可以[在这里找到](https://www.django-rest-framework.org/community/release-notes#34)。 108 | -------------------------------------------------------------------------------- /Community/3.5_Announcement.md: -------------------------------------------------------------------------------- 1 | # Django REST framework 3.5 2 | 3.5 版本是计划系列中的第二个版本,它处理模式生成,超媒体支持,API 客户端库以及最终的实时支持。 3 | 4 | *** 5 | 6 | ## 资金 (Funding) 7 | 如果没有我们的[合作资助模式](https://www.django-rest-framework.org/community/funding/),3.5 版本是不可能实现的。如果您在商业上使用 REST framework,并且希望看到这项工作继续进行,我们强烈鼓励您通过[**注册付费计划**](https://www.django-rest-framework.org/community/funding/)来投资其持续开发。 8 | 9 | [![](https://fund-rest-framework.s3.amazonaws.com/rover_130x130.png)](http://jobs.rover.com/) [![](https://fund-rest-framework.s3.amazonaws.com/sentry130.png)](https://getsentry.com/welcome/) [![](https://fund-rest-framework.s3.amazonaws.com/stream-130.png)](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf) [![](https://fund-rest-framework.s3.amazonaws.com/Machinalis130.png)](https://www.machinalis.com/#services) 10 | 11 | *非常感谢我们所有的[赞助商](https://fund.django-rest-framework.org/topics/funding/#our-sponsors),特别是我们的高级支持者 [Rover](http://jobs.rover.com/),[Sentry](https://getsentry.com/welcome/),[Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf) 和 [Machinalis](https://www.machinalis.com/#services)。* 12 | 13 | *** 14 | 15 | ## 改进了模式生成 (Improved schema generation) 16 | 视图上的文档字符串现在被引入模式定义,允许您[使用模式定义来记录 API](https://www.django-rest-framework.org/api-guide/schemas/#schemas-as-documentation)。 17 | 18 | 现在还有一个快捷函数 `get_schema_view()`,它使向 API [添加模式视图](https://www.django-rest-framework.org/api-guide/schemas/#the-get_schema_view-shortcut)变得更容易。 19 | 20 | 例如,要将 swagger 模式包含到您的 API 中,您可以执行以下操作: 21 | 22 | - 运行 `pip install django-rest-swagger`。 23 | - 将 `'rest_framework_swagger'` 添加到 `INSTALLED_APPS` 设置中。 24 | - 在 URL conf 中包括模式视图: 25 | 26 | ```python 27 | from rest_framework.schemas import get_schema_view 28 | from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer 29 | 30 | schema_view = get_schema_view( 31 | title='Example API', 32 | renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer] 33 | ) 34 | 35 | urlpatterns = [ 36 | url(r'^swagger/$', schema_view), 37 | ... 38 | ] 39 | ``` 40 | 41 | 对模式生成进行了大量修复。这些应解决任何使用 `django-rest-swagger` 包的最新版本的人的问题。 42 | 43 | 其中一些更改确实会影响生成的模式结构,因此如果您已经使用了模式生成,那么您应该确保检查[弃用说明](https://www.django-rest-framework.org/community/3.5-announcement/#deprecations),特别是如果您当前正在使用动态客户端库与 API 进行交互。 44 | 45 | 最后,我们还将模式生成作为[公开文档化的 API](https://www.django-rest-framework.org/api-guide/schemas/#schemagenerator) 公开,允许您更容易地覆盖该行为。 46 | 47 | ## 请求测试客户端 (Requests test client) 48 | 您现在可以使用 `requests` 库测试项目。 49 | 50 | 这将公开与使用标准请求会话实例完全相同的接口。 51 | ```python 52 | client = RequestsClient() 53 | response = client.get('http://testserver/users/') 54 | assert response.status_code == 200 55 | ``` 56 | 57 | 这个接口不会向网络发送任何 HTTP 请求,而是将所有发出的请求强制放入 WSGI,并直接调用应用程序。 58 | 59 | ## Core API client 60 | 您现在还可以使用 `coreapi` 客户端库通过与项目交互来测试项目。 61 | ```python 62 | # 获取 API 模式 63 | client = CoreAPIClient() 64 | schema = client.get('http://testserver/schema/') 65 | 66 | # 创建新组织 67 | params = {'name': 'MegaCorp', 'status': 'active'} 68 | client.action(schema, ['organisations', 'create'], params) 69 | 70 | # 确保该组织存在于列表中 71 | data = client.action(schema, ['organisations', 'list']) 72 | assert(len(data) == 1) 73 | assert(data == [{'name': 'MegaCorp', 'status': 'active'}]) 74 | ``` 75 | 76 | 同样,这将使用 WSGI 接口直接调用应用程序,而不是进行实际的网络调用。 77 | 78 | 如果您计划使用 `coreapi` 客户端库或其他一些自动生成的客户端主要与您的 API 进行交互,那么这是一个不错的选择。 79 | 80 | ## 实时测试 (Live tests) 81 | `requests` 客户端和 `coreapi` 客户端都有一个有趣的方面,即它们允许您以这样一种方式编写测试,即它们也可以针对实时服务运行。 82 | 83 | 通过将基于 WSGI 的客户端实例切换到 `requests.Session` 或 `coreapi.Client` 的实际实例,您可以让测试用例进行实际的网络调用。 84 | 85 | 能够编写可以训练您的临时或生产环境的测试用例是一个强有力的工具。但是,为了做到这一点,您需要密切关注如何处理设置和拆卸,以确保将测试数据与其他实时或临时数据严格隔离。 86 | 87 | ## RAML support 88 | 我们现在已经初步支持 [RAML 文档生成](https://github.com/encode/django-rest-raml)。 89 | 90 | ![](https://www.django-rest-framework.org/img/raml.png) 91 | 92 | 计划进一步开展编码和文档生成工作,以便在以后提供诸如 “立即尝试” 支持等功能。 93 | 94 | 此工作现在还意味着您可以使用 Core API 客户端库与公开 RAML 规范的 API 进行交互。 95 | 96 | RAML 编解码器给出了一些以这种方式与 Spotify API 交互的示例。 97 | 98 | ## 验证码 (Validation codes) 99 | REST framework 引发的异常现在包括短代码标识符。当与我们的自定义错误处理一起使用时,现在允许您修改 API 错误消息的样式。 100 | 101 | 作为示例,这允许以下错误响应的样式: 102 | ```python 103 | { 104 | "message": "You do not have permission to perform this action.", 105 | "code": "permission_denied" 106 | } 107 | ``` 108 | 109 | 这对于验证错误特别有用,验证错误使用适当的代码来识别不同类型的失败… 110 | ```python 111 | { 112 | "name": {"message": "This field is required.", "code": "required"}, 113 | "age": {"message": "A valid integer is required.", "code": "invalid"} 114 | } 115 | ``` 116 | 117 | ## 客户端上传和下载支持 (Client upload & download support) 118 | Python `coreapi` 客户端库和 Core API 命令行工具现在都完全支持文件[上传](https://core-api.github.io/python-client/api-guide/utils/#file)和[下载](https://core-api.github.io/python-client/api-guide/codecs/#downloadcodec)。 119 | 120 | *** 121 | 122 | ## 弃用 (Deprecations) 123 | ### 从路由器生成模式 (Generating schemas from Router) 124 | 用于生成模式视图的路由器参数 (例如 `schema_title`) 在正在等待弃用。 125 | 126 | 您应该使用 `get_schema_view()` 函数,而不是使用 `DefaultRouter(schema_title='Example API')`,并在 URL conf 中包含该视图。 127 | 128 | 确保在路由器 url 之前包含视图。例如: 129 | ```python 130 | from rest_framework.schemas import get_schema_view 131 | from my_project.routers import router 132 | 133 | schema_view = get_schema_view(title='Example API') 134 | 135 | urlpatterns = [ 136 | url('^$', schema_view), 137 | url(r'^', include(router.urls)), 138 | ] 139 | ``` 140 | 141 | ### 模式路径表示 (Schema path representations) 142 | 模式路径中的 `'pk'` 标识符现在默认映射到实际的模型字段名。这通常是 `'id'`。 143 | 144 | 这为模式提供了更好的外部表示,减少了暴露实现细节。它还反映了使用带有 `fields = '__all__'` 的 ModelSerializer 类的行为。 145 | 146 | 您可以通过在 REST framework 设置中设置 `'SCHEMA_COERCE_PATH_PK': False` 来恢复到以前的行为。 147 | 148 | ### 模式操作名称表示 (Schema action name representations) 149 | 内部 `retrieve()` 和 `destroy()` 方法名现在强制为 `read` 和 `delete` 的外部表示。 150 | 151 | 您可以通过在 REST framework 设置中设置 `'SCHEMA_COERCE_METHOD_NAMES': {}` 来恢复到以前的行为。 152 | 153 | ### DjangoFilterBackend 154 | 内置的 `DjangoFilterBackend` 的功能现在完全包含在 `django-filter` 包中。 155 | 156 | 您应该更改导入和 REST framework 筛选器设置,如下所示: 157 | 158 | - `rest_framework.filters.DjangoFilterBackend` 变为 `django_filters.rest_framework.DjangoFilterBackend`。 159 | - `rest_framework.filters.FilterSet` 变为 `django_filters.rest_framework.FilterSet`。 160 | 161 | 现有的导入将继续有效,但现在正在等待弃用。 162 | 163 | ### CoreJSON 媒体类型 (CoreJSON media type) 164 | `CoreJSON` 的媒体类型现在是 `application/json+coreapi`,而不是以前的 `application/vnd.json+coreapi`。这使得它更符合其他自定义媒体类型,比如 Swagger 和 RAML 所使用的那些类型。 165 | 166 | 客户端目前接受任何一种媒体类型。旧的样式媒体类型将在以后被废弃。 167 | 168 | ### ModelSerializer 'fields' and 'exclude' 169 | ModelSerializer 和 HyperlinkedModelSerializer 必须包含字段选项或排除选项。`fields = '__all__'` 快捷方式可用于显式地包含所有字段。 170 | 171 | 如果没有设置 `fields` 或 `exclude` 字段,将在 3.3 版本中引发等待弃用警告,并在 3.4 中引发弃用警告。 172 | 173 | *** 174 | -------------------------------------------------------------------------------- /Community/3.6_Announcement.md: -------------------------------------------------------------------------------- 1 | # Django REST framework 3.6 2 | 3.6 版本为 REST framework 增加了两个主要的新功能。 3 | 4 | 1. 内置交互式 API 文档支持。 5 | 2. 新的 JavaScript 客户端库。 6 | 7 | ![image](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Image/api-docs.gif?raw=true) 8 | 9 | *上图:交互式 API 文档。* 10 | 11 | *** 12 | 13 | ## 资金 (Funding) 14 | 如果没有 [Mozilla](https://www.django-rest-framework.org/community/mozilla-grant/) 对项目的支持以及我们的[合作资金模式](https://www.django-rest-framework.org/community/funding/),3.6 版本是不可能实现的。 15 | 16 | 如果您在商业上使用 REST framework,并且希望看到这项工作继续进行,我们强烈鼓励您通过[**注册付费计划**](https://www.django-rest-framework.org/community/funding/)来投资其持续开发。 17 | 18 | [![](https://fund-rest-framework.s3.amazonaws.com/rover_130x130.png)](http://jobs.rover.com/) [![](https://fund-rest-framework.s3.amazonaws.com/sentry130.png)](https://getsentry.com/welcome/) [![](https://fund-rest-framework.s3.amazonaws.com/stream-130.png)](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf) [![](https://fund-rest-framework.s3.amazonaws.com/Machinalis130.png)](https://www.machinalis.com/#services) 19 | 20 | [![](https://fund-rest-framework.s3.amazonaws.com/rollbar.png)](https://rollbar.com/) [![](https://fund-rest-framework.s3.amazonaws.com/mp-text-logo.png)](https://micropyramid.com/django-rest-framework-development-services/) 21 | 22 | *非常感谢我们所有的[赞助商](https://fund.django-rest-framework.org/topics/funding/#our-sponsors),特别是我们的高级支持者 [Rover](http://jobs.rover.com/),[Sentry](https://getsentry.com/welcome/),[Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf),[Machinalis](https://www.machinalis.com/#services),[Rollbar](https://rollbar.com/) 和 [MicroPyramid](https://micropyramid.com/django-rest-framework-development-services/)。* 23 | 24 | *** 25 | 26 | ## 交互式 API 文档 (Interactive API documentation) 27 | REST framework 的新 API 文档支持许多功能: 28 | 29 | - 实时 API 交互。 30 | - 支持各种身份验证方案。 31 | - Python、JavaScript 和命令行客户端的代码片段。 32 | 33 | `coreapi` 库作为 API 文档的依赖关系是必需的。确保安装最新版本 (2.3.0 或以上)。`pygments` 和 `markdown` 库是可选的,但建议使用。 34 | 35 | 要安装 API 文档,您需要将其包含在您的项目 URLconf 中: 36 | ```python 37 | from rest_framework.documentation import include_docs_urls 38 | 39 | API_TITLE = 'API title' 40 | API_DESCRIPTION = '...' 41 | 42 | urlpatterns = [ 43 | ... 44 | url(r'^docs/', include_docs_urls(title=API_TITLE, description=API_DESCRIPTION)) 45 | ] 46 | ``` 47 | 48 | 一旦安装完毕,您会看到一些像这样的东西: 49 | 50 | ![](https://www.django-rest-framework.org/img/api-docs.png) 51 | 52 | 在接下来的几周里,我们可能会对 API 文档进行进一步的改进。请记住,这是一个新特性,如果您遇到任何问题或限制,请给我们反馈。 53 | 54 | 有关记录 API 端点的详细信息,请参阅 [“记录 API”](https://www.django-rest-framework.org/topics/documenting-your-api/) 部分。 55 | 56 | *** 57 | 58 | ## JavaScript 客户端库 (JavaScript client library) 59 | JavaScript 客户端库允许您加载 API 模式,然后在应用层接口上与该 API 交互,而不是显式地构造 fetch 请求。 60 | 61 | 这里有一个简单的示例来演示: 62 | 63 | - 加载客户端库和模式。 64 | - 实例化经过身份验证的客户端。 65 | - 使用客户端发出 API 请求。 66 | 67 | **index.html** 68 | ```python 69 | 70 | 71 | 72 | 73 | 86 | 87 | 88 | ``` 89 | 90 | JavaScript 客户端库支持各种身份验证方案,可以由项目本身使用,也可以作为与 API 交互的外部客户端使用。 91 | 92 | 客户端不限于使用 REST framework API,尽管它目前仅支持加载 CoreJSON API 模式。计划支持 Swagger 和其他 API 模式。 93 | 94 | 有关更多详细信息,请参阅 [JavaScript 客户端库文档](https://www.django-rest-framework.org/topics/api-clients/#javascript-client-library)。 95 | 96 | ## Python 客户端库的身份验证类 (Authentication classes for the Python client library) 97 | Python 客户端库中以前的身份验证支持仅限于允许用户提供显式标头值。 98 | 99 | 通过引入 `BasicAuthentication`、`TokenAuthentication` 和 `SessionAuthentication` 方案,我们现在可以更好地支持处理身份验证的细节。 100 | 101 | 您可以在实例化新客户端时包含身份验证方案。 102 | ```python 103 | auth = coreapi.auth.TokenAuthentication(scheme='JWT', token='xxx-xxx-xxx') 104 | client = coreapi.Client(auth=auth) 105 | ``` 106 | 107 | 有关更多信息,请参阅 [Python 客户端库文档](https://www.django-rest-framework.org/topics/api-clients/#python-client-library)。 108 | 109 | *** 110 | 111 | ## 弃用 (Deprecations) 112 | ### 更新 coreapi (Updating coreapi) 113 | 如果您正在使用 REST framework 的模式生成,或者想要使用 API ​​文档,那么您需要更新到最新版本的 coreapi。(2.3.0) 114 | 115 | ### 从路由器生成模式 (Generating schemas from Router) 116 | 3.5 版本用于生成模式视图的路由器参数 (例如 `schema_title`,`schema_url` 和 `schema_renderers`) 的 “等待弃用” 现在已升级为 “弃用” 警告。 117 | 118 | 您应该使用 `get_schema_view()` 函数,而不是使用 `DefaultRouter(schema_title='Example API')`,并在 URL conf 中显式包含视图。 119 | 120 | ### DjangoFilterBackend 121 | 3.5 版本内置的 `DjangoFilterBackend` “等待弃用” 警告现已升级为 “弃用” 警告。 122 | 123 | 您应该更改您的导入和 REST framework 过滤器设置如下: 124 | 125 | - `rest_framework.filters.DjangoFilterBackend` 变为 `django_filters.rest_framework.DjangoFilterBackend`。 126 | - `rest_framework.filters.FilterSet` 变为 `django_filters.rest_framework.FilterSet`。 127 | 128 | *** 129 | 130 | ## What's next 131 | 在未来几周内,API 文档和 JavaScript 客户端库可能会有一些改进,其中可能包括以下部分: 132 | 133 | - 支持私有 API 文档,需要登录。 134 | - JavaScript 客户端和 API 文档中的文件上载和下载支持。 135 | - JavaScript 客户端库的综合文档。 136 | - 自动在 API 文档代码段中包含身份验证详细信息。 137 | - 在命令行客户端中添加身份验证支持。 138 | - 支持在 JavaScript 客户端中加载 Swagger 和其他模式。 139 | - 改进了对记录参数模式和响应模式的支持。 140 | - 完善 API 文档交互模式。 141 | 142 | 一旦这些改进工作完成,我们将开始实时支持 3.7 版本的特性工作。 143 | -------------------------------------------------------------------------------- /Community/3.7_Announcement.md: -------------------------------------------------------------------------------- 1 | # Django REST framework 3.7 2 | 3.7 版本侧重于对模式生成和交互式 API 文档的改进。 3 | 4 | 这个版本已经由赞助该版本的 [Bayer](https://www.bayer.com/) 推出。 5 | 6 |
7 | 8 | *** 9 | 10 | ## 资金 (Funding) 11 | 如果您在商业上使用 REST framework,并且希望看到这项工作继续进行,我们强烈鼓励您通过[**注册付费计划**](https://www.django-rest-framework.org/community/funding/)来投资其持续开发。 12 | 13 | [![](https://fund-rest-framework.s3.amazonaws.com/rover_130x130.png)](http://jobs.rover.com/) [![](https://fund-rest-framework.s3.amazonaws.com/sentry130.png)](https://getsentry.com/welcome/) [![](https://fund-rest-framework.s3.amazonaws.com/stream-130.png)](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf) [![](https://fund-rest-framework.s3.amazonaws.com/Machinalis130.png)](https://www.machinalis.com/#services) 14 | 15 | [![](https://fund-rest-framework.s3.amazonaws.com/rollbar.png)](https://rollbar.com/) 16 | 17 | *除了我们的发行赞助商,我们还要特别感谢我们的高级支持者,[Rover](http://jobs.rover.com/),[Sentry](https://getsentry.com/welcome/),[Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf),[Machinalis](https://www.machinalis.com/#services) 和 [Rollbar](https://rollbar.com/)。* 18 | 19 | *** 20 | 21 | ## 自定义 API 文档和模式生成 (Customizing API docs & schema generation.) 22 | 3.5 中引入的模式生成和 3.6 中相关的 API 文档生成都是非常强大的特性,但是在视图内省无法正确识别特定视图的模式的情况下,它们受到了一定的限制。 23 | 24 | 为了解决这个问题,我们现在增加了 API 模式的每个视图自定义的能力。我们为此添加的接口允许对视图中应该包含哪些字段进行基本的手动重写,也允许对模式生成进行更复杂的编程重写。我们相信此版本全面地解决了模式特性的一些现有缺点。 25 | 26 | 让我们快速了解一下使用新功能... 27 | 28 | `APIView` 类具有 `schema` 属性,用于控制如何生成该特定视图的 Schema。默认行为是使用 `AutoSchema` 类。 29 | ```python 30 | from rest_framework.views import APIView 31 | from rest_framework.schemas import AutoSchema 32 | 33 | class CustomView(APIView): 34 | schema = AutoSchema() # 仅供演示。这是默认行为。 35 | ``` 36 | 37 | 我们可以从 API 模式和文档中删除视图,如下所示: 38 | ```python 39 | class CustomView(APIView): 40 | schema = None 41 | ``` 42 | 43 | 如果我们想主要使用默认行为,但另外在特定视图中包含一些其他字段,我们现在可以轻松地这样做... 44 | ```python 45 | class CustomView(APIView): 46 | schema = AutoSchema(manual_fields=[ 47 | coreapi.Field('search', location='query') 48 | ]) 49 | ``` 50 | 51 | 要忽略特定视图的自动生成,而是显式地指定模式,我们使用 `ManualSchema` 类代替… 52 | ```python 53 | class CustomView(APIView): 54 | schema = ManualSchema(fields=[...]) 55 | ``` 56 | 57 | 对于更高级的行为,您可以将 `AutoSchema` 子类化以提供自定义模式生成,并将其应用于特定视图。 58 | ```python 59 | class CustomView(APIView): 60 | schema = CustomizedSchemaGeneration() 61 | ``` 62 | 63 | 有关新功能的完整详细信息,请参阅 [Schema 文档](https://www.django-rest-framework.org/api-guide/schemas/)。 64 | 65 | *** 66 | 67 | ## Django 2.0 支持 (Django 2.0 support) 68 | REST framework 3.7 支持 Django 版本 1.10,1.11 和 2.0 alpha。 69 | 70 | *** 71 | 72 | ## 小修正和改进 (Minor fixes and improvements) 73 | 此版本中有大量小修复和改进。有关完整列表,请参阅[发行说明](https://www.django-rest-framework.org/community/release-notes/)页面。 74 | 75 | 目前[该项目的公开票](https://github.com/encode/django-rest-framework/issues)数量处于相当一段时间以来的最低水平,我们将继续关注将其减少到可管理的数量。 76 | 77 | *** 78 | 79 | ## 弃用 (Deprecations) 80 | ### `exclude_from_schema` 81 | `@api_view` 装饰器的 `APIView.exclude_from_schema` 和 `exclude_from_schema` 参数,现在是 `PendingDeprecation`。它们将在 3.8 版本中移至弃用,并在 3.9 中完全删除。 82 | 83 | 对于 `APIView`,您应该在视图类上设置 `schema = None` 属性。 84 | 85 | 对于基于函数的视图,可以使用 `@schema` 装饰器将视图从模式中排除,方法是使用 `@schema(None)`。 86 | 87 | ### `DjangoFilterBackend` 88 | `DjangoFilterBackend` 在 3.5 中被移至等待弃用,并在 3.6 中弃用。它现在已从核心框架中删除。 89 | 90 | 该功能仍然完全可用,但是在 `django-filter` 包中提供。 91 | 92 | *** 93 | 94 | ## What's next 95 | 我们仍计划通过提供与Django通道集成的文档来改进对REST框架的实时支持,以及添加对更轻松地向现有 HTTP 端点添加 WebSocket 支持的支持。 96 | 97 | 这很可能是定时的,以便这里的任何 REST framework 开发都与 [API Star](https://github.com/encode/apistar) 上的类似工作相关联。 98 | -------------------------------------------------------------------------------- /Community/3.8_Announcement.md: -------------------------------------------------------------------------------- 1 | # Django REST framework 3.8 2 | 3.8 版本是一个以维护为重点的版本,它解决了大量以前悬而未决的问题,并为未来的变化奠定了基础。 3 | 4 | *** 5 | 6 | ## 资金 (Funding) 7 | 如果您在商业上使用 REST framework,并且希望看到这项工作继续进行,我们强烈鼓励您通过[**注册付费计划**](https://www.django-rest-framework.org/community/funding/)来投资其持续开发。 8 | 9 | *我们要特别感谢我们的高级支持者,[Rover](http://jobs.rover.com/),[Sentry](https://getsentry.com/welcome/),[Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf),[Machinalis](https://www.machinalis.com/#services) 和 [Rollbar](https://rollbar.com/)。* 10 | 11 | *** 12 | 13 | ## 突破性变化 (Breaking Changes) 14 | ### 在 Field 上改变 `read_only` 和 `default` 的行为 (Altered the behaviour of `read_only` plus `default` on Field.) 15 | \#5886 `read_only` 字段现在总是被排除在可写字段之外。 16 | 17 | 以前的 `read_only` 字段在与 `default` 值组合时将使用 `default` 来创建和更新操作。这在某些情况下是违反直觉的,并导致难以支持可空关系上的点状 `source` 属性。 18 | 19 | 为了维护旧的行为,您可能需要在视图中调用 `save()` 时传递 `read_only` 字段的值: 20 | ```python 21 | def perform_create(self, serializer): 22 | serializer.save(owner=self.request.user) 23 | ``` 24 | 25 | 或者,您可以根据需要在序列化器上重写 `save()` 或 `create()` 或 `update()`。 26 | 27 | *** 28 | 29 | ## 弃用 (Deprecations) 30 | ### `action` 装饰器替换 `list_route` 和 `detail_route` (`action` decorator replaces `list_route` and `detail_route`) 31 | \#5705 `list_route` 和 `detail_route` 已合并为单个 `action` 装饰器。这改进了视图集操作内省,并允许在未来版本中的可浏览 API 中显示额外的操作。 32 | 33 | `list_route` 和 `detail_route` 现在都在等待弃用。它们将在 3.9 中被弃用,并在 3.10 中被完全删除。 34 | 35 | 新的 `action` 装饰器采用布尔 `detail` 参数。 36 | 37 | - 使用 `@action(detail=True)` 替换 `detail_route`。 38 | - 使用 `@action(detail=False)` 替换 `list_route`。 39 | 40 | ### `exclude_from_schema` 41 | `@api_view` 装饰器的 `APIView.exclude_from_schema` 和 `exclude_from_schema` 参数现在被弃用了。它们将在 3.9 中完全删除。 42 | 43 | 对于 `APIView`,您应该在视图类上设置 `schema = None` 属性。 44 | 45 | 对于基于函数的视图,可以使用 `@schema` 装饰器将视图从模式中排除,方法是使用 `@schema(None)`。 46 | 47 | *** 48 | 49 | ## 小修正和改进 (Minor fixes and improvements) 50 | 此版本中有大量小修正和改进。有关完整列表,请参阅[发行说明](https://www.django-rest-framework.org/community/release-notes/)页面。 51 | 52 | ## What's next 53 | 我们目前正致力于将 [OpenAPI](https://www.openapis.org/) 作为默认模式输出。我们还将重新访问 API 文档生成和客户端库。 54 | 55 | 我们正在进行一些整合以实现这一目标。计划 3.9 将删除 `coreapi` 和 `coreschema` 库,而是将 `apistar` 用于 API 文档生成、模式生成和 API 客户端库。 56 | -------------------------------------------------------------------------------- /Community/3.9_Announcement.md: -------------------------------------------------------------------------------- 1 | # Django REST framework 3.9 2 | 3.9 版本允许访问可浏览 API 中的额外操作,引入可组合权限和内置的 [OpenAPI](https://www.openapis.org/) 模式支持。(以前称为 Swagger) 3 | 4 | *** 5 | 6 | ## 资金 (Funding) 7 | 如果您在商业上使用 REST framework,并且希望看到这项工作继续进行,我们强烈鼓励您通过[**注册付费计划**](https://www.django-rest-framework.org/community/funding/)来投资其持续开发。 8 | 9 | [![](https://fund-rest-framework.s3.amazonaws.com/rover_130x130.png)](http://jobs.rover.com/) [![](https://fund-rest-framework.s3.amazonaws.com/sentry130.png)](https://getsentry.com/welcome/) [![](https://fund-rest-framework.s3.amazonaws.com/stream-130.png)](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf) [![](https://fund-rest-framework.s3.amazonaws.com/auklet-new.png)](https://auklet.io/) 10 | 11 | [![](https://fund-rest-framework.s3.amazonaws.com/rollbar2.png)](https://rollbar.com/) [![](https://fund-rest-framework.s3.amazonaws.com/cadre.png)](https://cadre.com/) [![](https://fund-rest-framework.s3.amazonaws.com/load-impact.png)](https://loadimpact.com/?utm_campaign=Sponsorship%20links&utm_source=drf&utm_medium=drf) [![](https://fund-rest-framework.s3.amazonaws.com/kloudless.png)](https://hubs.ly/H0f30Lf0) 12 | 13 | *非常感谢我们所有[出色的赞助商](https://fund.django-rest-framework.org/topics/funding/#our-sponsors),特别是我们的高级支持者,[Rover](http://jobs.rover.com/),[Sentry](https://getsentry.com/welcome/),[Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf),[Auklet](https://auklet.io/),[Rollbar](https://rollbar.com/),[Cadre](https://cadre.com/),[Load Impact](https://loadimpact.com/?utm_campaign=Sponsorship%20links&utm_source=drf&utm_medium=drf) 和 [Kloudless](https://hubs.ly/H0f30Lf0)。* 14 | 15 | *** 16 | 17 | ## 内置 OpenAPI 模式支持 (Built-in OpenAPI schema support) 18 | REST framework 现在具有直接包含 OpenAPI 模式支持的第一个通道。(以前称为 Swagger) 19 | 20 | 确切的说: 21 | 22 | - 现在有 `OpenAPIRenderer` 和 `JSONOpenAPIRenderer` 类处理将 `coreapi.Document` 实例编码为 OpenAPI YAML 或 OpenAPI JSON。 23 | - `get_schema_view(…)` 方法现在默认为 OpenAPI YAML,如果通过 HTTP 内容协商选择 CoreJSON,它将作为次要选项。 24 | - 有一个新的管理命令 `generateschema`,您可以使用它将模式转储到存储库中。 25 | 26 | 以下是向 URL conf 添加 OpenAPI 模式的示例: 27 | ```python 28 | from rest_framework.schemas import get_schema_view 29 | from rest_framework.renderers import JSONOpenAPIRenderer 30 | 31 | schema_view = get_schema_view( 32 | title='Server Monitoring API', 33 | url='https://www.example.org/api/', 34 | renderer_classes=[JSONOpenAPIRenderer] 35 | ) 36 | 37 | urlpatterns = [ 38 | url('^schema.json$', schema_view), 39 | ... 40 | ] 41 | ``` 42 | 43 | 下面是如何使用 `generateschema` 管理命令: 44 | ```python 45 | $ python manage.py generateschema --format openapi > schema.yml 46 | ``` 47 | 48 | 您可以使用许多不同的工具来处理 OpenAPI 模式。我们正在研究的一个选项是 [API Star](https://docs.apistar.com/) 命令行工具。 49 | 50 | 您可以使用 `apistar` 来验证您的 API 模式: 51 | ```python 52 | $ apistar validate --path schema.json --format openapi 53 | ✓ Valid OpenAPI schema. 54 | ``` 55 | 56 | 或者构建 API 文档: 57 | ```python 58 | $ apistar docs --path schema.json --format openapi 59 | ✓ Documentation built at "build/index.html". 60 | ``` 61 | 62 | API Star 还包括一个[动态客户端库](https://docs.apistar.com/client-library/),该库使用 API​​ 模式自动提供客户端库接口以发出请求。 63 | 64 | ## 可组合的权限类 (Composable permission classes) 65 | 您现在可以使用和/或运算符 `&` 和 `|` 来组合权限类。 66 | 67 | 例如... 68 | ```python 69 | permission_classes = [IsAuthenticated & (ReadOnly | IsAdmin)] 70 | ``` 71 | 72 | 如果您使用自定义权限类,那么请确保您从 `BasePermission` 继承子类来启用此支持。 73 | 74 | ## 可浏览 API 中可用的视图集额外操作 (ViewSet Extra Actions available in the Browsable API) 75 | 在 v3.8 中引入了 `action` 装饰器之后,现在可以从可浏览 API 获得视图集上定义的额外动作。 76 | 77 | ![](https://user-images.githubusercontent.com/2370209/32976956-1ca9ab7e-cbf1-11e7-981a-a20cb1e83d63.png) 78 | 79 | 在定义时,会显示 “额外操作” 下拉列表,适当地过滤为详细/非详细操作。 80 | 81 | *** 82 | 83 | ## 支持的版本 (Supported Versions) 84 | REST framework 3.9 支持 Django 版本 1.11,2.0 和 2.1。 85 | 86 | *** 87 | 88 | ## 弃用 (Deprecations) 89 | ### `DjangoObjectPermissionsFilter` 移动到第三方包 (`DjangoObjectPermissionsFilter` moved to third-party package.) 90 | `DjangoObjectPermissionsFilter` 类是等待弃用,将在 3.10 中弃用并在 3.11 中完全删除。 91 | 92 | 它已被转移到第三方 `djangorestframework-guardian` 包。请改用它。 93 | 94 | ### 路由器参数/方法重命名为使用 `basename` 以保持一致性 (Router argument/method renamed to use `basename` for consistency.) 95 | - 已重命名 `Router.register` `base_name` 参数以 `basename` 取代。 96 | - 已重命名 `Router.get_default_base_name` 方法以 `Router.get_default_basename` 取代。见 [#5990](https://github.com/encode/django-rest-framework/pull/5990)。 97 | 98 | `base_name` 和 `get_default_base_name()` 正在等待弃用。它们将在 3.10 中被弃用,并在 3.11 中被完全删除。 99 | 100 | ### `action` 装饰器替换 `list_route` 和 `detail_route` (`action` decorator replaces `list_route` and `detail_route`) 101 | `list_route` 和 `detail_route` 现在被弃用以单个 `action` 装饰器取代。它们将在 3.10 完全删除。 102 | 103 | `action` 装饰器采用布尔 `detail` 参数。 104 | 105 | - 使用 `@action(detail=True)` 替换 `detail_route`。 106 | - 使用 `@action(detail=False)` 替换 `list_route`。 107 | 108 | ### `exclude_from_schema` 109 | 现在已删除了 `@api_view` 的 `APIView.exclude_from_schema` 和 `exclude_from_schema` 参数。 110 | 111 | 对于 `APIView`,您应该在视图类上设置 `schema = None` 属性。 112 | 113 | 对于基于函数的视图,可以使用 `@schema` 装饰器将视图从模式中排除,方法是使用 `@schema(None)`。 114 | 115 | *** 116 | 117 | ## 小修正和改进 (Minor fixes and improvements) 118 | 此版本中有大量小修正和改进。有关完整列表,请参阅[发行说明](https://www.django-rest-framework.org/community/release-notes/)页面。 119 | 120 | ## What's next 121 | 我们计划迭代​​地将 OpenAPI 转变为标准模式表示。这意味着 `coreapi` 依赖关系将逐渐被删除,我们将直接生成模式,而不是构建 CoreAPI `Document` 对象。 122 | 123 | OpenAPI 显然已成为指定 Web API 的标准,因此在我们的模式无关的文档模型中不再有什么价值。进行此更改意味着我们可以更轻松地利用全套 OpenAPI 功能。 124 | 125 | 这也将使更广泛的工具可用。 126 | 127 | 我们将重点继续开发 [API Star](https://docs.apistar.com/) 库和客户端工具,使之成为生成 API 文档、验证 API 模式和提供动态客户端库的推荐选项。 128 | 129 | 关于 ASGI 格局的成熟方面,还有大量正在进行的工作,其中一些工作最终可能会[反馈到 Django](https://www.aeracode.org/2018/06/04/django-async-roadmap/)。 130 | 131 | 在 [Uvicorn](https://www.uvicorn.org/) 网站服务器上将有进一步的工作,以及为 [Starlette](https://www.starlette.io/) 网页框架计划的许多功能,该框架正在构建与 ASGI 一起工作的基础工具集。 132 | -------------------------------------------------------------------------------- /Community/Contributing_to_REST_framework.md: -------------------------------------------------------------------------------- 1 | # 对 REST framework 的贡献 (Contributing to REST framework) 2 | 世界真的只能一次改变一件事。艺术正在挑选那件作品。—— [Tim Berners-Lee](https://www.w3.org/People/Berners-Lee/FAQ.html) 3 | 4 | 您可以通过多种方式为 Django REST framework 做出贡献。我们希望它是一个由社区主导的项目,所以请参与并帮助塑造项目的未来。 5 | 6 | ## 社区 (Community) 7 | 您可以做的最重要的事情是尽可能积极地参与推动 REST framework 项目。代码贡献作为参与项目的主要方式经常被高估,我们认为情况并非如此。 8 | 9 | 如果您使用 REST framework,我们希望您能够畅所欲言地讲述您的经验 - 您可以考虑撰写关于使用 REST framework 的博客文章,或者发布关于使用特定 JavaScript 框架构建项目的教程。初学者的经验可能特别有用,因为您将处于最佳位置,以评估哪些 REST framework 更难以理解和使用。 10 | 11 | 您可以帮助社区向前发展的其他非常棒的方法包括帮助回答讨论组的问题,或者在 StackOverflow 上设置一个电子邮件警告,这样您就可以得到关于 `django-rest-framework` 标签的任何新问题的通知。 12 | 13 | 在回答问题时,请确保通过超链接尽可能地帮助未来的贡献者找到相关的线程和票据,并在相关时包含这些项目的反向链接。 14 | 15 | ## 行为守则 (Code of conduct) 16 | 请保持礼貌和专业的语气。对于一些用户来说,关于REST framework 邮件列表或票据跟踪器的讨论可能是他们与开源社区的首次接触。第一印象很重要,所以让我们试着让每个人都感到受欢迎。 17 | 18 | 注意你选择的语言。例如,在一个男性占主导地位的环境中,以 “嘿,伙伴” 开头的帖子可能会被无意中排除。在这种情况下,使用中性语言同样容易,也更加包容。 19 | 20 | [Django 行为准则](https://www.djangoproject.com/conduct/)为参与社区论坛提供了更全面的指导方针。 21 | 22 | # 问题 (Issues) 23 | 如果你能确保在正确的频道上解决问题,这真的很有帮助。使用问题应针对讨论组。应在 GitHub [问题跟踪器](https://github.com/encode/django-rest-framework/issues?state=open)上引发功能请求、错误报告和其他问题。 24 | 25 | 关于良好问题报告的一些提示: 26 | 27 | - 在描述问题时,尝试根据您认为需要更改的行为而不是您认为需要更改的代码来表达您的标签。 28 | - 首先搜索问题列表以查找相关项目,并确保在报告问题之前运行最新版本的 REST framework。 29 | - 如果报告错误,则尝试包含带有失败测试用例的 pull 请求。这将有助于我们快速确定是否存在有效问题,并确保在有问题时更快地解决问题。 30 | - 功能请求通常会被关闭,建议它们在核心 REST framework 库之外实现。保持作为第三方库实现的新功能请求允许我们降低 REST framework 的维护开销,因此重点可以放在持续稳定性,错误修正和优秀文档上。 31 | - 结束问题并不一定意味着讨论的结束。如果您认为您的问题已被错误关闭,请解释原因,我们会考虑是否需要重新打开。 32 | 33 | ## 分类问题 (Triaging issues) 34 | 参与分类传入问题是开始贡献的好方法。进入票据跟踪器的每一张票都需要进行审查,以确定下一步应该采取什么步骤。任何人都可以帮忙解决这个问题,你只需要愿意 35 | 36 | - 读完这张票 - 它是否有意义,是否缺少任何能更好地解释它的上下文? 37 | - 票据是否在正确的地方报告,是否更适合在讨论组进行讨论? 38 | - 如果票据是错误报告,你能复制它吗?您是否能够编写一个演示该问题且可以作为拉取请求提交的失败测试用例? 39 | - 如果票据是功能请求,您是否同意,并且功能请求是否可以实现为第三方程序包? 40 | - 如果一张票没有多少活动,并且它解决了您需要的一些问题,那么请在这张票上进行评论,并尝试找出让它再次运行所需要的东西。 41 | 42 | # 发展 (Development) 43 | 要开始开发 Django REST framework,首先在 GitHub 上从 [Django REST Framework repo](https://github.com/encode/django-rest-framework) 创建一个 Fork。 44 | 45 | 然后克隆您的 fork。clone 命令将如下所示,使用您的 GitHub 用户名而不是 YOUR-USERNAME: 46 | ```python 47 | git clone https://github.com/YOUR-USERNAME/Spoon-Knife 48 | ``` 49 | 50 | 有关更多帮助,请参阅 GitHub 的 [Fork a Repo](https://help.github.com/articles/fork-a-repo/) 指南。 51 | 52 | 更改应该大致遵循 [PEP 8](https://www.python.org/dev/peps/pep-0008/) 样式约定,我们建议您设置编辑器以自动指示不符合的样式。 53 | 54 | ## 测试 (Testing) 55 | 要运行测试,请克隆存储库,然后: 56 | ```python 57 | # Setup the virtual environment 58 | virtualenv env 59 | source env/bin/activate 60 | pip install django 61 | pip install -r requirements.txt 62 | 63 | # Run the tests 64 | ./runtests.py 65 | ``` 66 | 67 | ### 测试选项 (Test options) 68 | 使用更简洁的输出样式运行。 69 | ```python 70 | ./runtests.py -q 71 | ``` 72 | 73 | 使用更简洁的输出样式运行测试,没有覆盖,没有 flake8。 74 | ```python 75 | ./runtests.py --fast 76 | ``` 77 | 78 | 不要运行 flake8 代码检查。 79 | ```python 80 | ./runtests.py --nolint 81 | ``` 82 | 83 | 只运行 flake8 代码检查,不要运行测试。 84 | ```python 85 | ./runtests.py --lintonly 86 | ``` 87 | 88 | 为给定的测试用例运行测试。 89 | ```python 90 | ./runtests.py MyTestCase 91 | ``` 92 | 93 | 为给定的测试方法运行测试。 94 | ```python 95 | ./runtests.py MyTestCase.test_this_method 96 | ``` 97 | 98 | 为给定的测试方法以较短形式运行测试。 99 | ```python 100 | ./runtests.py test_this_method 101 | ``` 102 | 103 | 注意:测试用例和测试方法匹配是模糊的,有时会运行包含与给定命令行输入的部分字符串匹配的其他测试。 104 | 105 | ### 针对多种环境运行 (Running against multiple environments) 106 | 您还可以使用优秀的 [tox](https://tox.readthedocs.io/en/latest/) 测试工具对所有受支持的 Python 和 Django 版本运行测试。全局安装 `tox`,然后运行: 107 | ```python 108 | tox 109 | ``` 110 | 111 | ## 拉取请求 (Pull requests) 112 | 尽早发出拉取请求是个好主意。拉取请求代表讨论的开始,并不一定需要是最终的完成提交。 113 | 114 | 在开始处理拉取请求之前,最好先建立一个新分支。这意味着您以后可以切换回处理另一个单独的问题,而不会干扰正在进行的拉取请求。 115 | 116 | 记住,如果你有一个未完成的拉取请求,那么将新的提交推送到你的GitHub仓库也会自动更新拉取请求。 117 | 118 | GitHub 有关处理拉取请求的文档可在[此处获得](https://help.github.com/articles/using-pull-requests)。 119 | 120 | 总是在提交拉取请求之前运行测试,理想情况下运行 `tox`,以检查您的修改是否兼容 Python 2 和 Python 3,以及它们是否在所有受支持的 Django 版本上正确运行。 121 | 122 | 完成拉取请求后,请查看 GitHub 界面中的 Travis 构建状态,并确保测试按预期运行。 123 | 124 |
125 | 126 | *上图:Travis 构建通知* 127 | 128 | ## 管理兼容性问题 (Managing compatibility issues) 129 | 有时,为了确保您的代码适用于各种不同版本的 Django,Python 或第三方库,您需要根据环境运行稍微不同的代码。以这种方式分支的任何代码都应该隔离到 `compat.py` 模块中,并且应该提供代码库的其余部分可以使用的单个公共接口。 130 | 131 | # 文档 (Documentation) 132 | REST framework 的文档是从 [docs 目录](https://github.com/encode/django-rest-framework/tree/master/docs)中的 [Markdown](https://daringfireball.net/projects/markdown/basics) 源文件构建的。 133 | 134 | 有许多伟大的 Markdown 编辑器使得使用文档非常容易。[Mac 的 Mou 编辑器](http://mouapp.com/)是强烈推荐的编辑器之一。 135 | 136 | ## 构建文档 (Building the documentation) 137 | 要构建文档,请使用 `pip install mkdocs` 安装 MkDocs,然后运行以下命令。 138 | ```python 139 | mkdocs build 140 | ``` 141 | 142 | 这将把文档构建到 `site` 目录中。 143 | 144 | 您可以使用 `serve` 命令构建文档并在浏览器窗口中打开预览。 145 | ```python 146 | mkdocs serve 147 | ``` 148 | 149 | ## 语言样式 (Language style) 150 | 文档应采用美式英语。文档的基调是非常重要的 - 尽可能坚持简单、朴素、客观和平衡的风格。 151 | 152 | 其他一些提示: 153 | 154 | - 保持段落相当短。 155 | - 不要使用 'e.g.' 等缩写词而是使用长形式,例如 'For example'。 156 | 157 | ## Markdown 样式 (Markdown style) 158 | 在处理文档时,您应遵循几个约定。 159 | 160 | ##### 1.Headers 161 | 标头应该使用散列样式。例如: 162 | ```python 163 | ### Some important topic 164 | ``` 165 | 166 | 不应使用下划线样式。**不要这样做:** 167 | ```python 168 | Some important topic 169 | ==================== 170 | ``` 171 | 172 | ##### 2. Links 173 | 链接应该始终使用引用样式,并将引用的超链接保留在文档的末尾。 174 | ```python 175 | Here is a link to [some other thing][other-thing]. 176 | 177 | More text... 178 | 179 | [other-thing]: http://example.com/other/thing 180 | ``` 181 | 182 | 此样式有助于保持文档源的一致性和可读性。 183 | 184 | 如果要超链接到另一个 REST framework 文档,则应使用相对链接,并链接到 `.md` 后缀。例如: 185 | ```python 186 | [authentication]: ../api-guide/authentication.md 187 | ``` 188 | 189 | 以此样式链接意味着您可以单击 Markdown 编辑器中的超链接以打开引用的文档。构建文档后,这些链接将转换为 HTML 页面的常规链接。 190 | 191 | ##### 3. Notes 192 | 如果你想吸引注意或警告,使用一对封闭线,像这样: 193 | ```python 194 | --- 195 | 196 | **Note:** A useful documentation note. 197 | 198 | --- 199 | ``` 200 | -------------------------------------------------------------------------------- /Community/Funding.md: -------------------------------------------------------------------------------- 1 | # 资金 (Funding) 2 | 如果您在商业上使用 REST framework,我们强烈建议您通过注册付费计划来投资其持续开发。 3 | 4 | **我们相信,通过鼓励我们的用户共同分担开发成本,协作资助的软件可以提供出色的投资回报。** 5 | 6 | 注册付费计划将: 7 | 8 | - 直接有助于更快的发布、更多的特性和更高质量的软件。 9 | - 允许在文档、问题分类和社区支持方面投入更多的时间。 10 | - 保护 REST framework 的未来发展。 11 | 12 | REST framework 仍然是开源和许可执照的,但我们坚信,对于项目的用户来说,投资于其正在进行的开发是符合商业利益的。 13 | 14 | *** 15 | 16 | ## 到目前为止已经提供什么资金 (What funding has enabled so far) 17 | - 3.4 和 3.5 版本,包括 Swagger 和 RAML 的模式生成、Python 客户端库、命令行客户端,以及解决大量未解决的问题。 18 | - 3.6 版本,包括 JavaScript 客户端库和 API 文档,以及自动生成的代码示例。 19 | - Tom Christie,Django REST framework 的创建者,全职工作。 20 | - 自从汤姆·克里斯蒂 (Tom Christie) 开始全职工作以来,每月大约有 80-90 个问题和拉取请求被关闭。 21 | - 社区和运营经理兼职 4 个月,帮助业务成熟和赞助增长。 22 | - 为 JavaScript 客户端库和 API 文档工具的工作承包开发时间。 23 | 24 | *** 25 | 26 | ## 未来会有什么样的资金支持 (What future funding will enable) 27 | - 使用 WebSockets 实时 API 支持。这将包括使用 REST framework 和 Django 频道的文档和支持,以及将 WebSocket 支持集成到客户端库中。 28 | - 更好的身份验证默认值,可能会将 JWT & CORs 支持引入核心包。 29 | - 长期保障社区和运营经理的地位。 30 | - 开放和获得一个兼职职位,专注于在票务分类和解决。 31 | - 为在一系列编程语言中构建 API 客户端库的开发时间付费。这些将直接集成到即将发布的 API 文档中。 32 | 33 | 今天就注册付费计划,并帮助确保 REST framework 成为一个可持续的、全职资助的项目。 34 | 35 | *** 36 | 37 | ## 我们的赞助商和用户说了什么 38 | *作为开发人员,Django REST framework 对于构成 Django 和它的社区的所有伟大事物来说,都是一个明显而自然的扩展。入门很容易,同时提供简单的抽象,使其灵活和可定制。贡献和支持 Django REST framework 有助于确保它的未来,并以某种方式帮助 Django 和 Python 生态系统。* 39 | — *José Padilla, Django REST framework contributor* 40 | 41 | *Python 编程语言的头号特性是它的社区。这样的社区之所以成为可能,是因为这种语言的开源特性以及由此而来的所有文化。构建伟大的开源项目需要伟大的头脑。鉴于此,我们 Vinta 不仅为赞助 DRF 背后的团队感到自豪,而且还认识到他们带来的投资回报率。* 42 | — *Filipe Ximenes, Vinta Software* 43 | 44 | *这个项目继续存在真的很棒。代码库是一流的,维护人员致力于最高水平的质量。DRF 是 Django 如今成为 Web 框架首选的核心原因之一。在我看来,它为整个开发社区设置了 rest 框架的标准。* 45 | — *agconti, Django REST framework user* 46 | 47 | *** 48 | 49 | ## 个人计划 (Individual plan) 50 | 对于有兴趣看到 REST framework 继续改进的个人,建议使用此订阅。 51 | 52 | 如果您使用 REST framework 作为全职员工,请考虑建议您的公司制定一个[企业计划](https://fund.django-rest-framework.org/topics/funding/#corporate-plans)。 53 | 54 | ## 企业计划 (Corporate plans) 55 | 对于公开或私下使用 REST framework 的公司和组织,建议使用这些订阅。 56 | 57 | 作为交换资金,您还将在我们的网站上获得广告空间,允许您**向全球数万名开发人员宣传您的公司或产品**。 58 | 59 | 我们的专业和优质计划还包括**优先支持**。在任何时候,您的工程师都可以升级问题或讨论组线程,我们将确保在下一个工作日内获得有保证的响应。 60 | 61 | ## 责任 (Accountability) 62 | 为了使项目尽可能透明,我们发布[每月进度报告](http://www.encode.io/reports/february-2017),并定期包括财务报告和成本分析。 63 | 64 | ## 我们的赞助商 (Our sponsors) 65 | 略。 66 | -------------------------------------------------------------------------------- /Community/Jobs.md: -------------------------------------------------------------------------------- 1 | # 工作 (Jobs) 2 | 正在寻找一个新的 Django REST Framework 相关角色?在这个网站上,我们提供了一些可能有用的工作资源列表。如果[我们的任何赞助商都在招聘](https://fund.django-rest-framework.org/topics/funding/),也值得一试。 3 | 4 | ## 寻找 Django REST framework 工作的地方 (Places to look for Django REST Framework Jobs) 5 | - [https://www.djangoproject.com/community/jobs/](https://www.djangoproject.com/community/jobs/) 6 | - [https://www.python.org/jobs/](https://www.python.org/jobs/) 7 | - [https://djangogigs.com](https://djangogigs.com) 8 | - [https://djangojobs.net/jobs/](https://djangojobs.net/jobs/) 9 | - [http://djangojobbers.com](http://djangojobbers.com) 10 | - [https://www.indeed.com/q-Django-jobs.html](https://www.indeed.com/q-Django-jobs.html) 11 | - [https://stackoverflow.com/jobs/developer-jobs-using-django](https://stackoverflow.com/jobs/developer-jobs-using-django) 12 | - [https://www.upwork.com/o/jobs/browse/skill/django-framework/](https://www.upwork.com/o/jobs/browse/skill/django-framework/) 13 | - [https://www.technojobs.co.uk/django-jobs](https://www.technojobs.co.uk/django-jobs) 14 | - [https://remoteok.io/remote-django-jobs](https://remoteok.io/remote-django-jobs) 15 | - [https://www.remotepython.com/jobs/](https://www.remotepython.com/jobs/) 16 | - [https://weworkcontract.com/python-contract-jobs](https://weworkcontract.com/python-contract-jobs) 17 | 18 | 知道我们列表中缺少的 Django REST Framework 工作的任何其他优秀资源吗?请[提交拉取请求](https://github.com/encode/django-rest-framework)或[发送电子邮件给我们](mailto:anna@django-rest-framework.org)。 19 | 20 | 不知道您还能帮忙吗?帮助 Django REST Framework 的最好方法之一是询问面试官他们的公司是否已经注册了 [REST Framework 赞助](https://fund.django-rest-framework.org/topics/funding/)。 21 | -------------------------------------------------------------------------------- /Community/Kickstarter_Announcement.md: -------------------------------------------------------------------------------- 1 | # Kickstarting Django REST framework 3 2 | *** 3 | 4 | 5 | 6 | *** 7 | 8 | 为了继续推动这个项目向前发展,我启动了一个 Kickstarter 活动来资助一个主要的新版本 - Django REST framework 3 的开发。 9 | 10 | ## 项目详情 (Project details) 11 | 这个新版本将允许我们全面解决框架的一些缺点,并将致力于包括以下内容: 12 | 13 | - 更快,更简单,更易于使用的序列化器。 14 | - 可浏览 API 的替代管理样式界面。 15 | - 搜索和过滤控件可在可浏览的 API 中访问。 16 | - 替代 API 分页样式。 17 | - 有关 API 版本控制的文档。 18 | - 未分类票的分类。 19 | - 提高项目的持续质量和可维护性。 20 | 21 | [项目页面](https://www.kickstarter.com/projects/tomchristie/django-rest-framework-3)上现在有可用的完整的详细信息。 22 | 23 | 如果您有兴趣帮助实现可持续的开源开发,请[访问 Kickstarter 页面](https://www.kickstarter.com/projects/tomchristie/django-rest-framework-3)并考虑资助该项目。 24 | 25 | 我迫不及待地想看看这会把我们带到哪里去! 26 | 27 | 非常感谢大家到目前为止对你的支持, 28 | 29 | Tom Christie:) 30 | 31 | *** 32 | 33 | ## 赞助商 (Sponsors) 34 | 我们现在已经超越了我们的所有目标,获得了惊人的 30,000英镑 (约合 50,000 美元),这意味着我将能够大大超出我们原先的计划。我非常感谢所有优秀的公司和个人,他们一直慷慨地支持这个项目,并使其成为可能。 35 | 36 | *** 37 | 38 | ### 铂金赞助商 (Platinum sponsors) 39 | 我们的铂金赞助商各自为 Django REST framework 的未来发展做出了巨大贡献,我对你们感激不尽。 40 | 41 | 具体赞助商:略。 42 | 43 | ### 黄金赞助商 (Gold sponsors) 44 | 我们的黄金赞助商包括大大小小的公司。非常感谢他们对该项目的大量资助以及他们对可持续开源开发的承诺。 45 | 46 | 具体赞助商:略。 47 | 48 | ### 白银赞助商 (Silver sponsors) 49 | 非常感谢我们的白银赞助者所做的巨大的财务贡献。我要特别感谢那些选择私下支持这个项目的个人。 50 | 51 | 具体赞助商:略。 52 | 53 | ### 倡导者 (Advocates) 54 | 以下个人为 Django REST framework 3 的开发做出了重要的财务贡献,对此我只能表示巨大的、热烈的和真诚的感谢! 55 | 56 | 个人支持者:略。 57 | 58 | ### 支持者 (Supporters) 59 | 此外,还有近 300 名个人选择帮助资助其他级别的项目,或者选择匿名捐赠。再次感谢,谢谢,谢谢,谢谢! 60 | -------------------------------------------------------------------------------- /Community/Mozilla_Grant.md: -------------------------------------------------------------------------------- 1 | # Mozilla Grant 2 | 我们最近[获得了 Mozilla 的拨款](https://blog.mozilla.org/blog/2016/04/13/mozilla-open-source-support-moss-update-q1-2016/),以资助 REST framework 的下一个主要版本。这项工作通过引入支持能够与 REST framework API 动态交互的客户端库,专注于无缝的客户端集成。该框架将提供超媒体或模式端点,这将公开与客户端库交互的可用接口。 3 | 4 | 此外,我们将构建 Django 频道提供的实时支持,支持和记录如何使用 REST framework 构建实时 API。同样,这将包括在相关客户端库中支持的工作,使构建丰富的交互式应用程序更加容易。 5 | 6 | [Core API](https://www.coreapi.org/) 项目将为我们的客户端库支持提供基础,并允许我们支持使用广泛的模式和超媒体格式交互。值得注意的是,这些客户端库也不会紧密耦合到单独的 REST framework API,并且能够与任何公开受支持的模式或超媒体格式的 API 交互。 7 | 8 | 具体来说,工作包括: 9 | 10 | ## 客户端库 (Client libraries) 11 | 这项工作将包括内置模式和超媒体支持,允许动态客户端库与 API 交互。我还将发布 Python 和 Javascript 客户端库,以及命令行客户端、新的教程部分和进一步的文档。 12 | 13 | - REST framework 中的客户端库支持。 14 | - REST framework API 的模式和超媒体支持。 15 | - 测试客户端,允许您编写模拟与 API 交互的客户端库的测试。 16 | - 有关使用客户端库与 REST framework API 交互的新教程部分。 17 | - Python 客户端库。 18 | - JavaScript 客户端库。 19 | - 命令行客户端。 20 | 21 | ## 实时 API (Realtime APIs) 22 | 下一个目标是在 Django 频道提供的实时支持的基础上,添加用于构建实时 API 端点的支持和文档。 23 | 24 | - 使用 REST framework 和 Django 频道支持 API 订阅端点。 25 | - 有关使用 REST framework 构建实时 API 端点的新教程部分。 26 | - Python 和 Javascript 客户端库中的实时支持。 27 | 28 | ## 责任 (Accountability) 29 | 为了确保我能够完全专注于确保可持续且资金充足的开源业务,我将于 2016 年 5 月底离开我目前在 [DabApps](https://www.dabapps.com/) 的职位。 30 | 31 | 我已经成立了一家英国有限公司 [Encode](https://www.encode.io/),它将作为 REST framework 背后的商业实体。我将每月发布来自 Encode 的进展报告,包括 Mozilla 的拨款,以及通过 [REST framework 付费计划](https://www.django-rest-framework.org/community/funding/)资助的开发时间。 32 | 33 | ## 保持与我们每月进度报告的同步… 34 | -------------------------------------------------------------------------------- /Community/Project_management.md: -------------------------------------------------------------------------------- 1 | # 项目管理 (Project management) 2 | “没有人可以吹响交响乐;它需要整个管弦乐队演奏它” —— Halford E. Luccock 3 | 4 | 本文档概述了 REST framework 的项目管理流程。 5 | 6 | 其目的是确保项目具有较高的 [“公共要素”](https://en.wikipedia.org/wiki/Bus_factor),并且在可预见的未来可以继续得到良好的支持。欢迎提出改进我们流程的建议。 7 | 8 | *** 9 | 10 | ## 维护团队 (Maintenance team) 11 | 我们有一个季度维护周期,新成员可以加入维护团队。我们目前将团队规模限制为5名成员,并且可能会鼓励人们离开团队一段时间以允许新成员参与。 12 | 13 | #### 现在的团队 (Current team) 14 | [2015 年第四季度维护团队](https://github.com/encode/django-rest-framework/issues/2190): 15 | 16 | - [@tomchristie](https://github.com/encode/) 17 | - [@xordoquy](https://github.com/xordoquy/) (发布经理。) 18 | - [@carltongibson](https://github.com/carltongibson/) 19 | - [@kevin-brown](https://github.com/kevin-brown/) 20 | - [@jpadilla](https://github.com/jpadilla/) 21 | 22 | #### 维护周期 (Maintenance cycles) 23 | 每个维护周期都由使用 `Process` 标签打开的问题启动。 24 | 25 | - 要考虑维护者角色,只需对该问题进行评论。 26 | - 现有成员必须通过勾选自己的名字明确地选择进入下一个周期。 27 | - 关于即将到来的团队的最终决定将由 `@tomchristie` 做出。 28 | 29 | 维护团队的成员将作为协作者添加到存储库中。 30 | 31 | 以下模板应用于问题描述,并作为选择团队的正式流程。 32 | ```python 33 | This issue is for determining the maintenance team for the *** period. 34 | 35 | Please see the [Project management](http://www.django-rest-framework.org/topics/project-management/) section of our documentation for more details. 36 | 37 | --- 38 | 39 | #### Renewing existing members. 40 | 41 | The following people are the current maintenance team. Please checkmark your name if you wish to continue to have write permission on the repository for the *** period. 42 | 43 | - [ ] @*** 44 | - [ ] @*** 45 | - [ ] @*** 46 | - [ ] @*** 47 | - [ ] @*** 48 | 49 | --- 50 | 51 | #### New members. 52 | 53 | If you wish to be considered for this or a future date, please comment against this or subsequent issues. 54 | 55 | To modify this process for future maintenance cycles make a pull request to the [project management](http://www.django-rest-framework.org/topics/project-management/) documentation. 56 | ``` 57 | 58 | #### 团队成员的责任 (Responsibilities of team members) 59 | 团队成员有以下责任。 60 | 61 | - 关闭无效或已解决的票证。 62 | - 在票据中添加分类标签和里程碑。 63 | - 合并最终的拉取请求。 64 | - 使用 `mkdocs gh-deploy` 构建和部署文档。 65 | - 构建和更新包含的翻译包。 66 | 67 | 维护者的进一步说明: 68 | 69 | - 代码更改应以拉取请求的形式出现 - 不要直接推送到 master。 70 | - 维护者通常不应合并他们自己的拉取请求。 71 | - 每个问题/拉取请求在分类后应该只有一个标签。 72 | - 使用 [is:open no:label](https://github.com/encode/django-rest-framework/issues?q=is%3Aopen+no%3Alabel) 搜索未分类的问题。 73 | 74 | 应该注意的是,积极参与 REST framework 项目显然**不需要成为维护团队的一部分**。几乎问题分类和项目改进的每个导入部分都可以被积极地处理,而不管您在存储库中的协作者状态如何。 75 | 76 | *** 77 | 78 | ## 发布过程 (Release process) 79 | 每个季度维护周期都会选择发布管理员。 80 | 81 | - 管理员应该由 `@tomchristie` 选中。 82 | - 然后,管理员将把维护者角色添加到 PyPI 包中。 83 | - 之前的管理员将从 PyPI 包中删除维护者角色。 84 | 85 | 我们的 PyPI 版本将由当前版本管理员或 `@tomchristie` 处理。每个版本都应该有一个标记有 `Release` 标签的开放问题,并标记为相应的里程碑。 86 | 87 | 应使用以下模板来描述问题,并将其用作发布清单。 88 | ```python 89 | Release manager is @***. 90 | Pull request is #***. 91 | 92 | During development cycle: 93 | 94 | - [ ] Upload the new content to be translated to [transifex](http://www.django-rest-framework.org/topics/project-management/#translations). 95 | 96 | 97 | Checklist: 98 | 99 | - [ ] Create pull request for [release notes](https://github.com/encode/django-rest-framework/blob/master/docs/topics/release-notes.md) based on the [*.*.* milestone](https://github.com/encode/django-rest-framework/milestones/***). 100 | - [ ] Update supported versions: 101 | - [ ] `setup.py` `python_requires` list 102 | - [ ] `setup.py` Python & Django version trove classifiers 103 | - [ ] `README` Python & Django versions 104 | - [ ] `docs` Python & Django versions 105 | - [ ] Update the translations from [transifex](http://www.django-rest-framework.org/topics/project-management/#translations). 106 | - [ ] Ensure the pull request increments the version to `*.*.*` in [`restframework/__init__.py`](https://github.com/encode/django-rest-framework/blob/master/rest_framework/__init__.py). 107 | - [ ] Confirm with @tomchristie that release is finalized and ready to go. 108 | - [ ] Ensure that release date is included in pull request. 109 | - [ ] Merge the release pull request. 110 | - [ ] Push the package to PyPI with `./setup.py publish`. 111 | - [ ] Tag the release, with `git tag -a *.*.* -m 'version *.*.*'; git push --tags`. 112 | - [ ] Deploy the documentation with `mkdocs gh-deploy`. 113 | - [ ] Make a release announcement on the [discussion group](https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework). 114 | - [ ] Make a release announcement on twitter. 115 | - [ ] Close the milestone on GitHub. 116 | 117 | To modify this process for future releases make a pull request to the [project management](http://www.django-rest-framework.org/topics/project-management/) documentation. 118 | ``` 119 | 120 | 当将版本推到 PyPI 时,请确保您的环境已经从我们的开发 `requirement.txt` 中安装了。以便文档和 PyPI 安装始终是根据固定的一组软件包构建的。 121 | 122 | *** 123 | 124 | ## 翻译 (Translations) 125 | 维护团队负责管理 REST framework 中的翻译包。通过 [transifex 服务](https://www.transifex.com/projects/p/django-rest-framework/)管理将源字符串翻译成多种语言。 126 | 127 | ### 管理 Transifex (Managing Transifex) 128 | [官方 Transifex 客户端](https://pypi.org/project/transifex-client/)用于上传和下载翻译到 Transifex。客户端使用 pip 安装: 129 | ```python 130 | pip install transifex-client 131 | ``` 132 | 133 | 要使用它,您需要登录 Transifex 并输入密码,并且您需要拥有 Transifex 项目的管理访问权限。您需要创建一个包含您的凭据的 `~/.transifexrc` 文件。 134 | ```python 135 | [https://www.transifex.com] 136 | username = *** 137 | token = *** 138 | password = *** 139 | hostname = https://www.transifex.com 140 | ``` 141 | 142 | ### 上传新的源文件 (Upload new source files) 143 | 当任何用户可见字符串发生更改时,应将其上传到 Transifex,以便翻译人员可以开始翻译它们。要做到这一点,只需运行: 144 | ```python 145 | # 1. 更新源 django.po 文件,这是美国英语版本。 146 | cd rest_framework 147 | django-admin.py makemessages -l en_US 148 | # 2. 将源 django.po 文件推送到 Transifex。 149 | cd .. 150 | tx push -s 151 | ``` 152 | 153 | 推送源文件时,Transifex 将更新资源的源字符串以匹配新源文件中的源字符串。 154 | 155 | 以下是如何处理旧源文件和新源文件之间的差异: 156 | 157 | - 将添加新字符串。 158 | - 修改后的字符串也会被添加。 159 | - 新源文件中不存在的字符串及其翻译将从数据库中删除。如果稍后重新添加源字符串,则 [Transifex 翻译记忆库](http://docs.transifex.com/guides/tm#let-tm-automatically-populate-translations)将自动包含翻译字符串。 160 | 161 | ### 下载翻译 (Download translations) 162 | 翻译人员完成翻译后,他们的工作需要从 Transifex 下载到 REST framework 存储库中。为此,请运行: 163 | ```python 164 | # 3. 从 Transifex 中提取已翻译的 django.po 文件。 165 | tx pull -a --minimum-perc 10 166 | cd rest_framework 167 | # 4. 编译所有支持语言的二进制 .mo 文件。 168 | django-admin.py compilemessages 169 | ``` 170 | 171 | *** 172 | 173 | ## 项目要求 (Project requirements) 174 | 我们所有的测试要求都固定在精确的版本上,以确保我们的测试运行具有可重复性。我们维护 `requirements` 目录中的需求。需求文件从 `tox.ini` 配置文件中引用,确保我们在测试中使用的包版本具有单一的事实来源。 175 | 176 | 通常应将包升级视为隔离拉取请求。您可以使用 `pip list --outdated` 来检查是否有更新版本的软件包可用。 177 | 178 | *** 179 | 180 | ## 项目所有权 (Project ownership) 181 | PyPI 包由 `@tomchristie` 拥有。作为备份,`@j4mie` 还拥有该软件包的所有权。 182 | 183 | 如果 `@tomchristie` 不再参与这个项目,那么 `@j4mie` 有责任移交所有权。 184 | 185 | #### 未解决的管理和所有权问题 (Outstanding management & ownership issues) 186 | 以下问题仍需解决: 187 | 188 | - [考虑将 repo 移动到适当的 GitHub 组织中](https://github.com/encode/django-rest-framework/issues/2162)。 189 | - 确保 `@jamie` 具有对 `django-rest-framework.org` 域设置和管理员的备份访问权限。 190 | - [生动的示例](https://restframework.herokuapp.com/) API 的文档所有权。 191 | - [邮件列表](https://groups.google.com/forum/#!forum/django-rest-framework)和 IRC 通道的文档所有权。 192 | - 安全邮件列表的文档所有权和管理。 193 | -------------------------------------------------------------------------------- /Community/Tutorials_and_Resources.md: -------------------------------------------------------------------------------- 1 | # 教程和资源 (Tutorials and Resources) 2 | 有大量资源可供学习和使用 Django REST framework。我们尝试在此处提供全面的可用列表。 3 | 4 | ## 书籍 (Books) 5 | [![](https://www.django-rest-framework.org/img/books/hwa-cover.png)](https://hellowebapp.com/order/) [![](https://www.django-rest-framework.org/img/books/tsd-cover.png)](https://www.twoscoopspress.com/products/two-scoops-of-django-1-11) 6 | 7 | ## 教程 (Tutorials) 8 | - [Django REST framework 初学者指南](https://code.tutsplus.com/tutorials/beginners-guide-to-the-django-rest-framework--cms-19786) 9 | - [Django REST framework - 简介](https://realpython.com/blog/python/django-rest-framework-quick-start/) 10 | - [Django REST Framework 教程](https://tests4geeks.com/django-rest-framework-tutorial/) 11 | - [Django REST Framework 课程](https://teamtreehouse.com/library/django-rest-framework) 12 | - [使用 Django REST Framework 构建 RESTful API](https://agiliq.com/blog/2014/12/building-a-restful-api-with-django-rest-framework/) 13 | - [Django REST Framework 和 AngularJS 入门](http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html) 14 | - [使用 Django REST Framework 和 AngularJS 的端到端 Web 应用程序](http://mourafiq.com/2013/07/01/end-to-end-web-app-with-django-angular-1.html) 15 | - [启动您的 API - Django REST Framework 第 1 部分](https://godjango.com/41-start-your-api-django-rest-framework-part-1/) 16 | - [权限和身份验证 - Django REST Framework 第 2 部分](https://godjango.com/43-permissions-authentication-django-rest-framework-part-2/) 17 | - [视图集和路由器 - Django REST Framework 第 3 部分](https://godjango.com/45-viewsets-and-routers-django-rest-framework-part-3/) 18 | - [Django REST Framework 用户端点](https://richardtier.com/2014/02/25/django-rest-framework-user-endpoint/) 19 | - [使用 Django REST Framework 检查凭据](https://richardtier.com/2014/03/06/110/) 20 | - [使用 Python 和 Django Rest Framework 创建生产就绪 API - 第 1 部分](https://www.andreagrandi.it/2016/09/28/creating-production-ready-api-python-django-rest-framework-part-1/) 21 | - [使用 Python 和 Django Rest Framework 创建生产就绪 API - 第 2 部分](https://www.andreagrandi.it/2016/10/01/creating-a-production-ready-api-with-python-and-django-rest-framework-part-2/) 22 | - [Django REST Framework 教程 - 构建博客 API](https://wsvincent.com/django-rest-framework-tutorial/) 23 | - [Django REST Framework & React 教程 - 构建 Todo 列表 API](https://wsvincent.com/django-rest-framework-react-tutorial/) 24 | - [教程:使用 React 的 Django REST (Django 2.0)](https://www.valentinog.com/blog/tutorial-api-django-rest-react/) 25 | 26 | ## Videos 27 | ### 演讲 (Talks) 28 | - [升级!重新思考 Web API 框架](https://www.youtube.com/watch?v=Rk6MHZdust4) 29 | - [如何使用 Django OAuth Toolkit 创建完全成熟的 REST API](https://www.youtube.com/watch?v=M6Ud3qC2tTk) 30 | - [Django REST API - 您可以在 25 分钟内轻松学习它](https://www.youtube.com/watch?v=cqP758k1BaQ) 31 | - [Tom Christie about Django Rest Framework at Django: Under The Hood](https://www.youtube.com/watch?v=3cSsbe-tA0E) 32 | - [Django REST framework :模式、超媒体和客户端库](https://www.youtube.com/watch?v=FjmiGh7OqVg) 33 | 34 | ### 教程 (Tutorials) 35 | - [Django REST Framework 第 1 部分](http://www.neckbeardrepublic.com/screencasts/django-rest-framework-part-1) 36 | - [您的 PJ 中的 Django REST Framework!](https://www.youtube.com/watch?v=xMtHsWa72Ww) 37 | - [使用 Django 和 Django REST Framework 构建 REST API](https://www.youtube.com/watch?v=PwssEec3IRw) 38 | - [使用 Django REST Framework 的 Blog API](https://www.youtube.com/watch?v=XMu0T6L2KRQ&list=PLEsfXFp6DpzTOcOVdZF-th7BS_GYGguAS) 39 | - [Ember 和 Django 第 1 部分](http://www.neckbeardrepublic.com/screencasts/ember-and-django-part-1) 40 | - [Django REST Framework 图像上传教程 (使用 AngularJS)](https://www.youtube.com/watch?v=hMiNTCIY7dw&list=PLUe5s-xycYk_X0vDjYBmKuIya2a2myF8O) 41 | - [Django REST Framework 教程](https://www.youtube.com/watch?v=axRCBgbOJp8&list=PLJtp8Jm8EDzjgVg9vVyIUMoGyqtegj7FH) 42 | 43 | ## 文章 (Articles) 44 | - [Web API 性能:分析 Django REST Framework](https://www.dabapps.com/blog/api-performance-profiling-django-rest-framework/) 45 | - [使用 Django 和 Django REST Framework 进行 API 开发](https://bnotions.com/api-development-with-django-and-django-rest-framework/) 46 | - [集成 Pandas,Django REST Framework 和 Bokeh](https://machinalis.com/blog/pandas-django-rest-framework-bokeh/) 47 | - [控制 Web 应用程序和 API 的不确定性](https://machinalis.com/blog/controlling-uncertainty-on-web-applications-and-apis/) 48 | - [使用数据库后端在 Django REST Framework 中进行全文搜索](https://machinalis.com/blog/full-text-search-on-django-rest-framework/) 49 | - [使用 Django REST Framework 和自定义第三方 OAuth2 后端进行 OAuth2 身份验证](https://machinalis.com/blog/oauth2-authentication/) 50 | - [使用 Django REST Framework 的嵌套资源](https://machinalis.com/blog/nested-resources-with-django/) 51 | - [使用 Django REST Framework 的图像字段](https://machinalis.com/blog/image-fields-with-django-rest-framework/) 52 | - [Chatbot 使用 Django REST Framework + api.ai + Slack - 第 1/3 部分](https://chatbotslife.com/chatbot-using-django-rest-framework-api-ai-slack-part-1-3-69c7e38b7b1e#.g2aceuncf) 53 | - [使用 DRF 和 EmberJS 的新 Django 管理员......有什么新闻?](https://blog.levit.be/new-django-admin-with-emberjs-what-are-the-news/) 54 | - [有关 Django REST Framework 的博客文章](https://medium.com/django-rest-framework) 55 | 56 | ### 文献 (Documentations) 57 | - [优雅的 Django REST Framework](http://www.cdrf.co/) 58 | - [DRF-schema-adapter](https://drf-schema-adapter.readthedocs.io/en/latest/) 59 | 60 | 想要将您的 Django REST Framework 演讲/教程/文章添加到我们的网站吗?或者知道这里尚未包含的资源?请[提交拉取请求](https://github.com/encode/django-rest-framework)或[发送电子邮件给我们](anna@django-rest-framework.org)! 61 | -------------------------------------------------------------------------------- /Image/api-docs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangweiren/Django-REST-framework-documentation/a810e00a4c8ac4646919e500042f2cfa75188464/Image/api-docs.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Django REST framework 中文文档 2 | 如果本文对您有所帮助,请在 [github](https://github.com/fangweiren/Django-REST-framework-documentation) 上 **star** 该项目。 3 | 4 | 本人博客:[Django REST framework 中文文档](http://www.iamnancy.top/djangorestframework/Home/),跟官网布局基本一致,更直观。 5 | 6 | ## 版本说明 7 | 这是 Django REST framework v3 版本的中文翻译文档。 8 | 9 | - djangorestframework v3.8.2 10 | - django 2.0.6 11 | - python 3.5.2 12 | - httpie 0.9.9 13 | 14 | ## 教程 (Tutorial) 15 | - [Quickstart](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Tutorial/Quickstart.md) 16 | - [1 - Serialization](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Tutorial/Serialization.md) 17 | - [2 - Requests & Responses](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Tutorial/Requests-and-Responses.md) 18 | - [3 - Class-based views](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Tutorial/Class-based-views.md) 19 | - [4 - Authentication & permissions](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Tutorial/Authentication-and-Permissions.md) 20 | - [5 - Relationships & hyperlinked APIs](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Tutorial/Relationships-and-Hyperlinked-APIs.md) 21 | - [6 - Viewsets & routers](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Tutorial/ViewSets-and-Routers.md) 22 | - [7 - Schemas & client libraries](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Tutorial/Schemas-and-client_libraries.md) 23 | 24 | ## API 指南 (API Guide) 25 | - [Requests](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Requests.md) 26 | - [Responses](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Responses.md) 27 | - [Views](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Views.md) 28 | - [Generic views](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Generic-views.md) 29 | - [Viewsets](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/ViewSets.md) 30 | - [Routers](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Routers.md) 31 | - [Parsers](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Parsers.md) 32 | - [Renderers](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Renderers.md) 33 | - [Serializers](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Serializers.md) 34 | - [Serializer fields](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Serializer-fields.md) 35 | - [Serializer relations](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Serializer-relations.md) 36 | - [Validators](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Validators.md) 37 | - [Authentication](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Authentication.md) 38 | - [Permissions](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Permissions.md) 39 | - [Throttling](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Throttling.md) 40 | - [Filtering](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Filtering.md) 41 | - [Pagination](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Pagination.md) 42 | - [Versioning](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Versioning.md) 43 | - [Content negotiation](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Content-negotiation.md) 44 | - [Metadata](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Metadata.md) 45 | - [Schemas](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Schemas.md) 46 | - [Format suffixes](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Format-suffixes.md) 47 | - [Returning URLs](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Returning-URLs.md) 48 | - [Exceptions](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Exceptions.md) 49 | - [Status codes](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Status-Codes.md) 50 | - [Testing](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Testing.md) 51 | - [Settings](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/API-Guide/Settings.md) 52 | 53 | ## 主题 (Topics) 54 | - [Documenting your API](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Topics/Documenting_your_API.md) 55 | - [API Clients](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Topics/API_Clients.md) 56 | - [Internationalization](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Topics/Internationalization.md) 57 | - [AJAX, CSRF & CORS](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Topics/AJAX%2C_CSRF_%26_CORS.md) 58 | - [HTML & Forms](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Topics/HTML_%26_Forms.md) 59 | - [Browser enhancements](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Topics/Browser_enhancements.md) 60 | - [The Browsable API](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Topics/The_Browsable_API.md) 61 | - [REST, Hypermedia & HATEOAS](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Topics/REST%2C_Hypermedia_%26_HATEOAS.md) 62 | 63 | ## 社区 (Community) 64 | - [Tutorials and Resources](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/Tutorials_and_Resources.md) 65 | - [Third Party Packages](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/Third_Party_Packages.md) 66 | - [Contributing to REST framework](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/Contributing_to_REST_framework.md) 67 | - [Project management](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/Project_management.md) 68 | - [Release Notes](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/Release_Notes.md) 69 | - [3.9 Announcement](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/3.9_Announcement.md) 70 | - [3.8 Announcement](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/3.8_Announcement.md) 71 | - [3.7 Announcement](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/3.7_Announcement.md) 72 | - [3.6 Announcement](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/3.6_Announcement.md) 73 | - [3.5 Announcement](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/3.5_Announcement.md) 74 | - [3.4 Announcement](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/3.4_Announcement.md) 75 | - [3.3 Announcement](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/3.3_Announcement.md) 76 | - [3.2 Announcement](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/3.2_Announcement.md) 77 | - [3.1 Announcement](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/3.1_Announcement.md) 78 | - [3.0 Announcement](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/3.0_Announcement.md) 79 | - [Kickstarter Announcement](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/Kickstarter_Announcement.md) 80 | - [Mozilla Grant](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/Mozilla_Grant.md) 81 | - [Funding](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/Funding.md) 82 | - [Jobs](https://github.com/fangweiren/Django-REST-framework-documentation/blob/master/Community/Jobs.md) 83 | 84 | ## 后记 85 | Django REST framework 中文版系列文章是本人从官方网站文档学习时根据 Google 翻译 + 自己理解的成果,本人能力有限,如有翻译不当之处,欢迎讨论。 86 | 87 | - Github 地址:[Django REST framework 中文文档](https://github.com/fangweiren/Django-REST-framework-documentation) 88 | - 本人博客:[Home - Django REST framework](http://www.iamnancy.top/djangorestframework/Home/) 89 | - 官网网站:[Django REST framework](http://www.django-rest-framework.org/) 90 | -------------------------------------------------------------------------------- /Topics/AJAX,_CSRF_&_CORS.md: -------------------------------------------------------------------------------- 1 | # 使用 AJAX,CSRF 和 CORS (Working with AJAX, CSRF & CORS) 2 | “仔细查看您自己网站上可能存在的 CSRF / XSRF 漏洞。它们是最糟糕的一种漏洞——很容易被攻击者利用,但对软件开发人员来说却不那么直观易懂,至少在您被攻击之前是这样。” —— [Jeff Atwood](https://blog.codinghorror.com/preventing-csrf-and-xsrf-attacks/) 3 | 4 | ## Javascript 客户端 (Javascript clients) 5 | 如果您正在构建 JavaScript 客户端来与 Web API 进行交互,那么您需要考虑客户端是否可以使用与网站其他部分相同的身份验证策略,并确定是否需要使用 CSRF 令牌或 CORS 标头。 6 | 7 | 在与它们交互的 API 相同的上下文中发出的 AJAX 请求通常使用 `SessionAuthentication`。这确保了一旦用户登录,就可以使用与网站其余部分相同的基于会话的身份验证来对发出的任何 AJAX 请求进行身份验证。 8 | 9 | 在与其通信的 API 不同的站点上进行的 AJAX 请求通常需要使用非基于会话的身份验证方案,例如 `TokenAuthentication`。 10 | 11 | ## CSRF 防护 (CSRF protection) 12 | [跨站点请求伪造](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF))防护是一种防范特定类型攻击的机制,当用户未注销网站并继续拥有有效会话时,可能会发生此类攻击。在这种情况下,恶意站点可能能够在登录会话的上下文内对目标站点执行操作。 13 | 14 | 为了防范这类攻击,您需要做两件事: 15 | 16 | 1. 确保 “安全” 的 HTTP 操作 (如 `GET`、`HEAD` 和 `OPTIONS`) 不能用于更改任何服务器端状态。 17 | 2. 确保任何 “不安全” 的 HTTP 操作 (如 `POST`、`PUT`、`PATCH` 和 `DELETE`) 始终需要有效的 CSRF 令牌。 18 | 19 | 如果您正在使用 `SessionAuthentication`,则需要为任何 `POST`,`PUT`,`PATCH` 或 `DELETE` 操作包含有效的 CSRF 令牌。 20 | 21 | 为了发出 AJAX 请求,您需要在 HTTP 标头中包含 CSRF 令牌,如 [Django 文档中所述](https://docs.djangoproject.com/en/stable/ref/csrf/#ajax)。 22 | 23 | ## CORS 24 | [跨域资源共享](https://www.w3.org/TR/cors/)是一种允许客户端与托管在不同域上的 API 进行交互的机制。CORS 的工作原理是要求服务器包含一组特定的标头,允许浏览器确定是否以及何时允许跨域请求。 25 | 26 | 在 REST framework 中处理 CORS 的最佳方法是在中间件中添加所需的响应标头。这确保了透明地支持 CORS,而无需更改视图中的任何行为。 27 | 28 | [Otto Yiu](https://github.com/ottoyiu/) 维护着 [django-cors-headers](https://github.com/ottoyiu/django-cors-headers/) 包,已知它可以与 REST framework API 一起正常工作。 29 | -------------------------------------------------------------------------------- /Topics/Browser_enhancements.md: -------------------------------------------------------------------------------- 1 | # 浏览器增强 (Browser enhancements) 2 | “重载 POST 有两个无争议的用途。第一种是为不支持 PUT 或 DELETE 的 Web 浏览器等客户端模拟 HTTP 的统一接口” —— [RESTful Web Services](https://www.amazon.com/RESTful-Web-Services-Leonard-Richardson/dp/0596529260), Leonard Richardson & Sam Ruby. 3 | 4 | 为了让可浏览的 API 发挥作用,REST framework 需要提供一些浏览器增强功能。 5 | 6 | 从 3.3.0 版本开始,这些都是通过 javascript 启用的,使用 [ajax-form](https://github.com/encode/ajax-form) 库。 7 | 8 | ## 基于浏览器的 PUT、DELETE 等... (Browser based PUT, DELETE, etc...) 9 | [AJAX 表单库](https://github.com/encode/ajax-form)支持基于浏览器的 HTML 表单上的 `PUT`、`DELETE` 和其他方法。 10 | 11 | 包含库后,在表单上使用 `data-method` 属性,如下所示: 12 | ```python 13 |
14 | 15 | ... 16 |
17 | ``` 18 | 19 | 请注意,在 3.3.0 之前,这种支持是基于服务器端而不是基于 JavaScript 的。由于在请求解析中引入的微妙问题,不再支持方法重载样式 (在 Ruby on Rails 中使用)。 20 | 21 | ## 基于浏览器的非表单内容提交 (Browser based submission of non-form content) 22 | [AJAX 表单库](https://github.com/encode/ajax-form)使用具有 `data-override='content-type'` 和 `data-override='content'` 属性的表单字段支持基于浏览器的内容类型 (例如 JSON) 提交。 23 | 24 | 举个栗子: 25 | ```python 26 |
27 | 28 | 29 | 30 |
31 | ``` 32 | 33 | 请注意,在 3.3.0 之前,这种支持是基于服务器端而不是基于 JavaScript 的。 34 | 35 | ## 基于 URL 的格式后缀 (URL based format suffixes) 36 | REST framework 可以采用 `?format=json` 样式的 URL 参数,这对于确定应从视图返回哪种内容类型是一个有用的快捷方式。 37 | 38 | 使用 `URL_FORMAT_OVERRIDE` 设置控制此行为。 39 | 40 | ## 基于 HTTP 标头的方法覆盖 (HTTP header based method overriding) 41 | 在版本 3.3.0 之前,支持半扩展头 `X-HTTP-Method-Override` 来覆盖请求方法。此行为不再是核心行为,但可以根据需要使用中间件添加。 42 | 43 | 举个栗子: 44 | ```python 45 | METHOD_OVERRIDE_HEADER = 'HTTP_X_HTTP_METHOD_OVERRIDE' 46 | 47 | class MethodOverrideMiddleware(object): 48 | def process_view(self, request, callback, callback_args, callback_kwargs): 49 | if request.method != 'POST': 50 | return 51 | if METHOD_OVERRIDE_HEADER not in request.META: 52 | return 53 | request.method = request.META[METHOD_OVERRIDE_HEADER] 54 | ``` 55 | 56 | ## 基于 URL 的接受标头 (URL based accept headers) 57 | 直到 3.3.0 版本,REST framework 包含了对 `?accept=application/json` 样式 URL 参数的内置支持,这将允许重写 `Accept` 标头。 58 | 59 | 自引入内容协商 API 以来,此行为不再包含在核心中,但如果需要,可以使用自定义内容协商类添加。 60 | 61 | 例如: 62 | ```python 63 | class AcceptQueryParamOverride() 64 | def get_accept_list(self, request): 65 | header = request.META.get('HTTP_ACCEPT', '*/*') 66 | header = request.query_params.get('_accept', header) 67 | return [token.strip() for token in header.split(',')] 68 | ``` 69 | 70 | ## HTML5 不支持 PUT 和 DELETE 表单吗? (Doesn't HTML5 support PUT and DELETE forms?) 71 | 不。它曾一度用于支持 `PUT` 和 `DELETE` 表单,但后来从[规范中删除](https://www.w3.org/TR/html5-diff/#changes-2010-06-24)了。目前[仍在讨论](http://amundsen.com/examples/put-delete-forms/)添加对 `PUT` 和 `DELETE` 的支持,以及如何支持除表单编码数据之外的内容类型。 72 | -------------------------------------------------------------------------------- /Topics/HTML_&_Forms.md: -------------------------------------------------------------------------------- 1 | # HTML 和表单 (HTML & Forms) 2 | REST framework 适用于返回 API 样式响应和常规 HTML 页面。此外,序列化器可用作 HTML 表单并在模板中渲染。 3 | 4 | ## 渲染 HTML (Rendering HTML) 5 | 为了返回 HTML 响应,您需要使用 `TemplateHTMLRenderer` 或 `StaticHTMLRenderer`。 6 | 7 | `TemplateHTMLRenderer` 类期望响应包含上下文数据的字典,并基于必须在视图或响应中指定的模板渲染 HTML 页面。 8 | 9 | `StaticHTMLRender` 类期望响应包含预渲染 HTML 内容的字符串。 10 | 11 | 由于静态 HTML 页面通常具有与 API 响应不同的行为,因此您可能需要显式编写任何 HTML 视图,而不是依赖于内置的通用视图。 12 | 13 | 下面是一个视图示例,它返回 “Profile” 实例列表,在 HTML 模板中渲染: 14 | 15 | **views.py**: 16 | ```python 17 | from my_project.example.models import Profile 18 | from rest_framework.renderers import TemplateHTMLRenderer 19 | from rest_framework.response import Response 20 | from rest_framework.views import APIView 21 | 22 | 23 | class ProfileList(APIView): 24 | renderer_classes = [TemplateHTMLRenderer] 25 | template_name = 'profile_list.html' 26 | 27 | def get(self, request): 28 | queryset = Profile.objects.all() 29 | return Response({'profiles': queryset}) 30 | ``` 31 | 32 | **profile_list.html**: 33 | ```python 34 | 35 |

Profiles

36 |
    37 | {% for profile in profiles %} 38 |
  • {{ profile.name }}
  • 39 | {% endfor %} 40 |
41 | 42 | ``` 43 | 44 | ## 渲染表单 (Rendering Forms) 45 | 序列化器可以通过使用 `render_form` 模板标记渲染为表单,并将序列化器实例作为模板的上下文。 46 | 47 | 以下视图演示了在模板中使用序列化器查看和更新​​模型实例的示例: 48 | 49 | **views.py**: 50 | ```python 51 | from django.shortcuts import get_object_or_404 52 | from my_project.example.models import Profile 53 | from rest_framework.renderers import TemplateHTMLRenderer 54 | from rest_framework.views import APIView 55 | 56 | 57 | class ProfileDetail(APIView): 58 | renderer_classes = [TemplateHTMLRenderer] 59 | template_name = 'profile_detail.html' 60 | 61 | def get(self, request, pk): 62 | profile = get_object_or_404(Profile, pk=pk) 63 | serializer = ProfileSerializer(profile) 64 | return Response({'serializer': serializer, 'profile': profile}) 65 | 66 | def post(self, request, pk): 67 | profile = get_object_or_404(Profile, pk=pk) 68 | serializer = ProfileSerializer(profile, data=request.data) 69 | if not serializer.is_valid(): 70 | return Response({'serializer': serializer, 'profile': profile}) 71 | serializer.save() 72 | return redirect('profile-list') 73 | ``` 74 | 75 | **profile_detail.html**: 76 | ```python 77 | {% load rest_framework %} 78 | 79 | 80 | 81 |

Profile - {{ profile.name }}

82 | 83 |
84 | {% csrf_token %} 85 | {% render_form serializer %} 86 | 87 |
88 | 89 | 90 | ``` 91 | 92 | ### 使用模板包 (Using template packs) 93 | `render_form` 标记采用可选的 `template_pack` 参数,该参数指定应该使用哪个模板目录来渲染表单和表单字段。 94 | 95 | REST framework 包括三个内置的模板包,全部基于 Bootstrap 3。内置样式是 `horizontal`,`vertical` 和 `inline`。默认样式为 `horizontal`。要使用任何这些模板包,您还需要包含 Bootstrap 3 CSS。 96 | 97 | 以下 HTML 将链接到 Bootstrap 3 CSS 的 CDN 托管版本: 98 | ```python 99 | 100 | … 101 | 102 | 103 | ``` 104 | 105 | 第三方包可以包括备用模板包,通过捆绑包含必要表单和字段模板的模板目录。 106 | 107 | 让我们看看如何渲染三个可用的模板包。对于这些示例,我们将使用单个序列化器类来渲染 “登录” 表单。 108 | ```python 109 | class LoginSerializer(serializers.Serializer): 110 | email = serializers.EmailField( 111 | max_length=100, 112 | style={'placeholder': 'Email', 'autofocus': True} 113 | ) 114 | password = serializers.CharField( 115 | max_length=100, 116 | style={'input_type': 'password', 'placeholder': 'Password'} 117 | ) 118 | remember_me = serializers.BooleanField() 119 | ``` 120 | 121 | *** 122 | 123 | #### `rest_framework/vertical` 124 | 使用标准 Bootstrap 布局在其相应的控件输入上方显示表单标签。 125 | 126 | *这是默认模板包。* 127 | ```python 128 | {% load rest_framework %} 129 | 130 | ... 131 | 132 |
133 | {% csrf_token %} 134 | {% render_form serializer template_pack='rest_framework/vertical' %} 135 | 136 |
137 | ``` 138 | 139 | ![](http://www.django-rest-framework.org/img/vertical.png) 140 | 141 | *** 142 | 143 | #### `rest_framework/horizontal` 144 | 使用 2/10 列拆分将标签和控件并排显示。 145 | 146 | *这是可浏览 API 和管理员渲染器中使用的表单样式。* 147 | ```python 148 | {% load rest_framework %} 149 | 150 | ... 151 | 152 |
153 | {% csrf_token %} 154 | {% render_form serializer %} 155 |
156 |
157 | 158 |
159 |
160 |
161 | ``` 162 | 163 | ![](http://www.django-rest-framework.org/img/horizontal.png) 164 | 165 | *** 166 | 167 | #### `rest_framework/inline` 168 | 一个紧凑的表单样式,以内联方式显示所有控件。 169 | ```python 170 | {% load rest_framework %} 171 | 172 | ... 173 | 174 |
175 | {% csrf_token %} 176 | {% render_form serializer template_pack='rest_framework/inline' %} 177 | 178 |
179 | ``` 180 | 181 | ![](http://www.django-rest-framework.org/img/inline.png) 182 | 183 | ## 字段样式 (Field styles) 184 | 序列化器字段可以通过使用 `style` 关键字参数自定义其渲染样式。此参数是控制所用模板和布局的选项字典。 185 | 186 | 自定义字段样式的最常用方法是使用 `base_template` 样式关键字参数来选择应使用模板包中的哪个模板。 187 | 188 | 例如,要将 `CharField` 渲染为 HTML textarea 而不是默认的 HTML 输入,您可以使用以下内容: 189 | ```python 190 | details = serializers.CharField( 191 | max_length=1000, 192 | style={'base_template': 'textarea.html'} 193 | ) 194 | ``` 195 | 196 | 如果您希望使用*不是包含的模板包的一部分*的自定义模板渲染字段,则可以使用 `template` 样式选项来完全指定模板名称: 197 | ```python 198 | details = serializers.CharField( 199 | max_length=1000, 200 | style={'template': 'my-field-templates/custom-input.html'} 201 | ) 202 | ``` 203 | 204 | 字段模板还可以使用其他样式属性,具体取决于其类型。例如,`textarea.html` 模板还接受 `rows` 属性,该属性可用于影响控件的大小。 205 | ```python 206 | details = serializers.CharField( 207 | max_length=1000, 208 | style={'base_template': 'textarea.html', 'rows': 10} 209 | ) 210 | ``` 211 | 212 | 下面列出了 `base_template` 选项及其相关样式选项的完整列表。 213 | 214 | | base_template | 有效字段类型 | 其它样式选项 | 215 | | :-------- | :--------| :-- | 216 | | input.html | 任何字符串,数字或日期/时间字段 | input_type, placeholder, hide_label, autofocus | 217 | | textarea.html | `CharField` | rows, placeholder, hide_label | 218 | | select.html | `ChoiceField` 或相关字段类型 | hide_label | 219 | | radio.html | `ChoiceField` 或相关字段类型 | inline, hide_label | 220 | | select_multiple.html | `MultipleChoiceField` 或带有 `many=True` 的关系字段 | hide_label | 221 | | checkbox_multiple.html | `MultipleChoiceField` 或带有 `many=True` 的关系字段 | inline, hide_label | 222 | | checkbox.html | `BooleanField` | hide_label | 223 | | fieldset.html | 嵌套的序列化器 | hide_label | 224 | |list_fieldset.html | `ListField` 或带有 `many=True` 的嵌套序列化器 | hide_label | 225 | -------------------------------------------------------------------------------- /Topics/Internationalization.md: -------------------------------------------------------------------------------- 1 | # 国际化 (Internationalization) 2 | 支持国际化不是可选的。它必须是核心功能。—— [Jannis Leidel,2015 年在 Django Under the Hood 演讲](https://youtu.be/Wa0VfS2q94Y)。 3 | 4 | REST framework 附带可翻译的错误消息。您可以启用 [Django 的标准翻译机制](https://docs.djangoproject.com/en/1.7/topics/i18n/translation)以您的语言显示这些内容。 5 | 6 | 这样做可以让您: 7 | 8 | - 使用标准 `LANGUAGE_CODE` Django 设置,选择英语以外的语言作为默认语言。 9 | - 允许客户端使用 Django 中包含的 `LocaleMiddleware` 自己选择语言。API 客户端的典型用法是包含 `Accept-Language` 请求标头。 10 | 11 | ## 启用国际化 API (Enabling internationalized APIs) 12 | 您可以使用标准 Django `LANGUAGE_CODE` 设置更改默认语言: 13 | ```python 14 | LANGUAGE_CODE = "es-es" 15 | ``` 16 | 17 | 您可以通过将 `LocalMiddleware` 添加到 `MIDDLEWARE_CLASSES` 设置来打开每个请求的语言请求: 18 | ```python 19 | MIDDLEWARE_CLASSES = [ 20 | ... 21 | 'django.middleware.locale.LocaleMiddleware' 22 | ] 23 | ``` 24 | 25 | 当启用每个请求的国际化时,客户端请求将尽可能尊重 `Accept-Language` 标头。例如,让我们请求不受支持的媒体类型: 26 | 27 | **Request** 28 | ```python 29 | GET /api/users HTTP/1.1 30 | Accept: application/xml 31 | Accept-Language: es-es 32 | Host: example.org 33 | ``` 34 | 35 | **Response** 36 | ```python 37 | HTTP/1.0 406 NOT ACCEPTABLE 38 | 39 | {"detail": "No se ha podido satisfacer la solicitud de cabecera de Accept."} 40 | ``` 41 | 42 | REST framework 包括这些内置的翻译,既适用于标准异常情况,也适用于序列化器验证错误。 43 | 44 | 请注意,翻译仅适用于错误字符串本身。错误消息的格式和字段名称的键将保持不变。示例 `400 Bad Request` 响应正文可能如下所示: 45 | ```python 46 | {"detail": {"username": ["Esse campo deve ser unico."]}} 47 | ``` 48 | 49 | 如果您想对响应的部分 (例如 `detail` 和 `non_field_errors`) 使用不同的字符串,那么可以使用自定义异常处理程序修改此行为。 50 | 51 | #### 指定支持的语言集 (Specifying the set of supported languages.) 52 | 默认情况下,将支持所有可用语言。 53 | 54 | 如果您只想支持可用语言的子集,请使用 Django 的标准 `LANGUAGES` 设置: 55 | ```python 56 | LANGUAGES = [ 57 | ('de', _('German')), 58 | ('en', _('English')), 59 | ] 60 | ``` 61 | 62 | ## 添加新的翻译 (Adding new translations) 63 | REST framework 翻译使用 [Transifex](https://www.transifex.com/projects/p/django-rest-framework/) 在线管理。您可以使用 Transifex 服务添加新的翻译语言。然后,维护团队将确保这些翻译字符串包含在 REST framework 包中。 64 | 65 | 有时您可能需要在本地向项目添加翻译字符串。你可能需要这样做,如果: 66 | 67 | - 您希望使用尚未在 Transifex 上翻译的语言使用 REST Framework。 68 | - 您的项目包含自定义错误消息,这些消息不是 REST framework 的默认翻译字符串的一部分。 69 | 70 | #### 在本地翻译新语言 (Translating a new language locally) 71 | 该指南假设您已经熟悉如何翻译 Django 应用程序。如果你不是,那么首先阅读 [Django 的翻译文档](https://docs.djangoproject.com/en/1.7/topics/i18n/translation)。 72 | 73 | 如果您要翻译新语言,则需要翻译现有的 REST framework 错误消息: 74 | 75 | 1. 创建一个要存储国际化资源的新文件夹。将此路径添加到 `LOCALE_PATHS` 设置。 76 | 2. 现在为要翻译的语言创建一个子文件夹。文件夹应该使用地区名称符号命名。例如:`de`,`pt_BR`,`es_AR`。 77 | 3. 现在将[基本翻译文件](https://raw.githubusercontent.com/encode/django-rest-framework/master/rest_framework/locale/en_US/LC_MESSAGES/django.po)从 REST framework 源代码复制到您的翻译文件夹中。 78 | 4. 编辑刚刚复制的 `django.po` 文件,翻译所有错误消息。 79 | 5. 运行 `manage.py compilemessages -l pt_BR` 以使 Django 可以使用的翻译。您应该看到像 `processing file django.po in <...>/locale/pt_BR/LC_MESSAGES` 的消息。 80 | 6. 重新启动开发服务器以查看更改是否生效。 81 | 82 | 如果您只翻译项目代码库中存在的自定义错误消息,则无需将 REST framework 源 `django.po` 文件复制到 `LOCALE_PATHS` 文件夹中,而只需运行 Django 的标准 `makemessages` 进程即可。 83 | 84 | ## 如何确定语言 (How the language is determined) 85 | 如果您要允许每个请求的语言首选项,则需要在 `MIDDLEWARE_CLASSES` 设置中包括 `django.middleware.locale.LocaleMiddleware`。 86 | 87 | 您可以在 [Django 文档](https://docs.djangoproject.com/en/1.7/topics/i18n/translation/#how-django-discovers-language-preference)中找到有关如何确定语言首选项的更多信息。作为参考,该方法是: 88 | 89 | 1. 首先,它在请求的 URL 中查找语言前缀。 90 | 2. 如果失败,它会在当前用户的会话中查找 `LANGUAGE_SESSION_KEY` 键。 91 | 3. 如果失败,它会查找 cookie。 92 | 4. 如果失败,它会查看 `Accept-Language` HTTP 标头。 93 | 5. 如果失败,它使用全局 `LANGUAGE_CODE` 设置。 94 | 95 | 对于 API 客户端,其中最合适的通常是使用 `Accept-Language` 标头;除非使用会话身份验证,否则会话和 cookie 将不可用,通常更好的做法是优先使用 API 客户端的 `Accept-Language` 标头,而不是使用语言 URL 前缀。 96 | -------------------------------------------------------------------------------- /Topics/REST,_Hypermedia_&_HATEOAS.md: -------------------------------------------------------------------------------- 1 | # REST, Hypermedia & HATEOAS 2 | 你一直用 “REST” 这个词。我不认为这意味着你认为它意味着什么。—— Mike Amundsen,[REST fest 2012主题演讲](https://vimeo.com/channels/restfest/page:2)。 3 | 4 | 首先,免责声明。“Django REST framework ” 这个名称早在 2011 年初就已经确定,之所以选择这个名称只是为了确保开发人员能够轻松找到该项目。在整个文档中,我们尝试使用更简单和技术上更正确的 “Web API” 术语。 5 | 6 | 如果您是认真设计超媒体 API 的,那么您应该查看本文档之外的参考资料,以帮助您进行设计选择。 7 | 8 | 以下属于“必读”类别。 9 | 10 | - Roy Fielding 的论文 - [架构样式和基于网络的软件体系结构设计](https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm)。 11 | - Roy Fielding 的 “[REST API 必须是超文本驱动](http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven)” 博客文章。 12 | - Leonard Richardson 和 Mike Amundsen 的 [RESTful Web API](http://restfulwebapis.org/)。 13 | - Mike Amundsen 的[使用 HTML5 和 Node 构建超媒体 API](https://www.amazon.com/Building-Hypermedia-APIs-HTML5-Node/dp/1449306578)。 14 | - Steve Klabnik 的[设计超媒体 API](http://designinghypermediaapis.com/)。 15 | - [Richardson 成熟度模型](https://martinfowler.com/articles/richardsonMaturityModel.html) 16 | 17 | 有关更全面的背景信息,请查看 Klabnik 的 [Hypermedia API 阅读列表](http://blog.steveklabnik.com/posts/2012-02-27-hypermedia-api-reading-list)。 18 | 19 | ## 使用 REST framework 构建超媒体 API (Building Hypermedia APIs with REST framework) 20 | REST framework 是一个不可知的 Web API 工具包。它确实有助于指导您构建连接良好的 API,并且可以轻松设计适当的媒体类型,但它并不严格执行任何特定的设计风格。 21 | 22 | ## REST framework 提供了什么 (What REST framework provides.) 23 | 不言而喻,REST framework 使构建超媒体 API 成为可能。它提供的可浏览 API 是基于 HTML 构建的 - web 的超媒体语言。 24 | 25 | REST framework 还包括[序列化](https://www.django-rest-framework.org/api-guide/serializers/)和[解析器](https://www.django-rest-framework.org/api-guide/parsers/)/[渲染器](https://www.django-rest-framework.org/api-guide/renderers/)组件,可以轻松构建适当的媒体类型,建立连接良好的系统的[超链接关系](https://www.django-rest-framework.org/api-guide/fields/),以及对[内容协商](https://www.django-rest-framework.org/api-guide/content-negotiation/)的强大支持。 26 | 27 | ## REST framework 没有提供什么 (What REST framework doesn't provide.) 28 | REST framework 没有做的是默认情况下为您提供机器可读的超媒体格式,如 [HAL](http://stateless.co/hal_specification.html),[Collection + JSON](http://www.amundsen.com/media-types/collection/),[JSON API](http://jsonapi.org/) 或 HTML [微格式](http://microformats.org/wiki/Main_Page),或者能够自动神奇地创建完整的 HATEOAS 样式 API,其中包括基于超媒体的表单描述和语义标记的超链接。这样做将涉及对真正超出框架范围的 API 设计做出自圆其说的选择。 29 | -------------------------------------------------------------------------------- /Topics/The_Browsable_API.md: -------------------------------------------------------------------------------- 1 | # 可浏览的 API (The Browsable API) 2 | 这是一个极其错误的真理……我们应该培养思考我们正在做什么的习惯。事实恰恰相反。文明的进步是通过扩展我们不需要思考就能完成的重要操作的数量。—— [Alfred North Whitehead](https://en.wikiquote.org/wiki/Alfred_North_Whitehead),数学概论 (1911) 3 | 4 | API 可能代表应用程序编程接口,但是人类也必须能够读取 API;必须有人进行编程。Django REST Framework 支持在请求 HTML 格式时为每个资源生成人性化的 HTML 输出。这些页面可以方便地浏览资源,以及使用 `POST`、`PUT` 和 `DELETE` 向资源提交数据的表单。 5 | 6 | ## URLs 7 | 如果您在资源输出中包含完全限定的 URL,它们将会被 “uralized”,并且可以被人们点击以方便浏览。`rest_framework` 包为此包含了 `reverse` 帮助。 8 | 9 | ## 格式 (Formats) 10 | 默认情况下,API 将返回标头指定的格式,在浏览器中是 HTML。可以在请求中使用 `?format=` 指定格式,因此您可以通过向 URL 添加 `?format=json` 来查看浏览器中的原始 JSON 响应。在 [Firefox](https://addons.mozilla.org/en-US/firefox/addon/jsonview/) 和 [Chrome](https://chrome.google.com/webstore/detail/chklaanhfefbnpoihckbnefhakgolnmc) 中有一些有用的扩展可以查看 JSON。 11 | 12 | ## 自定义 (Customizing) 13 | 可浏览的 API 使用 [Twitter 的 Bootstrap](https://getbootstrap.com/) (v3.3.5) 构建,使其易于自定义外观。 14 | 15 | 要自定义默认样式,请创建一个名为 `rest_framework/api.html` 的模板,该模板从 `rest_framework/base.html` 扩展而来。例如: 16 | 17 | **templates/rest_framework/api.html** 18 | ```python 19 | {% extends "rest_framework/base.html" %} 20 | 21 | ... # 使用所需的自定义覆盖块 22 | ``` 23 | 24 | ### 覆盖默认主题 (Overriding the default theme) 25 | 要替换默认主题,请将 `bootstrap_theme` 块添加到 `api.html` 并将 `link` 插入到所需 Bootstrap 主题 css 文件。这将完全取代包含的主题。 26 | ```python 27 | {% block bootstrap_theme %} 28 | 29 | {% endblock %} 30 | ``` 31 | 32 | 在 [Bootswatch](https://bootswatch.com/) 可以找到合适的预先制作的替代主题。要使用任何 Bootswatch 主题,只需下载主题的 `bootstrap.min.css` 文件,将其添加到项目中,然后如上所述替换默认文件。 33 | 34 | 您还可以使用 `bootstrap_navbar_variant` 块更改导航栏变量,默认情况下为 `navbar-inverse`。空的 `{% block bootstrap_navbar_variant %}{% endblock %}` 将使用原始的 Bootstrap 导航栏样式。 35 | 36 | 完整示例: 37 | ```python 38 | {% extends "rest_framework/base.html" %} 39 | 40 | {% block bootstrap_theme %} 41 | 42 | {% endblock %} 43 | 44 | {% block bootstrap_navbar_variant %}{% endblock %} 45 | ``` 46 | 47 | 对于更具体的 CSS 调整,而不是简单地覆盖默认 bootstrap 主题,您可以覆盖 `style` 块。 48 | 49 | *** 50 | 51 | ![](https://www.django-rest-framework.org/img/cerulean.png) 52 | 53 | *bootswatch 'Cerulean' 主题的屏幕截图* 54 | 55 | *** 56 | 57 | ![](https://www.django-rest-framework.org/img/slate.png) 58 | 59 | *bootswatch 'Slate' 主题的屏幕截图* 60 | 61 | *** 62 | 63 | ### 块 (Blocks) 64 | 可浏览的 API 基本模板中可用的所有块都可以在您的 `api.html` 中使用。 65 | 66 | - `body` - 整个 html ``。 67 | - `bodyclass` - `` 标记的类属性,默认为空。 68 | - `bootstrap_theme` - Bootstrap 主题的 CSS。 69 | - `bootstrap_navbar_variant` - 导航栏的 CSS 类。 70 | - `branding` - 导航栏的烙印部分,请参阅 [Bootstrap 组件](https://getbootstrap.com/2.3.2/components.html#navbar)。 71 | - `breadcrumbs` - 链接显示资源嵌套,允许用户备份资源。建议保留这些,但可以使用 breadcrumbs 块覆盖它们。 72 | - `script` - 页面的 JavaScript 文件。 73 | - `style` - 页面的 CSS 样式表。 74 | - `title` - 页面标题。 75 | - `userlinks` - 这是标题右侧的链接列表,默认情况下包含登录/注销链接。要添加链接而不是替换,请使用 `{{ block.super }}` 来保留身份验证链接。 76 | 77 | #### 组件 (Components) 78 | 所有标准的 [Bootstrap 组件](https://getbootstrap.com/2.3.2/components.html)都可用。 79 | 80 | #### 工具提示 (Tooltips) 81 | 可浏览的 API 使用 Bootstrap 工具提示组件。带有 `js-tooltip` 类和 `title` 属性的任何元素,其标题内容都将显示悬停事件的工具提示。 82 | 83 | ### 登录模板 (Login Template) 84 | 要添加烙印并自定义登录模板的外观,请创建名为 `login.html` 的模板并将其添加到项目中,例如:`templates/rest_framework/login.html`。模板应该从 `rest_framework/login_base.html` 扩展。 85 | 86 | 您可以通过包含烙印块来添加您的网站名称或烙印: 87 | ```python 88 | {% block branding %} 89 |

My Site Name

90 | {% endblock %} 91 | ``` 92 | 93 | 您还可以通过添加类似于 `api.html` 的 `bootstrap_theme` 或 `style` 块来自定义样式。 94 | 95 | ### 高级定制 (Advanced Customization) 96 | #### 上下文 (Context) 97 | 模板可用的上下文: 98 | 99 | - `allowed_methods`:资源允许的方法列表 100 | - `api_settings`:API设置 101 | - `available_formats`:资源允许的格式列表 102 | - `breadcrumblist`:嵌套资源链后面的链接列表 103 | - `content`:API 响应的内容 104 | - `description`:从文档字符串生成的资源的描述 105 | - `name`:资源的名称 106 | - `post_form`:POST 表单使用的表单实例 (如果允许的话) 107 | - `put_form`:PUT 表单使用的表单实例 (如果允许的话) 108 | - `display_edit_forms`:一个布尔值,指示是否显示 POST,PUT 和 PATCH 表单 109 | - `request`:请求对象 110 | - `response`:响应对象 111 | - `version`:Django REST Framework 的版本 112 | - `view`:处理请求的视图 113 | - `FORMAT_PARAM`:视图可以接受格式覆盖 114 | - `METHOD_PARAM`:视图可以接受方法覆盖 115 | 116 | 您可以覆盖 `BrowsableAPIRenderer.get_context()` 方法以自定义传递给模板的上下文。 117 | 118 | #### 不使用 base.html (Not using base.html) 119 | 对于更高级的定制,例如没有 Bootstrap 基础或者没有与站点其他部分更紧密的集成,您可以简单地选择不使用 `api.html` 扩展 `base.html`。然后页面内容和功能完全取决于您。 120 | 121 | #### 使用大量项处理 `ChoiceField` (Handling `ChoiceField` with large numbers of items.) 122 | 当关系或 `ChoiceField` 包含太多项时,渲染包含所有选项的小部件可能会变得非常慢,并导致可浏览的 API 渲染性能不佳。 123 | 124 | 在本例中,最简单的选项是用标准文本输入替换选择输入。例如: 125 | ```python 126 | author = serializers.HyperlinkedRelatedField( 127 | queryset=User.objects.all(), 128 | style={'base_template': 'input.html'} 129 | ) 130 | ``` 131 | 132 | #### 自动完成 (Autocomplete) 133 | 另一个更复杂的选项是用自动完成小部件替换输入,该小部件仅根据需要加载和渲染可用选项的子集。如果您需要这样做,您需要做一些工作来自己构建自定义自动完成 HTML 模板。 134 | 135 | [自动完成小部件有多种软件包](https://www.djangopackages.com/grids/g/auto-complete/),例如 [django-autocomplete-light](https://github.com/yourlabs/django-autocomplete-light),您可能需要参考这些软件包。注意,您不能简单地将这些组件作为标准小部件包含,但是需要显式地编写 HTML 模板。这是因为 REST framework 3.0 不再支持 `widget` 关键字参数,因为它现在使用模板化 HTML 生成。 136 | 137 | *** 138 | -------------------------------------------------------------------------------- /Tutorial/Authentication-and-Permissions.md: -------------------------------------------------------------------------------- 1 | # 教程 4:认证和权限 2 | 目前我们的 API 对谁可以编辑或删除 snippet 代码没有任何限制。我们希望有一些更高级的行为来确保: 3 | 4 | - snippets 代码始终与创建者相关联。 5 | - 只有经过身份验证的用户可以创建 snippets。 6 | - 只有 snippets 的创建者可以更新或删除它。 7 | - 未经身份验证的请求应具有完全只读访问权限。 8 | 9 | ## 将信息添加到我们的模型 10 | 我们将对 `Snippet` 模型类进行一些更改。首先,我们添加几个字段。其中一个字段将用于表示创建 snippet 代码的用户。另一个字段将用于存储高亮显示的 HTML 内容表示的代码。 11 | 12 | 将以下两个字段添加到 `models.py` 文件中的 `Snippet` 模型中。 13 | ```python 14 | class Snippet(models.Model): 15 | created = models.DateTimeField(auto_now_add=True) 16 | title = models.CharField(max_length=100, blank=True, default='') 17 | code = models.TextField() 18 | linenos = models.BooleanField(default=False) # 是否显示行号 19 | language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100) 20 | style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100) 21 | owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE) 22 | highlighted = models.TextField() 23 | ``` 24 | 我们还需要确保在保存模型时,使用 `pygments` 代码高亮库填充 highlighted 字段。 25 | 26 | 我们需要导入额外的模块: 27 | ```python 28 | from pygments.lexers import get_lexer_by_name 29 | from pygments.formatters.html import HtmlFormatter 30 | from pygments import highlight 31 | ``` 32 | 现在我们可以在我们的模型类中添加一个 `.save()` 方法: 33 | ```python 34 | def save(self, *args, **kwargs): 35 | """ 36 | 使用 pygments 库创建一个高亮显示的 HTML 表示的 snippet 代码。 37 | """ 38 | lexer = get_lexer_by_name(self.language) 39 | linenos = 'table' if self.linenos else False 40 | options = {'title': self.title} if self.title else {} 41 | formatter = HtmlFormatter(style=self.style, linenos=linenos, full=True, **options) 42 | self.highlighted = highlight(self.code, lexer, formatter) 43 | super(Snippet, self).save(*args, **kwargs) 44 | ``` 45 | 完成这些工作后,我们需要更新我们的数据库表。通常这种情况我们会创建一个数据库迁移来实现这一点,但作为教程的目的,让我们删除数据库并重新开始。 46 | ```python 47 | rm -f db.sqlite3 48 | rm -r snippets/migrations 49 | python manage.py makemigrations snippets 50 | python manage.py migrate 51 | ``` 52 | 您可能还想创建几个不同的用户,以用于测试 API。最快捷的方法是使用 `createsuperuser` 命令。 53 | ```python 54 | python manage.py createsuperuser 55 | ``` 56 | 57 | ## 为我们的用户模型添加端点 58 | 现在我们有一些用户可以使用,我们最好将这些用户的表示添加到我们的 API 中。创建一个新的序列化器很简单,在 `serializers.py` 文件中添加: 59 | ```python 60 | from django.contrib.auth.models import User 61 | 62 | class UserSerializer(serializers.ModelSerializer): 63 | snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all()) 64 | 65 | class Meta: 66 | model = User 67 | fields = ('id', 'username', 'snippets') 68 | ``` 69 | 由于 `'snippets'` 在用户模型上是反向关系,当在使用 `ModelSerializer` 类时,它将不会被默认包含,所以我们需要为它添加一个显式字段。 70 | 71 | 我们还会在 `views.py` 中添加几个视图。我们想要使用用户表示的只读视图,所以我们将使用 `ListAPIView` 和 `RetrieveAPIView` 通用的基于类的视图。 72 | ```python 73 | from django.contrib.auth.models import User 74 | 75 | 76 | class UserList(generics.ListAPIView): 77 | queryset = User.objects.all() 78 | serializer_class = UserSerializer 79 | 80 | 81 | class UserDetail(generics.RetrieveAPIView): 82 | queryset = User.objects.all() 83 | serializer_class = UserSerializer 84 | ``` 85 | 确保也导入了 `UserSerializer` 类 86 | ```python 87 | from snippets.serializers import UserSerializer 88 | ``` 89 | 最后,我们需要通过在 URL conf 中引用这些视图来将这些视图添加到 API 中。将以下内容添加到 `urls.py` 中的模式中。 90 | ```python 91 | url(r'^users/$', views.UserList.as_view()), 92 | url(r'^users/(?P[0-9]+)/$', views.UserDetail.as_view()), 93 | ``` 94 | 95 | ## 将 Snippets 和用户关联 96 | 现在,如果我们创建了一个 snippet 代码,那么无法将创建 snippet 的用户与 snippet 实例关联起来。用户不是作为序列化表示的一部分发送的,而是作为传入请求的属性。 97 | 98 | 我们处理方式是在我们的 snippet 视图上覆盖 `.perform_create()` 方法,这允许我们修改实例保存的管理方式,并处理传入请求或请求的 URL 中隐含的任何信息。 99 | 100 | 在 `SnippetList` 视图类中(`snippets/views.py`),添加以下方法: 101 | ```python 102 | def perform_create(self, serializer): 103 | serializer.save(owner=self.request.user) 104 | ``` 105 | 现在我们的序列化器的 `create()` 方法将被传递一个附加的 `'owner'` 字段以及来自请求的验证数据。 106 | 107 | ## 更新我们的序列化器 108 | 现在 snippets 与创建它们的用户相关联了,让我们更新我们的 `SnippetSerializer` 以体现这一点。将以下字段添加到 `serializers.py` 中的序列化器的定义中: 109 | ```python 110 | owner = serializers.ReadOnlyField(source='owner.username') 111 | ``` 112 | 注意:确保您还将 `'owner'` 添加到内部 `Meta` 类的字段列表中。 113 | 114 | 这个字段正在做一件很有趣的事情。`source` 参数控制哪个属性用于填充字段,并且可以指向序列化实例上的任何属性。它也可以采用如上所示的点符号(.),在这种情况下,它将以与 Django 模板语言相似的方式遍历给定的属性。 115 | 116 | 我们添加的字段是无类型的 `ReadOnlyField` 类,与其他类型的字段相反,如 `CharField`,`BooleanField` 等...无类型的 `ReadOnlyField` 始终是只读的,只能用于序列化表示,但不能用于在反序列化时更新模型实例。我们在这里也可以使用 `CharField(read_only=True)`。 117 | 118 | ## 添加视图所需的权限 119 | 现在,snippets 和用户相关联,我们希望确保只有经过身份验证的用户才能创建,更新和删除 snippets。 120 | 121 | REST framework 包含许多权限类,我们可以用来限制谁可以访问给定的视图。在这种情况下,我们需要的是 `IsAuthenticatedOrReadOnly` 类,它将确保经过身份验证的请求获得读写访问权限,未经身份验证的请求将获得只读访问权限。 122 | 123 | 首先要在视图模块中导入以下内容 124 | ```python 125 | from rest_framework import permissions 126 | ``` 127 | 然后,将以下属性添加到 `SnippetList` 和 `SnippetDetail` 视图类中。 128 | ```python 129 | permission_classes = (permissions.IsAuthenticatedOrReadOnly,) 130 | ``` 131 | 132 | ## 添加登录到可浏览 API 133 | 如果您现在打开浏览器并导航到可浏览的 API,那么您将发现无法再创建新的 snippets。为了做到这一点,我们需要能够以用户身份登录。 134 | 135 | 我们可以通过编辑项目级 `urls.py` 文件(`tutorial/urls.py`)中的 URLconf 来添加可浏览 API 的登录视图。 136 | 137 | 在文件顶部添加以下导入: 138 | ```python 139 | from django.conf.urls import include 140 | ``` 141 | 并且,在文件末尾添加一个模式以包括可浏览的 API 的登录和注销视图。 142 | ```python 143 | urlpatterns = [ 144 | url(r'^api-auth/', include('rest_framework.urls')), 145 | ] 146 | ``` 147 | 模式的 `r'^api-auth/'` 部分实际上可以是你要使用的任何 URL。 148 | 149 | 现在,如果您再次打开浏览器并刷新页面,则会在页面右上方看到一个“登录”链接。如果您用前面创建的用户登录,就可以再次创建 snippets。 150 | 151 | 一旦创建了一些 snippets 后,导航到 `'/users/'` 端点,并注意到在每个用户的 'snippets' 字段中包含与每个用户相关联的 snippet id 的列表。 152 | 153 | ## 对象级权限 154 | 实际上,我们希望所有人都可以看到所有 snippets,但也要确保只有创建 snippet 的用户才能更新或删除它。 155 | 156 | 为此,我们需要创建一个自定义权限。 157 | 158 | 在 snippets 应用中,创建一个新文件 `permissions.py` 159 | ```python 160 | from rest_framework import permissions 161 | 162 | 163 | class IsOwnerOrReadOnly(permissions.BasePermission): 164 | """ 165 | 自定义权限只允许对象的所有者编辑它。 166 | """ 167 | def has_object_permission(self, request, view, obj): 168 | # 读取权限被允许用于任何请求, 169 | # 所以我们始终允许 GET,HEAD 或 OPTIONS 请求。 170 | if request.method in permissions.SAFE_METHODS: 171 | return True 172 | 173 | # 写入权限只允许给 snippet 的所有者。 174 | return obj.owner == request.user 175 | ``` 176 | 现在我们可以通过编辑 `SnippetDetail` 视图类中的 `permission_classes` 属性来将自定义权限添加到我们的snippet 实例端点: 177 | ```python 178 | permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,) 179 | ``` 180 | 确保要先导入 `IsOwnerOrReadOnly` 类。 181 | ```python 182 | from snippets.permissions import IsOwnerOrReadOnly 183 | ``` 184 | 现在,如果再次打开浏览器,你会发现 “DELETE” 和 “PUT” 操作只出现在以 snippet 创建者的身份登录的 snippet 实例端点上。 185 | 186 | ## 使用 API 进行身份验证 187 | 因为现在我们在 API 上有一组权限,如果我们想要编辑任何 snippet,我们需要验证我们的请求。我们还没有设置任何认证类,因此当前应用的是默认的 `SessionAuthentication` 和 `BasicAuthentication`。 188 | 189 | 当我们通过 Web 浏览器与 API 进行交互时,我们可以登录,然后浏览器会话将为请求提供所需的身份验证。 190 | 191 | 如果我们以编程方式与 API 进行交互,那么我们需要在每个请求上明确提供身份验证凭据。 192 | 193 | 如果我们尝试创建一个没有进行身份验证的 snippet,我们会得到一个错误: 194 | ```python 195 | (venv) fangweiren@ubuntu:~/Django_REST_framework$ http POST http://127.0.0.1:8000/snippets/ code="print 123" 196 | HTTP/1.1 403 Forbidden 197 | Allow: GET, HEAD, OPTIONS 198 | Content-Length: 58 199 | Content-Type: application/json 200 | Date: Sun, 17 Jun 2018 14:56:36 GMT 201 | Server: WSGIServer/0.2 CPython/3.5.2 202 | Vary: Accept, Cookie 203 | X-Frame-Options: SAMEORIGIN 204 | 205 | { 206 | "detail": "Authentication credentials were not provided." 207 | } 208 | ``` 209 | 我们可以通过添加我们之前创建的用户的用户名和密码来发送成功的请求。 210 | ```python 211 | (venv) fangweiren@ubuntu:~/Django_REST_framework$ http -a djrest:aa112211 POST http://127.0.0.1:8000/snippets/ code="print 789" 212 | HTTP/1.1 201 Created 213 | Allow: GET, POST, HEAD, OPTIONS 214 | Content-Length: 110 215 | Content-Type: application/json 216 | Date: Mon, 18 Jun 2018 14:55:44 GMT 217 | Server: WSGIServer/0.2 CPython/3.5.2 218 | Vary: Accept, Cookie 219 | X-Frame-Options: SAMEORIGIN 220 | 221 | { 222 | "code": "print 789", 223 | "id": 4, 224 | "language": "python", 225 | "linenos": false, 226 | "owner": "djrest", 227 | "style": "friendly", 228 | "title": "" 229 | } 230 | ``` 231 | 232 | ## 总结 233 | 我们现在已经在我们的 Web API 上获得了相当精细的一组权限集合,并为系统用户和他们创建的 snippet 提供了终点。 234 | 235 | 在[本教程的第 5 部分](http://www.iamnancy.top/post/171/)中,我们将介绍如何通过为高亮显示 snippet 创建 HTML 端点来将所有内容联结在一起,并通过对系统内的关系使用超链接来提高 API 的凝聚力。 236 | -------------------------------------------------------------------------------- /Tutorial/Class-based-views.md: -------------------------------------------------------------------------------- 1 | # 教程 3:基于类的视图 2 | 我们也可以使用基于类的视图来编写 API 视图,而不是基于函数的视图。正如我们将看到的,这是一个强大的模式,可以让我们重用通用功能,并帮助我们保持代码 [DRY](https://en.wikipedia.org/wiki/Don't_repeat_yourself)。 3 | 4 | ## 使用基于类的视图重写我们的 API 5 | 我们将首先将基于类的视图重写根视图。所有这些都涉及一些对 `views.py` 的重构。 6 | ```python 7 | class SnippetList(APIView): 8 | """ 9 | 列出所有的代码 snippet,或创建一个新的 snippet。 10 | """ 11 | def get(self, request, format=None): 12 | snippets = Snippet.objects.all() 13 | serializer = SnippetSerializer(snippets, many=True) 14 | return Response(serializer.data) 15 | 16 | def post(self, request, format=None): 17 | serializer = SnippetSerializer(data=request.data) 18 | if serializer.is_valid(): 19 | serializer.save() 20 | return Response(serializer.data, status=status.HTTP_201_CREATED) 21 | return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 22 | ``` 23 | 到现在为止还挺好。它看起来与以前的情况非常相似,但是我们在不同的 HTTP 方法之间有更好的分离。我们还需要更新 `views.py` 中的实例视图。 24 | ```python 25 | class SnippetDetail(APIView): 26 | """ 27 | 获取,更新或删除一个代码 snippet 28 | """ 29 | def get_object(self, pk): 30 | try: 31 | return Snippet.objects.get(pk=pk) 32 | except Snippet.DoesNotExist: 33 | raise Http404 34 | 35 | def get(self, request, pk, format=None): 36 | snippet = self.get_object(pk) 37 | serializer = SnippetSerializer(snippet) 38 | return Response(serializer.data) 39 | 40 | def put(self, request, pk, format=None): 41 | snippet = self.get_object(pk) 42 | serializer = SnippetSerializer(snippet, data=request.data) 43 | if serializer.is_valid(): 44 | serializer.save() 45 | return Response(serializer.data) 46 | return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 47 | 48 | def delete(self, request, pk, format=None): 49 | snippet = self.get_object(pk) 50 | snippet.delete() 51 | return Response(status=status.HTTP_204_NO_CONTENT) 52 | ``` 53 | 看起来不错,它现在仍然非常类似于基于函数的视图。 54 | 55 | 使用基于类的视图,我们现在还需要稍微重构 `snippets/urls.py`。 56 | ```python 57 | from django.conf.urls import url 58 | from rest_framework.urlpatterns import format_suffix_patterns 59 | from snippets import views 60 | 61 | 62 | urlpatterns = [ 63 | url(r'^snippets/$', views.SnippetList.as_view()), 64 | url(r'^snippets/(?P[0-9]+)/$', views.SnippetDetail.as_view()), 65 | ] 66 | 67 | urlpatterns = format_suffix_patterns(urlpatterns) 68 | ``` 69 | 好的,我们完成了。 如果你运行开发服务器,一切都应该像以前一样工作。 70 | 71 | ## 使用混合 (mixins) 72 | 使用基于类的视图的优势之一是我们可以很容易撰写可重复使用的行为。 73 | 74 | 到目前为止,我们使用的创建/获取/更新/删除操作和我们创建的任何基于模型的 API 视图非常相似。这些常见的行为是在 REST framework 的 mixin 类中实现的。 75 | 76 | 让我们来看看我们是如何通过使用 mixin 类编写视图的。这是我们的 `snippets/views.py` 模块。 77 | ```python 78 | from snippets.models import Snippet 79 | from snippets.serializers import SnippetSerializer 80 | from rest_framework import mixins 81 | from rest_framework import generics 82 | 83 | 84 | class SnippetList(mixins.ListModelMixin, 85 | mixins.CreateModelMixin, 86 | generics.GenericAPIView): 87 | queryset = Snippet.objects.all() 88 | serializer_class = SnippetSerializer 89 | 90 | def get(self, request, *args, **kwargs): 91 | return self.list(request, *args, **kwargs) 92 | 93 | def post(self, request, *args, **kwargs): 94 | return self.create(request, *args, **kwargs) 95 | ``` 96 | 我们将花一点时间仔细检查这里发生了什么。我们使用 `GenericAPIView` 构建了视图,并且用上了 `ListModelMixin` 和 `CreateModelMixin`。 97 | 98 | 基类提供核心功能,而 mixin 类提供 `.list()` 和 `.create()` 操作。然后我们明确地将 `get` 和 `post` 方法绑定到适当的操作。到目前为止显而易见。 99 | ```python 100 | class SnippetDetail(mixins.RetrieveModelMixin, 101 | mixins.UpdateModelMixin, 102 | mixins.DestroyModelMixin, 103 | generics.GenericAPIView): 104 | queryset = Snippet.objects.all() 105 | serializer_class = SnippetSerializer 106 | 107 | def get(self, request, *args, **kwargs): 108 | return self.retrieve(request, *args, **kwargs) 109 | 110 | def put(self, request, *args, **kwargs): 111 | return self.update(request, *args, **kwargs) 112 | 113 | def delete(self, request, *args, **kwargs): 114 | return self.destroy(request, *args, **kwargs) 115 | ``` 116 | 非常相似。我们再次使用 `GenericAPIView` 类来提供核心功能,并添加 mixins 来提供 `.retrieve()`,`.update()` 和 `.destroy()` 操作。 117 | 118 | ## 使用通用的基于类的视图 119 | 我们使用 mixin 类,使用比以前稍少的代码重写了视图,但我们可以更进一步。REST framework 提供了一套已经混合的通用视图,我们可以简化 `views.py `模块。 120 | ```python 121 | from snippets.models import Snippet 122 | from snippets.serializers import SnippetSerializer 123 | from rest_framework import generics 124 | 125 | 126 | class SnippetList(generics.ListCreateAPIView): 127 | queryset = Snippet.objects.all() 128 | serializer_class = SnippetSerializer 129 | 130 | 131 | class SnippetDetail(generics.RetrieveUpdateDestroyAPIView): 132 | queryset = Snippet.objects.all() 133 | serializer_class = SnippetSerializer 134 | ``` 135 | 哇,非常简洁。我们可以使用很多现成的代码,让我们的代码看起来非常清晰、简洁,惯用的 Django。 136 | 137 | 接下来,我们将转到[本教程的第 4 部分](http://www.iamnancy.top/post/170/),我们将介绍如何处理 API 的身份验证和权限。 138 | -------------------------------------------------------------------------------- /Tutorial/Quickstart.md: -------------------------------------------------------------------------------- 1 | # 环境 2 | Python 3.5.2 3 | Django 2.0.6 4 | djangorestframework 3.8.2 5 | httpie 0.9.9 6 | 7 | # 快速开始 8 | 我们将创建一个简单的 API 来允许管理员用户查看和编辑系统中的用户和组。 9 | 10 | ## 项目设置 11 | 创建一个名为 `tutorial` 的新 Django 项目,然后创建一个名为 `quickstart` 的新应用。 12 | ```python 13 | # 创建项目目录 14 | mkdir tutorial 15 | cd tutorial 16 | 17 | # 创建 virtualenv 环境来隔离我们本地的包依赖关系 18 | virtualenv env 19 | source env/bin/activate # 在 Windows 上使用 `env\Scripts\activate` 20 | 21 | # 在创建的虚拟环境中安装 Django 和 Django REST framework 22 | pip install django 23 | pip install djangorestframework 24 | 25 | # 创建一个新项目,并只有一个应用 26 | django-admin.py startproject tutorial . # Note the trailing '.' character 27 | cd tutorial 28 | django-admin.py startapp quickstart 29 | cd .. 30 | ``` 31 | 项目布局应该如下所示: 32 | ```python 33 | (venv) fang@ubuntu:~/tutorial$ pwd 34 | /home/fang/tutorial 35 | (venv) fang@ubuntu:~/tutorial$ find . 36 | ... 37 | ./manage.py 38 | ./tutorial 39 | ./tutorial/urls.py 40 | ./tutorial/wsgi.py 41 | ./tutorial/settings.py 42 | ./tutorial/quickstart 43 | ./tutorial/quickstart/apps.py 44 | ./tutorial/quickstart/tests.py 45 | ./tutorial/quickstart/migrations 46 | ./tutorial/quickstart/migrations/__init__.py 47 | ./tutorial/quickstart/models.py 48 | ./tutorial/quickstart/admin.py 49 | ./tutorial/quickstart/views.py 50 | ./tutorial/quickstart/__init__.py 51 | ./tutorial/__init__.py 52 | ``` 53 | 在项目目录中创建应用程序可能看起来很不寻常。使用项目的名称空间避免了与外部模块的名称冲突(话题超出了快速入门的范围)。 54 | 55 | 现在第一次同步您的数据库: 56 | ```python 57 | (venv) fang@ubuntu:~/tutorial$ python manage.py migrate 58 | ``` 59 | 我们还将创建一个名为 `admin` 的初始用户,其密码为 `password123`。稍后在示例中,我们将验证该用户。 60 | ```python 61 | (venv) fang@ubuntu:~/tutorial$ python manage.py createsuperuser --email admin@example.com --username admin 62 | ``` 63 | 一旦你建立了一个数据库并创建了初始用户并且准备就绪,打开应用程序的目录,我们开始编码...... 64 | 65 | ## 序列化 66 | 首先我们要定义一些序列化器。我们来创建一个名为 `tutorial/quickstart/serializers.py` 的新模块,我们将用它来表示数据。 67 | ```python 68 | from django.contrib.auth.models import User, Group 69 | from rest_framework import serializers 70 | 71 | 72 | class UserSerializer(serializers.HyperlinkedModelSerializer): 73 | class Meta: 74 | model = User 75 | fields = ('url', 'username', 'email', 'groups') 76 | 77 | 78 | class GroupSerializer(serializers.HyperlinkedModelSerializer): 79 | class Meta: 80 | model = Group 81 | fields = ('url', 'name') 82 | ``` 83 | 请注意,在本例中我们用到了超链接关系,使用 `HyperlinkedModelSerializer`。您还可以使用主键和各种其他关系,但超链接是很棒的 RESTful 设计。 84 | 85 | ## 视图 86 | 好了,我们最好写一些视图。打开 `tutorial/quickstart/views.py` 并输入。 87 | ```python 88 | from django.contrib.auth.models import User, Group 89 | from rest_framework import viewsets 90 | from tutorial.quickstart.serializers import UserSerializer, GroupSerializer 91 | 92 | 93 | class UserViewSet(viewsets.ModelViewSet): 94 | """ 95 | 允许用户查看或编辑的 API 端点。 96 | """ 97 | queryset = User.objects.all().order_by('-date_joined') 98 | serializer_class = UserSerializer 99 | 100 | 101 | class GroupViewSet(viewsets.ModelViewSet): 102 | """ 103 | 允许组查看或编辑的 API 端点。 104 | """ 105 | queryset = Group.objects.all() 106 | serializer_class = GroupSerializer 107 | ``` 108 | 我们将所有通用行为分组到 `ViewSets` 类中,而不是编写多个视图。 109 | 110 | 如果需要,我们可以轻松地将这些视图分解为单个视图,但使用视图集可以使视图逻辑组织得非常好,并且非常简洁。 111 | 112 | ## URLs 113 | 好的,现在让我们配置 API 的 URL。在 `tutorial / urls.py` 上... 114 | ``` 115 | from django.conf.urls import url, include 116 | from rest_framework import routers 117 | 118 | from tutorial.quickstart import views 119 | 120 | router = routers.DefaultRouter() 121 | router.register(r'users', views.UserViewSet) 122 | router.register(r'group', views.GroupViewSet) 123 | 124 | # 使用自动 URL 路由连接 API。 125 | # 另外,我们还包括可浏览 API 的登录 URL。 126 | urlpatterns = [ 127 | url(r'^', include(router.urls)), 128 | url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) 129 | ] 130 | ``` 131 | 因为我们使用视图集而不是视图,所以我们可以通过简单地向路由器类注册视图集来自动为我们的 API 生成 URL conf。 132 | 133 | 同样,如果我们需要更多地控制 API URL,我们可以简单地使用常规的基于类的视图,并明确地编写 URL conf。 134 | 135 | 最后,我们将包括默认的登录和注销视图,以用于可浏览的 API。这是可选的,但如果您的 API 需要身份验证并且您想使用可浏览的 API,则会很有用。 136 | 137 | ## 设置 138 | 将 `rest_framework` 添加到 `INSTALLED_APPS`。设置模块将位于 `tutorial/settings.py` 中 139 | ```python 140 | INSTALLED_APPS = [ 141 | 'django.contrib.admin', 142 | 'django.contrib.auth', 143 | 'django.contrib.contenttypes', 144 | 'django.contrib.sessions', 145 | 'django.contrib.messages', 146 | 'django.contrib.staticfiles', 147 | 'rest_framework', 148 | ] 149 | ``` 150 | 好的,我们完成了。 151 | 152 | ## 测试我们的 API 153 | 我们现在准备测试我们构建的 API。让我们从命令行启动服务器。 154 | ```python 155 | $ python manage.py runserver 156 | ``` 157 | 我们现在可以从命令行使用 curl 等工具访问我们的 API ... 158 | ```python 159 | (venv) fang@ubuntu:~/tutorial$ curl -H 'Accept: application/json; indent=4' -u admin:password123 http://127.0.0.1:8000/users/ 160 | [ 161 | { 162 | "url": "http://127.0.0.1:8000/users/2/", 163 | "username": "tom", 164 | "email": "atom@example.com", 165 | "groups": [] 166 | }, 167 | { 168 | "url": "http://127.0.0.1:8000/users/1/", 169 | "username": "admin", 170 | "email": "admin@example.com", 171 | "groups": [] 172 | } 173 | ``` 174 | 或者命令行工具 `httpie` ... 175 | ```python 176 | (venv) fang@ubuntu:~/tutorial$ http -a admin:password123 http://127.0.0.1:8000/users/ 177 | HTTP/1.1 200 OK 178 | Allow: GET, POST, HEAD, OPTIONS 179 | Content-Length: 198 180 | Content-Type: application/json 181 | Date: Mon, 25 Jun 2018 14:22:19 GMT 182 | Server: WSGIServer/0.2 CPython/3.5.2 183 | Vary: Accept, Cookie 184 | X-Frame-Options: SAMEORIGIN 185 | 186 | [ 187 | { 188 | "email": "atom@example.com", 189 | "groups": [], 190 | "url": "http://127.0.0.1:8000/users/2/", 191 | "username": "tom" 192 | }, 193 | { 194 | "email": "admin@example.com", 195 | "groups": [], 196 | "url": "http://127.0.0.1:8000/users/1/", 197 | "username": "admin" 198 | } 199 | ] 200 | ``` 201 | 或者直接通过浏览器访问 URL `http://127.0.0.1:8000/users/` ... 202 | ![f_38619336.png](http://pan.16mb.com/data/f_38619336.png) 203 | 204 | 如果你正在使用浏览器访问,请确保从右上角的进行登录。 205 | 206 | 太棒了,那很容易! 207 | 208 | 如果您想更深入地了解 REST framework 是如何通过[本教程](http://www.iamnancy.top/post/167/)组合在一起的,或者开始浏览 [API 指南](http://www.django-rest-framework.org/#api-guide)。 209 | -------------------------------------------------------------------------------- /Tutorial/Relationships-and-Hyperlinked-APIs.md: -------------------------------------------------------------------------------- 1 | # 教程 5:关系和超链接 API 2 | 目前我们的 API 中的关系是通过使用主键来表示。在本教程的这一部分中,我们将改进 API 的内聚力和可发现性,而不是使用超链接来进行关系。 3 | 4 | ## 为我们的 API 的根地址创建端点 5 | 现在我们有 'snippets' 和 'users' 端点,但我们没有一个指向我们 API 的入口。要创建一个,我们将使用基于函数的常规视图和我们前面介绍的 `@api_view` 装饰器。在你的 `snippets / views.py` 中添加: 6 | ```python 7 | from rest_framework.decorators import api_view 8 | from rest_framework.response import Response 9 | from rest_framework.reverse import reverse 10 | 11 | 12 | @api_view(['GET']) 13 | def api_root(request, format=None): 14 | return Response({ 15 | 'users': reverse('user-list', request=request, format=format), 16 | 'snippets': reverse('snippet-list', request=request, format=format) 17 | }) 18 | ``` 19 | 这里应该注意两件事。首先,我们使用 REST framework 的 reverse 函数来返回完全限定的URL;其次,URL 模式通过我们稍后将在我们的 `snippets/urls.py` 中声明的便利名称进行标识。 20 | 21 | ## 为高亮显示 snippets 创建端点 22 | 另一个明显的事情是我们的 pastebin API 仍然缺少高亮显示代码的端点。 23 | 24 | 与所有其他 API 端点不同,我们不想使用 JSON,而只是呈现 HTML 表示。REST framework 提供了两种 HTML 渲染器,一种是使用模板来处理渲染的 HTML,另一种是处理预渲染的 HTML。第二个渲染器是我们要用于此端点的渲染器。 25 | 26 | 在创建代码高亮显示视图时,我们需要考虑的另一件事是不存在我们可以使用的具体的通用视图。我们不是返回一个对象实例,而是返回一个对象实例的属性。 27 | 28 | 我们将使用基类来表示实例,并创建我们自己的 `.get()` 方法,而不是使用具体的通用视图。在您的 `snippets/views.py` 中添加: 29 | ```python 30 | from rest_framework import renderers 31 | from rest_framework.response import Response 32 | 33 | class SnippetHighlight(generics.GenericAPIView): 34 | queryset = Snippet.objects.all() 35 | renderer_classes = (renderers.StaticHTMLRenderer,) 36 | 37 | def get(self, request, *args, **kwargs): 38 | snippet = self.get_object() 39 | return Response(snippet.highlighted) 40 | ``` 41 | 像往常一样,我们需要将我们创建的新视图添加到 URLconf 中。我们将在 `snippets/urls.py` 中为我们的新 API 根路径添加一个 url 模式: 42 | ```python 43 | url(r'^$', views.api_root), 44 | ``` 45 | 然后为高亮 snippet 添加一个 url 模式: 46 | ```python 47 | url(r'^snippets/(?P[0-9]+)/highlight/$', views.SnippetHighlight.as_view()), 48 | ``` 49 | 50 | ## 超链接我们的 API 51 | 处理实体之间的关系是 Web API 设计中更具挑战性的方面之一。这里有一些我们可能会选择代表关系的不同方法: 52 | 53 | - 使用主键。 54 | - 在实体之间使用超链接。 55 | - 在相关实体上使用唯一的标识字段。 56 | - 使用默认字符串代表相关实体。 57 | - 将相关实体嵌套在父代表中。 58 | - 一些其他自定义表示。 59 | 60 | REST framework 支持所有这些样式,并且可以在正向或反向关系中应用它们,或者通过自定义管理器 (如通用外键) 应用它们。 61 | 62 | 在这种情况下,我们希望在实体之间使用超链接样式。为了做到这一点,我们将修改我们的序列化器来扩展 `HyperlinkedModelSerializer`,而不是现有的 `ModelSerializer`。 63 | 64 | `HyperlinkedModelSerializer` 与 `ModelSerializer` 有以下区别: 65 | 66 | - 它默认不包含 id 字段。 67 | - 它包含一个 `url` 字段,使用 `HyperlinkedIdentityField`。 68 | - 关系使用 `HyperlinkedRelatedField`,而不是 `PrimaryKeyRelatedField`。 69 | 70 | 我们可以轻松地重写现有的序列化器来使用超链接。在你的 `snippets/serializers.py` 中添加: 71 | ```python 72 | class SnippetSerializer(serializers.HyperlinkedModelSerializer): 73 | owner = serializers.ReadOnlyField(source='owner.username') 74 | highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html') 75 | 76 | class Meta: 77 | model = Snippet 78 | fields = ('url', 'id', 'highlight', 'owner', 79 | 'title', 'code', 'linenos', 'language', 'style') 80 | 81 | 82 | class UserSerializer(serializers.HyperlinkedModelSerializer): 83 | snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True) 84 | 85 | class Meta: 86 | model = User 87 | fields = ('url', 'id', 'username', 'snippets') 88 | ``` 89 | 请注意,我们还添加了一个新的 `'highlight'` 字段。该字段与 `url` 字段类型相同,不同之处在于它指向的是 `'snippet-highlight'` url 模式,而不是 `'snippet-detail'` url 模式。 90 | 91 | 因为我们已经包含了格式后缀的 URL,例如 `'.json'`,我们还需要在 `highlight` 字段上指出任何格式后缀的超链接返回应该使用 `.html'` 后缀。 92 | 93 | ## 确保我们的 URL 模式被命名 94 | 如果我们要有超链接的 API,我们需要确保命名 URL 模式。我们来看看我们需要命名的 URL 模式。 95 | 96 | - 我们的 API 根地址是指 `'user-list'` 和 `'snippet-list'`。 97 | - 我们的 snippet 序列化器包含一个指向 `'snippet-highlight'` 的字段。 98 | - 我们的 user 序列化器包含一个指向 `'snippet-detail'` 的字段。 99 | - 我们的 snippet 和 user 序列化器包括 `'url'` 字段,默认情况下将指向 `'{model_name}-detail'`,在这个例子中就是 `'snippet-detail'` 和 `'user-detail'`。 100 | 101 | 将所有这些名称添加到我们的 URLconf 后,我们最后的 `snippets/urls.py` 文件应如下所示: 102 | ```python 103 | from django.conf.urls import url, include 104 | from rest_framework.urlpatterns import format_suffix_patterns 105 | from snippets import views 106 | 107 | urlpatterns = format_suffix_patterns([ 108 | url(r'^$', views.api_root), 109 | url(r'^snippets/$', 110 | views.SnippetList.as_view(), 111 | name='snippet-list'), 112 | url(r'^snippets/(?P[0-9]+)/$', 113 | views.SnippetDetail.as_view(), 114 | name='snippet-detail'), 115 | url(r'^snippets/(?P[0-9]+)/highlight/$', 116 | views.SnippetHighlight.as_view(), 117 | name='snippet-highlight'), 118 | url(r'^users/$', 119 | views.UserList.as_view(), 120 | name='user-list'), 121 | url(r'^users/(?P[0-9]+)/$', 122 | views.UserDetail.as_view(), 123 | name='user-detail') 124 | ]) 125 | ``` 126 | 127 | ## 添加分页 128 | users 和 snippets 的列表视图最终会返回很多实例,所以我们真的要确保对结果进行分页,并允许 API 客户端遍历每个单独的页面。 129 | 130 | 我们可以通过稍微修改 `tutorial/settings.py` 文件来更改默认列表样式以使用分页。添加以下设置: 131 | ```python 132 | REST_FRAMEWORK = { 133 | 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 134 | 'PAGE_SIZE': 10 135 | } 136 | ``` 137 | 请注意,REST framework 中的所有设置都放在一个名为 `REST_FRAMEWORK` 的字典中,这有助于让他们与其他项目设置保持良好的分离。 138 | 139 | 如果需要,我们也可以自定义分页样式,但在这个例子中,我们将一直使用默认设置。 140 | 141 | ## 浏览 API 142 | 如果我们打开浏览器并导航到可浏览的 API,您会发现现在您可以通过简单的链接访问 API。 143 | 144 | 您还可以在 snippet 实例上看到 “highlight” 链接,这会带您跳转到代码高亮显示的 HTML 页面。 145 | 146 | 在[本教程的第 6 部分](http://www.iamnancy.top/post/172/)中,我们将介绍如何使用 ViewSets 和 Routers 来减少构建 API 所需的代码量。 147 | -------------------------------------------------------------------------------- /Tutorial/Requests-and-Responses.md: -------------------------------------------------------------------------------- 1 | # 教程 2:请求和响应 2 | 从现在开始,我们将真正开始接触 REST framework 的核心。 我们来介绍几个基本的构建模块。 3 | 4 | ## 请求对象 (request objects) 5 | REST framework 引入了一个扩展了常规 `HttpRequest` 的 `Request` 对象,并提供了更灵活的请求解析。`Request` 对象的核心功能是 `request.data` 属性,它与 `request.POST` 类似,但对于使用 Web API 更加有用。 6 | ```python 7 | request.POST # 只处理表单数据。 只适用于 'POST' 方法。 8 | request.data # 处理任意数据。 适用于 'POST','PUT' 和 'PATCH' 方法。 9 | ``` 10 | 11 | ## 响应对象 (Response objects) 12 | REST framework 还引入了一个 `Response` 对象,该对象是一种获取未渲染内容的 `TemplateResponse` 类型,并使用内容协商来确定正确内容类型返回给客户端。 13 | ```python 14 | return Response(data) # 渲染成客户端请求的内容类型。 15 | ``` 16 | 17 | ## 状态码 (Status codes) 18 | 在您的视图中使用数字 HTTP 状态码并不总是利于代码的阅读,如果写错代码,很容易被忽略。REST framework 为每个状态码提供更明确的标识符,譬如 `status` 模块中的 `HTTP_400_BAD_REQUEST`。使用它们是一个好主意,而不是使用数字标识符。 19 | 20 | ## 包装 API 视图 (Wrapping API views) 21 | REST framework 提供了两种编写 API 视图的封装。 22 | 23 | 1. 用于基于函数视图的 `@api_view` 装饰器。 24 | 2. 用于基于类视图的 `APIView` 类。 25 | 26 | 这些视图封装提供了一些功能,例如确保你的视图能够接收 `Request` 实例,并将上下文添加到 `Response` 对象,使得内容协商可以正常的运作。 27 | 28 | 视图封装还内置了一些行为,例如在适当的时候返回 `405 Method Not Allowed` 响应,并处理访问错误的输入 `request.data` 时触发的任何 `ParseError` 异常。 29 | 30 | ## 组合在一起 31 | 好的,让我们开始使用这些新的组件来写几个视图。 32 | 33 | 我们不再需要 `views.py` 中 `JSONResponse` 类,所以把它删除掉。然后,我们可以重构我们的视图。 34 | ```python 35 | @api_view(['GET', 'POST']) 36 | def snippet_list(request): 37 | """ 38 | 列出所有的代码 snippet,或创建一个新的 snippet。 39 | """ 40 | if request.method == 'GET': 41 | snippets = Snippet.objects.all() 42 | serializer = SnippetSerializer(snippets, many=True) 43 | return Response(serializer.data) 44 | 45 | elif request.method == 'POST': 46 | serializer = SnippetSerializer(data=request.data) 47 | if serializer.is_valid(): 48 | serializer.save() 49 | return Response(serializer.data, status=status.HTTP_201_CREATED) 50 | return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 51 | ``` 52 | 我们的实例视图比前面的示例有所改进。它稍微简洁一点,现在的代码与我们使用 Forms API 时非常相似。我们还使用了指定的状态码,这使得响应更加明显。 53 | 54 | 以下是 `views.py` 模块中单个 snippet 的视图。 55 | ```python 56 | @api_view(['GET', 'PUT', 'DELETE']) 57 | def snippet_detail(request, pk): 58 | """ 59 | 获取,更新或删除一个代码 snippet 60 | """ 61 | try: 62 | snippet = Snippet.objects.get(pk=pk) 63 | except Snippet.DoesNotExist: 64 | return Response(status=status.HTTP_404_NOT_FOUND) 65 | 66 | if request.method == 'GET': 67 | serializer = SnippetSerializer(snippet) 68 | return Response(serializer.data) 69 | 70 | elif request.method == 'PUT': 71 | serializer = SnippetSerializer(snippet, data=request.data) 72 | if serializer.is_valid(): 73 | serializer.save() 74 | return Response(serializer.data) 75 | return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 76 | 77 | elif request.method == 'DELETE': 78 | snippet.delete() 79 | return Response(status=status.HTTP_204_NO_CONTENT) 80 | ``` 81 | 这对我们来说应该都是非常熟悉的 - 它和正常 Django 视图并没有什么不同。 82 | 83 | 注意,我们不再显式地将请求或响应绑定到给定的内容类型。`request.data` 可以处理传入的 `json` 请求,但它也可以处理其他格式。同样,我们返回带有数据的响应对象,但允许 REST framework 将响应渲染成正确的内容类型。 84 | 85 | ## 为我们的网址添加可选的格式后缀 86 | 为了利用我们的响应不再被硬连接到单个内容类型的事实,让我们将格式后缀的支持添加到我们的API端点。使用格式后缀,为我们提供了明确指向给定格式的 URL,这意味着我们的 API 可以处理一些 URLs,类似这样的格式 http://example.com/api/items/4.json。 87 | 88 | 像下面这样在这两个视图中添加一个 `format` 关键字参数。 89 | ```python 90 | def snippet_list(request, format=None): 91 | ``` 92 | 和 93 | ```python 94 | def snippet_detail(request, pk, format=None): 95 | ``` 96 | 现在更新 `snippet/urls.py` 文件,在现有的 urls 基础上追加一组 `format_suffix_patterns` 。 97 | ```python 98 | from django.conf.urls import url 99 | from rest_framework.urlpatterns import format_suffix_patterns 100 | from snippets import views 101 | 102 | urlpatterns = [ 103 | url(r'^snippets/$', views.snippet_list), 104 | url(r'^snippets/(?P[0-9]+)$', views.snippet_detail), 105 | ] 106 | 107 | urlpatterns = format_suffix_patterns(urlpatterns) 108 | ``` 109 | 我们不一定需要添加这些额外的 url 模式,但它给了我们一个简单,清晰的方式来引用特定的格式。 110 | 111 | ## 它看起来如何? 112 | 继续从命令行测试 API,就像我们在教程第一部分所做的那样。一切都非常类似,尽管我们对一些无效的请求有更好的错误处理。 113 | 114 | 我们可以像以前一样获取所有 snippet 的列表。 115 | ```python 116 | (env) fang@ubuntu:~/django_rest_framework/tutorial$ http http://127.0.0.1:8000/snippets/ 117 | HTTP/1.1 200 OK 118 | Allow: POST, OPTIONS, GET 119 | Content-Length: 317 120 | Content-Type: application/json 121 | Date: Thu, 14 Jun 2018 15:49:40 GMT 122 | Server: WSGIServer/0.2 CPython/3.5.2 123 | Vary: Accept, Cookie 124 | X-Frame-Options: SAMEORIGIN 125 | 126 | [ 127 | { 128 | "code": "foo = \"bar\"\n", 129 | "id": 1, 130 | "language": "python", 131 | "linenos": false, 132 | "style": "friendly", 133 | "title": "" 134 | }, 135 | { 136 | "code": "print \"hello, world\"\n", 137 | "id": 2, 138 | "language": "python", 139 | "linenos": false, 140 | "style": "friendly", 141 | "title": "" 142 | }, 143 | { 144 | "code": "print \"hello, world\"", 145 | "id": 3, 146 | "language": "python", 147 | "linenos": false, 148 | "style": "friendly", 149 | "title": "" 150 | } 151 | ] 152 | ``` 153 | 我们可以通过使用 `Accept` 请求头,来控制我们返回的响应的格式。 154 | ```python 155 | http http://127.0.0.1:8000/snippets/ Accept:application/json # 请求 JSON 156 | http http://127.0.0.1:8000/snippets/ Accept:text/html # 请求 HTML 157 | ``` 158 | 或者通过追加格式后缀: 159 | ```python 160 | http http://127.0.0.1:8000/snippets.json # JSON 后缀 161 | http http://127.0.0.1:8000/snippets.api # 可浏览 API 后缀 162 | ``` 163 | 类似的,我们可以通过 `Content-Type` 请求头来控制我们发送请求的格式。 164 | ```python 165 | # POST 表单数据 166 | (env) fang@ubuntu:~/django_rest_framework/tutorial$ http --form POST http://127.0.0.1:8000/snippets/ code="print 123" 167 | HTTP/1.1 201 Created 168 | Allow: POST, OPTIONS, GET 169 | Content-Length: 93 170 | Content-Type: application/json 171 | Date: Thu, 14 Jun 2018 16:00:37 GMT 172 | Server: WSGIServer/0.2 CPython/3.5.2 173 | Vary: Accept, Cookie 174 | X-Frame-Options: SAMEORIGIN 175 | 176 | { 177 | "code": "print 123", 178 | "id": 4, 179 | "language": "python", 180 | "linenos": false, 181 | "style": "friendly", 182 | "title": "" 183 | } 184 | 185 | # POST JSON 数据 186 | (env) fang@ubuntu:~/django_rest_framework/tutorial$ http --json POST http://127.0.0.1:8000/snippets/ code="print 456" 187 | HTTP/1.1 201 Created 188 | Allow: POST, OPTIONS, GET 189 | Content-Length: 93 190 | Content-Type: application/json 191 | Date: Thu, 14 Jun 2018 16:02:24 GMT 192 | Server: WSGIServer/0.2 CPython/3.5.2 193 | Vary: Accept, Cookie 194 | X-Frame-Options: SAMEORIGIN 195 | 196 | { 197 | "code": "print 456", 198 | "id": 5, 199 | "language": "python", 200 | "linenos": false, 201 | "style": "friendly", 202 | "title": "" 203 | } 204 | ``` 205 | 如果你向上述 `http` 请求中添加 `--debug` ,则可以在请求头中查看请求类型。 206 | 207 | 现在可以在浏览器中访问 `http://127.0.0.1:8000/snippets/` 查看 API 。 208 | 209 | ### 可视化 210 | 由于 API 是基于客户端发起的请求来选择响应内容的格式,因此,当接收到来自浏览器的请求时,会默认以 `HTML` 格式来描述数据。这允许 API 返回网页完全可浏览的 HTML。 211 | 212 | 拥有可浏览的网页 API 是一个巨大的胜利,并且使开发和使用 API 更加容易。它也大大降低了其他开发人员检查和使用 API 的门槛。 213 | 214 | 有关可浏览的 API 功能以及如何对其进行定制的更多信息,请参阅[可浏览的 API](http://www.django-rest-framework.org/topics/browsable-api/)主题。 215 | 216 | ## 下一步是什么? 217 | 在[教程的第三部分](http://www.iamnancy.top/post/169/),我们将开始使用基于类的视图,并查看通用视图如何减少我们需要编写的代码量。 218 | -------------------------------------------------------------------------------- /Tutorial/Schemas-and-client_libraries.md: -------------------------------------------------------------------------------- 1 | # 教程 7:Schemas 和客户端库 2 | schema 是一个机器可读文档,描述可用的 API 端点,它们的 URL 以及它们支持的操作。 3 | 4 | Schemas 可以是自动生成文档的有用工具,也可以用来驱动可以与 API 交互的动态客户端库。 5 | 6 | ## Core API 7 | 为了提供 schema 支持,REST framework 使用 [Core API](www.coreapi.org)。 8 | 9 | Core API 是用于描述 API 的文档规范。它用于提供可用端点的内部表示格式和 API 公开的可能交互。它既可以用于服务器端,也可以用于客户端。 10 | 11 | 当使用服务器端时,Core API 允许 API 支持对各种 schema 或超媒体格式的渲染。 12 | 13 | 当使用客户端时,Core API 允许动态驱动的客户端库,可以与任何公开支持的 schema 或超媒体格式的 API 进行交互。 14 | 15 | ## 添加 schema 16 | REST framework 支持显式定义的 schema 视图或自动生成的 schemas。由于我们使用视图集和路由器,因此我们可以简单地使用自动 schema 生成。 17 | 18 | 您需要安装 `coreapi` Python 包以便包含 API 模式。 19 | ```python 20 | $ pip install coreapi 21 | ``` 22 | 我们现在可以通过在我们的 URL 配置中包含一个自动生成的 schema 视图来为我们的 API 包含schema。 23 | ```python 24 | from rest_framework.schemas import get_schema_view 25 | 26 | schema_view = get_schema_view(title='Pastebin API') 27 | 28 | urlpatterns = [ 29 | url(r'^schema/$', schema_view), 30 | ... 31 | ] 32 | ``` 33 | 如果你在浏览器中访问 /schema/ 端点,那么你现在应该看到 `corejson` 表示成为可选项。 34 | ![f_34764973.png](http://pan.16mb.com/data/f_34764973.png) 35 | 36 | 我们还可以通过在 `Accept` 头中指定所需的内容类型来从命令行请求 schema。 37 | ```python 38 | (env) fang@ubuntu:~/django_rest_framework/tutorial$ http http://127.0.0.1:8000/schema/ Accept:application/coreapi+json 39 | HTTP/1.1 200 OK 40 | Allow: GET, HEAD, OPTIONS 41 | Content-Length: 1893 42 | Content-Type: application/coreapi+json 43 | Date: Wed, 20 Jun 2018 10:44:13 GMT 44 | Server: WSGIServer/0.2 CPython/3.5.2 45 | Vary: Accept, Cookie 46 | X-Frame-Options: SAMEORIGIN 47 | 48 | { 49 | "_meta": { 50 | "title": "Pastebin API", 51 | "url": "http://127.0.0.1:8000/schema/" 52 | }, 53 | "_type": "document", 54 | ... 55 | ``` 56 | 默认的输出风格是使用 `Core JSON` 编码。 57 | 58 | 还支持其他架构格式,如 `Open API` (以前称为Swagger)。 59 | 60 | ## 使用命令行客户端 61 | 现在我们的 API 公开了一个 schema 端点,我们可以使用一个动态客户端库来与 API 进行交互。为了演示这一点,我们使用 Core API 命令行客户端。 62 | 63 | 命令行客户端可用作 `coreapi-cli` 软件包: 64 | ```python 65 | $ pip install coreapi-cli 66 | ``` 67 | 现在检查它在命令行上是否可用... 68 | ```python 69 | (env) fang@ubuntu:~/django_rest_framework/tutorial$ coreapi 70 | Usage: coreapi [OPTIONS] COMMAND [ARGS]... 71 | 72 | Command line client for interacting with CoreAPI services. 73 | 74 | Visit http://www.coreapi.org for more information. 75 | 76 | Options: 77 | --version Display the package version number. 78 | --help Show this message and exit. 79 | 80 | Commands: 81 | action Interact with the active document. 82 | bookmarks Add, remove and show bookmarks. 83 | clear Clear the active document and other state. 84 | codecs Manage the installed codecs. 85 | credentials Configure request credentials. 86 | describe Display description for link at given PATH. 87 | dump Dump a document to console. 88 | get Fetch a document from the given URL. 89 | headers Configure custom request headers. 90 | history Navigate the browser history. 91 | load Load a document from disk. 92 | reload Reload the current document. 93 | show Display the current document. 94 | ``` 95 | 首先,我们将使用命令行客户端加载 API schema。 96 | ```python 97 | (env) fang@ubuntu:~/django_rest_framework/tutorial$ coreapi get http://127.0.0.1:8000/schema/ 98 | 99 | snippets: { 100 | list([page]) 101 | read(id) 102 | highlight(id) 103 | } 104 | users: { 105 | list([page]) 106 | read(id) 107 | } 108 | ``` 109 | 我们还没有进行身份验证,所以现在我们只能看到只读端点,与我们如何设置 API 的权限一致。 110 | 111 | 让我们尝试使用命令行客户端列出现有的 snippets: 112 | ```python 113 | (env) fang@ubuntu:~/django_rest_framework/tutorial$ coreapi action snippets list 114 | { 115 | "count": 4, 116 | "next": null, 117 | "previous": null, 118 | "results": [ 119 | { 120 | "url": "http://127.0.0.1:8000/snippets/1/", 121 | "id": 1, 122 | "highlight": "http://127.0.0.1:8000/snippets/1/highlight/", 123 | "owner": "djrest", 124 | "title": "test one", 125 | "code": "hello, world", 126 | "linenos": false, 127 | "language": "abap", 128 | "style": "abap" 129 | }, 130 | ... 131 | ] 132 | } 133 | ``` 134 | 一些 API 端点需要命名参数。例如,为了要获取特定的高亮 HTML 表示的 snippet,我们需要提供一个 id。 135 | ```python 136 | (env) fang@ubuntu:~/django_rest_framework/tutorial$ coreapi action snippets highlight --param id=1 137 | 139 | 140 | 141 | 142 | test one 143 | 144 | 149 | 150 | 151 |

test one

152 | 153 |
hello, world
154 | 
155 | 156 | 157 | ``` 158 | 159 | ## 认证我们的客户端 160 | 如果我们希望能够创建,编辑和删除 snippets,我们需要进行有效性用户身份验证。在这种情况下,我们只使用基本的 auth。 161 | 162 | 请确保使用您的实际的用户名和密码替换下面的 `` 和 ``。 163 | ```python 164 | (env) fang@ubuntu:~/django_rest_framework/tutorial$ coreapi credentials add 127.0.0.1 djrest:aa112211 --auth basic 165 | Added credentials 166 | 127.0.0.1 "Basic ZGpyZXN0OmFhMTEyMjEx" 167 | ``` 168 | 现在,如果我们再次获取 schema,我们应该能够看到一组完整的可用交互。 169 | ```python 170 | (env) fang@ubuntu:~/django_rest_framework/tutorial$ coreapi reload 171 | 172 | snippets: { 173 | list([page]) 174 | create(code, [title], [linenos], [language], [style]) 175 | read(id) 176 | update(id, code, [title], [linenos], [language], [style]) 177 | partial_update(id, [title], [code], [linenos], [language], [style]) 178 | delete(id) 179 | highlight(id) 180 | } 181 | users: { 182 | list([page]) 183 | read(id) 184 | } 185 | ``` 186 | 我们现在可以与这些端点进行交互。例如,要创建一个新的 snippet: 187 | ```python 188 | (env) fang@ubuntu:~/django_rest_framework/tutorial$ coreapi action snippets create --param title="Example" --param code="print('hello, world')" 189 | { 190 | "url": "http://127.0.0.1:8000/snippets/6/", 191 | "id": 6, 192 | "highlight": "http://127.0.0.1:8000/snippets/6/highlight/", 193 | "owner": "djrest", 194 | "title": "Example", 195 | "code": "print('hello, world')", 196 | "linenos": false, 197 | "language": "python", 198 | "style": "friendly" 199 | } 200 | ``` 201 | 并删除 snippet: 202 | ```python 203 | (env) fang@ubuntu:~/django_rest_framework/tutorial$ coreapi action snippets delete --param id=6 204 | ``` 205 | 除了命令行客户端之外,开发人员还可以使用客户端库与 API 进行交互。Python 客户端库是第一个可用的客户端库,并且计划很快发布 Javascript 客户端库。 206 | 207 | 有关自定义 schema 生成和使用 Core API 客户端库的更多详细信息,您需要参阅完整的文档。 208 | 209 | ## 回顾我们的工作 210 | 拥有非常小的代码量,现在我们有了一个完整的 pastebin Web API,它完全是 Web 可浏览的,包括一个模式驱动的客户端库,并完成了身份验证、每个对象权限和多个渲染器格式。 211 | 212 | 我们已经走过了设计过程的每个步骤,并且看到如果我们需要自定义任何东西,我们都可以按部就班的简单地使用常规的 Django 视图实现。 213 | 214 | 您可以在 GitHub 上查看[最终的教程代码](https://github.com/fangweiren/Django_REST_framework),或者在[沙箱](https://restframework.herokuapp.com/)中试用一个实例。 215 | 216 | ## 勇往直前 217 | 我们已经到达了教程的最后。如果您想更多地参与 REST framework 项目,可以从以下几个地方开始: 218 | 219 | - 通过审查和提交问题,并提出拉取请求,为 [GitHub](https://github.com/encode/django-rest-framework) 做出贡献。 220 | - 加入 [REST framework 讨论组](https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework),并帮助构建社区。 221 | - 在 Twitter 上关注[作者](https://twitter.com/_tomchristie)并打个招呼。 222 | 223 | **现在就去构建非常棒的东西吧。** 224 | -------------------------------------------------------------------------------- /Tutorial/ViewSets-and-Routers.md: -------------------------------------------------------------------------------- 1 | # 教程 6:ViewSets & Routers 2 | REST framework 包括一个用于处理 `ViewSets` 的抽象,它允许开发人员集中精力对 API 的状态和交互进行建模,并保留 URL 结构,根据通用约定自动处理。 3 | 4 | `ViewSet` 类与 `View` 类几乎相同,只是它们提供诸如 `read` 或 `update` 等操作,而不提供诸如 `get` 或 `put` 等方法处理程序。 5 | 6 | 一个 `ViewSet` 类最后时刻只绑定一组方法处理程序,当它被实例化为一组视图时,通常通过使用一个 `Router` 类来处理定义复杂的 url。 7 | 8 | ## 使用 ViewSets 重构 9 | 我们来看看当前的一组视图,并将它们重构为视图集。 10 | 11 | 首先让我们将 `UserList` 和 `UserDetail` 视图重构为单个 `UserViewSet`。我们可以删除这两个视图,并用一个类替换它们: 12 | ```python 13 | class UserViewSet(viewsets.ReadOnlyModelViewSet): 14 | """ 15 | 这个视图集自动提供 `list` 和 `detail` 操作。 16 | """ 17 | queryset = User.objects.all() 18 | serializer_class = UserSerializer 19 | ``` 20 | 这里我们使用了 `ReadOnlyModelViewSet` 类来自动提供默认的 “只读” 操作。我们仍然像我们使用常规视图时一样设置 `queryset` 和 `serializer_class` 属性,但我们不再需要为两个单独的类提供相同的信息。 21 | 22 | 接下来我们将替换 `SnippetList`,`SnippetDetail` 和 `SnippetHighlight` 视图类。我们可以删除这三个视图,并再次用一个类替换它们。 23 | ```python 24 | class SnippetViewSet(viewsets.ModelViewSet): 25 | """ 26 | 这个视图集自动提供 `list`,`create`,`retrieve`,`update`和`destroy`操作。 27 | 28 | 另外我们还提供了一个额外的 `highlight` 操作。 29 | """ 30 | queryset = Snippet.objects.all() 31 | serializer_class = SnippetSerializer 32 | permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,) 33 | 34 | @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer]) 35 | def highlight(self, request, *args, **kwargs): 36 | snippet = self.get_object() 37 | return Response(snippet.highlighted) 38 | 39 | def perform_create(self, serializer): 40 | serializer.save(owner=self.request.user) 41 | ``` 42 | 这次我们使用 `ModelViewSet` 类来获得完整的默认读写操作集。 43 | 44 | 请注意,我们还使用了 `@action` 装饰器来创建一个名为 `highlight` 的自定义操作。这个装饰器可以用来添加任何不符合标准 `create`/`update`/`delete` 样式的自定义端点。 45 | 46 | 使用 `@action` 装饰器的自定义操作默认会响应 `GET` 请求。如果我们需要响应 `POST` 请求的操作,我们可以使用 `methods` 参数。 47 | 48 | 自定义操作的 URL 默认取决于方法名称本身。如果要更改 URL 的构造方式,可以包含 `url_path` 作为装饰器关键字参数。 49 | 50 | ## 明确地将 ViewSets 绑定到 URL 51 | 当我们定义 URLConf 时,处理程序方法只能绑定到操作上。为了看看到底发生了什么,让我们首先从我们的 ViewSets 中明确地创建一组视图。 52 | 53 | 在 `snippets/urls.py` 文件中,我们将 `ViewSet` 类绑定到一组具体视图中。 54 | ```python 55 | from snippets.views import SnippetViewSet, UserViewSet, api_root 56 | from rest_framework import renderers 57 | 58 | snippet_list = SnippetViewSet.as_view({ 59 | 'get': 'list', 60 | 'post': 'create' 61 | }) 62 | snippet_detail = SnippetViewSet.as_view({ 63 | 'get': 'retrieve', 64 | 'put': 'update', 65 | 'patch': 'partial_update', 66 | 'delete': 'destroy' 67 | }) 68 | snippet_highlight = SnippetViewSet.as_view({ 69 | 'get': 'highlight' 70 | }, renderer_classes=[renderers.StaticHTMLRenderer]) 71 | user_list = UserViewSet.as_view({ 72 | 'get': 'list' 73 | }) 74 | user_detail = UserViewSet.as_view({ 75 | 'get': 'retrieve' 76 | }) 77 | ``` 78 | 请注意,我们是如何从每个 `ViewSet` 类创建多个视图,通过将 http 方法绑定到每个视图所需的操作中。 79 | 80 | 现在我们已经将资源绑定到具体的视图中,我们可以像往常一样在 URL conf 中注册视图。 81 | ```python 82 | urlpatterns = format_suffix_patterns([ 83 | url(r'^$', api_root), 84 | url(r'^snippets/$', snippet_list, name='snippet-list'), 85 | url(r'^snippets/(?P[0-9]+)/$', snippet_detail, name='snippet-detail'), 86 | url(r'^snippets/(?P[0-9]+)/highlight/$', snippet_highlight, name='snippet-highlight'), 87 | url(r'^users/$', user_list, name='user-list'), 88 | url(r'^users/(?P[0-9]+)/$', user_detail, name='user-detail') 89 | ]) 90 | ``` 91 | 92 | ## 使用 Routers 93 | 因为我们使用 `ViewSet` 类而不是 `View` 类,所以实际上我们不需要自己设计 URL。将资源连接到视图和 URL 的约定可以使用 `Router` 类自动处理。我们需要做的就是使用路由器注册相应的视图集,然后让它执行其余操作。 94 | 95 | 这是我们重新连线的 `snippets/urls.py` 文件。 96 | ```python 97 | from django.conf.urls import url, include 98 | from rest_framework.routers import DefaultRouter 99 | 100 | from snippets import views 101 | 102 | # 创建路由器并注册我们的视图。 103 | router = DefaultRouter() 104 | router.register(r'snippets', views.SnippetViewSet) 105 | router.register(r'users', views.UserViewSet) 106 | 107 | # API URL 现在由路由器自动确定。 108 | urlpatterns = [ 109 | url(r'^', include(router.urls)) 110 | ] 111 | ``` 112 | 用路由器注册视图集类似于提供 urlpattern。我们包括两个参数——视图的 URL 前缀和视图集本身。 113 | 114 | 我们使用的 `DefaultRouter` 类也为我们自动创建了 API 根视图,所以我们现在可以从 `views` 模块中删除 `api_root` 方法。 115 | 116 | ## 视图与视图集之间的权衡 117 | 使用视图集可以是一个非常有用的抽象。它有助于确保 URL 约定在您的 API 中保持一致,最大限度地减少编写所需的代码量,并允许您专注于 API 提供的交互和表示,而不是 URL conf 的细节。 118 | 119 | 这并不意味着它总是正确的做法。当使用基于类的视图而不是基于函数的视图时,也有类似的权衡考虑。使用视图集不像单独构建视图那样明确。 120 | 121 | 在[本教程的第 7 部分](http://www.iamnancy.top/post/173/)中,我们将介绍如何添加 API 模式,以及如何使用客户端库或命令行工具与我们的 API 进行交互。 122 | --------------------------------------------------------------------------------