Merge branch 'master' into 39/update-schemas

This commit is contained in:
Tom Christie 2018-10-03 12:07:53 +01:00
commit 0e73cdc6d8
68 changed files with 425 additions and 182 deletions

View File

@ -1,26 +1,30 @@
language: python
cache: pip
python:
- "2.7"
- "3.4"
- "3.5"
sudo: false
env:
- DJANGO=1.11
- DJANGO=2.0
- DJANGO=2.1
- DJANGO=master
matrix:
fast_finish: true
include:
- { python: "3.6", env: DJANGO=master }
- { python: "2.7", env: DJANGO=1.11 }
- { python: "3.4", env: DJANGO=1.11 }
- { python: "3.4", env: DJANGO=2.0 }
- { python: "3.5", env: DJANGO=1.11 }
- { python: "3.5", env: DJANGO=2.0 }
- { python: "3.5", env: DJANGO=2.1 }
- { python: "3.5", env: DJANGO=master }
- { python: "3.6", env: DJANGO=1.11 }
- { python: "3.6", env: DJANGO=2.0 }
- { python: "3.6", env: DJANGO=2.1 }
- { python: "3.6", env: DJANGO=master }
- { python: "3.7", env: DJANGO=2.0, dist: xenial, sudo: true }
- { python: "3.7", env: DJANGO=2.1, dist: xenial, sudo: true }
- { python: "3.7", env: DJANGO=master, dist: xenial, sudo: true }
- { python: "3.6", env: TOXENV=base }
- { python: "2.7", env: TOXENV=lint }
- { python: "2.7", env: TOXENV=docs }
@ -29,22 +33,15 @@ matrix:
env: TOXENV=dist
script:
- python setup.py bdist_wheel
- rm -r djangorestframework.egg-info # see #6139
- tox --installpkg ./dist/djangorestframework-*.whl
- tox # test sdist
exclude:
- { python: "2.7", env: DJANGO=master }
- { python: "2.7", env: DJANGO=2.0 }
- { python: "2.7", env: DJANGO=2.1 }
- { python: "3.4", env: DJANGO=master }
- { python: "3.4", env: DJANGO=2.1 }
allow_failures:
- env: DJANGO=master
- env: DJANGO=2.1
install:
- pip install tox tox-travis
- pip install tox tox-venv tox-travis
script:
- tox

View File

@ -3,7 +3,7 @@
- [ ] I have verified that that issue exists against the `master` branch of Django REST framework.
- [ ] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [ ] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [ ] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](http://www.django-rest-framework.org/topics/third-party-resources/#about-third-party-packages) where possible.)
- [ ] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](https://www.django-rest-framework.org/topics/third-party-resources/#about-third-party-packages) where possible.)
- [ ] I have reduced the issue to the simplest possible case.
- [ ] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)

View File

@ -1,6 +1,6 @@
# License
Copyright © 2011-present, [Encode OSS Ltd](http://www.encode.io/).
Copyright © 2011-present, [Encode OSS Ltd](https://www.encode.io/).
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -6,7 +6,7 @@
**Awesome web-browsable Web APIs.**
Full documentation for the project is available at [http://www.django-rest-framework.org][docs].
Full documentation for the project is available at [https://www.django-rest-framework.org/][docs].
---
@ -22,11 +22,11 @@ The initial aim is to provide a single full-time position on REST framework.
[![][rover-img]][rover-url]
[![][sentry-img]][sentry-url]
[![][stream-img]][stream-url]
[![][machinalis-img]][machinalis-url]
[![][rollbar-img]][rollbar-url]
[![][cadre-img]][cadre-url]
[![][load-impact-img]][load-impact-url]
Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover][rover-url], [Sentry][sentry-url], [Stream][stream-url], [Machinalis][machinalis-url], [Rollbar][rollbar-url], and [Cadre][cadre-url].
Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover][rover-url], [Sentry][sentry-url], [Stream][stream-url], [Rollbar][rollbar-url], [Cadre][cadre-url], and [Load Impact][load-impact-url].
---
@ -52,8 +52,8 @@ There is a live example API for testing purposes, [available here][sandbox].
# Requirements
* Python (2.7, 3.4, 3.5, 3.6)
* Django (1.11, 2.0)
* Python (2.7, 3.4, 3.5, 3.6, 3.7)
* Django (1.11, 2.0, 2.1)
# Installation
@ -142,14 +142,14 @@ You can now open the API in your browser at `http://127.0.0.1:8000/`, and view y
You can also interact with the API using command line tools such as [`curl`](https://curl.haxx.se/). For example, to list the users endpoint:
$ curl -H 'Accept: application/json; indent=4' -u admin:password http://127.0.0.1:8000/users/
[
{
"url": "http://127.0.0.1:8000/users/1/",
"username": "admin",
"email": "admin@example.com",
"is_staff": true,
}
]
[
{
"url": "http://127.0.0.1:8000/users/1/",
"username": "admin",
"email": "admin@example.com",
"is_staff": true,
}
]
Or to create a new user:
@ -163,7 +163,7 @@ Or to create a new user:
# Documentation & Support
Full documentation for the project is available at [http://www.django-rest-framework.org][docs].
Full documentation for the project is available at [https://www.django-rest-framework.org/][docs].
For questions and support, use the [REST framework discussion group][group], or `#restframework` on freenode IRC.
@ -191,28 +191,28 @@ Send a description of the issue via email to [rest-framework-security@googlegrou
[rover-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/rover-readme.png
[sentry-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/sentry-readme.png
[stream-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/stream-readme.png
[machinalis-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/machinalis-readme.png
[rollbar-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/rollbar-readme.png
[cadre-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/cadre-readme.png
[load-impact-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/load-impact-readme.png
[rover-url]: http://jobs.rover.com/
[sentry-url]: https://getsentry.com/welcome/
[stream-url]: https://getstream.io/try-the-api/?utm_source=drf&utm_medium=banner&utm_campaign=drf
[machinalis-url]: https://hello.machinalis.co.uk/
[rollbar-url]: https://rollbar.com/
[cadre-url]: https://cadre.com/
[load-impact-url]: https://loadimpact.com/?utm_campaign=Sponsorship%20links&utm_source=drf&utm_medium=drf
[oauth1-section]: http://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth
[oauth2-section]: http://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit
[serializer-section]: http://www.django-rest-framework.org/api-guide/serializers/#serializers
[modelserializer-section]: http://www.django-rest-framework.org/api-guide/serializers/#modelserializer
[functionview-section]: http://www.django-rest-framework.org/api-guide/views/#function-based-views
[generic-views]: http://www.django-rest-framework.org/api-guide/generic-views/
[viewsets]: http://www.django-rest-framework.org/api-guide/viewsets/
[routers]: http://www.django-rest-framework.org/api-guide/routers/
[serializers]: http://www.django-rest-framework.org/api-guide/serializers/
[authentication]: http://www.django-rest-framework.org/api-guide/authentication/
[image]: http://www.django-rest-framework.org/img/quickstart.png
[oauth1-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth
[oauth2-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit
[serializer-section]: https://www.django-rest-framework.org/api-guide/serializers/#serializers
[modelserializer-section]: https://www.django-rest-framework.org/api-guide/serializers/#modelserializer
[functionview-section]: https://www.django-rest-framework.org/api-guide/views/#function-based-views
[generic-views]: https://www.django-rest-framework.org/api-guide/generic-views/
[viewsets]: https://www.django-rest-framework.org/api-guide/viewsets/
[routers]: https://www.django-rest-framework.org/api-guide/routers/
[serializers]: https://www.django-rest-framework.org/api-guide/serializers/
[authentication]: https://www.django-rest-framework.org/api-guide/authentication/
[image]: https://www.django-rest-framework.org/img/quickstart.png
[docs]: http://www.django-rest-framework.org/
[docs]: https://www.django-rest-framework.org/
[security-mail]: mailto:rest-framework-security@googlegroups.com

View File

@ -1,8 +1,11 @@
coverage:
status:
project: false
patch: true
changes: true
precision: 2
round: down
range: "80...100"
comment:
layout: "diff"
status:
project: yes
patch: no
changes: no
comment: off

View File

@ -397,7 +397,7 @@ HTTP digest authentication is a widely implemented scheme that was intended to r
## JSON Web Token Authentication
JSON Web Token is a fairly new standard which can be used for token-based authentication. Unlike the built-in TokenAuthentication scheme, JWT Authentication doesn't need to use a database to validate a token. [Blimp][blimp] maintains the [djangorestframework-jwt][djangorestframework-jwt] package which provides a JWT Authentication class as well as a mechanism for clients to obtain a JWT given the username and password. An alternative package for JWT authentication is [djangorestframework-simplejwt][djangorestframework-simplejwt] which provides different features as well as a pluggable token blacklist app.
JSON Web Token is a fairly new standard which can be used for token-based authentication. Unlike the built-in TokenAuthentication scheme, JWT Authentication doesn't need to use a database to validate a token. A package for JWT authentication is [djangorestframework-simplejwt][djangorestframework-simplejwt] which provides some features as well as a pluggable token blacklist app.
## Hawk HTTP Authentication
@ -445,8 +445,6 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a
[django-oauth-toolkit]: https://github.com/evonove/django-oauth-toolkit
[evonove]: https://github.com/evonove/
[oauthlib]: https://github.com/idan/oauthlib
[blimp]: https://github.com/GetBlimp
[djangorestframework-jwt]: https://github.com/GetBlimp/django-rest-framework-jwt
[djangorestframework-simplejwt]: https://github.com/davesque/django-rest-framework-simplejwt
[etoccalino]: https://github.com/etoccalino/
[djangorestframework-httpsignature]: https://github.com/etoccalino/django-rest-framework-httpsignature

View File

@ -285,6 +285,12 @@ The `ordering` attribute may be either a string or a list/tuple of strings.
The `DjangoObjectPermissionsFilter` is intended to be used together with the [`django-guardian`][guardian] package, with custom `'view'` permissions added. The filter will ensure that querysets only returns objects for which the user has the appropriate view permission.
---
**Note:** This filter has been deprecated as of version 3.9 and moved to the 3rd-party [`djangorestframework-guardian` package][django-rest-framework-guardian].
---
If you're using `DjangoObjectPermissionsFilter`, you'll probably also want to add an appropriate object permissions class, to ensure that users can only operate on instances if they have the appropriate object permissions. The easiest way to do this is to subclass `DjangoObjectPermissions` and add `'view'` permissions to the `perms_map` attribute.
A complete example using both `DjangoObjectPermissionsFilter` and `DjangoObjectPermissions` might look something like this.
@ -388,6 +394,7 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter]
[view-permissions-blogpost]: https://blog.nyaruka.com/adding-a-view-permission-to-django-models
[search-django-admin]: https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields
[django-rest-framework-filters]: https://github.com/philipn/django-rest-framework-filters
[django-rest-framework-guardian]: https://github.com/rpkilby/django-rest-framework-guardian
[django-rest-framework-word-search-filter]: https://github.com/trollknurr/django-rest-framework-word-search-filter
[django-url-filter]: https://github.com/miki725/django-url-filter
[drf-url-filter]: https://github.com/manjitkumar/drf-url-filters

View File

@ -90,4 +90,4 @@ It is actually a misconception. For example, take the following quote from Roy
The quote does not mention Accept headers, but it does make it clear that format suffixes should be considered an acceptable pattern.
[cite]: http://tech.groups.yahoo.com/group/rest-discuss/message/5857
[cite2]: http://tech.groups.yahoo.com/group/rest-discuss/message/14844
[cite2]: https://groups.yahoo.com/neo/groups/rest-discuss/conversations/topics/14844

View File

@ -117,5 +117,5 @@ If you wish to do so, it also provides an exporter that can export those schema
[cite]: https://tools.ietf.org/html/rfc7231#section-4.3.7
[no-options]: https://www.mnot.net/blog/2012/10/29/NO_OPTIONS
[json-schema]: http://json-schema.org/
[json-schema]: https://json-schema.org/
[drf-schema-adapter]: https://github.com/drf-forms/drf-schema-adapter

View File

@ -46,7 +46,7 @@ If you want to modify particular aspects of the pagination style, you'll want to
page_size_query_param = 'page_size'
max_page_size = 1000
You can then apply your new style to a view using the `.pagination_class` attribute:
You can then apply your new style to a view using the `pagination_class` attribute:
class BillingRecordsView(generics.ListAPIView):
queryset = Billing.objects.all()
@ -319,5 +319,5 @@ The [`django-rest-framework-link-header-pagination` package][drf-link-header-pag
[paginate-by-max-mixin]: https://chibisov.github.io/drf-extensions/docs/#paginatebymaxmixin
[drf-proxy-pagination]: https://github.com/tuffnatty/drf-proxy-pagination
[drf-link-header-pagination]: https://github.com/tbeadle/django-rest-framework-link-header-pagination
[disqus-cursor-api]: http://cra.mr/2011/03/08/building-cursors-for-the-disqus-api
[disqus-cursor-api]: https://cra.mr/2011/03/08/building-cursors-for-the-disqus-api
[float_cursor_pagination_example]: https://gist.github.com/keturn/8bc88525a183fd41c73ffb729b8865be#file-fpcursorpagination-py

View File

@ -168,9 +168,7 @@ As with `DjangoModelPermissions` you can use custom model permissions by overrid
---
**Note**: If you need object level `view` permissions for `GET`, `HEAD` and `OPTIONS` requests, you'll want to consider also adding the `DjangoObjectPermissionsFilter` class to ensure that list endpoints only return results including objects for which the user has appropriate view permissions.
---
**Note**: If you need object level `view` permissions for `GET`, `HEAD` and `OPTIONS` requests and are using django-guardian for your object-level permissions backend, you'll want to consider using the `DjangoObjectPermissionsFilter` class provided by the [`djangorestframework-guardian` package][django-rest-framework-guardian]. It ensures that list endpoints only return results including objects for which the user has appropriate view permissions.
---
@ -287,3 +285,4 @@ The [Django Rest Framework Role Filters][django-rest-framework-role-filters] pac
[django-rest-framework-roles]: https://github.com/computer-lab/django-rest-framework-roles
[django-rest-framework-api-key]: https://github.com/manosim/django-rest-framework-api-key
[django-rest-framework-role-filters]: https://github.com/allisson/django-rest-framework-role-filters
[django-rest-framework-guardian]: https://github.com/rpkilby/django-rest-framework-guardian

View File

@ -594,7 +594,7 @@ The [rest-framework-generic-relations][drf-nested-relations] library provides re
[cite]: https://lwn.net/Articles/193245/
[reverse-relationships]: https://docs.djangoproject.com/en/stable/topics/db/queries/#following-relationships-backward
[routers]: http://www.django-rest-framework.org/api-guide/routers#defaultrouter
[routers]: https://www.django-rest-framework.org/api-guide/routers#defaultrouter
[generic-relations]: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#id1
[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
[drf-nested-relations]: https://github.com/Ian-Foote/rest-framework-generic-relations

View File

@ -457,6 +457,43 @@ Modify your REST framework settings.
[MessagePack][messagepack] is a fast, efficient binary serialization format. [Juan Riaza][juanriaza] maintains the [djangorestframework-msgpack][djangorestframework-msgpack] package which provides MessagePack renderer and parser support for REST framework.
## XLSX (Binary Spreadsheet Endpoints)
XLSX is the world's most popular binary spreadsheet format. [Tim Allen][flipperpa] of [The Wharton School][wharton] maintains [drf-renderer-xlsx][drf-renderer-xlsx], which renders an endpoint as an XLSX spreadsheet using OpenPyXL, and allows the client to download it. Spreadsheets can be styled on a per-view basis.
#### Installation & configuration
Install using pip.
$ pip install drf-renderer-xlsx
Modify your REST framework settings.
REST_FRAMEWORK = {
...
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
'drf_renderer_xlsx.renderers.XLSXRenderer',
),
}
To avoid having a file streamed without a filename (which the browser will often default to the filename "download", with no extension), we need to use a mixin to override the `Content-Disposition` header. If no filename is provided, it will default to `export.xlsx`. For example:
from rest_framework.viewsets import ReadOnlyModelViewSet
from drf_renderer_xlsx.mixins import XLSXFileMixin
from drf_renderer_xlsx.renderers import XLSXRenderer
from .models import MyExampleModel
from .serializers import MyExampleSerializer
class MyExampleViewSet(XLSXFileMixin, ReadOnlyModelViewSet):
queryset = MyExampleModel.objects.all()
serializer_class = MyExampleSerializer
renderer_classes = (XLSXRenderer,)
filename = 'my_export.xlsx'
## CSV
Comma-separated values are a plain-text tabular data format, that can be easily imported into spreadsheet applications. [Mjumbe Poe][mjumbewu] maintains the [djangorestframework-csv][djangorestframework-csv] package which provides CSV renderer support for REST framework.
@ -484,19 +521,22 @@ Comma-separated values are a plain-text tabular data format, that can be easily
[browser-accept-headers]: http://www.gethifi.com/blog/browser-rest-http-accept-headers
[testing]: testing.md
[HATEOAS]: http://timelessrepo.com/haters-gonna-hateoas
[quote]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
[quote]: https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
[application/vnd.github+json]: https://developer.github.com/v3/media/
[application/vnd.collection+json]: http://www.amundsen.com/media-types/collection/
[django-error-views]: https://docs.djangoproject.com/en/stable/topics/http/views/#customizing-error-views
[rest-framework-jsonp]: https://jpadilla.github.io/django-rest-framework-jsonp/
[cors]: https://www.w3.org/TR/cors/
[cors-docs]: http://www.django-rest-framework.org/topics/ajax-csrf-cors/
[cors-docs]: https://www.django-rest-framework.org/topics/ajax-csrf-cors/
[jsonp-security]: https://stackoverflow.com/questions/613962/is-jsonp-safe-to-use
[rest-framework-yaml]: https://jpadilla.github.io/django-rest-framework-yaml/
[rest-framework-xml]: https://jpadilla.github.io/django-rest-framework-xml/
[messagepack]: https://msgpack.org/
[juanriaza]: https://github.com/juanriaza
[mjumbewu]: https://github.com/mjumbewu
[flipperpa]: https://githuc.com/flipperpa
[wharton]: https://github.com/wharton
[drf-renderer-xlsx]: https://github.com/wharton/drf-renderer-xlsx
[vbabiy]: https://github.com/vbabiy
[rest-framework-yaml]: https://jpadilla.github.io/django-rest-framework-yaml/
[rest-framework-xml]: https://jpadilla.github.io/django-rest-framework-xml/

View File

@ -90,7 +90,7 @@ You won't typically need to access this property.
---
**Note:** You may see a `WrappedAttributeError` raised when calling the `.user` or `.auth` properties. These errors originate from an authenticator as a standard `AttributeError`, however it's necessary that they be re-raised as a different exception type in order to prevent them from being suppressed by the outer property access. Python will not recognize that the `AttributeError` orginates from the authenticator and will instaed assume that the request object does not have a `.user` or `.auth` property. The authenticator will need to be fixed.
**Note:** You may see a `WrappedAttributeError` raised when calling the `.user` or `.auth` properties. These errors originate from an authenticator as a standard `AttributeError`, however it's necessary that they be re-raised as a different exception type in order to prevent them from being suppressed by the outer property access. Python will not recognize that the `AttributeError` orginates from the authenticator and will instead assume that the request object does not have a `.user` or `.auth` property. The authenticator will need to be fixed.
---

View File

@ -325,7 +325,7 @@ The [wq.db package][wq.db] provides an advanced [ModelRouter][wq.db-router] clas
The [`DRF-extensions` package][drf-extensions] provides [routers][drf-extensions-routers] for creating [nested viewsets][drf-extensions-nested-viewsets], [collection level controllers][drf-extensions-collection-level-controllers] with [customizable endpoint names][drf-extensions-customizable-endpoint-names].
[cite]: http://guides.rubyonrails.org/routing.html
[cite]: https://guides.rubyonrails.org/routing.html
[route-decorators]: viewsets.md#marking-extra-actions-for-routing
[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
[wq.db]: https://wq.io/wq.db

View File

@ -146,7 +146,7 @@ example above.
Automatic schema generation is provided by the `SchemaGenerator` class.
`SchemaGenerator` processes a list of routed URL pattterns and compiles the
`SchemaGenerator` processes a list of routed URL patterns and compiles the
appropriately structured Core API Document.
Basic usage is just to provide the title for your schema and call
@ -818,7 +818,7 @@ A short description of the meaning and intended usage of the input field.
## drf-yasg - Yet Another Swagger Generator
[drf-yasg][drf-yasg] generates [OpenAPI][open-api] documents suitable for code generation - nested schemas,
[drf-yasg][drf-yasg] generates [OpenAPI][open-api] documents suitable for code generation - nested schemas,
named models, response bodies, enum/pattern/min/max validators, form parameters, etc.
@ -829,12 +829,12 @@ in [OpenAPI][open-api] format.
[cite]: https://blog.heroku.com/archives/2014/1/8/json_schema_for_heroku_platform_api
[coreapi]: http://www.coreapi.org/
[corejson]: http://www.coreapi.org/specification/encoding/#core-json-encoding
[coreapi]: https://www.coreapi.org/
[corejson]: https://www.coreapi.org/specification/encoding/#core-json-encoding
[drf-yasg]: https://github.com/axnsan12/drf-yasg/
[open-api]: https://openapis.org/
[drf-openapi]: https://github.com/limdauto/drf_openapi
[json-hyperschema]: http://json-schema.org/latest/json-schema-hypermedia.html
[json-hyperschema]: https://json-schema.org/latest/json-schema-hypermedia.html
[api-blueprint]: https://apiblueprint.org/
[static-files]: https://docs.djangoproject.com/en/stable/howto/static-files/
[named-arguments]: https://docs.djangoproject.com/en/stable/topics/http/urls/#named-groups

View File

@ -57,10 +57,10 @@ At this point we've translated the model instance into Python native datatypes.
Deserialization is similar. First we parse a stream into Python native datatypes...
from django.utils.six import BytesIO
import io
from rest_framework.parsers import JSONParser
stream = BytesIO(json)
stream = io.BytesIO(json)
data = JSONParser().parse(stream)
...then we restore those native datatypes into a dictionary of validated data.
@ -1030,7 +1030,7 @@ Similar to Django forms, you can extend and reuse serializers through inheritanc
class MyBaseSerializer(Serializer):
my_field = serializers.CharField()
def validate_my_field(self):
def validate_my_field(self, value):
...
class MySerializer(MyBaseSerializer):

View File

@ -275,7 +275,7 @@ A validator may be any callable that raises a `serializers.ValidationError` on f
You can specify custom field-level validation by adding `.validate_<field_name>` methods
to your `Serializer` subclass. This is documented in the
[Serializer docs](http://www.django-rest-framework.org/api-guide/serializers/#field-level-validation)
[Serializer docs](https://www.django-rest-framework.org/api-guide/serializers/#field-level-validation)
## Class-based

View File

@ -127,7 +127,7 @@ You may inspect these attributes to adjust behaviour based on the current action
## Marking extra actions for routing
If you have ad-hoc methods that should be routable, you can mark them as such with the `@action` decorator. Like regular actions, extra actions may be intended for either a list of objects, or a single instance. To indicate this, set the `detail` argument to `True` or `False`. The router will configure its URL patterns accordingly. e.g., the `DefaultRouter` will configure detail actions to contain `pk` in their URL patterns.
If you have ad-hoc methods that should be routable, you can mark them as such with the `@action` decorator. Like regular actions, extra actions may be intended for either a single object, or an entire collection. To indicate this, set the `detail` argument to `True` or `False`. The router will configure its URL patterns accordingly. e.g., the `DefaultRouter` will configure detail actions to contain `pk` in their URL patterns.
A more complete example of extra actions:
@ -158,7 +158,7 @@ A more complete example of extra actions:
@action(detail=False)
def recent_users(self, request):
recent_users = User.objects.all().order('-last_login')
recent_users = User.objects.all().order_by('-last_login')
page = self.paginate_queryset(recent_users)
if page is not None:
@ -174,7 +174,7 @@ The decorator can additionally take extra arguments that will be set for the rou
def set_password(self, request, pk=None):
...
These decorator will route `GET` requests by default, but may also accept other HTTP methods by setting the `methods` argument. For example:
The `action` decorator will route `GET` requests by default, but may also accept other HTTP methods by setting the `methods` argument. For example:
@action(detail=True, methods=['post', 'delete'])
def unset_password(self, request, pk=None):
@ -186,7 +186,7 @@ To view all extra actions, call the `.get_extra_actions()` method.
### Routing additional HTTP methods for extra actions
Extra actions can be mapped to different `ViewSet` methods. For example, the above password set/unset methods could be consolidated into a single route. Note that additional mappings do not accept arguments.
Extra actions can map additional HTTP methods to separate `ViewSet` methods. For example, the above password set/unset methods could be consolidated into a single route. Note that additional mappings do not accept arguments.
```python
@action(detail=True, methods=['put'], name='Change Password')
@ -314,5 +314,5 @@ To create a base viewset class that provides `create`, `list` and `retrieve` ope
By creating your own base `ViewSet` classes, you can provide common behavior that can be reused in multiple viewsets across your API.
[cite]: http://guides.rubyonrails.org/routing.html
[cite]: https://guides.rubyonrails.org/routing.html
[routers]: routers.md

View File

@ -960,6 +960,6 @@ The 3.2 release is planned to introduce an alternative admin-style interface to
You can follow development on the GitHub site, where we use [milestones to indicate planning timescales](https://github.com/encode/django-rest-framework/milestones).
[kickstarter]: https://www.kickstarter.com/projects/tomchristie/django-rest-framework-3
[sponsors]: http://www.django-rest-framework.org/topics/kickstarter-announcement/#sponsors
[sponsors]: https://www.django-rest-framework.org/topics/kickstarter-announcement/#sponsors
[mixins.py]: https://github.com/encode/django-rest-framework/blob/master/rest_framework/mixins.py
[django-localization]: https://docs.djangoproject.com/en/stable/topics/i18n/translation/#localization-how-to-create-language-files

View File

@ -10,7 +10,7 @@ We've also fixed a huge number of issues, and made numerous cleanups and improve
Over the course of the 3.1.x series we've [resolved nearly 600 tickets](https://github.com/encode/django-rest-framework/issues?utf8=%E2%9C%93&q=closed%3A%3E2015-03-05) on our GitHub issue tracker. This means we're currently running at a rate of **closing around 100 issues or pull requests per month**.
None of this would have been possible without the support of our wonderful Kickstarter backers. If you're looking for a job in Django development we'd strongly recommend taking [a look through our sponsors](http://www.django-rest-framework.org/topics/kickstarter-announcement/#sponsors) and finding out who's hiring.
None of this would have been possible without the support of our wonderful Kickstarter backers. If you're looking for a job in Django development we'd strongly recommend taking [a look through our sponsors](https://www.django-rest-framework.org/topics/kickstarter-announcement/#sponsors) and finding out who's hiring.
## AdminRenderer

View File

@ -178,12 +178,12 @@ The full set of itemized release notes [are available here][release-notes].
[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors
[moss]: mozilla-grant.md
[funding]: funding.md
[core-api]: http://www.coreapi.org/
[core-api]: https://www.coreapi.org/
[command-line-client]: api-clients#command-line-client
[client-library]: api-clients#python-client-library
[core-json]: http://www.coreapi.org/specification/encoding/#core-json-encoding
[core-json]: https://www.coreapi.org/specification/encoding/#core-json-encoding
[swagger]: https://openapis.org/specification
[hyperschema]: http://json-schema.org/latest/json-schema-hypermedia.html
[hyperschema]: https://json-schema.org/latest/json-schema-hypermedia.html
[api-blueprint]: https://apiblueprint.org/
[tut-7]: ../tutorial/7-schemas-and-client-libraries/
[schema-generation]: ../api-guide/schemas/

View File

@ -123,10 +123,10 @@ REST framework continues to be open-source and permissively licensed, but we fir
## What funding has enabled so far
* The [3.4](http://www.django-rest-framework.org/topics/3.4-announcement/) and [3.5](http://www.django-rest-framework.org/topics/3.5-announcement/) releases, including schema generation for both Swagger and RAML, a Python client library, a Command Line client, and addressing of a large number of outstanding issues.
* The [3.6](http://www.django-rest-framework.org/topics/3.6-announcement/) release, including JavaScript client library, and API documentation, complete with auto-generated code samples.
* The [3.7 release](http://www.django-rest-framework.org/topics/3.7-announcement/), made possible due to our collaborative funding model, focuses on improvements to schema generation and the interactive API documentation.
* The recent [3.8 release](http://www.django-rest-framework.org/topics/3.8-announcement/).
* The [3.4](https://www.django-rest-framework.org/topics/3.4-announcement/) and [3.5](https://www.django-rest-framework.org/topics/3.5-announcement/) releases, including schema generation for both Swagger and RAML, a Python client library, a Command Line client, and addressing of a large number of outstanding issues.
* The [3.6](https://www.django-rest-framework.org/topics/3.6-announcement/) release, including JavaScript client library, and API documentation, complete with auto-generated code samples.
* The [3.7 release](https://www.django-rest-framework.org/topics/3.7-announcement/), made possible due to our collaborative funding model, focuses on improvements to schema generation and the interactive API documentation.
* The recent [3.8 release](https://www.django-rest-framework.org/topics/3.8-announcement/).
* Tom Christie, the creator of Django REST framework, working on the project full-time.
* Around 80-90 issues and pull requests closed per month since Tom Christie started working on the project full-time.
* A community & operations manager position part-time for 4 months, helping mature the business and grow sponsorship.
@ -341,7 +341,7 @@ For further enquires please contact <a href=mailto:funding@django-rest-framework
## Accountability
In an effort to keep the project as transparent as possible, we are releasing [monthly progress reports](http://www.encode.io/reports/march-2018) and regularly include financial reports and cost breakdowns.
In an effort to keep the project as transparent as possible, we are releasing [monthly progress reports](https://www.encode.io/reports/march-2018) and regularly include financial reports and cost breakdowns.
<!-- Begin MailChimp Signup Form -->
<link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css">

View File

@ -78,7 +78,7 @@ Our gold sponsors include companies large and small. Many thanks for their signi
<li><a href="https://mirusresearch.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-mirus_research.png);">Mirus Research</a></li>
<li><a href="https://hipolabs.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-hipo.png);">Hipo</a></li>
<li><a href="https://www.byte.nl/" rel="nofollow" style="background-image:url(../../img/sponsors/2-byte.png);">Byte</a></li>
<li><a href="http://lightningkite.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-lightning_kite.png);">Lightning Kite</a></li>
<li><a href="https://www.lightningkite.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-lightning_kite.png);">Lightning Kite</a></li>
<li><a href="https://opbeat.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-opbeat.png);">Opbeat</a></li>
<li><a href="https://koordinates.com" rel="nofollow" style="background-image:url(../../img/sponsors/2-koordinates.png);">Koordinates</a></li>
<li><a href="http://pulsecode.ca" rel="nofollow" style="background-image:url(../../img/sponsors/2-pulsecode.png);">Pulsecode Inc.</a></li>
@ -116,7 +116,7 @@ The serious financial contribution that our silver sponsors have made is very mu
<li><a href="https://garfo.io/" rel="nofollow" style="background-image:url(../../img/sponsors/3-garfo.png);">Garfo</a></li>
<li><a href="https://goshippo.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-shippo.png);">Shippo</a></li>
<li><a href="http://www.gizmag.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-gizmag.png);">Gizmag</a></li>
<li><a href="http://www.tivix.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-tivix.png);">Tivix</a></li>
<li><a href="https://www.tivix.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-tivix.png);">Tivix</a></li>
<li><a href="https://www.safaribooksonline.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-safari.png);">Safari</a></li>
<li><a href="http://brightloop.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-brightloop.png);">Bright Loop</a></li>
<li><a href="http://www.aba-systems.com.au/" rel="nofollow" style="background-image:url(../../img/sponsors/3-aba.png);">ABA Systems</a></li>
@ -131,7 +131,7 @@ The serious financial contribution that our silver sponsors have made is very mu
<li><a href="https://fluxility.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-fluxility.png);">Fluxility</a></li>
<li><a href="https://teonite.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-teonite.png);">Teonite</a></li>
<li><a href="https://trackmaven.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-trackmaven.png);">TrackMaven</a></li>
<li><a href="http://www.phurba.net/" rel="nofollow" style="background-image:url(../../img/sponsors/3-phurba.png);">Phurba</a></li>
<li><a href="https://www.phurba.net/" rel="nofollow" style="background-image:url(../../img/sponsors/3-phurba.png);">Phurba</a></li>
<li><a href="https://www.nephila.it/it/" rel="nofollow" style="background-image:url(../../img/sponsors/3-nephila.png);">Nephila</a></li>
<li><a href="http://www.aditium.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-aditium.png);">Aditium</a></li>
<li><a href="https://www.eyesopen.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-openeye.png);">OpenEye Scientific Software</a></li>

View File

@ -4,7 +4,7 @@ We have recently been [awarded a Mozilla grant](https://blog.mozilla.org/blog/20
Additionally, we will be building on the realtime support that Django Channels provides, supporting and documenting how to build realtime APIs with REST framework. Again, this will include supporting work in the associated client libraries, making it easier to build richly interactive applications.
The [Core API](http://www.coreapi.org) project will provide the foundations for our client library support, and will allow us to support interaction using a wide range of schemas and hypermedia formats. It's worth noting that these client libraries won't be tightly coupled to solely REST framework APIs either, and will be able to interact with *any* API that exposes a supported schema or hypermedia format.
The [Core API](https://www.coreapi.org/) project will provide the foundations for our client library support, and will allow us to support interaction using a wide range of schemas and hypermedia formats. It's worth noting that these client libraries won't be tightly coupled to solely REST framework APIs either, and will be able to interact with *any* API that exposes a supported schema or hypermedia format.
Specifically, the work includes:
@ -34,7 +34,7 @@ In order to ensure that I can be fully focused on trying to secure a sustainable
& well-funded open source business I will be leaving my current role at [DabApps](https://www.dabapps.com/)
at the end of May 2016.
I have formed a UK limited company, [Encode](http://www.encode.io), which will
I have formed a UK limited company, [Encode](https://www.encode.io/), which will
act as the business entity behind REST framework. I will be issuing monthly reports
from Encode on progress both towards the Mozilla grant, and for development time
funded via the [REST framework paid plans](funding.md).

View File

@ -39,7 +39,7 @@ The following template should be used for the description of the issue, and serv
This issue is for determining the maintenance team for the *** period.
Please see the [Project management](http://www.django-rest-framework.org/topics/project-management/) section of our documentation for more details.
Please see the [Project management](https://www.django-rest-framework.org/topics/project-management/) section of our documentation for more details.
---
@ -59,7 +59,7 @@ The following template should be used for the description of the issue, and serv
If you wish to be considered for this or a future date, please comment against this or subsequent issues.
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.
To modify this process for future maintenance cycles make a pull request to the [project management](https://www.django-rest-framework.org/topics/project-management/) documentation.
#### Responsibilities of team members
@ -99,7 +99,7 @@ The following template should be used for the description of the issue, and serv
During development cycle:
- [ ] Upload the new content to be translated to [transifex](http://www.django-rest-framework.org/topics/project-management/#translations).
- [ ] Upload the new content to be translated to [transifex](https://www.django-rest-framework.org/topics/project-management/#translations).
Checklist:
@ -110,7 +110,7 @@ The following template should be used for the description of the issue, and serv
- [ ] `setup.py` Python & Django version trove classifiers
- [ ] `README` Python & Django versions
- [ ] `docs` Python & Django versions
- [ ] Update the translations from [transifex](http://www.django-rest-framework.org/topics/project-management/#translations).
- [ ] Update the translations from [transifex](https://www.django-rest-framework.org/topics/project-management/#translations).
- [ ] 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).
- [ ] Confirm with @tomchristie that release is finalized and ready to go.
- [ ] Ensure that release date is included in pull request.
@ -122,7 +122,7 @@ The following template should be used for the description of the issue, and serv
- [ ] Make a release announcement on twitter.
- [ ] Close the milestone on GitHub.
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.
To modify this process for future releases make a pull request to the [project management](https://www.django-rest-framework.org/topics/project-management/) documentation.
When pushing the release to PyPI ensure that your environment has been installed from our development `requirement.txt`, so that documentation and PyPI installs are consistently being built against a pinned set of packages.

View File

@ -46,6 +46,7 @@ You can determine your currently installed version using `pip show`:
* Deprecate the `Router.register` `base_name` argument in favor of `basename`. [#5990][gh5990]
* Deprecate the `Router.get_default_base_name` method in favor of `Router.get_default_basename`. [#5990][gh5990]
* Deprecate the `DjangoObjectPermissionsFilter` class, moved to the `djangorestframework-guardian` package. [#6075][gh6075]
## 3.8.x series
@ -1974,3 +1975,4 @@ For older release notes, [please see the version 2.x documentation][old-release-
<!-- 3.9.0 -->
[gh5990]: https://github.com/encode/django-rest-framework/issues/5990
[gh6075]: https://github.com/encode/django-rest-framework/issues/6075

View File

@ -85,8 +85,8 @@ Want your Django REST Framework talk/tutorial/article to be added to our website
[beginners-guide-to-the-django-rest-framework]: https://code.tutsplus.com/tutorials/beginners-guide-to-the-django-rest-framework--cms-19786
[getting-started-with-django-rest-framework-and-angularjs]: http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html
[end-to-end-web-app-with-django-rest-framework-angularjs]: http://mourafiq.com/2013/07/01/end-to-end-web-app-with-django-angular-1.html
[getting-started-with-django-rest-framework-and-angularjs]: https://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html
[end-to-end-web-app-with-django-rest-framework-angularjs]: https://mourafiq.com/2013/07/01/end-to-end-web-app-with-django-angular-1.html
[start-your-api-django-rest-framework-part-1]: https://godjango.com/41-start-your-api-django-rest-framework-part-1/
[permissions-authentication-django-rest-framework-part-2]: https://godjango.com/43-permissions-authentication-django-rest-framework-part-2/
[viewsets-and-routers-django-rest-framework-part-3]: https://godjango.com/45-viewsets-and-routers-django-rest-framework-part-3/

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -69,13 +69,13 @@ continued development by **[signing up for a paid plan][funding]**.
<li><a href="http://jobs.rover.com/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/rover_130x130.png)">Rover.com</a></li>
<li><a href="https://getsentry.com/welcome/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/sentry130.png)">Sentry</a></li>
<li><a href="https://getstream.io/try-the-api/?utm_source=drf&utm_medium=banner&utm_campaign=drf" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/stream-130.png)">Stream</a></li>
<li><a href="https://hello.machinalis.co.uk/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/Machinalis130.png)">Machinalis</a></li>
<li><a href="https://rollbar.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/rollbar2.png)">Rollbar</a></li>
<li><a href="https://cadre.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/cadre.png)">Cadre</a></li>
<li><a href="https://loadimpact.com/?utm_campaign=Sponsorship%20links&utm_source=drf&utm_medium=drf" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/load-impact.png)">Load Impact</a></li>
</ul>
<div style="clear: both; padding-bottom: 20px;"></div>
*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [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://hello.machinalis.co.uk/), [Rollbar](https://rollbar.com), and [Cadre](https://cadre.com).*
*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [Rollbar](https://rollbar.com), [Cadre](https://cadre.com), and [Load Impact](https://loadimpact.com/?utm_campaign=Sponsorship%20links&utm_source=drf&utm_medium=drf).*
---
@ -83,8 +83,8 @@ continued development by **[signing up for a paid plan][funding]**.
REST framework requires the following:
* Python (2.7, 3.4, 3.5, 3.6)
* Django (1.11, 2.0)
* Python (2.7, 3.4, 3.5, 3.6, 3.7)
* Django (1.11, 2.0, 2.1)
The following packages are optional:
@ -200,17 +200,22 @@ Send a description of the issue via email to [rest-framework-security@googlegrou
## License
Copyright (c) 2011-2017, Tom Christie
Copyright © 2011-present, [Encode OSS Ltd](https://www.encode.io/).
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED

View File

@ -521,7 +521,7 @@ You'll either want to include the API schema in your codebase directly, by copyi
})
[heroku-api]: https://devcenter.heroku.com/categories/platform-api
[heroku-example]: http://www.coreapi.org/tools-and-resources/example-services/#heroku-json-hyper-schema
[core-api]: http://www.coreapi.org/
[heroku-example]: https://www.coreapi.org/tools-and-resources/example-services/#heroku-json-hyper-schema
[core-api]: https://www.coreapi.org/
[schema-generation]: ../api-guide/schemas.md
[transport-adaptors]: http://docs.python-requests.org/en/master/user/advanced/#transport-adapters

View File

@ -82,6 +82,6 @@ as well as how to support content types other than form-encoded data.
[cite]: https://www.amazon.com/RESTful-Web-Services-Leonard-Richardson/dp/0596529260
[ajax-form]: https://github.com/encode/ajax-form
[rails]: http://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-put-or-delete-methods-work
[rails]: https://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-put-or-delete-methods-work
[html5]: https://www.w3.org/TR/html5-diff/#changes-2010-06-24
[put_delete]: http://amundsen.com/examples/put-delete-forms/

View File

@ -16,11 +16,11 @@ The built-in API documentation includes:
### Installation
The `coreapi` library is required as a dependancy for the API docs. Make sure
The `coreapi` library is required as a dependency for the API docs. Make sure
to install the latest version. The `pygments` and `markdown` libraries
are optional but recommended.
To install the API documentation, you'll need to include it in your projects URLconf:
To install the API documentation, you'll need to include it in your project's URLconf:
from rest_framework.documentation import include_docs_urls
@ -39,7 +39,7 @@ This will include two different views:
**Note**: By default `include_docs_urls` configures the underlying `SchemaView` to generate _public_ schemas.
This means that views will not be instantiated with a `request` instance. i.e. Inside the view `self.request` will be `None`.
To be compatible with this behaviour methods (such as `get_serializer` or `get_serializer_class` etc.) which inspect `self.request` or, particularly, `self.request.user` may need to be adjusted to handle this case.
To be compatible with this behaviour, methods (such as `get_serializer` or `get_serializer_class` etc.) which inspect `self.request` or, particularly, `self.request.user` may need to be adjusted to handle this case.
You may ensure views are given a `request` instance by calling `include_docs_urls` with `public=False`:
@ -313,7 +313,7 @@ In this approach, rather than documenting the available API endpoints up front,
To implement a hypermedia API you'll need to decide on an appropriate media type for the API, and implement a custom renderer and parser for that media type. The [REST, Hypermedia & HATEOAS][hypermedia-docs] section of the documentation includes pointers to background reading, as well as links to various hypermedia formats.
[cite]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
[cite]: https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
[drf-yasg]: https://github.com/axnsan12/drf-yasg/
[image-drf-yasg]: ../img/drf-yasg.png
[drf-openapi]: https://github.com/limdauto/drf_openapi/

View File

@ -4,7 +4,7 @@ REST framework is suitable for returning both API style responses, and regular H
## Rendering HTML
In order to return HTML responses you'll need to either `TemplateHTMLRenderer`, or `StaticHTMLRenderer`.
In order to return HTML responses you'll need to use either `TemplateHTMLRenderer`, or `StaticHTMLRenderer`.
The `TemplateHTMLRenderer` class expects the response to contain a dictionary of context data, and renders an HTML page based on a template that must be specified either in the view or on the response.

View File

@ -43,7 +43,7 @@ REST framework includes these built-in translations both for standard exception
Note that the translations only apply to the error strings themselves. The format of error messages, and the keys of field names will remain the same. An example `400 Bad Request` response body might look like this:
{"detail": {"username": ["Esse campo deve ser unico."]}}
{"detail": {"username": ["Esse campo deve ser único."]}}
If you want to use different string for parts of the response such as `detail` and `non_field_errors` then you can modify this behavior by using a [custom exception handler][custom-exception-handler].

View File

@ -36,7 +36,7 @@ What REST framework doesn't do is give you machine readable hypermedia formats s
[cite]: https://vimeo.com/channels/restfest/page:2
[dissertation]: https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
[hypertext-driven]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
[hypertext-driven]: https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
[restful-web-apis]: http://restfulwebapis.org/
[building-hypermedia-apis]: https://www.amazon.com/Building-Hypermedia-APIs-HTML5-Node/dp/1449306578
[designing-hypermedia-apis]: http://designinghypermediaapis.com/

View File

@ -27,7 +27,7 @@ Nested data structures are easy enough to work with if they're read-only - simpl
Some example output from our serializer.
{
'title': 'Leaving party preperations',
'title': 'Leaving party preparations',
'items': [
{'text': 'Compile playlist', 'is_completed': True},
{'text': 'Send invites', 'is_completed': False},

View File

@ -154,9 +154,9 @@ At this point we've translated the model instance into Python native datatypes.
Deserialization is similar. First we parse a stream into Python native datatypes...
from django.utils.six import BytesIO
import io
stream = BytesIO(content)
stream = io.BytesIO(content)
data = JSONParser().parse(stream)
...then we restore those native datatypes into a fully populated object instance.

View File

@ -89,7 +89,7 @@ Now check that it is available on the command line...
Command line client for interacting with CoreAPI services.
Visit http://www.coreapi.org for more information.
Visit https://www.coreapi.org/ for more information.
Options:
--version Display the package version number.
@ -220,8 +220,8 @@ We've reached the end of our tutorial. If you want to get more involved in the
**Now go build awesome things.**
[coreapi]: http://www.coreapi.org
[corejson]: http://www.coreapi.org/specification/encoding/#core-json-encoding
[coreapi]: https://www.coreapi.org/
[corejson]: https://www.coreapi.org/specification/encoding/#core-json-encoding
[openapi]: https://openapis.org/
[repo]: https://github.com/encode/rest-framework-tutorial
[sandbox]: https://restframework.herokuapp.com/

View File

@ -56,7 +56,7 @@ We'll also create an initial user named `admin` with a password of `password123`
python manage.py createsuperuser --email admin@example.com --username admin
Once you've set up a database and initial user created and ready to go, open up the app's directory and we'll get coding...
Once you've set up a database and the initial user is created and ready to go, open up the app's directory and we'll get coding...
## Serializers

View File

@ -4,6 +4,6 @@
<h1 id="404-page-not-found" style="text-align: center">404</h1>
<p style="text-align: center"><strong>Page not found</strong></p>
<p style="text-align: center">Try the <a href="http://www.django-rest-framework.org/">homepage</a>, or <a href="#searchModal" data-toggle="modal">search the documentation</a>.</p>
<p style="text-align: center">Try the <a href="https://www.django-rest-framework.org/">homepage</a>, or <a href="#searchModal" data-toggle="modal">search the documentation</a>.</p>
{% endblock %}

View File

@ -3,7 +3,7 @@
*
* Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Designed and built with all the love in the world @twitter by @mdo and @fat.
*/

View File

@ -3,7 +3,7 @@
*
* Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Designed and built with all the love in the world @twitter by @mdo and @fat.
*/

View File

@ -14,7 +14,7 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="http://www.django-rest-framework.org">Django REST framework</a>
<a class="brand" href="https://www.django-rest-framework.org/">Django REST framework</a>
<div class="nav-collapse collapse">
{% if nav|length>1 %}
<!-- Main navigation -->

View File

@ -1,5 +1,5 @@
site_name: Django REST framework
site_url: http://www.django-rest-framework.org/
site_url: https://www.django-rest-framework.org/
site_description: Django REST framework - Web APIs for Django
repo_url: https://github.com/encode/django-rest-framework

View File

@ -10,6 +10,13 @@ from django.core import validators
from django.utils import six
from django.views.generic import View
try:
# Python 3 (required for 3.8+)
from collections.abc import Mapping # noqa
except ImportError:
# Python 2.7
from collections import Mapping # noqa
try:
from django.urls import ( # noqa
URLPattern,
@ -22,6 +29,11 @@ except ImportError:
RegexURLResolver as URLResolver,
)
try:
from django.core.validators import ProhibitNullCharactersValidator # noqa
except ImportError:
ProhibitNullCharactersValidator = None
def get_original_route(urlpattern):
"""
@ -89,7 +101,7 @@ def unicode_to_repr(value):
def unicode_http_header(value):
# Coerce HTTP header value to unicode.
if isinstance(value, six.binary_type):
if isinstance(value, bytes):
return value.decode('iso-8859-1')
return value

View File

@ -131,7 +131,7 @@ def schema(view_inspector):
return decorator
def action(methods=None, detail=None, name=None, url_path=None, url_name=None, **kwargs):
def action(methods=None, detail=None, url_path=None, url_name=None, **kwargs):
"""
Mark a ViewSet method as a routable action.
@ -145,18 +145,22 @@ def action(methods=None, detail=None, name=None, url_path=None, url_name=None, *
"@action() missing required argument: 'detail'"
)
# name and suffix are mutually exclusive
if 'name' in kwargs and 'suffix' in kwargs:
raise TypeError("`name` and `suffix` are mutually exclusive arguments.")
def decorator(func):
func.mapping = MethodMapper(func, methods)
func.detail = detail
func.name = name if name else pretty_name(func.__name__)
func.url_path = url_path if url_path else func.__name__
func.url_name = url_name if url_name else func.__name__.replace('_', '-')
func.kwargs = kwargs
func.kwargs.update({
'name': func.name,
'description': func.__doc__ or None
})
# Set descriptive arguments for viewsets
if 'name' not in kwargs and 'suffix' not in kwargs:
func.kwargs['name'] = pretty_name(func.__name__)
func.kwargs['description'] = func.__doc__ or None
return func
return decorator

View File

@ -34,7 +34,8 @@ from pytz.exceptions import InvalidTimeError
from rest_framework import ISO_8601
from rest_framework.compat import (
MaxLengthValidator, MaxValueValidator, MinLengthValidator,
MinValueValidator, unicode_repr, unicode_to_repr
MinValueValidator, ProhibitNullCharactersValidator, unicode_repr,
unicode_to_repr
)
from rest_framework.exceptions import ErrorDetail, ValidationError
from rest_framework.settings import api_settings
@ -674,10 +675,7 @@ class BooleanField(Field):
'0', 0, 0.0,
False
}
def __init__(self, **kwargs):
assert 'allow_null' not in kwargs, '`allow_null` is not a valid option. Use `NullBooleanField` instead.'
super(BooleanField, self).__init__(**kwargs)
NULL_VALUES = {'null', 'Null', 'NULL', '', None}
def to_internal_value(self, data):
try:
@ -685,6 +683,8 @@ class BooleanField(Field):
return True
elif data in self.FALSE_VALUES:
return False
elif data in self.NULL_VALUES and self.allow_null:
return None
except TypeError: # Input is an unhashable type
pass
self.fail('invalid', input=data)
@ -694,6 +694,8 @@ class BooleanField(Field):
return True
elif value in self.FALSE_VALUES:
return False
if value in self.NULL_VALUES and self.allow_null:
return None
return bool(value)
@ -718,7 +720,7 @@ class NullBooleanField(Field):
'0', 0, 0.0,
False
}
NULL_VALUES = {'n', 'N', 'null', 'Null', 'NULL', '', None}
NULL_VALUES = {'null', 'Null', 'NULL', '', None}
def __init__(self, **kwargs):
assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.'
@ -754,7 +756,7 @@ class CharField(Field):
'invalid': _('Not a valid string.'),
'blank': _('This field may not be blank.'),
'max_length': _('Ensure this field has no more than {max_length} characters.'),
'min_length': _('Ensure this field has at least {min_length} characters.')
'min_length': _('Ensure this field has at least {min_length} characters.'),
}
initial = ''
@ -777,6 +779,10 @@ class CharField(Field):
self.validators.append(
MinLengthValidator(self.min_length, message=message))
# ProhibitNullCharactersValidator is None on Django < 2.0
if ProhibitNullCharactersValidator is not None:
self.validators.append(ProhibitNullCharactersValidator())
def run_validation(self, data=empty):
# Test for the empty string here so that it does not get validated,
# and so that subclasses do not need to handle it explicitly
@ -1779,7 +1785,7 @@ class JSONField(Field):
def to_internal_value(self, data):
try:
if self.binary or getattr(data, 'is_json_string', False):
if isinstance(data, six.binary_type):
if isinstance(data, bytes):
data = data.decode('utf-8')
return json.loads(data)
else:

View File

@ -5,6 +5,7 @@ returned by list views.
from __future__ import unicode_literals
import operator
import warnings
from functools import reduce
from django.core.exceptions import ImproperlyConfigured
@ -284,6 +285,11 @@ class DjangoObjectPermissionsFilter(BaseFilterBackend):
has read object level permissions.
"""
def __init__(self):
warnings.warn(
"`DjangoObjectPermissionsFilter` has been deprecated and moved to "
"the 3rd-party django-rest-framework-guardian package.",
DeprecationWarning, stacklevel=2
)
assert is_guardian_installed(), 'Using DjangoObjectPermissionsFilter, but django-guardian is not installed'
perm_format = '%(app_label)s.view_%(model_name)s'

View File

@ -1 +0,0 @@
# Just to keep things like ./manage.py test happy

View File

@ -472,7 +472,7 @@ class CursorPagination(BasePagination):
"""
The cursor pagination implementation is necessarily complex.
For an overview of the position/offset style we use, see this post:
http://cra.mr/2011/03/08/building-cursors-for-the-disqus-api
https://cra.mr/2011/03/08/building-cursors-for-the-disqus-api
"""
cursor_query_param = 'cursor'
cursor_query_description = _('The pagination cursor value.')
@ -544,12 +544,11 @@ class CursorPagination(BasePagination):
has_following_position = False
following_position = None
# If we have a reverse queryset, then the query ordering was in reverse
# so we need to reverse the items again before returning them to the user.
if reverse:
# If we have a reverse queryset, then the query ordering was in reverse
# so we need to reverse the items again before returning them to the user.
self.page = list(reversed(self.page))
if reverse:
# Determine next and previous positions for reverse cursors.
self.has_next = (current_position is not None) or (offset > 0)
self.has_previous = has_following_position

View File

@ -10,6 +10,7 @@ The wrapped request then offers a richer API, in particular :
"""
from __future__ import unicode_literals
import io
import sys
from contextlib import contextmanager
@ -301,7 +302,7 @@ class Request(object):
elif not self._request._read_started:
self._stream = self._request
else:
self._stream = six.BytesIO(self.body)
self._stream = io.BytesIO(self.body)
def _supports_form_parsing(self):
"""

View File

@ -15,7 +15,7 @@ from __future__ import unicode_literals
import copy
import inspect
import traceback
from collections import Mapping, OrderedDict
from collections import OrderedDict
from django.core.exceptions import ImproperlyConfigured
from django.core.exceptions import ValidationError as DjangoValidationError
@ -27,7 +27,7 @@ from django.utils import six, timezone
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from rest_framework.compat import postgres_fields, unicode_to_repr
from rest_framework.compat import Mapping, postgres_fields, unicode_to_repr
from rest_framework.exceptions import ErrorDetail, ValidationError
from rest_framework.fields import get_error_detail, set_value
from rest_framework.settings import api_settings

View File

@ -234,7 +234,7 @@ class APISettings(object):
return val
def __check_user_settings(self, user_settings):
SETTINGS_DOC = "http://www.django-rest-framework.org/api-guide/settings/"
SETTINGS_DOC = "https://www.django-rest-framework.org/api-guide/settings/"
for setting in REMOVED_SETTINGS:
if setting in user_settings:
raise RuntimeError("The '%s' setting has been removed. Please refer to '%s' for available settings." % (setting, SETTINGS_DOC))

View File

@ -34,7 +34,7 @@
<div class="container">
<span>
{% block branding %}
<a class='navbar-brand' rel="nofollow" href='http://www.django-rest-framework.org'>
<a class='navbar-brand' rel="nofollow" href='https://www.django-rest-framework.org/'>
Django REST framework
</a>
{% endblock %}

View File

@ -38,7 +38,7 @@
<div class="container">
<span>
{% block branding %}
<a class='navbar-brand' rel="nofollow" href='http://www.django-rest-framework.org'>
<a class='navbar-brand' rel="nofollow" href='https://www.django-rest-framework.org/'>
Django REST framework
</a>
{% endblock %}

View File

@ -47,7 +47,7 @@ class JSONEncoder(json.JSONEncoder):
return six.text_type(obj)
elif isinstance(obj, QuerySet):
return tuple(obj)
elif isinstance(obj, six.binary_type):
elif isinstance(obj, bytes):
# Best-effort for binary blobs. See #4187.
return obj.decode('utf-8')
elif hasattr(obj, 'tolist'):

View File

@ -23,7 +23,7 @@ from rest_framework.utils import formatting
def get_view_name(view):
"""
Given a view class, return a textual name to represent the view.
Given a view instance, return a textual name to represent the view.
This name is used in the browsable API, and in OPTIONS responses.
This function is the default for the `VIEW_NAME_FUNCTION` setting.
@ -48,7 +48,7 @@ def get_view_name(view):
def get_view_description(view, html=False):
"""
Given a view class, return a textual description to represent the view.
Given a view instance, return a textual description to represent the view.
This name is used in the browsable API, and in OPTIONS responses.
This function is the default for the `VIEW_DESCRIPTION_FUNCTION` setting.

View File

@ -183,7 +183,8 @@ class ViewSetMixin(object):
try:
url_name = '%s-%s' % (self.basename, action.url_name)
url = reverse(url_name, self.args, self.kwargs, request=self.request)
action_urls[action.name] = url
view = self.__class__(**action.kwargs)
action_urls[view.get_view_name()] = url
except NoReverseMatch:
pass # URL requires additional arguments, ignore

View File

@ -42,7 +42,7 @@ if sys.argv[-1] == 'publish':
setup(
name='djangorestframework',
version=version,
url='http://www.django-rest-framework.org',
url='https://www.django-rest-framework.org/',
license='BSD',
description='Web APIs for Django, made easy.',
long_description=read('README.md'),

View File

@ -179,7 +179,6 @@ class ActionDecoratorTestCase(TestCase):
assert test_action.mapping == {'get': 'test_action'}
assert test_action.detail is True
assert test_action.name == 'Test action'
assert test_action.url_path == 'test_action'
assert test_action.url_name == 'test-action'
assert test_action.kwargs == {
@ -213,6 +212,47 @@ class ActionDecoratorTestCase(TestCase):
for name in APIView.http_method_names:
assert test_action.mapping[name] == name
def test_view_name_kwargs(self):
"""
'name' and 'suffix' are mutually exclusive kwargs used for generating
a view's display name.
"""
# by default, generate name from method
@action(detail=True)
def test_action(request):
raise NotImplementedError
assert test_action.kwargs == {
'description': None,
'name': 'Test action',
}
# name kwarg supersedes name generation
@action(detail=True, name='test name')
def test_action(request):
raise NotImplementedError
assert test_action.kwargs == {
'description': None,
'name': 'test name',
}
# suffix kwarg supersedes name generation
@action(detail=True, suffix='Suffix')
def test_action(request):
raise NotImplementedError
assert test_action.kwargs == {
'description': None,
'suffix': 'Suffix',
}
# name + suffix is a conflict.
with pytest.raises(TypeError) as excinfo:
action(detail=True, name='test name', suffix='Suffix')
assert str(excinfo.value) == "`name` and `suffix` are mutually exclusive arguments."
def test_method_mapping(self):
@action(detail=False)
def test_action(request):
@ -223,7 +263,7 @@ class ActionDecoratorTestCase(TestCase):
raise NotImplementedError
# The secondary handler methods should not have the action attributes
for name in ['mapping', 'detail', 'name', 'url_path', 'url_name', 'kwargs']:
for name in ['mapping', 'detail', 'url_path', 'url_name', 'kwargs']:
assert hasattr(test_action, name) and not hasattr(test_action_post, name)
def test_method_mapping_already_mapped(self):

View File

@ -15,6 +15,7 @@ from django.utils.timezone import activate, deactivate, override, utc
import rest_framework
from rest_framework import exceptions, serializers
from rest_framework.compat import ProhibitNullCharactersValidator
from rest_framework.fields import DjangoImageField, is_simple_callable
try:
@ -657,7 +658,7 @@ class TestBooleanField(FieldValues):
class TestNullBooleanField(TestBooleanField):
"""
Valid and invalid values for `BooleanField`.
Valid and invalid values for `NullBooleanField`.
"""
valid_inputs = {
'true': True,
@ -682,6 +683,16 @@ class TestNullBooleanField(TestBooleanField):
field = serializers.NullBooleanField()
class TestNullableBooleanField(TestNullBooleanField):
"""
Valid and invalid values for `BooleanField` when `allow_null=True`.
"""
@property
def field(self):
return serializers.BooleanField(allow_null=True)
# String types...
class TestCharField(FieldValues):
@ -718,6 +729,17 @@ class TestCharField(FieldValues):
field.run_validation(' ')
assert exc_info.value.detail == ['This field may not be blank.']
@pytest.mark.skipif(ProhibitNullCharactersValidator is None, reason="Skipped on Django < 2.0")
def test_null_bytes(self):
field = serializers.CharField()
for value in ('\0', 'foo\0', '\0foo', 'foo\0foo'):
with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation(value)
assert exc_info.value.detail == [
'Null characters are not allowed.'
]
class TestEmailField(FieldValues):
"""

View File

@ -9,8 +9,10 @@ from __future__ import unicode_literals
import datetime
import decimal
import sys
from collections import OrderedDict
import django
import pytest
from django.core.exceptions import ImproperlyConfigured
from django.core.validators import (
@ -219,6 +221,25 @@ class TestRegularFieldMappings(TestCase):
)
self.assertEqual(unicode_repr(TestSerializer()), expected)
# merge this into test_regular_fields / RegularFieldsModel when
# Django 2.1 is the minimum supported version
@pytest.mark.skipif(django.VERSION < (2, 1), reason='Django version < 2.1')
def test_nullable_boolean_field(self):
class NullableBooleanModel(models.Model):
field = models.BooleanField(null=True, default=False)
class NullableBooleanSerializer(serializers.ModelSerializer):
class Meta:
model = NullableBooleanModel
fields = ['field']
expected = dedent("""
NullableBooleanSerializer():
field = BooleanField(allow_null=True, required=False)
""")
self.assertEqual(unicode_repr(NullableBooleanSerializer()), expected)
def test_method_field(self):
"""
Properties and methods on the model should be allowed as `Meta.fields`
@ -381,6 +402,10 @@ class TestDurationFieldMapping(TestCase):
TestSerializer():
id = IntegerField(label='ID', read_only=True)
duration_field = DurationField(max_value=datetime.timedelta(3), min_value=datetime.timedelta(1))
""") if sys.version_info < (3, 7) else dedent("""
TestSerializer():
id = IntegerField(label='ID', read_only=True)
duration_field = DurationField(max_value=datetime.timedelta(days=3), min_value=datetime.timedelta(days=1))
""")
self.assertEqual(unicode_repr(TestSerializer()), expected)

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import io
import math
import pytest
@ -10,7 +11,7 @@ from django.core.files.uploadhandler import (
)
from django.http.request import RawPostDataException
from django.test import TestCase
from django.utils.six import BytesIO, StringIO
from django.utils.six import StringIO
from rest_framework.exceptions import ParseError
from rest_framework.parsers import (
@ -43,7 +44,7 @@ class TestFileUploadParser(TestCase):
def setUp(self):
class MockRequest(object):
pass
self.stream = BytesIO(
self.stream = io.BytesIO(
"Test text file".encode('utf-8')
)
request = MockRequest()
@ -131,7 +132,7 @@ class TestFileUploadParser(TestCase):
class TestJSONParser(TestCase):
def bytes(self, value):
return BytesIO(value.encode('utf-8'))
return io.BytesIO(value.encode('utf-8'))
def test_float_strictness(self):
parser = JSONParser()

View File

@ -2,6 +2,7 @@ from __future__ import unicode_literals
import base64
import unittest
import warnings
import django
from django.contrib.auth.models import Group, Permission, User
@ -417,17 +418,34 @@ class ObjectPermissionsIntegrationTests(TestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Read list
def test_django_object_permissions_filter_deprecated(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
DjangoObjectPermissionsFilter()
message = ("`DjangoObjectPermissionsFilter` has been deprecated and moved "
"to the 3rd-party django-rest-framework-guardian package.")
self.assertEqual(len(w), 1)
self.assertIs(w[-1].category, DeprecationWarning)
self.assertEqual(str(w[-1].message), message)
def test_can_read_list_permissions(self):
request = factory.get('/', HTTP_AUTHORIZATION=self.credentials['readonly'])
object_permissions_list_view.cls.filter_backends = (DjangoObjectPermissionsFilter,)
response = object_permissions_list_view(request)
# TODO: remove in version 3.10
with warnings.catch_warnings(record=True):
warnings.simplefilter("always")
response = object_permissions_list_view(request)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data[0].get('id'), 1)
def test_cannot_read_list_permissions(self):
request = factory.get('/', HTTP_AUTHORIZATION=self.credentials['writeonly'])
object_permissions_list_view.cls.filter_backends = (DjangoObjectPermissionsFilter,)
response = object_permissions_list_view(request)
# TODO: remove in version 3.10
with warnings.catch_warnings(record=True):
warnings.simplefilter("always")
response = object_permissions_list_view(request)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertListEqual(response.data, [])

View File

@ -52,6 +52,14 @@ class ResourceViewSet(ModelViewSet):
def detail_action(self, request, *args, **kwargs):
raise NotImplementedError
@action(detail=True, name='Custom Name')
def named_action(self, request, *args, **kwargs):
raise NotImplementedError
@action(detail=True, suffix='Custom Suffix')
def suffixed_action(self, request, *args, **kwargs):
raise NotImplementedError
router = SimpleRouter()
router.register(r'resources', ResourceViewSet)
@ -145,6 +153,24 @@ class BreadcrumbTests(TestCase):
('Detail action', '/resources/1/detail_action/'),
]
def test_modelviewset_action_name_kwarg(self):
url = '/resources/1/named_action/'
assert get_breadcrumbs(url) == [
('Root', '/'),
('Resource List', '/resources/'),
('Resource Instance', '/resources/1/'),
('Custom Name', '/resources/1/named_action/'),
]
def test_modelviewset_action_suffix_kwarg(self):
url = '/resources/1/suffixed_action/'
assert get_breadcrumbs(url) == [
('Root', '/'),
('Resource List', '/resources/'),
('Resource Instance', '/resources/1/'),
('Resource Custom Suffix', '/resources/1/suffixed_action/'),
]
class JsonFloatTests(TestCase):
"""

View File

@ -63,9 +63,28 @@ class ActionViewSet(GenericViewSet):
raise NotImplementedError
class ActionNamesViewSet(GenericViewSet):
def retrieve(self, request, *args, **kwargs):
return Response()
@action(detail=True)
def unnamed_action(self, request, *args, **kwargs):
raise NotImplementedError
@action(detail=True, name='Custom Name')
def named_action(self, request, *args, **kwargs):
raise NotImplementedError
@action(detail=True, suffix='Custom Suffix')
def suffixed_action(self, request, *args, **kwargs):
raise NotImplementedError
router = SimpleRouter()
router.register(r'actions', ActionViewSet)
router.register(r'actions-alt', ActionViewSet, basename='actions-alt')
router.register(r'names', ActionNamesViewSet, basename='names')
urlpatterns = [
@ -172,6 +191,19 @@ class GetExtraActionUrlMapTests(TestCase):
def test_uninitialized_view(self):
self.assertEqual(ActionViewSet().get_extra_action_url_map(), OrderedDict())
def test_action_names(self):
# Action 'name' and 'suffix' kwargs should be respected
response = self.client.get('/api/names/1/')
view = response.renderer_context['view']
expected = OrderedDict([
('Custom Name', 'http://testserver/api/names/1/named_action/'),
('Action Names Custom Suffix', 'http://testserver/api/names/1/suffixed_action/'),
('Unnamed action', 'http://testserver/api/names/1/unnamed_action/'),
])
self.assertEqual(view.get_extra_action_url_map(), expected)
@override_settings(ROOT_URLCONF='tests.test_viewsets')
class ReverseActionTests(TestCase):

View File

@ -1,9 +1,9 @@
[tox]
envlist =
{py27,py34,py35,py36}-django111,
{py34,py35,py36}-django20,
{py35,py36}-django21
{py35,py36}-djangomaster,
{py34,py35,py36,py37}-django20,
{py35,py36,py37}-django21
{py35,py36,py37}-djangomaster,
base,dist,lint,docs,
[travis:env]