Merge branch 'master' into master

This commit is contained in:
Pramod Pujara 2021-03-08 10:56:21 +05:45 committed by GitHub
commit cbd4b79611
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
264 changed files with 18753 additions and 7216 deletions

1
.github/FUNDING.yml vendored
View File

@ -1 +1,2 @@
github: encode
custom: https://fund.django-rest-framework.org/topics/funding/

10
.github/ISSUE_TEMPLATE/1-issue.md vendored Normal file
View File

@ -0,0 +1,10 @@
---
name: Issue
about: Please only raise an issue if you've been advised to do so after discussion. Thanks! 🙏
---
## Checklist
- [ ] Raised initially as discussion #...
- [ ] 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/community/third-party-packages/#about-third-party-packages) where possible.)
- [ ] I have reduced the issue to the simplest possible case.

6
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,6 @@
blank_issues_enabled: false
contact_links:
- name: Discussions
url: https://github.com/encode/django-rest-framework/discussions
about: >
The "Discussions" forum is where you want to start. 💖

2
.gitignore vendored
View File

@ -2,6 +2,8 @@
*.db
*~
.*
*.py.bak
/site/
/htmlcov/

View File

@ -1,31 +1,36 @@
language: python
cache: pip
dist: xenial
dist: bionic
matrix:
fast_finish: true
include:
- { 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=2.2 }
- { 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=2.2 }
- { python: "3.6", env: DJANGO=master }
- { python: "3.6", env: DJANGO=3.0 }
- { python: "3.6", env: DJANGO=3.1 }
- { python: "3.6", env: DJANGO=3.2 }
- { python: "3.7", env: DJANGO=2.0 }
- { python: "3.7", env: DJANGO=2.1 }
- { python: "3.7", env: DJANGO=2.2 }
- { python: "3.7", env: DJANGO=master }
- { python: "3.7", env: DJANGO=3.0 }
- { python: "3.7", env: DJANGO=3.1 }
- { python: "3.7", env: DJANGO=3.2 }
- { python: "3.7", env: TOXENV=base }
- { python: "3.7", env: TOXENV=lint }
- { python: "3.7", env: TOXENV=docs }
- { python: "3.8", env: DJANGO=3.0 }
- { python: "3.8", env: DJANGO=3.1 }
- { python: "3.8", env: DJANGO=3.2 }
- { python: "3.8", env: DJANGO=master }
- python: "3.7"
- { python: "3.9", env: DJANGO=3.1 }
- { python: "3.9", env: DJANGO=3.2 }
- { python: "3.9", env: DJANGO=master }
- { python: "3.8", env: TOXENV=base }
- { python: "3.8", env: TOXENV=lint }
- { python: "3.8", env: TOXENV=docs }
- python: "3.8"
env: TOXENV=dist
script:
- python setup.py bdist_wheel
@ -35,9 +40,10 @@ matrix:
allow_failures:
- env: DJANGO=master
- env: DJANGO=3.2
install:
- pip install tox tox-venv tox-travis
- pip install tox tox-travis
script:
- tox

9
.tx/config Normal file
View File

@ -0,0 +1,9 @@
[main]
host = https://www.transifex.com
lang_map = sr@latin:sr_Latn, zh-Hans:zh_Hans, zh-Hant:zh_Hant
[django-rest-framework.djangopo]
file_filter = rest_framework/locale/<lang>/LC_MESSAGES/django.po
source_file = rest_framework/locale/en_US/LC_MESSAGES/django.po
source_lang = en_US
type = PO

View File

@ -1,14 +0,0 @@
## Checklist
- [ ] 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](https://www.django-rest-framework.org/community/third-party-packages/#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.)
## Steps to reproduce
## Expected behavior
## Actual behavior

View File

@ -1,5 +1,6 @@
include README.md
include LICENSE.md
recursive-include tests/* *
recursive-include rest_framework/static *.js *.css *.png *.ico *.eot *.svg *.ttf *.woff *.woff2
recursive-include rest_framework/templates *.html schema.js
recursive-include rest_framework/locale *.mo

View File

@ -22,12 +22,11 @@ The initial aim is to provide a single full-time position on REST framework.
[![][sentry-img]][sentry-url]
[![][stream-img]][stream-url]
[![][rollbar-img]][rollbar-url]
[![][cadre-img]][cadre-url]
[![][kloudless-img]][kloudless-url]
[![][esg-img]][esg-url]
[![][lightson-img]][lightson-url]
[![][retool-img]][retool-url]
[![][bitio-img]][bitio-url]
Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Rollbar][rollbar-url], [Cadre][cadre-url], [Kloudless][kloudless-url], [ESG][esg-url], and [Lights On Software][lightson-url].
Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Rollbar][rollbar-url], [ESG][esg-url], [Retool][retool-url], and [bit.io][bitio-url].
---
@ -53,8 +52,8 @@ There is a live example API for testing purposes, [available here][sandbox].
# Requirements
* Python (3.5, 3.6, 3.7)
* Django (1.11, 2.0, 2.1, 2.2)
* Python (3.5, 3.6, 3.7, 3.8, 3.9)
* Django (2.2, 3.0, 3.1)
We **highly recommend** and only officially support the latest patch release of
each Python and Django series.
@ -88,7 +87,7 @@ Startup up a new project like so...
Now edit the `example/urls.py` module in your project:
```python
from django.conf.urls import url, include
from django.urls import path, include
from django.contrib.auth.models import User
from rest_framework import serializers, viewsets, routers
@ -113,8 +112,8 @@ router.register(r'users', UserViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
path('', include(router.urls)),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
```
@ -190,23 +189,19 @@ Please see the [security policy][security-policy].
[funding]: https://fund.django-rest-framework.org/topics/funding/
[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors
[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
[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
[kloudless-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/kloudless-readme.png
[esg-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/esg-readme.png
[lightson-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/lightson-readme.png
[retool-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/retool-readme.png
[bitio-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/bitio-readme.png
[sentry-url]: https://getsentry.com/welcome/
[stream-url]: https://getstream.io/try-the-api/?utm_source=drf&utm_medium=banner&utm_campaign=drf
[rollbar-url]: https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial
[cadre-url]: https://cadre.com/
[kloudless-url]: https://hubs.ly/H0f30Lf0
[esg-url]: https://software.esg-usa.com/
[lightson-url]: https://lightsonsoftware.com
[retool-url]: https://retool.com/?utm_source=djangorest&utm_medium=sponsorship
[bitio-url]: https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship
[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

View File

@ -60,8 +60,8 @@ using the `APIView` class-based views.
def get(self, request, format=None):
content = {
'user': unicode(request.user), # `django.contrib.auth.User` instance.
'auth': unicode(request.auth), # None
'user': str(request.user), # `django.contrib.auth.User` instance.
'auth': str(request.auth), # None
}
return Response(content)
@ -72,8 +72,8 @@ Or, if you're using the `@api_view` decorator with function based views.
@permission_classes([IsAuthenticated])
def example_view(request, format=None):
content = {
'user': unicode(request.user), # `django.contrib.auth.User` instance.
'auth': unicode(request.auth), # None
'user': str(request.user), # `django.contrib.auth.User` instance.
'auth': str(request.auth), # None
}
return Response(content)
@ -199,7 +199,7 @@ When using `TokenAuthentication`, you may want to provide a mechanism for client
from rest_framework.authtoken import views
urlpatterns += [
url(r'^api-token-auth/', views.obtain_auth_token)
path('api-token-auth/', views.obtain_auth_token)
]
Note that the URL part of the pattern can be whatever you want to use.
@ -238,7 +238,7 @@ For example, you may return additional user information beyond the `token` value
And in your `urls.py`:
urlpatterns += [
url(r'^api-token-auth/', CustomAuthToken.as_view())
path('api-token-auth/', CustomAuthToken.as_view())
]
@ -304,7 +304,7 @@ If successfully authenticated, `RemoteUserAuthentication` provides the following
Consult your web server's documentation for information about configuring an authentication method, e.g.:
* [Apache Authentication How-To](https://httpd.apache.org/docs/2.4/howto/auth.html)
* [NGINX (Restricting Access)](https://www.nginx.com/resources/admin-guide/#restricting_access)
* [NGINX (Restricting Access)](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/)
# Custom authentication
@ -357,7 +357,7 @@ The following third party packages are also available.
## Django OAuth Toolkit
The [Django OAuth Toolkit][django-oauth-toolkit] package provides OAuth 2.0 support and works with Python 3.4+. The package is maintained by [Evonove][evonove] and uses the excellent [OAuthLib][oauthlib]. The package is well documented, and well supported and is currently our **recommended package for OAuth 2.0 support**.
The [Django OAuth Toolkit][django-oauth-toolkit] package provides OAuth 2.0 support and works with Python 3.4+. The package is maintained by [jazzband][jazzband] and uses the excellent [OAuthLib][oauthlib]. The package is well documented, and well supported and is currently our **recommended package for OAuth 2.0 support**.
#### Installation & configuration
@ -410,9 +410,15 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a
[Djoser][djoser] library provides a set of views to handle basic actions such as registration, login, logout, password reset and account activation. The package works with a custom user model and it uses token based authentication. This is a ready to use REST implementation of Django authentication system.
## django-rest-auth
## django-rest-auth / dj-rest-auth
[Django-rest-auth][django-rest-auth] library provides a set of REST API endpoints for registration, authentication (including social media authentication), password reset, retrieve and update user details, etc. By having these API endpoints, your client apps such as AngularJS, iOS, Android, and others can communicate to your Django backend site independently via REST APIs for user management.
This library provides a set of REST API endpoints for registration, authentication (including social media authentication), password reset, retrieve and update user details, etc. By having these API endpoints, your client apps such as AngularJS, iOS, Android, and others can communicate to your Django backend site independently via REST APIs for user management.
There are currently two forks of this project.
* [Django-rest-auth][django-rest-auth] is the original project, [but is not currently receiving updates](https://github.com/Tivix/django-rest-auth/issues/568).
* [Dj-rest-auth][dj-rest-auth] is a newer fork of the project.
## django-rest-framework-social-oauth2
@ -442,7 +448,7 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a
[djangorestframework-digestauth]: https://github.com/juanriaza/django-rest-framework-digestauth
[oauth-1.0a]: https://oauth.net/core/1.0a/
[django-oauth-toolkit]: https://github.com/evonove/django-oauth-toolkit
[evonove]: https://github.com/evonove/
[jazzband]: https://github.com/jazzband/
[oauthlib]: https://github.com/idan/oauthlib
[djangorestframework-simplejwt]: https://github.com/davesque/django-rest-framework-simplejwt
[etoccalino]: https://github.com/etoccalino/
@ -456,6 +462,7 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a
[mac]: https://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05
[djoser]: https://github.com/sunscrapers/djoser
[django-rest-auth]: https://github.com/Tivix/django-rest-auth
[dj-rest-auth]: https://github.com/jazzband/dj-rest-auth
[django-rest-framework-social-oauth2]: https://github.com/PhilipGarnero/django-rest-framework-social-oauth2
[django-rest-knox]: https://github.com/James1345/django-rest-knox
[drfpasswordless]: https://github.com/aaronn/django-rest-framework-passwordless

View File

@ -17,11 +17,16 @@ other cache decorators such as [`cache_page`][page] and
[`vary_on_cookie`][cookie].
```python
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import viewsets
class UserViewSet(viewsets.Viewset):
class UserViewSet(viewsets.ViewSet):
# Cache requested url for each user for 2 hours
@method_decorator(cache_page(60*60*2))
@ -32,6 +37,7 @@ class UserViewSet(viewsets.Viewset):
}
return Response(content)
class PostView(APIView):
# Cache page for the requested url

View File

@ -38,7 +38,7 @@ Might receive an error response indicating that the `DELETE` method is not allow
Validation errors are handled slightly differently, and will include the field names as the keys in the response. If the validation error was not specific to a particular field then it will use the "non_field_errors" key, or whatever string value has been set for the `NON_FIELD_ERRORS_KEY` setting.
Any example validation error might look like this:
An example validation error might look like this:
HTTP/1.1 400 Bad Request
Content-Type: application/json

View File

@ -50,9 +50,21 @@ If set, this gives the default value that will be used for the field if no input
The `default` is not applied during partial update operations. In the partial update case only fields that are provided in the incoming data will have a validated value returned.
May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a `set_context` method, that will be called each time before getting the value with the field instance as only argument. This works the same way as for [validators](validators.md#using-set_context).
May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a `requires_context = True` attribute, then the serializer field will be passed as an argument.
When serializing the instance, default will be used if the the object attribute or dictionary key is not present in the instance.
For example:
class CurrentUserDefault:
"""
May be applied as a `default=...` value on a serializer field.
Returns the current user.
"""
requires_context = True
def __call__(self, serializer_field):
return serializer_field.context['request'].user
When serializing the instance, default will be used if the object attribute or dictionary key is not present in the instance.
Note that setting a `default` value implies that the field is not required. Including both the `default` and `required` keyword arguments is invalid and will raise an error.
@ -359,7 +371,7 @@ Corresponds to `django.db.models.fields.TimeField`
* `format` - A string representing the output format. If not specified, this defaults to the same value as the `TIME_FORMAT` settings key, which will be `'iso-8601'` unless set. Setting to a format string indicates that `to_representation` return values should be coerced to string output. Format strings are described below. Setting this value to `None` indicates that Python `time` objects should be returned by `to_representation`. In this case the time encoding will be determined by the renderer.
* `input_formats` - A list of strings representing the input formats which may be used to parse the date. If not specified, the `TIME_INPUT_FORMATS` setting will be used, which defaults to `['iso-8601']`.
#### `TimeField` format strings
#### `TimeField` format strings
Format strings may either be [Python strftime formats][strftime] which explicitly specify the format, or the special string `'iso-8601'`, which indicates that [ISO 8601][iso8601] style times should be used. (eg `'12:34:56.000000'`)
@ -583,9 +595,7 @@ If you want to create a custom field, you'll need to subclass `Field` and then o
The `.to_representation()` method is called to convert the initial datatype into a primitive, serializable datatype.
The `to_internal_value()` method is called to restore a primitive datatype into its internal python representation. This method should raise a `serializers.ValidationError` if the data is invalid.
Note that the `WritableField` class that was present in version 2.x no longer exists. You should subclass `Field` and override `to_internal_value()` if the field supports data input.
The `.to_internal_value()` method is called to restore a primitive datatype into its internal python representation. This method should raise a `serializers.ValidationError` if the data is invalid.
## Examples
@ -593,7 +603,7 @@ Note that the `WritableField` class that was present in version 2.x no longer ex
Let's look at an example of serializing a class that represents an RGB color value:
class Color(object):
class Color:
"""
A color represented in the RGB colorspace.
"""
@ -713,7 +723,7 @@ the coordinate pair:
fields = ['label', 'coordinates']
Note that this example doesn't handle validation. Partly for that reason, in a
real project, the coordinate nesting might be better handled with a nested serialiser
real project, the coordinate nesting might be better handled with a nested serializer
using `source='*'`, with two `IntegerField` instances, each with their own `source`
pointing to the relevant field.
@ -746,7 +756,7 @@ suitable for updating our target object. With `source='*'`, the return from
('y_coordinate', 4),
('x_coordinate', 3)])
For completeness lets do the same thing again but with the nested serialiser
For completeness lets do the same thing again but with the nested serializer
approach suggested above:
class NestedCoordinateSerializer(serializers.Serializer):
@ -768,14 +778,14 @@ declarations. It's our `NestedCoordinateSerializer` that takes `source='*'`.
Our new `DataPointSerializer` exhibits the same behaviour as the custom field
approach.
Serialising:
Serializing:
>>> out_serializer = DataPointSerializer(instance)
>>> out_serializer.data
ReturnDict([('label', 'testing'),
('coordinates', OrderedDict([('x', 1), ('y', 2)]))])
Deserialising:
Deserializing:
>>> in_serializer = DataPointSerializer(data=data)
>>> in_serializer.is_valid()
@ -802,8 +812,8 @@ But we also get the built-in validation for free:
{'x': ['A valid integer is required.'],
'y': ['A valid integer is required.']})])
For this reason, the nested serialiser approach would be the first to try. You
would use the custom field approach when the nested serialiser becomes infeasible
For this reason, the nested serializer approach would be the first to try. You
would use the custom field approach when the nested serializer becomes infeasible
or overly complex.

View File

@ -45,7 +45,7 @@ Another style of filtering might involve restricting the queryset based on some
For example if your URL config contained an entry like this:
url('^purchases/(?P<username>.+)/$', PurchaseList.as_view()),
re_path('^purchases/(?P<username>.+)/$', PurchaseList.as_view()),
You could then write a view that returned a purchase queryset filtered by the username portion of the URL:
@ -145,10 +145,18 @@ Note that you can use both an overridden `.get_queryset()` and generic filtering
The [`django-filter`][django-filter-docs] library includes a `DjangoFilterBackend` class which
supports highly customizable field filtering for REST framework.
To use `DjangoFilterBackend`, first install `django-filter`. Then add `django_filters` to Django's `INSTALLED_APPS`
To use `DjangoFilterBackend`, first install `django-filter`.
pip install django-filter
Then add `'django_filters'` to Django's `INSTALLED_APPS`:
INSTALLED_APPS = [
...
'django_filters',
...
]
You should now either add the filter backend to your settings:
REST_FRAMEWORK = {
@ -205,6 +213,10 @@ This will allow the client to filter the items in the list by making queries suc
You can also perform a related lookup on a ForeignKey or ManyToManyField with the lookup API double-underscore notation:
search_fields = ['username', 'email', 'profile__profession']
For [JSONField][JSONField] and [HStoreField][HStoreField] fields you can filter based on nested values within the data structure using the same double-underscore notation:
search_fields = ['data__breed', 'data__owner__other_pets__0__name']
By default, searches will use case-insensitive partial matches. The search parameter may contain multiple search terms, which should be whitespace and/or comma separated. If multiple search terms are used then objects will be returned in the list only if all the provided terms are matched.
@ -212,7 +224,7 @@ The search behavior may be restricted by prepending various characters to the `s
* '^' Starts-with search.
* '=' Exact matches.
* '@' Full-text search. (Currently only supported Django's MySQL backend.)
* '@' Full-text search. (Currently only supported Django's [PostgreSQL backend](https://docs.djangoproject.com/en/dev/ref/contrib/postgres/search/).)
* '$' Regex search.
For example:
@ -360,3 +372,5 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter]
[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
[HStoreField]: https://docs.djangoproject.com/en/3.0/ref/contrib/postgres/fields/#hstorefield
[JSONField]: https://docs.djangoproject.com/en/3.0/ref/contrib/postgres/fields/#jsonfield

View File

@ -32,9 +32,9 @@ Example:
from blog import views
urlpatterns = [
url(r'^/$', views.apt_root),
url(r'^comments/$', views.comment_list),
url(r'^comments/(?P<pk>[0-9]+)/$', views.comment_detail)
path('', views.apt_root),
path('comments/', views.comment_list),
path('comments/<int:pk>/', views.comment_detail)
]
urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html'])

View File

@ -45,7 +45,7 @@ For more complex cases you might also want to override various methods on the vi
For very simple cases you might want to pass through any class attributes using the `.as_view()` method. For example, your URLconf might include something like the following entry:
url(r'^/users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')
path('users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')
---
@ -175,8 +175,6 @@ You can also use these hooks to provide additional validation, by raising a `Val
raise ValidationError('You have already signed up')
serializer.save(user=self.request.user)
**Note**: These methods replace the old-style version 2.x `pre_save`, `post_save`, `pre_delete` and `post_delete` methods, which are no longer available.
**Other methods**:
You won't typically need to override the following methods, although you might need to call into them if you're writing custom views using `GenericAPIView`.
@ -321,7 +319,7 @@ Often you'll want to use the existing generic views, but use some slightly custo
For example, if you need to lookup objects based on multiple fields in the URL conf, you could create a mixin class like the following:
class MultipleFieldLookupMixin(object):
class MultipleFieldLookupMixin:
"""
Apply this mixin to any view or viewset to get multiple field filtering
based on a `lookup_fields` attribute, instead of the default single field filtering.
@ -378,10 +376,6 @@ If you need to generic PUT-as-create behavior you may want to include something
The following third party packages provide additional generic view implementations.
## Django REST Framework bulk
The [django-rest-framework-bulk package][django-rest-framework-bulk] implements generic view mixins as well as some common concrete generic views to allow to apply bulk operations via API requests.
## Django Rest Multiple Models
[Django Rest Multiple Models][django-rest-multiple-models] provides a generic view (and mixin) for sending multiple serialized models and/or querysets via a single API request.
@ -394,5 +388,4 @@ The [django-rest-framework-bulk package][django-rest-framework-bulk] implements
[RetrieveModelMixin]: #retrievemodelmixin
[UpdateModelMixin]: #updatemodelmixin
[DestroyModelMixin]: #destroymodelmixin
[django-rest-framework-bulk]: https://github.com/miki725/django-rest-framework-bulk
[django-rest-multiple-models]: https://github.com/MattBroach/DjangoRestMultipleModels

View File

@ -71,7 +71,7 @@ If you have specific requirements for creating schema endpoints that are accesse
For example, the following additional route could be used on a viewset to provide a linkable schema endpoint.
@action(methods=['GET'], detail=False)
def schema(self, request):
def api_schema(self, request):
meta = self.metadata_class()
data = meta.determine_metadata(request, self)
return Response(data)

View File

@ -73,7 +73,7 @@ Or, if you're using the `@api_view` decorator with function based views.
## JSONParser
Parses `JSON` request content.
Parses `JSON` request content. `request.data` will be populated with a dictionary of data.
**.media_type**: `application/json`
@ -125,7 +125,7 @@ If it is called without a `filename` URL keyword argument, then the client must
# urls.py
urlpatterns = [
# ...
url(r'^upload/(?P<filename>[^/]+)$', FileUploadView.as_view())
re_path(r'^upload/(?P<filename>[^/]+)$', FileUploadView.as_view())
]
---

View File

@ -231,7 +231,7 @@ If you need to test if a request is a read operation or a write operation, you s
---
Custom permissions will raise a `PermissionDenied` exception if the test fails. To change the error message associated with the exception, implement a `message` attribute directly on your custom permission. Otherwise the `default_detail` attribute from `PermissionDenied` will be used.
Custom permissions will raise a `PermissionDenied` exception if the test fails. To change the error message associated with the exception, implement a `message` attribute directly on your custom permission. Otherwise the `default_detail` attribute from `PermissionDenied` will be used. Similarly, to change the code identifier associated with the exception, implement a `code` attribute directly on your custom permission - otherwise the `default_code` attribute from `PermissionDenied` will be used.
from rest_framework import permissions
@ -243,19 +243,19 @@ Custom permissions will raise a `PermissionDenied` exception if the test fails.
## Examples
The following is an example of a permission class that checks the incoming request's IP address against a blacklist, and denies the request if the IP has been blacklisted.
The following is an example of a permission class that checks the incoming request's IP address against a blocklist, and denies the request if the IP has been blocked.
from rest_framework import permissions
class BlacklistPermission(permissions.BasePermission):
class BlocklistPermission(permissions.BasePermission):
"""
Global permission check for blacklisted IPs.
Global permission check for blocked IPs.
"""
def has_permission(self, request, view):
ip_addr = request.META['REMOTE_ADDR']
blacklisted = Blacklist.objects.filter(ip_addr=ip_addr).exists()
return not blacklisted
blocked = Blocklist.objects.filter(ip_addr=ip_addr).exists()
return not blocked
As well as global permissions, that are run against all incoming requests, you can also create object-level permissions, that are only run against operations that affect a particular object instance. For example:
@ -312,6 +312,11 @@ The [Django REST Framework API Key][djangorestframework-api-key] package provide
The [Django Rest Framework Role Filters][django-rest-framework-role-filters] package provides simple filtering over multiple types of roles.
## Django Rest Framework PSQ
The [Django Rest Framework PSQ][drf-psq] package is an extension that gives support for having action-based **permission_classes**, **serializer_class**, and **queryset** dependent on permission-based rules.
[cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html
[authentication]: authentication.md
[throttling]: throttling.md
@ -322,9 +327,10 @@ The [Django Rest Framework Role Filters][django-rest-framework-role-filters] pac
[filtering]: filtering.md
[composed-permissions]: https://github.com/niwibe/djangorestframework-composed-permissions
[rest-condition]: https://github.com/caxap/rest_condition
[dry-rest-permissions]: https://github.com/Helioscene/dry-rest-permissions
[dry-rest-permissions]: https://github.com/FJNR-inc/dry-rest-permissions
[django-rest-framework-roles]: https://github.com/computer-lab/django-rest-framework-roles
[djangorestframework-api-key]: https://florimondmanca.github.io/djangorestframework-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
[drf-access-policy]: https://github.com/rsinger86/drf-access-policy
[drf-psq]: https://github.com/drf-psq/drf-psq

View File

@ -56,7 +56,7 @@ In order to explain the various types of relational fields, we'll use a couple o
`StringRelatedField` may be used to represent the target of the relationship using its `__str__` method.
For example, the following serializer.
For example, the following serializer:
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.StringRelatedField(many=True)
@ -65,7 +65,7 @@ For example, the following serializer.
model = Album
fields = ['album_name', 'artist', 'tracks']
Would serialize to the following representation.
Would serialize to the following representation:
{
'album_name': 'Things We Lost In The Fire',
@ -245,7 +245,9 @@ This field is always read-only.
# Nested relationships
Nested relationships can be expressed by using serializers as fields.
As opposed to previously discussed _references_ to another entity, the referred entity can instead also be embedded or _nested_
in the representation of the object that refers to it.
Such nested relationships can be expressed by using serializers as fields.
If the field is used to represent a to-many relationship, you should add the `many=True` flag to the serializer field.
@ -289,7 +291,7 @@ Would serialize to a nested representation like this:
## Writable nested serializers
By default nested serializers are read-only. If you want to support write-operations to a nested serializer field you'll need to create `create()` and/or `update()` methods in order to explicitly specify how the child relationships should be saved.
By default nested serializers are read-only. If you want to support write-operations to a nested serializer field you'll need to create `create()` and/or `update()` methods in order to explicitly specify how the child relationships should be saved:
class TrackSerializer(serializers.ModelSerializer):
class Meta:
@ -335,13 +337,13 @@ output representation should be generated from the model instance.
To implement a custom relational field, you should override `RelatedField`, and implement the `.to_representation(self, value)` method. This method takes the target of the field as the `value` argument, and should return the representation that should be used to serialize the target. The `value` argument will typically be a model instance.
If you want to implement a read-write relational field, you must also implement the `.to_internal_value(self, data)` method.
If you want to implement a read-write relational field, you must also implement the [`.to_internal_value(self, data)` method][to_internal_value].
To provide a dynamic queryset based on the `context`, you can also override `.get_queryset(self)` instead of specifying `.queryset` on the class or when initializing the field.
## Example
For example, we could define a relational field to serialize a track to a custom string representation, using its ordering, title, and duration.
For example, we could define a relational field to serialize a track to a custom string representation, using its ordering, title, and duration:
import time
@ -357,7 +359,7 @@ For example, we could define a relational field to serialize a track to a custom
model = Album
fields = ['album_name', 'artist', 'tracks']
This custom field would then serialize to the following representation.
This custom field would then serialize to the following representation:
{
'album_name': 'Sometimes I Wish We Were an Eagle',
@ -533,7 +535,7 @@ And the following two models, which may have associated tags:
text = models.CharField(max_length=1000)
tags = GenericRelation(TaggedItem)
We could define a custom field that could be used to serialize tagged instances, using the type of each instance to determine how it should be serialized.
We could define a custom field that could be used to serialize tagged instances, using the type of each instance to determine how it should be serialized:
class TaggedObjectRelatedField(serializers.RelatedField):
"""
@ -601,5 +603,6 @@ The [rest-framework-generic-relations][drf-nested-relations] library provides re
[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
[django-intermediary-manytomany]: https://docs.djangoproject.com/en/2.2/topics/db/models/#intermediary-manytomany
[django-intermediary-manytomany]: https://docs.djangoproject.com/en/stable/topics/db/models/#intermediary-manytomany
[dealing-with-nested-objects]: https://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects
[to_internal_value]: https://www.django-rest-framework.org/api-guide/serializers/#to_internal_valueself-data

View File

@ -103,6 +103,16 @@ Unlike other renderers, the data passed to the `Response` does not need to be se
The TemplateHTMLRenderer will create a `RequestContext`, using the `response.data` as the context dict, and determine a template name to use to render the context.
---
**Note:** When used with a view that makes use of a serializer the `Response` sent for rendering may not be a dictionay and will need to be wrapped in a dict before returning to allow the TemplateHTMLRenderer to render it. For example:
```
response.data = {'results': response.data}
```
---
The template name is determined by (in order of preference):
1. An explicit `template_name` argument passed to the response.
@ -273,7 +283,7 @@ By default this will include the following keys: `view`, `request`, `response`,
The following is an example plaintext renderer that will return a response with the `data` parameter as the content of the response.
from django.utils.encoding import smart_unicode
from django.utils.encoding import smart_text
from rest_framework import renderers
@ -282,7 +292,7 @@ The following is an example plaintext renderer that will return a response with
format = 'txt'
def render(self, data, media_type=None, renderer_context=None):
return data.encode(self.charset)
return smart_text(data, encoding=self.charset)
## Setting the character set
@ -503,7 +513,7 @@ Comma-separated values are a plain-text tabular data format, that can be easily
## UltraJSON
[UltraJSON][ultrajson] is an optimized C JSON encoder which can give significantly faster JSON rendering. [Jacob Haslehurst][hzy] maintains the [drf-ujson-renderer][drf-ujson-renderer] package which implements JSON rendering using the UJSON package.
[UltraJSON][ultrajson] is an optimized C JSON encoder which can give significantly faster JSON rendering. [Adam Mertz][Amertz08] maintains [drf_ujson2][drf_ujson2], a fork of the now unmaintained [drf-ujson-renderer][drf-ujson-renderer], which implements JSON rendering using the UJSON package.
## CamelCase JSON
@ -547,8 +557,9 @@ Comma-separated values are a plain-text tabular data format, that can be easily
[djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack
[djangorestframework-csv]: https://github.com/mjumbewu/django-rest-framework-csv
[ultrajson]: https://github.com/esnme/ultrajson
[hzy]: https://github.com/hzy
[Amertz08]: https://github.com/Amertz08
[drf-ujson-renderer]: https://github.com/gizmag/drf-ujson-renderer
[drf_ujson2]: https://github.com/Amertz08/drf_ujson2
[djangorestframework-camel-case]: https://github.com/vbabiy/djangorestframework-camel-case
[Django REST Pandas]: https://github.com/wq/django-rest-pandas
[Pandas]: https://pandas.pydata.org/

View File

@ -23,7 +23,7 @@ REST framework's Request objects provide flexible request parsing that allows yo
* It includes all parsed content, including *file and non-file* inputs.
* It supports parsing the content of HTTP methods other than `POST`, meaning that you can access the content of `PUT` and `PATCH` requests.
* It supports REST framework's flexible request parsing, rather than just supporting form data. For example you can handle incoming JSON data in the same way that you handle incoming form data.
* It supports REST framework's flexible request parsing, rather than just supporting form data. For example you can handle incoming [JSON data] similarly to how you handle incoming [form data].
For more details see the [parsers documentation].
@ -49,7 +49,7 @@ If a client sends a request with a content-type that cannot be parsed then a `Un
# Content negotiation
The request exposes some properties that allow you to determine the result of the content negotiation stage. This allows you to implement behaviour such as selecting a different serialisation schemes for different media types.
The request exposes some properties that allow you to determine the result of the content negotiation stage. This allows you to implement behaviour such as selecting a different serialization schemes for different media types.
## .accepted_renderer
@ -136,5 +136,7 @@ Note that due to implementation reasons the `Request` class does not inherit fro
[cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion
[parsers documentation]: parsers.md
[JSON data]: parsers.md#jsonparser
[form data]: parsers.md#formparser
[authentication documentation]: authentication.md
[browser enhancements documentation]: ../topics/browser-enhancements.md

View File

@ -94,5 +94,5 @@ As with any other `TemplateResponse`, this method is called to render the serial
You won't typically need to call `.render()` yourself, as it's handled by Django's standard response cycle.
[cite]: https://docs.djangoproject.com/en/stable/stable/template-response/
[cite]: https://docs.djangoproject.com/en/stable/ref/template-response/
[statuscodes]: status-codes.md

View File

@ -63,7 +63,7 @@ For example, you can append `router.urls` to a list of existing views...
router.register(r'accounts', AccountViewSet)
urlpatterns = [
url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
path('forgot-password/', ForgotPasswordFormView.as_view()),
]
urlpatterns += router.urls
@ -71,22 +71,22 @@ For example, you can append `router.urls` to a list of existing views...
Alternatively you can use Django's `include` function, like so...
urlpatterns = [
url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
url(r'^', include(router.urls)),
path('forgot-password', ForgotPasswordFormView.as_view()),
path('', include(router.urls)),
]
You may use `include` with an application namespace:
urlpatterns = [
url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
url(r'^api/', include((router.urls, 'app_name'))),
path('forgot-password/', ForgotPasswordFormView.as_view()),
path('api/', include((router.urls, 'app_name'))),
]
Or both an application and instance namespace:
urlpatterns = [
url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
url(r'^api/', include((router.urls, 'app_name'), namespace='instance_name')),
path('forgot-password/', ForgotPasswordFormView.as_view()),
path('api/', include((router.urls, 'app_name'), namespace='instance_name')),
]
See Django's [URL namespaces docs][url-namespace-docs] and the [`include` API reference][include-api-reference] for more details.

View File

@ -1,6 +1,6 @@
---
source:
- schemas.py
- schemas
---
# Schema
@ -16,21 +16,40 @@ can interact with your API.
Django REST Framework provides support for automatic generation of
[OpenAPI][openapi] schemas.
## Overview
Schema generation has several moving parts. It's worth having an overview:
* `SchemaGenerator` is a top-level class that is responsible for walking your
configured URL patterns, finding `APIView` subclasses, enquiring for their
schema representation, and compiling the final schema object.
* `AutoSchema` encapsulates all the details necessary for per-view schema
introspection. Is attached to each view via the `schema` attribute. You
subclass `AutoSchema` in order to customize your schema.
* The `generateschema` management command allows you to generate a static schema
offline.
* Alternatively, you can route `SchemaView` to dynamically generate and serve
your schema.
* `settings.DEFAULT_SCHEMA_CLASS` allows you to specify an `AutoSchema`
subclass to serve as your project's default.
The following sections explain more.
## Generating an OpenAPI Schema
### Install `pyyaml`
### Install dependencies
You'll need to install `pyyaml`, so that you can render your generated schema
into the commonly used YAML-based OpenAPI format.
pip install pyyaml uritemplate
pip install pyyaml
* `pyyaml` is used to generate schema into YAML-based OpenAPI format.
* `uritemplate` is used internally to get parameters in path.
### Generating a static schema with the `generateschema` management command
If your schema is static, you can use the `generateschema` management command:
```bash
./manage.py generateschema > openapi-schema.yml
./manage.py generateschema --file openapi-schema.yml
```
Once you've generated a schema in this way you can annotate it with any
@ -60,7 +79,8 @@ urlpatterns = [
# * Provide view name for use with `reverse()`.
path('openapi', get_schema_view(
title="Your Project",
description="API for all things …"
description="API for all things …",
version="1.0.0"
), name='openapi-schema'),
# ...
]
@ -72,6 +92,7 @@ The `get_schema_view()` helper takes the following keyword arguments:
* `title`: May be used to provide a descriptive title for the schema definition.
* `description`: Longer descriptive text.
* `version`: The version of the API.
* `url`: May be used to pass a canonical base URL for the schema.
schema_view = get_schema_view(
@ -88,11 +109,12 @@ The `get_schema_view()` helper takes the following keyword arguments:
url='https://www.example.org/api/',
urlconf='myproject.urls'
)
* `patterns`: List of url patterns to limit the schema introspection to. If you
only want the `myproject.api` urls to be exposed in the schema:
schema_url_patterns = [
url(r'^api/', include('myproject.api.urls')),
path('api/', include('myproject.api.urls')),
]
schema_view = get_schema_view(
@ -113,23 +135,19 @@ The `get_schema_view()` helper takes the following keyword arguments:
be used to render the API root endpoint.
## Customizing Schema Generation
## SchemaGenerator
You may customize schema generation at the level of the schema as a whole, or
on a per-view basis.
**Schema-level customization**
### Schema Level Customization
```python
from rest_framework.schemas.openapi import SchemaGenerator
```
In order to customize the top-level schema sublass
`rest_framework.schemas.openapi.SchemaGenerator` and provide it as an argument
to the `generateschema` command or `get_schema_view()` helper function.
`SchemaGenerator` is a class that walks a list of routed URL patterns, requests
the schema for each view and collates the resulting OpenAPI schema.
#### SchemaGenerator
A class that walks a list of routed URL patterns, requests the schema for each
view and collates the resulting OpenAPI schema.
Typically you'll instantiate `SchemaGenerator` with a `title` argument, like so:
Typically you won't need to instantiate `SchemaGenerator` yourself, but you can
do so like so:
generator = SchemaGenerator(title='Stock Prices API')
@ -137,11 +155,17 @@ Arguments:
* `title` **required**: The name of the API.
* `description`: Longer descriptive text.
* `version`: The version of the API. Defaults to `0.1.0`.
* `url`: The root URL of the API schema. This option is not required unless the schema is included under path prefix.
* `patterns`: A list of URLs to inspect when generating the schema. Defaults to the project's URL conf.
* `urlconf`: A URL conf module name to use when generating the schema. Defaults to `settings.ROOT_URLCONF`.
##### get_schema(self, request)
In order to customize the top-level schema, subclass
`rest_framework.schemas.openapi.SchemaGenerator` and provide your subclass
as an argument to the `generateschema` command or `get_schema_view()` helper
function.
### get_schema(self, request)
Returns a dictionary that represents the OpenAPI schema:
@ -151,68 +175,245 @@ Returns a dictionary that represents the OpenAPI schema:
The `request` argument is optional, and may be used if you want to apply
per-user permissions to the resulting schema generation.
This is a good point to override if you want to customise the generated
dictionary, for example to add custom
[specification extensions][openapi-specification-extensions].
This is a good point to override if you want to customize the generated
dictionary For example you might wish to add terms of service to the [top-level
`info` object][info-object]:
### Per-View Customization
```
class TOSSchemaGenerator(SchemaGenerator):
def get_schema(self, *args, **kwargs):
schema = super().get_schema(*args, **kwargs)
schema["info"]["termsOfService"] = "https://example.com/tos.html"
return schema
```
## AutoSchema
**Per-View Customization**
```python
from rest_framework.schemas.openapi import AutoSchema
```
By default, view introspection is performed by an `AutoSchema` instance
accessible via the `schema` attribute on `APIView`. This provides the
appropriate [Open API operation object][openapi-operation] for the view,
request method and path:
accessible via the `schema` attribute on `APIView`.
auto_schema = view.schema
operation = auto_schema.get_operation(...)
auto_schema = some_view.schema
In compiling the schema, `SchemaGenerator` calls `view.schema.get_operation()`
for each view, allowed method, and path.
`AutoSchema` provides the OpenAPI elements needed for each view, request method
and path:
---
* A list of [OpenAPI components][openapi-components]. In DRF terms these are
mappings of serializers that describe request and response bodies.
* The appropriate [OpenAPI operation object][openapi-operation] that describes
the endpoint, including path and query parameters for pagination, filtering,
and so on.
**Note**: For basic `APIView` subclasses, default introspection is essentially
limited to the URL kwarg path parameters. For `GenericAPIView`
subclasses, which includes all the provided class based views, `AutoSchema` will
attempt to introspect serialiser, pagination and filter fields, as well as
provide richer path field descriptions. (The key hooks here are the relevant
`GenericAPIView` attributes and methods: `get_serializer`, `pagination_class`,
`filter_backends` and so on.)
```python
components = auto_schema.get_components(...)
operation = auto_schema.get_operation(...)
```
---
In compiling the schema, `SchemaGenerator` calls `get_components()` and
`get_operation()` for each view, allowed method, and path.
In order to customise the operation generation, you should provide an `AutoSchema` subclass, overriding `get_operation()` as you need:
----
**Note**: The automatic introspection of components, and many operation
parameters relies on the relevant attributes and methods of
`GenericAPIView`: `get_serializer()`, `pagination_class`, `filter_backends`,
etc. For basic `APIView` subclasses, default introspection is essentially limited to
the URL kwarg path parameters for this reason.
from rest_framework.views import APIView
from rest_framework.schemas.openapi import AutoSchema
----
class CustomSchema(AutoSchema):
def get_link(...):
# Implement custom introspection here (or in other sub-methods)
`AutoSchema` encapsulates the view introspection needed for schema generation.
Because of this all the schema generation logic is kept in a single place,
rather than being spread around the already extensive view, serializer and
field APIs.
class CustomView(APIView):
"""APIView subclass with custom schema introspection."""
schema = CustomSchema()
Keeping with this pattern, try not to let schema logic leak into your own
views, serializers, or fields when customizing the schema generation. You might
be tempted to do something like this:
This provides complete control over view introspection.
```python
class CustomSchema(AutoSchema):
"""
AutoSchema subclass using schema_extra_info on the view.
"""
...
You may disable schema generation for a view by setting `schema` to `None`:
class CustomView(APIView):
schema = CustomSchema()
schema_extra_info = ... some extra info ...
```
class CustomView(APIView):
...
schema = None # Will not appear in schema
Here, the `AutoSchema` subclass goes looking for `schema_extra_info` on the
view. This is _OK_ (it doesn't actually hurt) but it means you'll end up with
your schema logic spread out in a number of different places.
This also applies to extra actions for `ViewSet`s:
Instead try to subclass `AutoSchema` such that the `extra_info` doesn't leak
out into the view:
class CustomViewSet(viewsets.ModelViewSet):
```python
class BaseSchema(AutoSchema):
"""
AutoSchema subclass that knows how to use extra_info.
"""
...
@action(detail=True, schema=None)
def extra_action(self, request, pk=None):
...
class CustomSchema(BaseSchema):
extra_info = ... some extra info ...
If you wish to provide a base `AutoSchema` subclass to be used throughout your
project you may adjust `settings.DEFAULT_SCHEMA_CLASS` appropriately.
class CustomView(APIView):
schema = CustomSchema()
```
This style is slightly more verbose but maintains the encapsulation of the
schema related code. It's more _cohesive_ in the _parlance_. It'll keep the
rest of your API code more tidy.
If an option applies to many view classes, rather than creating a specific
subclass per-view, you may find it more convenient to allow specifying the
option as an `__init__()` kwarg to your base `AutoSchema` subclass:
```python
class CustomSchema(BaseSchema):
def __init__(self, **kwargs):
# store extra_info for later
self.extra_info = kwargs.pop("extra_info")
super().__init__(**kwargs)
class CustomView(APIView):
schema = CustomSchema(
extra_info=... some extra info ...
)
```
This saves you having to create a custom subclass per-view for a commonly used option.
Not all `AutoSchema` methods expose related `__init__()` kwargs, but those for
the more commonly needed options do.
### `AutoSchema` methods
#### `get_components()`
Generates the OpenAPI components that describe request and response bodies,
deriving their properties from the serializer.
Returns a dictionary mapping the component name to the generated
representation. By default this has just a single pair but you may override
`get_components()` to return multiple pairs if your view uses multiple
serializers.
#### `get_component_name()`
Computes the component's name from the serializer.
You may see warnings if your API has duplicate component names. If so you can override `get_component_name()` or pass the `component_name` `__init__()` kwarg (see below) to provide different names.
#### `map_serializer()`
Maps serializers to their OpenAPI representations.
Most serializers should conform to the standard OpenAPI `object` type, but you may
wish to override `map_serializer()` in order to customize this or other
serializer-level fields.
#### `map_field()`
Maps individual serializer fields to their schema representation. The base implementation
will handle the default fields that Django REST Framework provides.
For `SerializerMethodField` instances, for which the schema is unknown, or custom field subclasses you should override `map_field()` to generate the correct schema:
```python
class CustomSchema(AutoSchema):
"""Extension of ``AutoSchema`` to add support for custom field schemas."""
def map_field(self, field):
# Handle SerializerMethodFields or custom fields here...
# ...
return super().map_field(field)
```
Authors of third-party packages should aim to provide an `AutoSchema` subclass,
and a mixin, overriding `map_field()` so that users can easily generate schemas
for their custom fields.
#### `get_tags()`
OpenAPI groups operations by tags. By default tags taken from the first path
segment of the routed URL. For example, a URL like `/users/{id}/` will generate
the tag `users`.
You can pass an `__init__()` kwarg to manually specify tags (see below), or
override `get_tags()` to provide custom logic.
#### `get_operation()`
Returns the [OpenAPI operation object][openapi-operation] that describes the
endpoint, including path and query parameters for pagination, filtering, and so
on.
Together with `get_components()`, this is the main entry point to the view
introspection.
#### `get_operation_id()`
There must be a unique [operationid](openapi-operationid) for each operation.
By default the `operationId` is deduced from the model name, serializer name or
view name. The operationId looks like "listItems", "retrieveItem",
"updateItem", etc. The `operationId` is camelCase by convention.
#### `get_operation_id_base()`
If you have several views with the same model name, you may see duplicate
operationIds.
In order to work around this, you can override `get_operation_id_base()` to
provide a different base for name part of the ID.
### `AutoSchema.__init__()` kwargs
`AutoSchema` provides a number of `__init__()` kwargs that can be used for
common customizations, if the default generated values are not appropriate.
The available kwargs are:
* `tags`: Specify a list of tags.
* `component_name`: Specify the component name.
* `operation_id_base`: Specify the resource-name part of operation IDs.
You pass the kwargs when declaring the `AutoSchema` instance on your view:
```
class PetDetailView(generics.RetrieveUpdateDestroyAPIView):
schema = AutoSchema(
tags=['Pets'],
component_name='Pet',
operation_id_base='Pet',
)
...
```
Assuming a `Pet` model and `PetSerializer` serializer, the kwargs in this
example are probably not needed. Often, though, you'll need to pass the kwargs
if you have multiple view targeting the same model, or have multiple views with
identically named serializers.
If your views have related customizations that are needed frequently, you can
create a base `AutoSchema` subclass for your project that takes additional
`__init__()` kwargs to save subclassing `AutoSchema` for each view.
[openapi]: https://github.com/OAI/OpenAPI-Specification
[openapi-specification-extensions]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#specification-extensions
[openapi-operation]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject
[openapi-operation]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject
[openapi-tags]: https://swagger.io/specification/#tagObject
[openapi-operationid]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#fixed-fields-17
[openapi-components]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#componentsObject
[openapi-reference]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#referenceObject
[openapi-generator]: https://github.com/OpenAPITools/openapi-generator
[swagger-codegen]: https://github.com/swagger-api/swagger-codegen
[info-object]: https://swagger.io/specification/#infoObject

View File

@ -21,7 +21,7 @@ Let's start by creating a simple object we can use for example purposes:
from datetime import datetime
class Comment(object):
class Comment:
def __init__(self, email, content, created=None):
self.email = email
self.content = content
@ -161,7 +161,7 @@ Each key in the dictionary will be the field name, and the values will be lists
When deserializing a list of items, errors will be returned as a list of dictionaries representing each of the deserialized items.
#### Raising an exception on invalid data
#### Raising an exception on invalid data
The `.is_valid()` method takes an optional `raise_exception` flag that will cause it to raise a `serializers.ValidationError` exception if there are validation errors.
@ -238,10 +238,12 @@ Serializer classes can also include reusable validators that are applied to the
class Meta:
# Each room only has one event per day.
validators = UniqueTogetherValidator(
queryset=Event.objects.all(),
fields=['room_number', 'date']
)
validators = [
UniqueTogetherValidator(
queryset=Event.objects.all(),
fields=['room_number', 'date']
)
]
For more information see the [validators documentation](validators.md).
@ -249,7 +251,7 @@ For more information see the [validators documentation](validators.md).
When passing an initial object or queryset to a serializer instance, the object will be made available as `.instance`. If no initial object is passed then the `.instance` attribute will be `None`.
When passing data to a serializer instance, the unmodified data will be made available as `.initial_data`. If the data keyword argument is not passed then the `.initial_data` attribute will not exist.
When passing data to a serializer instance, the unmodified data will be made available as `.initial_data`. If the `data` keyword argument is not passed then the `.initial_data` attribute will not exist.
## Partial updates
@ -280,7 +282,7 @@ If a nested representation may optionally accept the `None` value you should pas
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
Similarly if a nested representation should be a list of items, you should pass the `many=True` flag to the nested serialized.
Similarly if a nested representation should be a list of items, you should pass the `many=True` flag to the nested serializer.
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False)
@ -333,7 +335,7 @@ Here's an example for an `.update()` method on our previous `UserSerializer` cla
def update(self, instance, validated_data):
profile_data = validated_data.pop('profile')
# Unless the application properly enforces that this field is
# always set, the follow could raise a `DoesNotExist`, which
# always set, the following could raise a `DoesNotExist`, which
# would need to be handled.
profile = instance.profile
@ -382,8 +384,8 @@ This manager class now more nicely encapsulates that user instances and profile
def create(self, validated_data):
return User.objects.create(
username=validated_data['username'],
email=validated_data['email']
is_premium_member=validated_data['profile']['is_premium_member']
email=validated_data['email'],
is_premium_member=validated_data['profile']['is_premium_member'],
has_support_contract=validated_data['profile']['has_support_contract']
)
@ -593,7 +595,7 @@ Normally if a `ModelSerializer` does not generate the fields you need by default
### `.serializer_field_mapping`
A mapping of Django model classes to REST framework serializer classes. You can override this mapping to alter the default serializer classes that should be used for each model class.
A mapping of Django model fields to REST framework serializer fields. You can override this mapping to alter the default serializer fields that should be used for each model field.
### `.serializer_related_field`
@ -887,10 +889,10 @@ To implement a read-only serializer using the `BaseSerializer` class, we just ne
It's simple to create a read-only serializer for converting `HighScore` instances into primitive data types.
class HighScoreSerializer(serializers.BaseSerializer):
def to_representation(self, obj):
def to_representation(self, instance):
return {
'score': obj.score,
'player_name': obj.player_name
'score': instance.score,
'player_name': instance.player_name
}
We can now use this class to serialize single `HighScore` instances:
@ -945,10 +947,10 @@ Here's a complete example of our previous `HighScoreSerializer`, that's been upd
'player_name': player_name
}
def to_representation(self, obj):
def to_representation(self, instance):
return {
'score': obj.score,
'player_name': obj.player_name
'score': instance.score,
'player_name': instance.player_name
}
def create(self, validated_data):
@ -965,10 +967,10 @@ The following class is an example of a generic serializer that can handle coerci
A read-only serializer that coerces arbitrary complex objects
into primitive representations.
"""
def to_representation(self, obj):
def to_representation(self, instance):
output = {}
for attribute_name in dir(obj):
attribute = getattr(obj, attribute_name)
for attribute_name in dir(instance):
attribute = getattr(instance, attribute_name)
if attribute_name.startswith('_'):
# Ignore private attributes.
pass
@ -1010,11 +1012,11 @@ Some reasons this might be useful include...
The signatures for these methods are as follows:
#### `.to_representation(self, obj)`
#### `.to_representation(self, instance)`
Takes the object instance that requires serialization, and should return a primitive representation. Typically this means returning a structure of built-in Python datatypes. The exact types that can be handled will depend on the render classes you have configured for your API.
May be overridden in order modify the representation style. For example:
May be overridden in order to modify the representation style. For example:
def to_representation(self, instance):
"""Convert `username` to lowercase."""
@ -1176,6 +1178,11 @@ The [html-json-forms][html-json-forms] package provides an algorithm and seriali
The [drf-writable-nested][drf-writable-nested] package provides writable nested model serializer which allows to create/update models with nested related data.
## DRF Encrypt Content
The [drf-encrypt-content][drf-encrypt-content] package helps you encrypt your data, serialized through ModelSerializer. It also contains some helper functions. Which helps you to encrypt your data.
[cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion
[relations]: relations.md
[model-managers]: https://docs.djangoproject.com/en/stable/topics/db/managers/
@ -1197,3 +1204,4 @@ The [drf-writable-nested][drf-writable-nested] package provides writable nested
[drf-serializer-extensions]: https://github.com/evenicoulddoit/django-rest-framework-serializer-extensions
[djangorestframework-queryfields]: https://djangorestframework-queryfields.readthedocs.io/
[drf-writable-nested]: https://github.com/beda-software/drf-writable-nested
[drf-encrypt-content]: https://github.com/oguzhancelikarslan/drf-encrypt-content

View File

@ -101,7 +101,7 @@ Default: `'rest_framework.negotiation.DefaultContentNegotiation'`
A view inspector class that will be used for schema generation.
Default: `'rest_framework.schemas.AutoSchema'`
Default: `'rest_framework.schemas.openapi.AutoSchema'`
---

View File

@ -221,7 +221,7 @@ If you're using `RequestsClient` you'll want to ensure that test setup, and resu
## Headers & Authentication
Custom headers and authentication credentials can be provided in the same way
as [when using a standard `requests.Session` instance](http://docs.python-requests.org/en/master/user/advanced/#session-objects).
as [when using a standard `requests.Session` instance][session_objects].
from requests.auth import HTTPBasicAuth
@ -414,3 +414,4 @@ For example, to add support for using `format='html'` in test requests, you migh
[requestfactory]: https://docs.djangoproject.com/en/stable/topics/testing/advanced/#django.test.client.RequestFactory
[configuration]: #configuration
[refresh_from_db_docs]: https://docs.djangoproject.com/en/1.11/ref/models/instances/#django.db.models.Model.refresh_from_db
[session_objects]: https://requests.readthedocs.io/en/master/user/advanced/#session-objects

View File

@ -59,7 +59,7 @@ using the `APIView` class-based views.
}
return Response(content)
Or, if you're using the `@api_view` decorator with function based views.
If you're using the `@api_view` decorator with function based views you can use the following decorator.
@api_view(['GET'])
@throttle_classes([UserRateThrottle])
@ -69,6 +69,16 @@ Or, if you're using the `@api_view` decorator with function based views.
}
return Response(content)
It's also possible to set throttle classes for routes that are created using the `@action` decorator.
Throttle classes set in this way will override any viewset level class settings.
@action(detail=True, methods=["post"], throttle_classes=[UserRateThrottle])
def example_adhoc_method(request, pk=None):
content = {
'status': 'request was permitted'
}
return Response(content)
## How clients are identified
The `X-Forwarded-For` HTTP header and `REMOTE_ADDR` WSGI variable are used to uniquely identify client IP addresses for throttling. If the `X-Forwarded-For` header is present then it will be used, otherwise the value of the `REMOTE_ADDR` variable from the WSGI environment will be used.

View File

@ -218,7 +218,7 @@ in the `.validate()` method, or else in the view.
For example:
class BillingRecordSerializer(serializers.ModelSerializer):
def validate(self, data):
def validate(self, attrs):
# Apply custom validation either here, or in the view.
class Meta:
@ -282,7 +282,7 @@ to your `Serializer` subclass. This is documented in the
To write a class-based validator, use the `__call__` method. Class-based validators are useful as they allow you to parameterize and reuse behavior.
class MultipleOf(object):
class MultipleOf:
def __init__(self, base):
self.base = base
@ -291,13 +291,17 @@ To write a class-based validator, use the `__call__` method. Class-based validat
message = 'This field must be a multiple of %d.' % self.base
raise serializers.ValidationError(message)
#### Using `set_context()`
#### Accessing the context
In some advanced cases you might want a validator to be passed the serializer field it is being used with as additional context. You can do so by declaring a `set_context` method on a class-based validator.
In some advanced cases you might want a validator to be passed the serializer
field it is being used with as additional context. You can do so by setting
a `requires_context = True` attribute on the validator. The `__call__` method
will then be called with the `serializer_field`
or `serializer` as an additional argument.
def set_context(self, serializer_field):
# Determine if this is an update or a create operation.
# In `__call__` we can then use that information to modify the validation behavior.
self.is_update = serializer_field.parent.instance is not None
requires_context = True
def __call__(self, value, serializer_field):
...
[cite]: https://docs.djangoproject.com/en/stable/ref/validators/

View File

@ -132,12 +132,12 @@ This scheme requires the client to specify the version as part of the URL path.
Your URL conf must include a pattern that matches the version with a `'version'` keyword argument, so that this information is available to the versioning scheme.
urlpatterns = [
url(
re_path(
r'^(?P<version>(v1|v2))/bookings/$',
bookings_list,
name='bookings-list'
),
url(
re_path(
r'^(?P<version>(v1|v2))/bookings/(?P<pk>[0-9]+)/$',
bookings_detail,
name='bookings-detail'
@ -158,14 +158,14 @@ In the following example we're giving a set of views two different possible URL
# bookings/urls.py
urlpatterns = [
url(r'^$', bookings_list, name='bookings-list'),
url(r'^(?P<pk>[0-9]+)/$', bookings_detail, name='bookings-detail')
re_path(r'^$', bookings_list, name='bookings-list'),
re_path(r'^(?P<pk>[0-9]+)/$', bookings_detail, name='bookings-detail')
]
# urls.py
urlpatterns = [
url(r'^v1/bookings/', include('bookings.urls', namespace='v1')),
url(r'^v2/bookings/', include('bookings.urls', namespace='v2'))
re_path(r'^v1/bookings/', include('bookings.urls', namespace='v1')),
re_path(r'^v2/bookings/', include('bookings.urls', namespace='v2'))
]
Both `URLPathVersioning` and `NamespaceVersioning` are reasonable if you just need a simple versioning scheme. The `URLPathVersioning` approach might be better suitable for small ad-hoc projects, and the `NamespaceVersioning` is probably easier to manage for larger projects.

View File

@ -169,7 +169,7 @@ To override the default settings, REST framework provides a set of additional de
from rest_framework.throttling import UserRateThrottle
class OncePerDayUserThrottle(UserRateThrottle):
rate = '1/day'
rate = '1/day'
@api_view(['GET'])
@throttle_classes([OncePerDayUserThrottle])

View File

@ -152,7 +152,7 @@ A more complete example of extra actions:
user = self.get_object()
serializer = PasswordSerializer(data=request.data)
if serializer.is_valid():
user.set_password(serializer.data['password'])
user.set_password(serializer.validated_data['password'])
user.save()
return Response({'status': 'password set'})
else:
@ -171,11 +171,6 @@ A more complete example of extra actions:
serializer = self.get_serializer(recent_users, many=True)
return Response(serializer.data)
The decorator can additionally take extra arguments that will be set for the routed view only. For example:
@action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):
...
The `action` decorator will route `GET` requests by default, but may also accept other HTTP methods by setting the `methods` argument. For example:
@ -183,7 +178,14 @@ The `action` decorator will route `GET` requests by default, but may also accept
def unset_password(self, request, pk=None):
...
The two new actions will then be available at the urls `^users/{pk}/set_password/$` and `^users/{pk}/unset_password/$`
The decorator allows you to override any viewset-level configuration such as `permission_classes`, `serializer_class`, `filter_backends`...:
@action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):
...
The two new actions will then be available at the urls `^users/{pk}/set_password/$` and `^users/{pk}/unset_password/$`. Use the `url_path` and `url_name` parameters to change the URL segement and the reverse URL name of the action.
To view all extra actions, call the `.get_extra_actions()` method.
@ -317,5 +319,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]: https://guides.rubyonrails.org/routing.html
[cite]: https://guides.rubyonrails.org/action_controller_overview.html
[routers]: routers.md

View File

@ -84,7 +84,7 @@ urlpatterns = [
### Customization
For customizations that you want to apply across the the entire API, you can subclass `rest_framework.schemas.openapi.SchemaGenerator` and provide it as an argument
For customizations that you want to apply across the entire API, you can subclass `rest_framework.schemas.openapi.SchemaGenerator` and provide it as an argument
to the `generateschema` command or `get_schema_view()` helper function.
For specific per-view customizations, you can subclass `AutoSchema`,
@ -144,4 +144,4 @@ continued development by **[signing up for a paid plan][funding]**.
[legacy-core-api-docs]:https://github.com/encode/django-rest-framework/blob/master/docs/coreapi/index.md
[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors
[funding]: community/funding.md
[funding]: funding.md

View File

@ -0,0 +1,117 @@
<style>
.promo li a {
float: left;
width: 130px;
height: 20px;
text-align: center;
margin: 10px 30px;
padding: 150px 0 0 0;
background-position: 0 50%;
background-size: 130px auto;
background-repeat: no-repeat;
font-size: 120%;
color: black;
}
.promo li {
list-style: none;
}
</style>
# Django REST framework 3.11
The 3.11 release adds support for Django 3.0.
* Our supported Python versions are now: 3.5, 3.6, 3.7, and 3.8.
* Our supported Django versions are now: 1.11, 2.0, 2.1, 2.2, and 3.0.
This release will be the last to support Python 3.5 or Django 1.11.
## OpenAPI Schema Generation Improvements
The OpenAPI schema generation continues to mature. Some highlights in 3.11
include:
* Automatic mapping of Django REST Framework renderers and parsers into OpenAPI
request and response media-types.
* Improved mapping JSON schema mapping types, for example in HStoreFields, and
with large integer values.
* Porting of the old CoreAPI parsing of docstrings to form OpenAPI operation
descriptions.
In this example view operation descriptions for the `get` and `post` methods will
be extracted from the class docstring:
```python
class DocStringExampleListView(APIView):
"""
get: A description of my GET operation.
post: A description of my POST operation.
"""
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get(self, request, *args, **kwargs):
...
def post(self, request, *args, **kwargs):
...
```
## Validator / Default Context
In some circumstances a Validator class or a Default class may need to access the serializer field with which it is called, or the `.context` with which the serializer was instantiated. In particular:
* Uniqueness validators need to be able to determine the name of the field to which they are applied, in order to run an appropriate database query.
* The `CurrentUserDefault` needs to be able to determine the context with which the serializer was instantiated, in order to return the current user instance.
Previous our approach to this was that implementations could include a `set_context` method, which would be called prior to validation. However this approach had issues with potential race conditions. We have now move this approach into a pending deprecation state. It will continue to function, but will be escalated to a deprecated state in 3.12, and removed entirely in 3.13.
Instead, validators or defaults which require the serializer context, should include a `requires_context = True` attribute on the class.
The `__call__` method should then include an additional `serializer_field` argument.
Validator implementations will look like this:
```python
class CustomValidator:
requires_context = True
def __call__(self, value, serializer_field):
...
```
Default implementations will look like this:
```python
class CustomDefault:
requires_context = True
def __call__(self, serializer_field):
...
```
---
## Funding
REST framework is a *collaboratively funded project*. If you use
REST framework commercially we strongly encourage you to invest in its
continued development by **[signing up for a paid plan][funding]**.
*Every single sign-up helps us make REST framework long-term financially sustainable.*
<ul class="premium-promo promo">
<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://software.esg-usa.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/esg-new-logo.png)">ESG</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://hubs.ly/H0f30Lf0" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/kloudless-plus-text.png)">Kloudless</a></li>
<li><a href="https://lightsonsoftware.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/lightson-dark.png)">Lights On Software</a></li>
<li><a href="https://retool.com/?utm_source=djangorest&utm_medium=sponsorship" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/retool-sidebar.png)">Retool</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, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), and [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship).*
[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors
[funding]: funding.md

View File

@ -0,0 +1,169 @@
<style>
.promo li a {
float: left;
width: 130px;
height: 20px;
text-align: center;
margin: 10px 30px;
padding: 150px 0 0 0;
background-position: 0 50%;
background-size: 130px auto;
background-repeat: no-repeat;
font-size: 120%;
color: black;
}
.promo li {
list-style: none;
}
</style>
# Django REST framework 3.12
REST framework 3.12 brings a handful of refinements to the OpenAPI schema
generation, plus support for Django's new database-agnostic `JSONField`,
and some improvements to the `SearchFilter` class.
## Grouping operations with tags.
Open API schemas will now automatically include tags, based on the first element
in the URL path.
For example...
Method | Path | Tags
--------------------------------|-----------------|-------------
`GET`, `PUT`, `PATCH`, `DELETE` | `/users/{id}/` | `['users']`
`GET`, `POST` | `/users/` | `['users']`
`GET`, `PUT`, `PATCH`, `DELETE` | `/orders/{id}/` | `['orders']`
`GET`, `POST` | `/orders/` | `['orders']`
The tags used for a particular view may also be overridden...
```python
class MyOrders(APIView):
schema = AutoSchema(tags=['users', 'orders'])
...
```
See [the schema documentation](https://www.django-rest-framework.org/api-guide/schemas/#grouping-operations-with-tags) for more information.
## Customizing the operation ID.
REST framework automatically determines operation IDs to use in OpenAPI
schemas. The latest version provides more control for overriding the behaviour
used to generate the operation IDs.
See [the schema documentation](https://www.django-rest-framework.org/api-guide/schemas/#operationid) for more information.
## Support for OpenAPI components.
In order to output more graceful OpenAPI schemes, REST framework 3.12 now
defines components in the schema, and then references them inside request
and response objects. This is in contrast with the previous approach, which
fully expanded the request and response bodies for each operation.
The names used for a component default to using the serializer class name, [but
may be overridden if needed](https://www.django-rest-framework.org/api-guide/schemas/#components
)...
```python
class MyOrders(APIView):
schema = AutoSchema(component_name="OrderDetails")
```
## More Public API
Many methods on the `AutoSchema` class have now been promoted to public API,
allowing you to more fully customize the schema generation. The following methods
are now available for overriding...
* `get_path_parameters`
* `get_pagination_parameters`
* `get_filter_parameters`
* `get_request_body`
* `get_responses`
* `get_serializer`
* `get_paginator`
* `map_serializer`
* `map_field`
* `map_choice_field`
* `map_field_validators`
* `allows_filters`.
See [the schema docs](https://www.django-rest-framework.org/api-guide/schemas/#per-view-customization)
for details on using custom `AutoSchema` subclasses.
## Support for JSONField.
Django 3.1 deprecated the existing `django.contrib.postgres.fields.JSONField`
in favour of a new database-agnositic `JSONField`.
REST framework 3.12 now supports this new model field, and `ModelSerializer`
classes will correctly map the model field.
## SearchFilter improvements
There are a couple of significant improvements to the `SearchFilter` class.
### Nested searches against JSONField and HStoreField
The class now supports nested search within `JSONField` and `HStoreField`, using
the double underscore notation for traversing which element of the field the
search should apply to.
```python
class SitesSearchView(generics.ListAPIView):
"""
An API view to return a list of archaeological sites, optionally filtered
by a search against the site name or location. (Location searches are
matched against the region and country names.)
"""
queryset = Sites.objects.all()
serializer_class = SitesSerializer
filter_backends = [filters.SearchFilter]
search_fields = ['site_name', 'location__region', 'location__country']
```
### Searches against annotate fields
Django allows querysets to create additional virtual fields, using the `.annotate`
method. We now support searching against annotate fields.
```python
class PublisherSearchView(generics.ListAPIView):
"""
Search for publishers, optionally filtering the search against the average
rating of all their books.
"""
queryset = Publisher.objects.annotate(avg_rating=Avg('book__rating'))
serializer_class = PublisherSerializer
filter_backends = [filters.SearchFilter]
search_fields = ['avg_rating']
```
---
## Funding
REST framework is a *collaboratively funded project*. If you use
REST framework commercially we strongly encourage you to invest in its
continued development by **[signing up for a paid plan][funding]**.
*Every single sign-up helps us make REST framework long-term financially sustainable.*
<ul class="premium-promo promo">
<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://software.esg-usa.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/esg-new-logo.png)">ESG</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://hubs.ly/H0f30Lf0" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/kloudless-plus-text.png)">Kloudless</a></li>
<li><a href="https://lightsonsoftware.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/lightson-dark.png)">Lights On Software</a></li>
<li><a href="https://retool.com/?utm_source=djangorest&utm_medium=sponsorship" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/retool-sidebar.png)">Retool</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, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), and [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship).*
[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors
[funding]: funding.md

View File

@ -69,7 +69,7 @@ schema_view = get_schema_view(
)
urlpatterns = [
url(r'^swagger/$', schema_view),
path('swagger/', schema_view),
...
]
```
@ -198,8 +198,8 @@ Make sure to include the view before your router urls. For example:
schema_view = get_schema_view(title='Example API')
urlpatterns = [
url('^$', schema_view),
url(r'^', include(router.urls)),
path('', schema_view),
path('', include(router.urls)),
]
### Schema path representations

View File

@ -73,7 +73,7 @@ To install the API documentation, you'll need to include it in your projects URL
urlpatterns = [
...
url(r'^docs/', include_docs_urls(title=API_TITLE, description=API_DESCRIPTION))
path('docs/', include_docs_urls(title=API_TITLE, description=API_DESCRIPTION))
]
Once installed you should see something a little like this:

View File

@ -62,6 +62,7 @@ Here's an example of adding an OpenAPI schema to the URL conf:
```python
from rest_framework.schemas import get_schema_view
from rest_framework.renderers import JSONOpenAPIRenderer
from django.urls import path
schema_view = get_schema_view(
title='Server Monitoring API',
@ -70,7 +71,7 @@ schema_view = get_schema_view(
)
urlpatterns = [
url('^schema.json$', schema_view),
path('schema.json', schema_view),
...
]
```

View File

@ -195,7 +195,6 @@ If `@tomchristie` ceases to participate in the project then `@j4mie` has respons
The following issues still need to be addressed:
* [Consider moving the repo into a proper GitHub organization][github-org].
* Ensure `@jamie` has back-up access to the `django-rest-framework.org` domain setup and admin.
* Document ownership of the [live example][sandbox] API.
* Document ownership of the [mailing list][mailing-list] and IRC channel.
@ -206,6 +205,5 @@ The following issues still need to be addressed:
[transifex-project]: https://www.transifex.com/projects/p/django-rest-framework/
[transifex-client]: https://pypi.org/project/transifex-client/
[translation-memory]: http://docs.transifex.com/guides/tm#let-tm-automatically-populate-translations
[github-org]: https://github.com/encode/django-rest-framework/issues/2162
[sandbox]: https://restframework.herokuapp.com/
[mailing-list]: https://groups.google.com/forum/#!forum/django-rest-framework

View File

@ -1,9 +1,5 @@
# Release Notes
> Release Early, Release Often
>
> &mdash; Eric S. Raymond, [The Cathedral and the Bazaar][cite].
## Versioning
Minor version numbers (0.0.x) are used for changes that are API compatible. You should be able to upgrade between minor point releases without any other code changes.
@ -38,8 +34,132 @@ You can determine your currently installed version using `pip show`:
---
## 3.12.x series
### 3.12.2
Date: 13th October 2020
* Fix issue if `rest_framework.authtoken.models` is imported, but `rest_framework.authtoken` is not in INSTALLED_APPS. [#7571]
* Ignore subclasses of BrowsableAPIRenderer in OpenAPI schema. [#7497]
* Narrower exception catching in serilizer fields, to ensure that any errors in broken `get_queryset()` methods are not masked. [#7480]
### 3.12.1
Date: 28th September 2020
* Add `TokenProxy` migration. [#7557]
### 3.12.0
Date: 28th September 2020
* Add `--file` option to `generateschema` command. [#7130]
* Support `tags` for OpenAPI schema generation. See [the schema docs](https://www.django-rest-framework.org/api-guide/schemas/#grouping-operations-with-tags). [#7184]
* Support customising the operation ID for schema generation. See [the schema docs](https://www.django-rest-framework.org/api-guide/schemas/#operationid). [#7190]
* Support OpenAPI components for schema generation. See [the schema docs](https://www.django-rest-framework.org/api-guide/schemas/#components). [#7124]
* The following methods on `AutoSchema` become public API: `get_path_parameters`, `get_pagination_parameters`, `get_filter_parameters`, `get_request_body`, `get_responses`, `get_serializer`, `get_paginator`, `map_serializer`, `map_field`, `map_choice_field`, `map_field_validators`, `allows_filters`. See [the schema docs](https://www.django-rest-framework.org/api-guide/schemas/#autoschema)
* Add support for Django 3.1's database-agnositic `JSONField`. [#7467]
* `SearchFilter` now supports nested search on `JSONField` and `HStoreField` model fields. [#7121]
* `SearchFilter` now supports searching on `annotate()` fields. [#6240]
* The authtoken model no longer exposes the `pk` in the admin URL. [#7341]
* Add `__repr__` for Request instances. [#7239]
* UTF-8 decoding with Latin-1 fallback for basic auth credentials. [#7193]
* CharField treats surrogate characters as a validation failure. [#7026]
* Don't include callables as default values in schemas. [#7105]
* Improve `ListField` schema output to include all available child information. [#7137]
* Allow `default=False` to be included for `BooleanField` schema outputs. [#7165]
* Include `"type"` information in `ChoiceField` schema outputs. [#7161]
* Include `"type": "object"` on schema objects. [#7169]
* Don't include component in schema output for DELETE requests. [#7229]
* Fix schema types for `DecimalField`. [#7254]
* Fix schema generation for `ObtainAuthToken` view. [#7211]
* Support passing `context=...` to view `.get_serializer()` methods. [#7298]
* Pass custom code to `PermissionDenied` if permission class has one set. [#7306]
* Include "example" in schema pagination output. [#7275]
* Default status code of 201 on schema output for POST requests. [#7206]
* Use camelCase for operation IDs in schema output. [#7208]
* Warn if duplicate operation IDs exist in schema output. [#7207]
* Improve handling of decimal type when mapping `ChoiceField` to a schema output. [#7264]
* Disable YAML aliases for OpenAPI schema outputs. [#7131]
* Fix action URL names for APIs included under a namespaced URL. [#7287]
* Update jQuery version from 3.4 to 3.5. [#7313]
* Fix `UniqueTogether` handling when serializer fields use `source=...`. [#7143]
* HTTP `HEAD` requests now set `self.action` correctly on a ViewSet instance. [#7223]
* Return a valid OpenAPI schema for the case where no API schema paths exist. [#7125]
* Include tests in package distribution. [#7145]
* Allow type checkers to support annotations like `ModelSerializer[Author]`. [#7385]
* Don't include invalid `charset=None` portion in the request `Content-Type` header when using APIClient. [#7400]
* Fix `\Z`/`\z` tokens in OpenAPI regexs. [#7389]
* Fix `PrimaryKeyRelatedField` and `HyperlinkedRelatedField` when source field is actually a property. [#7142]
* `Token.generate_key` is now a class method. [#7502]
* `@action` warns if method is wrapped in a decorator that does not preserve information using `@functools.wraps`. [#7098]
---
## 3.11.x series
### 3.11.2
**Date**: 30th September 2020
* **Security**: Drop `urlize_quoted_links` template tag in favour of Django's built-in `urlize`. Removes a XSS vulnerability for some kinds of content in the browsable API.
### 3.11.1
**Date**: 5th August 2020
* Fix compat with Django 3.1
### 3.11.0
**Date**: 12th December 2019
* Drop `.set_context` API [in favour of a `requires_context` marker](3.11-announcement.md#validator-default-context).
* Changed default widget for TextField with choices to select box. [#6892][gh6892]
* Supported nested writes on non-relational fields, such as JSONField. [#6916][gh6916]
* Include request/response media types in OpenAPI schemas, based on configured parsers/renderers. [#6865][gh6865]
* Include operation descriptions in OpenAPI schemas, based on the docstring on the view. [#6898][gh6898]
* Fix representation of serializers with all optional fields in OpenAPI schemas. [#6941][gh6941], [#6944][gh6944]
* Fix representation of `serializers.HStoreField` in OpenAPI schemas. [#6914][gh6914]
* Fix OpenAPI generation when title or version is not provided. [#6912][gh6912]
* Use `int64` representation for large integers in OpenAPI schemas. [#7018][gh7018]
* Improved error messages if no `.to_representation` implementation is provided on a field subclass. [#6996][gh6996]
* Fix for serializer classes that use multiple inheritance. [#6980][gh6980]
* Fix for reversing Hyperlinked URL fields with percent encoded components in the path. [#7059][gh7059]
* Update bootstrap to 3.4.1. [#6923][gh6923]
## 3.10.x series
### 3.10.3
**Date**: 4th September 2019
* Include API version in OpenAPI schema generation, defaulting to empty string.
* Add pagination properties to OpenAPI response schemas.
* Add missing "description" property to OpenAPI response schemas.
* Only include "required" for non-empty cases in OpenAPI schemas.
* Fix response schemas for "DELETE" case in OpenAPI schemas.
* Use an array type for list view response schemas.
* Use consistent `lowerInitialCamelCase` style in OpenAPI operation IDs.
* Fix `minLength`/`maxLength`/`minItems`/`maxItems` properties in OpenAPI schemas.
* Only call `FileField.url` once in serialization, for improved performance.
* Fix an edge case where throttling calculations could error after a configuration change.
### 3.10.2
**Date**: 29th July 2019
* Various `OpenAPI` schema fixes.
* Ability to specify urlconf in include_docs_urls.
### 3.10.1
**Date**: 17th July 2019
* Don't include autocomplete fields on TokenAuth admin, since it forces constraints on custom user models & admin.
* Require `uritemplate` for OpenAPI schema generation, but not `coreapi`.
### 3.10.0
**Date**: [15th July 2019][3.10.0-milestone]
@ -129,7 +249,7 @@ Be sure to upgrade to Python 3 before upgrading to Django REST Framework 3.10.
* Add testing of Python 3.7 support [#6141][gh6141]
* Test using Django 2.1 final release. [#6109][gh6109]
* Added djangorestframework-datatables to third-party packages [#5931][gh5931]
* Change ISO 8601 date format to exclude year/month [#5936][gh5936]
* Change ISO 8601 date format to exclude year/month-only options [#5936][gh5936]
* Update all pypi.python.org URLs to pypi.org [#5942][gh5942]
* Ensure that html forms (multipart form data) respect optional fields [#5927][gh5927]
* Allow hashing of ErrorDetail. [#5932][gh5932]
@ -142,7 +262,7 @@ Be sure to upgrade to Python 3 before upgrading to Django REST Framework 3.10.
* Fixed Javascript `e.indexOf` is not a function error [#5982][gh5982]
* Fix schemas for extra actions [#5992][gh5992]
* Improved get_error_detail to use error_dict/error_list [#5785][gh5785]
* Imprvied URLs in Admin renderer [#5988][gh5988]
* Improved URLs in Admin renderer [#5988][gh5988]
* Add "Community" section to docs, minor cleanup [#5993][gh5993]
* Moved guardian imports out of compat [#6054][gh6054]
* Deprecate the `DjangoObjectPermissionsFilter` class, moved to the `djangorestframework-guardian` package. [#6075][gh6075]
@ -193,11 +313,11 @@ Be sure to upgrade to Python 3 before upgrading to Django REST Framework 3.10.
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
Alternatively you may override `save()` or `create()` or `update()` on the serialiser as appropriate.
Alternatively you may override `save()` or `create()` or `update()` on the serializer as appropriate.
* Correct allow_null behaviour when required=False [#5888][gh5888]
Without an explicit `default`, `allow_null` implies a default of `null` for outgoing serialisation. Previously such
Without an explicit `default`, `allow_null` implies a default of `null` for outgoing serialization. Previously such
fields were being skipped when read-only or otherwise not required.
**Possible backwards compatibility break** if you were relying on such fields being excluded from the outgoing
@ -435,7 +555,7 @@ Be sure to upgrade to Python 3 before upgrading to Django REST Framework 3.10.
* Deprecated `exclude_from_schema` on `APIView` and `api_view` decorator. Set `schema = None` or `@schema(None)` as appropriate. [#5422][gh5422]
* Timezone-aware `DateTimeField`s now respect active or default `timezone` during serialization, instead of always using UTC. [#5435][gh5435]
Resolves inconsistency whereby instances were serialised with supplied datetime for `create` but UTC for `retrieve`. [#3732][gh3732]
Resolves inconsistency whereby instances were serialized with supplied datetime for `create` but UTC for `retrieve`. [#3732][gh3732]
**Possible backwards compatibility break** if you were relying on datetime strings being UTC. Have client interpret datetimes or [set default or active timezone (docs)][djangodocs-set-timezone] to UTC if needed.
@ -2150,3 +2270,18 @@ For older release notes, [please see the version 2.x documentation][old-release-
<!-- 3.10.0 -->
[gh6680]: https://github.com/encode/django-rest-framework/issues/6680
[gh6317]: https://github.com/encode/django-rest-framework/issues/6317
<!-- 3.11.0 -->
[gh6892]: https://github.com/encode/django-rest-framework/issues/6892
[gh6916]: https://github.com/encode/django-rest-framework/issues/6916
[gh6865]: https://github.com/encode/django-rest-framework/issues/6865
[gh6898]: https://github.com/encode/django-rest-framework/issues/6898
[gh6941]: https://github.com/encode/django-rest-framework/issues/6941
[gh6944]: https://github.com/encode/django-rest-framework/issues/6944
[gh6914]: https://github.com/encode/django-rest-framework/issues/6914
[gh6912]: https://github.com/encode/django-rest-framework/issues/6912
[gh7018]: https://github.com/encode/django-rest-framework/issues/7018
[gh6996]: https://github.com/encode/django-rest-framework/issues/6996
[gh6980]: https://github.com/encode/django-rest-framework/issues/6980
[gh7059]: https://github.com/encode/django-rest-framework/issues/7059
[gh6923]: https://github.com/encode/django-rest-framework/issues/6923

View File

@ -198,6 +198,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
* [rest_condition][rest-condition] - Another extension for building complex permissions in a simple and convenient way.
* [dry-rest-permissions][dry-rest-permissions] - Provides a simple way to define permissions for individual api actions.
* [drf-access-policy][drf-access-policy] - Declarative and flexible permissions inspired by AWS' IAM policies.
* [drf-psq][drf-psq] - An extension that gives support for having action-based **permission_classes**, **serializer_class**, and **queryset** dependent on permission-based rules.
### Serializers
@ -211,6 +212,8 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
* [djangorestframework-queryfields][djangorestframework-queryfields] - Serializer mixin allowing clients to control which fields will be sent in the API response.
* [drf-flex-fields][drf-flex-fields] - Serializer providing dynamic field expansion and sparse field sets via URL parameters.
* [drf-action-serializer][drf-action-serializer] - Serializer providing per-action fields config for use with ViewSets to prevent having to write multiple serializers.
* [djangorestframework-dataclasses][djangorestframework-dataclasses] - Serializer providing automatic field generation for Python dataclasses, like the built-in ModelSerializer does for models.
* [django-restql][django-restql] - Turn your REST API into a GraphQL like API(It allows clients to control which fields will be sent in a response, uses GraphQL like syntax, supports read and write on both flat and nested fields).
### Serializer fields
@ -220,8 +223,8 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
### Views
* [djangorestframework-bulk][djangorestframework-bulk] - Implements generic view mixins as well as some common concrete generic views to allow to apply bulk operations via API requests.
* [django-rest-multiple-models][django-rest-multiple-models] - Provides a generic view (and mixin) for sending multiple serialized models and/or querysets via a single API request.
* [drf-typed-views][drf-typed-views] - Use Python type annotations to validate/deserialize request parameters. Inspired by API Star, Hug and FastAPI.
### Routers
@ -238,7 +241,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
* [djangorestframework-csv][djangorestframework-csv] - Provides CSV renderer support.
* [djangorestframework-jsonapi][djangorestframework-jsonapi] - Provides a parser, renderer, serializers, and other tools to help build an API that is compliant with the jsonapi.org spec.
* [drf_ujson][drf_ujson] - Implements JSON rendering using the UJSON package.
* [drf_ujson2][drf_ujson2] - Implements JSON rendering using the UJSON package.
* [rest-pandas][rest-pandas] - Pandas DataFrame-powered renderers including Excel, CSV, and SVG formats.
* [djangorestframework-rapidjson][djangorestframework-rapidjson] - Provides rapidjson support with parser and renderer.
@ -252,8 +255,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
### Misc
* [cookiecutter-django-rest][cookiecutter-django-rest] - A cookiecutter template that takes care of the setup and configuration so you can focus on making your REST apis awesome.
* [djangorestrelationalhyperlink][djangorestrelationalhyperlink] - A hyperlinked serialiser that can can be used to alter relationships via hyperlinks, but otherwise like a hyperlink model serializer.
* [django-rest-swagger][django-rest-swagger] - An API documentation generator for Swagger UI.
* [djangorestrelationalhyperlink][djangorestrelationalhyperlink] - A hyperlinked serializer that can can be used to alter relationships via hyperlinks, but otherwise like a hyperlink model serializer.
* [django-rest-framework-proxy][django-rest-framework-proxy] - Proxy to redirect incoming request to another API server.
* [gaiarestframework][gaiarestframework] - Utils for django-rest-framework
* [drf-extensions][drf-extensions] - A collection of custom extensions
@ -270,6 +272,10 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
* [django-rest-framework-condition][django-rest-framework-condition] - Decorators for managing HTTP cache headers for Django REST framework (ETag and Last-modified).
* [django-rest-witchcraft][django-rest-witchcraft] - Provides DRF integration with SQLAlchemy with SQLAlchemy model serializers/viewsets and a bunch of other goodies
* [djangorestframework-mvt][djangorestframework-mvt] - An extension for creating views that serve Postgres data as Map Box Vector Tiles.
* [drf-viewset-profiler][drf-viewset-profiler] - Lib to profile all methods from a viewset line by line.
* [djangorestframework-features][djangorestframework-features] - Advanced schema generation and more based on named features.
* [django-elasticsearch-dsl-drf][django-elasticsearch-dsl-drf] - Integrate Elasticsearch DSL with Django REST framework. Package provides views, serializers, filter backends, pagination and other handy add-ons.
* [django-api-client][django-api-client] - DRF client that groups the Endpoint response, for use in CBVs and FBV as if you were working with Django's Native Models..
[cite]: http://www.software-ecosystems.com/Software_Ecosystems/Ecosystems.html
[cookiecutter]: https://github.com/jpadilla/cookiecutter-django-rest-framework
@ -303,19 +309,17 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
[djangorestframework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore
[drf-compound-fields]: https://github.com/estebistec/drf-compound-fields
[django-extra-fields]: https://github.com/Hipo/drf-extra-fields
[djangorestframework-bulk]: https://github.com/miki725/django-rest-framework-bulk
[django-rest-multiple-models]: https://github.com/MattBroach/DjangoRestMultipleModels
[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
[wq.db.rest]: https://wq.io/docs/about-rest
[djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack
[djangorestframework-camel-case]: https://github.com/vbabiy/djangorestframework-camel-case
[djangorestframework-csv]: https://github.com/mjumbewu/django-rest-framework-csv
[drf_ujson]: https://github.com/gizmag/drf-ujson-renderer
[drf_ujson2]: https://github.com/Amertz08/drf_ujson2
[rest-pandas]: https://github.com/wq/django-rest-pandas
[djangorestframework-rapidjson]: https://github.com/allisson/django-rest-framework-rapidjson
[djangorestframework-chain]: https://github.com/philipn/django-rest-framework-chain
[djangorestrelationalhyperlink]: https://github.com/fredkingham/django_rest_model_hyperlink_serializers_project
[django-rest-swagger]: https://github.com/marcgibbons/django-rest-swagger
[django-rest-framework-proxy]: https://github.com/eofs/django-rest-framework-proxy
[gaiarestframework]: https://github.com/AppsFuel/gaiarestframework
[drf-extensions]: https://github.com/chibisov/drf-extensions
@ -325,7 +329,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
[django-versatileimagefield-drf-docs]:https://django-versatileimagefield.readthedocs.io/en/latest/drf_integration.html
[drf-tracking]: https://github.com/aschn/drf-tracking
[django-rest-framework-braces]: https://github.com/dealertrack/django-rest-framework-braces
[dry-rest-permissions]: https://github.com/Helioscene/dry-rest-permissions
[dry-rest-permissions]: https://github.com/FJNR-inc/dry-rest-permissions
[django-url-filter]: https://github.com/miki725/django-url-filter
[drf-url-filter]: https://github.com/manjitkumar/drf-url-filters
[cookiecutter-django-rest]: https://github.com/agconti/cookiecutter-django-rest
@ -347,6 +351,14 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
[django-rest-witchcraft]: https://github.com/shosca/django-rest-witchcraft
[drf-access-policy]: https://github.com/rsinger86/drf-access-policy
[drf-flex-fields]: https://github.com/rsinger86/drf-flex-fields
[drf-typed-views]: https://github.com/rsinger86/drf-typed-views
[drf-action-serializer]: https://github.com/gregschmit/drf-action-serializer
[djangorestframework-dataclasses]: https://github.com/oxan/djangorestframework-dataclasses
[django-restql]: https://github.com/yezyilomo/django-restql
[djangorestframework-mvt]: https://github.com/corteva/djangorestframework-mvt
[django-rest-framework-guardian]: https://github.com/rpkilby/django-rest-framework-guardian
[drf-viewset-profiler]: https://github.com/fvlima/drf-viewset-profiler
[djangorestframework-features]: https://github.com/cloudcode-hungary/django-rest-framework-features/
[django-elasticsearch-dsl-drf]: https://github.com/barseghyanartur/django-elasticsearch-dsl-drf
[django-api-client]: https://github.com/rhenter/django-api-client
[drf-psq]: https://github.com/drf-psq/drf-psq

View File

@ -11,8 +11,8 @@ There are a wide range of resources available for learning and using Django REST
<a class="book-cover" href="https://www.twoscoopspress.com/products/two-scoops-of-django-1-11">
<img src="../../img/books/tsd-cover.png"/>
</a>
<a class="book-cover" href="https://wsvincent.com/books/">
<img src="../../img/books/rad-cover.png"/>
<a class="book-cover" href="https://djangoforapis.com">
<img src="../../img/books/dfa-cover.jpg"/>
</a>
<a class="book-cover" href="https://books.agiliq.com/projects/django-api-polls-tutorial/en/latest/">
<img src="../../img/books/bda-cover.png"/>
@ -101,12 +101,12 @@ Want your Django REST Framework talk/tutorial/article to be added to our website
[django-rest-framework-course]: https://teamtreehouse.com/library/django-rest-framework
[pycon-uk-2016]: https://www.youtube.com/watch?v=FjmiGh7OqVg
[django-under-hood-2014]: https://www.youtube.com/watch?v=3cSsbe-tA0E
[integrating-pandas-drf-and-bokeh]: https://machinalis.com/blog/pandas-django-rest-framework-bokeh/
[controlling-uncertainty-on-web-apps-and-apis]: https://machinalis.com/blog/controlling-uncertainty-on-web-applications-and-apis/
[full-text-search-in-drf]: https://machinalis.com/blog/full-text-search-on-django-rest-framework/
[oauth2-authentication-with-drf]: https://machinalis.com/blog/oauth2-authentication/
[nested-resources-with-drf]: https://machinalis.com/blog/nested-resources-with-django/
[image-fields-with-drf]: https://machinalis.com/blog/image-fields-with-django-rest-framework/
[integrating-pandas-drf-and-bokeh]: https://web.archive.org/web/20180104205117/http://machinalis.com/blog/pandas-django-rest-framework-bokeh/
[controlling-uncertainty-on-web-apps-and-apis]: https://web.archive.org/web/20180104205043/https://machinalis.com/blog/controlling-uncertainty-on-web-applications-and-apis/
[full-text-search-in-drf]: https://web.archive.org/web/20180104205059/http://machinalis.com/blog/full-text-search-on-django-rest-framework/
[oauth2-authentication-with-drf]: https://web.archive.org/web/20180104205054/http://machinalis.com/blog/oauth2-authentication/
[nested-resources-with-drf]: https://web.archive.org/web/20180104205109/http://machinalis.com/blog/nested-resources-with-django/
[image-fields-with-drf]: https://web.archive.org/web/20180104205048/http://machinalis.com/blog/image-fields-with-django-rest-framework/
[chatbot-using-drf-part1]: https://chatbotslife.com/chatbot-using-django-rest-framework-api-ai-slack-part-1-3-69c7e38b7b1e#.g2aceuncf
[new-django-admin-with-drf-and-emberjs]: https://blog.levit.be/new-django-admin-with-emberjs-what-are-the-news/
[drf-schema]: https://drf-schema-adapter.readthedocs.io/en/latest/

View File

@ -19,7 +19,7 @@ To install the API documentation, you'll need to include it in your project's UR
urlpatterns = [
...
url(r'^docs/', include_docs_urls(title='My API title'))
path('docs/', include_docs_urls(title='My API title'))
]
This will include two different views:
@ -41,7 +41,7 @@ You may ensure views are given a `request` instance by calling `include_docs_url
urlpatterns = [
...
# Generate schema with valid `request` instance:
url(r'^docs/', include_docs_urls(title='My API title', public=False))
path('docs/', include_docs_urls(title='My API title', public=False))
]

View File

@ -43,11 +43,12 @@ To add a dynamically generated schema view to your API, use `get_schema_view`.
```python
from rest_framework.schemas import get_schema_view
from django.urls import path
schema_view = get_schema_view(title="Example API")
urlpatterns = [
url('^schema$', schema_view),
path('schema', schema_view),
...
]
```
@ -191,7 +192,7 @@ each view, allowed method and path.)
**Note**: For basic `APIView` subclasses, default introspection is essentially
limited to the URL kwarg path parameters. For `GenericAPIView`
subclasses, which includes all the provided class based views, `AutoSchema` will
attempt to introspect serialiser, pagination and filter fields, as well as
attempt to introspect serializer, pagination and filter fields, as well as
provide richer path field descriptions. (The key hooks here are the relevant
`GenericAPIView` attributes and methods: `get_serializer`, `pagination_class`,
`filter_backends` and so on.)
@ -292,7 +293,7 @@ The simplest way to include a schema in your project is to use the
schema_view = get_schema_view(title="Server Monitoring API")
urlpatterns = [
url('^$', schema_view),
path('', schema_view),
...
]
@ -358,7 +359,7 @@ List of url patterns to limit the schema introspection to. If you only want the
to be exposed in the schema:
schema_url_patterns = [
url(r'^api/', include('myproject.api.urls')),
path('api/', include('myproject.api.urls')),
]
schema_view = get_schema_view(
@ -411,7 +412,7 @@ return the schema.
**urls.py:**
urlpatterns = [
url('/', schema_view),
path('', schema_view),
...
]
@ -827,10 +828,17 @@ A short description of the meaning and intended usage of the input field.
[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.
## drf-spectacular - Sane and flexible OpenAPI 3.0 schema generation for Django REST framework
[drf-spectacular][drf-spectacular] is a [OpenAPI 3][open-api] schema generation tool with explicit focus on extensibility,
customizability and client generation. It's usage patterns are very similar to [drf-yasg][drf-yasg].
[cite]: https://blog.heroku.com/archives/2014/1/8/json_schema_for_heroku_platform_api
[coreapi]: https://www.coreapi.org/
[corejson]: https://www.coreapi.org/specification/encoding/#core-json-encoding
[drf-yasg]: https://github.com/axnsan12/drf-yasg/
[drf-spectacular]: https://github.com/tfranzel/drf-spectacular/
[open-api]: https://openapis.org/
[json-hyperschema]: https://json-schema.org/latest/json-schema-hypermedia.html
[api-blueprint]: https://apiblueprint.org/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -52,7 +52,7 @@ Some reasons you might want to use REST framework:
* [Authentication policies][authentication] including packages for [OAuth1a][oauth1-section] and [OAuth2][oauth2-section].
* [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources.
* Customizable all the way down - just use [regular function-based views][functionview-section] if you don't need the [more][generic-views] [powerful][viewsets] [features][routers].
* [Extensive documentation][index], and [great community support][group].
* Extensive documentation, and [great community support][group].
* Used and trusted by internationally recognised companies including [Mozilla][mozilla], [Red Hat][redhat], [Heroku][heroku], and [Eventbrite][eventbrite].
---
@ -70,13 +70,12 @@ continued development by **[signing up for a paid plan][funding]**.
<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://software.esg-usa.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/esg-new-logo.png)">ESG</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://hubs.ly/H0f30Lf0" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/kloudless-plus-text.png)">Kloudless</a></li>
<li><a href="https://lightsonsoftware.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/lightson-dark.png)">Lights On Software</a></li>
<li><a href="https://retool.com/?utm_source=djangorest&utm_medium=sponsorship" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/retool-sidebar.png)">Retool</a></li>
<li><a href="https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/bitio_logo_gold_background.png)">bit.io</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, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), and [Lights On Software](https://lightsonsoftware.com).*
*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), and [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship).*
---
@ -84,15 +83,15 @@ continued development by **[signing up for a paid plan][funding]**.
REST framework requires the following:
* Python (3.5, 3.6, 3.7)
* Django (1.11, 2.0, 2.1, 2.2)
* Python (3.5, 3.6, 3.7, 3.8, 3.9)
* Django (2.2, 3.0, 3.1)
We **highly recommend** and only officially support the latest patch release of
each Python and Django series.
The following packages are optional:
* [coreapi][coreapi] (1.32.0+) - Schema generation support.
* [PyYAML][pyyaml], [uritemplate][uriteemplate] (5.1+, 3.0.0+) - Schema generation support.
* [Markdown][markdown] (3.0.0+) - Markdown support for the browsable API.
* [Pygments][pygments] (2.4.0+) - Add syntax highlighting to Markdown processing.
* [django-filter][django-filter] (1.0.1+) - Filtering support.
@ -121,7 +120,7 @@ If you're intending to use the browsable API you'll probably also want to add RE
urlpatterns = [
...
url(r'^api-auth/', include('rest_framework.urls'))
path('api-auth/', include('rest_framework.urls'))
]
Note that the URL path can be whatever you want.
@ -147,7 +146,7 @@ Don't forget to make sure you've also added `rest_framework` to your `INSTALLED_
We're ready to create our API now.
Here's our project's root `urls.py` module:
from django.conf.urls import url, include
from django.urls import path, include
from django.contrib.auth.models import User
from rest_framework import routers, serializers, viewsets
@ -169,8 +168,8 @@ Here's our project's root `urls.py` module:
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
path('', include(router.urls)),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
You can now open the API in your browser at [http://127.0.0.1:8000/](http://127.0.0.1:8000/), and view your new 'users' API. If you use the login control in the top right corner you'll also be able to add, create and delete users from the system.
@ -191,11 +190,6 @@ For support please see the [REST framework discussion group][group], try the `#
For priority support please sign up for a [professional or premium sponsorship plan](https://fund.django-rest-framework.org/topics/funding/).
For updates on REST framework development, you may also want to follow [the author][twitter] on Twitter.
<a style="padding-top: 10px" href="https://twitter.com/_tomchristie" class="twitter-follow-button" data-show-count="false">Follow @_tomchristie</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
## Security
If you believe youve found something in Django REST framework which has security implications, please **do not raise the issue in a public forum**.
@ -236,7 +230,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[redhat]: https://www.redhat.com/
[heroku]: https://www.heroku.com/
[eventbrite]: https://www.eventbrite.co.uk/about/
[coreapi]: https://pypi.org/project/coreapi/
[pyyaml]: https://pypi.org/project/PyYAML/
[uriteemplate]: https://pypi.org/project/uritemplate/
[markdown]: https://pypi.org/project/Markdown/
[pygments]: https://pypi.org/project/Pygments/
[django-filter]: https://pypi.org/project/django-filter/

View File

@ -384,7 +384,7 @@ First, install the API documentation views. These will include the schema resour
urlpatterns = [
...
url(r'^docs/', include_docs_urls(title='My API service'))
path('docs/', include_docs_urls(title='My API service'), name='api-docs'),
]
Once the API documentation URLs are installed, you'll be able to include both the required JavaScript resources. Note that the ordering of these two lines is important, as the schema loading requires CoreAPI to already be installed.
@ -401,14 +401,14 @@ Once the API documentation URLs are installed, you'll be able to include both th
The `coreapi` library, and the `schema` object will now both be available on the `window` instance.
const coreapi = window.coreapi
const schema = window.schema
const coreapi = window.coreapi;
const schema = window.schema;
## Instantiating a client
In order to interact with the API you'll need a client instance.
var client = new coreapi.Client()
var client = new coreapi.Client();
Typically you'll also want to provide some authentication credentials when
instantiating the client.
@ -421,9 +421,9 @@ the user to login, and then instantiate a client using session authentication:
let auth = new coreapi.auth.SessionAuthentication({
csrfCookieName: 'csrftoken',
csrfHeaderName: 'X-CSRFToken'
})
let client = new coreapi.Client({auth: auth})
csrfHeaderName: 'X-CSRFToken',
});
let client = new coreapi.Client({auth: auth});
The authentication scheme will handle including a CSRF header in any outgoing
requests for unsafe HTTP methods.
@ -434,10 +434,10 @@ The `TokenAuthentication` class can be used to support REST framework's built-in
`TokenAuthentication`, as well as OAuth and JWT schemes.
let auth = new coreapi.auth.TokenAuthentication({
scheme: 'JWT'
token: '<token>'
})
let client = new coreapi.Client({auth: auth})
scheme: 'JWT',
token: '<token>',
});
let client = new coreapi.Client({auth: auth});
When using TokenAuthentication you'll probably need to implement a login flow
using the CoreAPI client.
@ -448,20 +448,20 @@ request to an "obtain token" endpoint
For example, using the "Django REST framework JWT" package
// Setup some globally accessible state
window.client = new coreapi.Client()
window.loggedIn = false
window.client = new coreapi.Client();
window.loggedIn = false;
function loginUser(username, password) {
let action = ["api-token-auth", "obtain-token"]
let params = {username: "example", email: "example@example.com"}
let action = ["api-token-auth", "obtain-token"];
let params = {username: "example", email: "example@example.com"};
client.action(schema, action, params).then(function(result) {
// On success, instantiate an authenticated client.
let auth = window.coreapi.auth.TokenAuthentication({
scheme: 'JWT',
token: result['token']
token: result['token'],
})
window.client = coreapi.Client({auth: auth})
window.loggedIn = true
window.client = coreapi.Client({auth: auth});
window.loggedIn = true;
}).catch(function (error) {
// Handle error case where eg. user provides incorrect credentials.
})
@ -473,23 +473,23 @@ The `BasicAuthentication` class can be used to support HTTP Basic Authentication
let auth = new coreapi.auth.BasicAuthentication({
username: '<username>',
password: '<password>'
password: '<password>',
})
let client = new coreapi.Client({auth: auth})
let client = new coreapi.Client({auth: auth});
## Using the client
Making requests:
let action = ["users", "list"]
let action = ["users", "list"];
client.action(schema, action).then(function(result) {
// Return value is in 'result'
})
Including parameters:
let action = ["users", "create"]
let params = {username: "example", email: "example@example.com"}
let action = ["users", "create"];
let params = {username: "example", email: "example@example.com"};
client.action(schema, action, params).then(function(result) {
// Return value is in 'result'
})
@ -512,12 +512,12 @@ The coreapi package is available on NPM.
You'll either want to include the API schema in your codebase directly, by copying it from the `schema.js` resource, or else load the schema asynchronously. For example:
let client = new coreapi.Client()
let schema = null
let client = new coreapi.Client();
let schema = null;
client.get("https://api.example.org/").then(function(data) {
// Load a CoreJSON API schema.
schema = data
console.log('schema loaded')
schema = data;
console.log('schema loaded');
})
[heroku-api]: https://devcenter.heroku.com/categories/platform-api

View File

@ -45,7 +45,11 @@ this:
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
layout: "BaseLayout"
layout: "BaseLayout",
requestInterceptor: (request) => {
request.headers['X-CSRFToken'] = "{{ csrf_token }}"
return request;
}
})
</script>
</body>
@ -74,7 +78,7 @@ See the [Swagger UI documentation][swagger-ui] for advanced usage.
### A minimal example with ReDoc.
Assuming you've followed the example from the schemas documentation for routing
a dynamic `SchemaView`, a minimal Django template for using Swagger UI might be
a dynamic `SchemaView`, a minimal Django template for using ReDoc might be
this:
```html
@ -138,55 +142,15 @@ This also translates into a very useful interactive documentation viewer in the
![Screenshot - drf-yasg][image-drf-yasg]
---
#### drf-spectacular - Sane and flexible OpenAPI 3.0 schema generation for Django REST framework
#### Django REST Swagger
[drf-spectacular][drf-spectacular] is a [OpenAPI 3][open-api] schema generation tool with explicit focus on extensibility,
customizability and client generation. Usage patterns are very similar to [drf-yasg][drf-yasg].
Marc Gibbons' [Django REST Swagger][django-rest-swagger] integrates REST framework with the [Swagger][swagger] API documentation tool. The package produces well presented API documentation, and includes interactive tools for testing API endpoints.
Django REST Swagger supports REST framework versions 2.3 and above.
Mark is also the author of the [REST Framework Docs][rest-framework-docs] package which offers clean, simple autogenerated documentation for your API but is deprecated and has moved to Django REST Swagger.
This package is fully documented, well supported, and comes highly recommended.
![Screenshot - Django REST Swagger][image-django-rest-swagger]
---
### DRF AutoDocs
Oleksander Mashianovs' [DRF Auto Docs][drfautodocs-repo] automated api renderer.
Collects almost all the code you written into documentation effortlessly.
Supports:
* functional view docs
* tree-like structure
* Docstrings:
* markdown
* preserve space & newlines
* formatting with nice syntax
* Fields:
* choices rendering
* help_text (to specify SerializerMethodField output, etc)
* smart read_only/required rendering
* Endpoint properties:
* filter_backends
* authentication_classes
* permission_classes
* extra url params(GET params)
![whole structure](http://joxi.ru/52aBGNI4k3oyA0.jpg)
---
#### Apiary
There are various other online tools and services for providing API documentation. One notable service is [Apiary][apiary]. With Apiary, you describe your API using a simple markdown-like syntax. The generated documentation includes API interaction, a mock server for testing & prototyping, and various other tools.
![Screenshot - Apiary][image-apiary]
It aims to extract as much schema information as possible, while providing decorators and extensions for easy
customization. There is explicit support for [swagger-codegen][swagger], [SwaggerUI][swagger-ui] and [Redoc][redoc],
i18n, versioning, authentication, polymorphism (dynamic requests and responses), query/path/header parameters,
documentation and more. Several popular plugins for DRF are supported out-of-the-box as well.
---
@ -221,7 +185,7 @@ If the python `Markdown` library is installed, then [markdown syntax][markdown]
[ref]: http://example.com/activating-accounts
"""
Note that when using viewsets the basic docstring is used for all generated views. To provide descriptions for each view, such as for the the list and retrieve views, use docstring sections as described in [Schemas as documentation: Examples][schemas-examples].
Note that when using viewsets the basic docstring is used for all generated views. To provide descriptions for each view, such as for the list and retrieve views, use docstring sections as described in [Schemas as documentation: Examples][schemas-examples].
#### The `OPTIONS` method
@ -253,22 +217,18 @@ 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]: 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
[drfautodocs-repo]: https://github.com/iMakedonsky/drf-autodocs
[django-rest-swagger]: https://github.com/marcgibbons/django-rest-swagger
[swagger]: https://swagger.io/
[open-api]: https://openapis.org/
[rest-framework-docs]: https://github.com/marcgibbons/django-rest-framework-docs
[apiary]: https://apiary.io/
[markdown]: https://daringfireball.net/projects/markdown/syntax
[hypermedia-docs]: rest-hypermedia-hateoas.md
[image-django-rest-swagger]: ../img/django-rest-swagger.png
[image-apiary]: ../img/apiary.png
[metadata-docs]: ../api-guide/metadata.md
[schemas-examples]: ../api-guide/schemas.md#examples
[image-drf-yasg]: ../img/drf-yasg.png
[image-self-describing-api]: ../img/self-describing.png
[metadata-docs]: ../api-guide/metadata/
[schemas-examples]: ../api-guide/schemas/#examples
[swagger-ui]: https://swagger.io/tools/swagger-ui/
[drf-yasg]: https://github.com/axnsan12/drf-yasg/
[drf-spectacular]: https://github.com/tfranzel/drf-spectacular/
[markdown]: https://daringfireball.net/projects/markdown/syntax
[open-api]: https://openapis.org/
[redoc]: https://github.com/Rebilly/ReDoc
[swagger]: https://swagger.io/
[swagger-ui]: https://swagger.io/tools/swagger-ui/

View File

@ -103,10 +103,10 @@ You can find more information on how the language preference is determined in th
For API clients the most appropriate of these will typically be to use the `Accept-Language` header; Sessions and cookies will not be available unless using session authentication, and generally better practice to prefer an `Accept-Language` header for API clients rather than using language URL prefixes.
[cite]: https://youtu.be/Wa0VfS2q94Y
[django-translation]: https://docs.djangoproject.com/en/1.7/topics/i18n/translation
[django-translation]: https://docs.djangoproject.com/en/stable/topics/i18n/translation
[custom-exception-handler]: ../api-guide/exceptions.md#custom-exception-handling
[transifex-project]: https://www.transifex.com/projects/p/django-rest-framework/
[django-po-source]: https://raw.githubusercontent.com/encode/django-rest-framework/master/rest_framework/locale/en_US/LC_MESSAGES/django.po
[django-language-preference]: https://docs.djangoproject.com/en/1.7/topics/i18n/translation/#how-django-discovers-language-preference
[django-locale-paths]: https://docs.djangoproject.com/en/1.7/ref/settings/#std:setting-LOCALE_PATHS
[django-locale-name]: https://docs.djangoproject.com/en/1.7/topics/i18n/#term-locale-name
[django-language-preference]: https://docs.djangoproject.com/en/stable/topics/i18n/translation/#how-django-discovers-language-preference
[django-locale-paths]: https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-LOCALE_PATHS
[django-locale-name]: https://docs.djangoproject.com/en/stable/topics/i18n/#term-locale-name

View File

@ -34,7 +34,7 @@ REST framework also includes [serialization] and [parser]/[renderer] components
What REST framework doesn't do is give you machine readable hypermedia formats such as [HAL][hal], [Collection+JSON][collection], [JSON API][json-api] or HTML [microformats] by default, or the ability to auto-magically create fully HATEOAS style APIs that include hypermedia-based form descriptions and semantically labelled hyperlinks. Doing so would involve making opinionated choices about API design that should really remain outside of the framework's scope.
[cite]: https://vimeo.com/channels/restfest/page:2
[cite]: https://vimeo.com/channels/restfest/49503453
[dissertation]: https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
[hypertext-driven]: https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
[restful-web-apis]: http://restfulwebapis.org/

View File

@ -29,13 +29,11 @@ REST framework provides two wrappers you can use to write API views.
These wrappers provide a few bits of functionality such as making sure you receive `Request` instances in your view, and adding context to `Response` objects so that content negotiation can be performed.
The wrappers also provide behaviour such as returning `405 Method Not Allowed` responses when appropriate, and handling any `ParseError` exception that occurs when accessing `request.data` with malformed input.
The wrappers also provide behaviour such as returning `405 Method Not Allowed` responses when appropriate, and handling any `ParseError` exceptions that occur when accessing `request.data` with malformed input.
## Pulling it all together
Okay, let's go ahead and start using these new components to write a few views.
We don't need our `JSONResponse` class in `views.py` any more, so go ahead and delete that. Once that's done we can start refactoring our views slightly.
Okay, let's go ahead and start using these new components to refactor our views slightly.
from rest_framework import status
from rest_framework.decorators import api_view

View File

@ -137,7 +137,7 @@ We can add a login view for use with the browsable API, by editing the URLconf i
Add the following import at the top of the file:
from django.conf.urls import include
from django.urls import path, include
And, at the end of the file, add a pattern to include the login and logout views for the browsable API.

View File

@ -2,7 +2,7 @@
REST framework includes an abstraction for dealing with `ViewSets`, that allows the developer to concentrate on modeling the state and interactions of the API, and leave the URL construction to be handled automatically, based on common conventions.
`ViewSet` classes are almost the same thing as `View` classes, except that they provide operations such as `read`, or `update`, and not method handlers such as `get` or `put`.
`ViewSet` classes are almost the same thing as `View` classes, except that they provide operations such as `retrieve`, or `update`, and not method handlers such as `get` or `put`.
A `ViewSet` class is only bound to a set of method handlers at the last moment, when it is instantiated into a set of views, typically by using a `Router` class which handles the complexities of defining the URL conf for you.
@ -16,7 +16,7 @@ First of all let's refactor our `UserList` and `UserDetail` views into a single
class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""
This viewset automatically provides `list` and `detail` actions.
This viewset automatically provides `list` and `retrieve` actions.
"""
queryset = User.objects.all()
serializer_class = UserSerializer
@ -27,6 +27,7 @@ Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighl
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import permissions
class SnippetViewSet(viewsets.ModelViewSet):
"""

View File

@ -85,6 +85,7 @@ Right, we'd better write some views then. Open `tutorial/quickstart/views.py` a
from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from rest_framework import permissions
from quickstart.serializers import UserSerializer, GroupSerializer
@ -94,6 +95,7 @@ Right, we'd better write some views then. Open `tutorial/quickstart/views.py` a
"""
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
class GroupViewSet(viewsets.ModelViewSet):
@ -102,6 +104,7 @@ Right, we'd better write some views then. Open `tutorial/quickstart/views.py` a
"""
queryset = Group.objects.all()
serializer_class = GroupSerializer
permission_classes = [permissions.IsAuthenticated]
Rather than write multiple views we're grouping together all the common behavior into classes called `ViewSets`.
@ -134,12 +137,12 @@ Finally, we're including default login and logout views for use with the browsab
## Pagination
Pagination allows you to control how many objects per page are returned. To enable it add the following lines to `tutorial/settings.py`
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10
}
## Settings
Add `'rest_framework'` to `INSTALLED_APPS`. The settings module will be in `tutorial/settings.py`
@ -175,7 +178,7 @@ We can now access our API, both from the command-line, using tools like `curl`..
},
{
"email": "tom@example.com",
"groups": [ ],
"groups": [],
"url": "http://127.0.0.1:8000/users/2/",
"username": "tom"
}
@ -201,7 +204,7 @@ Or using the [httpie][httpie], command line tool...
},
{
"email": "tom@example.com",
"groups": [ ],
"groups": [],
"url": "http://127.0.0.1:8000/users/2/",
"username": "tom"
}
@ -221,5 +224,5 @@ If you want to get a more in depth understanding of how REST framework fits toge
[image]: ../img/quickstart.png
[tutorial]: 1-serialization.md
[guide]: ../#api-guide
[guide]: ../api-guide/requests.md
[httpie]: https://github.com/jakubroztocil/httpie#installation

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="https://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="{{ base_url }}">homepage</a>, or <a href="#mkdocs_search_modal" data-toggle="modal">search the documentation</a>.</p>
{% endblock %}

View File

@ -6,7 +6,7 @@ pre {
.dropdown .dropdown-menu {
display: none;
overflow-y: scroll;
overflow-y: auto;
}
.dropdown.open .dropdown-menu {
@ -74,6 +74,12 @@ pre {
white-space: pre;
}
code, pre {
font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif;
font-size: 13px;
}
/* Preserve the spacing of the navbar across different screen sizes. */
.navbar-inner {
/*padding: 5px 0;*/
@ -432,3 +438,4 @@ ul.sponsor {
margin: 0 !important;
display: inline-block !important;
}

View File

@ -5,22 +5,18 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>{% if page.title %}{{ page.title }} - {% endif %}{{ config.site_name }}</title>
<link href="{{ base_url }}/img/favicon.ico" rel="icon" type="image/x-icon">
<link href="{{ 'img/favicon.ico'|url }}" rel="icon" type="image/x-icon">
<link rel="canonical" href="{{ page.canonical_url|url }}" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Django, API, REST{% if page %}, {{ page.title }}{% endif %}">
<meta name="author" content="Tom Christie">
<!-- Le styles -->
<link href="{{ base_url }}/css/prettify.css" rel="stylesheet">
<link href="{{ base_url }}/css/bootstrap.css" rel="stylesheet">
<link href="{{ base_url }}/css/bootstrap-responsive.css" rel="stylesheet">
<link href="{{ base_url }}/css/default.css" rel="stylesheet">
<link href="{{ 'css/prettify.css'|url }}" rel="stylesheet">
<link href="{{ 'css/bootstrap.css'|url }}" rel="stylesheet">
<link href="{{ 'css/bootstrap-responsive.css'|url }}" rel="stylesheet">
<link href="{{ 'css/default.css'|url }}" rel="stylesheet">
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<script type="text/javascript">
var _gaq = _gaq || [];
@ -102,7 +98,7 @@
{% endfor %}
<div class="promo">
<hr/>
{% if page.toc %}<hr/>{% endif %}
<div id="sidebarInclude">
</div>
</ul>
@ -139,10 +135,10 @@
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script async src="https://fund.django-rest-framework.org/sidebar_include.js"></script>
<script src="{{ base_url }}/js/jquery-1.8.1-min.js"></script>
<script src="{{ base_url }}/js/prettify-1.0.js"></script>
<script src="{{ base_url }}/js/bootstrap-2.1.1-min.js"></script>
<script src="{{ base_url }}/js/theme.js"></script>
<script src="{{ 'js/jquery-1.8.1-min.js'|url }}"></script>
<script src="{{ 'js/prettify-1.0.js'|url }}"></script>
<script src="{{ 'js/bootstrap-2.1.1-min.js'|url }}"></script>
<script src="{{ 'js/theme.js'|url }}"></script>
<script>var base_url = '{{ base_url }}';</script>
{% for path in config.extra_javascript %}

View File

@ -66,6 +66,8 @@ nav:
- 'Contributing to REST framework': 'community/contributing.md'
- 'Project management': 'community/project-management.md'
- 'Release Notes': 'community/release-notes.md'
- '3.12 Announcement': 'community/3.12-announcement.md'
- '3.11 Announcement': 'community/3.11-announcement.md'
- '3.10 Announcement': 'community/3.10-announcement.md'
- '3.9 Announcement': 'community/3.9-announcement.md'
- '3.8 Announcement': 'community/3.8-announcement.md'

View File

@ -1,7 +1,7 @@
# PEP8 code linting, which we run on all commits.
flake8==3.5.0
flake8-tidy-imports==1.1.0
pycodestyle==2.3.1
flake8==3.8.3
flake8-tidy-imports==4.1.0
pycodestyle==2.6.0
# Sort and lint imports
isort==4.3.3
isort==5.4.2

View File

@ -1,2 +1,2 @@
# MkDocs to build our documentation.
mkdocs==1.0.4
mkdocs==1.1

View File

@ -1,8 +1,9 @@
# Optional packages which may be used with REST framework.
psycopg2-binary>=2.8.2, <2.9
markdown==3.1.1
psycopg2-binary>=2.8.5, <2.9
markdown==3.3;python_version>="3.6"
markdown==3.2.2;python_version=="3.5"
pygments==2.4.2
django-guardian==1.5.0
django-guardian==2.2.0
django-filter>=2.2.0, <2.3
coreapi==2.3.1
coreschema==0.0.4

View File

@ -1,8 +1,8 @@
# Wheel for PyPI installs.
wheel==0.30.0
wheel==0.34.2
# Twine for secured PyPI uploads.
twine==1.11.0
twine==3.1.1
# Transifex client for managing translation resources.
transifex-client==0.11
transifex-client==0.13.9

View File

@ -1,4 +1,5 @@
# Pytest for running the tests.
pytest>=5.0,<5.1
pytest-django>=3.5.1,<3.6
pytest>=5.4.1,<5.5
pytest-django>=3.9.0,<3.10
pytest-cov>=2.7.1
six>=1.14.0

View File

@ -7,10 +7,12 @@ ______ _____ _____ _____ __
\_| \_\____/\____/ \_/ |_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_|
"""
import django
__title__ = 'Django REST framework'
__version__ = '3.10.1'
__version__ = '3.12.2'
__author__ = 'Tom Christie'
__license__ = 'BSD 2-Clause'
__license__ = 'BSD 3-Clause'
__copyright__ = 'Copyright 2011-2019 Encode OSS Ltd'
# Version synonym
@ -22,12 +24,14 @@ HTTP_HEADER_ENCODING = 'iso-8859-1'
# Default datetime input and output formats
ISO_8601 = 'iso-8601'
default_app_config = 'rest_framework.apps.RestFrameworkConfig'
if django.VERSION < (3, 2):
default_app_config = 'rest_framework.apps.RestFrameworkConfig'
class RemovedInDRF311Warning(DeprecationWarning):
class RemovedInDRF313Warning(DeprecationWarning):
pass
class RemovedInDRF312Warning(PendingDeprecationWarning):
class RemovedInDRF314Warning(PendingDeprecationWarning):
pass

View File

@ -74,7 +74,11 @@ class BasicAuthentication(BaseAuthentication):
raise exceptions.AuthenticationFailed(msg)
try:
auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')
try:
auth_decoded = base64.b64decode(auth[1]).decode('utf-8')
except UnicodeDecodeError:
auth_decoded = base64.b64decode(auth[1]).decode('latin-1')
auth_parts = auth_decoded.partition(':')
except (TypeError, UnicodeDecodeError, binascii.Error):
msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
raise exceptions.AuthenticationFailed(msg)
@ -132,7 +136,10 @@ class SessionAuthentication(BaseAuthentication):
"""
Enforce CSRF validation for session based authentication.
"""
check = CSRFCheck()
def dummy_get_response(request): # pragma: no cover
return None
check = CSRFCheck(dummy_get_response)
# populates request.META['CSRF_COOKIE'], which is used in process_view()
check.process_request(request)
reason = check.process_view(request, None, (), {})

View File

@ -1 +1,4 @@
default_app_config = 'rest_framework.authtoken.apps.AuthTokenConfig'
import django
if django.VERSION < (3, 2):
default_app_config = 'rest_framework.authtoken.apps.AuthTokenConfig'

View File

@ -1,12 +1,51 @@
from django.contrib import admin
from django.contrib.admin.utils import quote
from django.contrib.admin.views.main import ChangeList
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.urls import reverse
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.models import Token, TokenProxy
User = get_user_model()
class TokenChangeList(ChangeList):
"""Map to matching User id"""
def url_for_result(self, result):
pk = result.user.pk
return reverse('admin:%s_%s_change' % (self.opts.app_label,
self.opts.model_name),
args=(quote(pk),),
current_app=self.model_admin.admin_site.name)
class TokenAdmin(admin.ModelAdmin):
list_display = ('key', 'user', 'created')
fields = ('user',)
ordering = ('-created',)
actions = None # Actions not compatible with mapped IDs.
def get_changelist(self, request, **kwargs):
return TokenChangeList
def get_object(self, request, object_id, from_field=None):
"""
Map from User ID to matching Token.
"""
queryset = self.get_queryset(request)
field = User._meta.pk
try:
object_id = field.to_python(object_id)
user = User.objects.get(**{field.name: object_id})
return queryset.get(user=user)
except (queryset.model.DoesNotExist, User.DoesNotExist, ValidationError, ValueError):
return None
def delete_model(self, request, obj):
# Map back to actual Token, since delete() uses pk.
token = Token.objects.get(key=obj.key)
return super().delete_model(request, token)
admin.site.register(Token, TokenAdmin)
admin.site.register(TokenProxy, TokenAdmin)

View File

@ -0,0 +1,25 @@
# Generated by Django 3.1.1 on 2020-09-28 09:34
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('authtoken', '0002_auto_20160226_1747'),
]
operations = [
migrations.CreateModel(
name='TokenProxy',
fields=[
],
options={
'verbose_name': 'token',
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('authtoken.token',),
),
]

View File

@ -32,8 +32,23 @@ class Token(models.Model):
self.key = self.generate_key()
return super().save(*args, **kwargs)
def generate_key(self):
@classmethod
def generate_key(cls):
return binascii.hexlify(os.urandom(20)).decode()
def __str__(self):
return self.key
class TokenProxy(Token):
"""
Proxy mapping pk to user pk for use in admin.
"""
@property
def pk(self):
return self.user.pk
class Meta:
proxy = 'rest_framework.authtoken' in settings.INSTALLED_APPS
abstract = 'rest_framework.authtoken' not in settings.INSTALLED_APPS
verbose_name = "token"

View File

@ -5,11 +5,19 @@ from rest_framework import serializers
class AuthTokenSerializer(serializers.Serializer):
username = serializers.CharField(label=_("Username"))
username = serializers.CharField(
label=_("Username"),
write_only=True
)
password = serializers.CharField(
label=_("Password"),
style={'input_type': 'password'},
trim_whitespace=False
trim_whitespace=False,
write_only=True
)
token = serializers.CharField(
label=_("Token"),
read_only=True
)
def validate(self, attrs):

View File

@ -4,6 +4,7 @@ from rest_framework.authtoken.serializers import AuthTokenSerializer
from rest_framework.compat import coreapi, coreschema
from rest_framework.response import Response
from rest_framework.schemas import ManualSchema
from rest_framework.schemas import coreapi as coreapi_schema
from rest_framework.views import APIView
@ -13,7 +14,8 @@ class ObtainAuthToken(APIView):
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = AuthTokenSerializer
if coreapi is not None and coreschema is not None:
if coreapi_schema.is_enabled():
schema = ManualSchema(
fields=[
coreapi.Field(
@ -38,9 +40,19 @@ class ObtainAuthToken(APIView):
encoding="application/json",
)
def get_serializer_context(self):
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
def get_serializer(self, *args, **kwargs):
kwargs['context'] = self.get_serializer_context()
return self.serializer_class(*args, **kwargs)
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)

View File

@ -9,7 +9,7 @@ def pagination_system_check(app_configs, **kwargs):
if api_settings.PAGE_SIZE and not api_settings.DEFAULT_PAGINATION_CLASS:
errors.append(
Warning(
"You have specified a default PAGE_SIZE pagination rest_framework setting,"
"You have specified a default PAGE_SIZE pagination rest_framework setting, "
"without specifying also a DEFAULT_PAGINATION_CLASS.",
hint="The default for DEFAULT_PAGINATION_CLASS is None. "
"In previous versions this was PageNumberPagination. "

View File

@ -2,75 +2,9 @@
The `compat` module provides support for backwards compatibility with older
versions of Django/Python, and compatibility wrappers around optional packages.
"""
import sys
from django.conf import settings
from django.views.generic import View
try:
from django.urls import ( # noqa
URLPattern,
URLResolver,
)
except ImportError:
# Will be removed in Django 2.0
from django.urls import ( # noqa
RegexURLPattern as URLPattern,
RegexURLResolver as URLResolver,
)
try:
from django.core.validators import ProhibitNullCharactersValidator # noqa
except ImportError:
ProhibitNullCharactersValidator = None
def get_original_route(urlpattern):
"""
Get the original route/regex that was typed in by the user into the path(), re_path() or url() directive. This
is in contrast with get_regex_pattern below, which for RoutePattern returns the raw regex generated from the path().
"""
if hasattr(urlpattern, 'pattern'):
# Django 2.0
return str(urlpattern.pattern)
else:
# Django < 2.0
return urlpattern.regex.pattern
def get_regex_pattern(urlpattern):
"""
Get the raw regex out of the urlpattern's RegexPattern or RoutePattern. This is always a regular expression,
unlike get_original_route above.
"""
if hasattr(urlpattern, 'pattern'):
# Django 2.0
return urlpattern.pattern.regex.pattern
else:
# Django < 2.0
return urlpattern.regex.pattern
def is_route_pattern(urlpattern):
if hasattr(urlpattern, 'pattern'):
# Django 2.0
from django.urls.resolvers import RoutePattern
return isinstance(urlpattern.pattern, RoutePattern)
else:
# Django < 2.0
return False
def make_url_resolver(regex, urlpatterns):
try:
# Django 2.0
from django.urls.resolvers import RegexPattern
return URLResolver(RegexPattern(regex), urlpatterns)
except ImportError:
# Django < 2.0
return URLResolver(regex, urlpatterns)
def unicode_http_header(value):
# Coerce HTTP header value to unicode.
@ -162,8 +96,8 @@ except ImportError:
try:
import pygments
from pygments.lexers import get_lexer_by_name, TextLexer
from pygments.formatters import HtmlFormatter
from pygments.lexers import TextLexer, get_lexer_by_name
def pygments_highlight(text, lang, style):
lexer = get_lexer_by_name(lang, stripall=False)
@ -187,9 +121,10 @@ if markdown is not None and pygments is not None:
# starting from this blogpost and modified to support current markdown extensions API
# https://zerokspot.com/weblog/2008/06/18/syntax-highlighting-in-markdown-with-pygments/
from markdown.preprocessors import Preprocessor
import re
from markdown.preprocessors import Preprocessor
class CodeBlockPreprocessor(Preprocessor):
pattern = re.compile(
r'^\s*``` *([^\n]+)\n(.+?)^\s*```', re.M | re.S)
@ -217,22 +152,8 @@ else:
return False
# Django 1.x url routing syntax. Remove when dropping Django 1.11 support.
try:
from django.urls import include, path, re_path, register_converter # noqa
except ImportError:
from django.conf.urls import include, url # noqa
path = None
register_converter = None
re_path = url
# `separators` argument to `json.dumps()` differs between 2.x and 3.x
# See: https://bugs.python.org/issue22767
SHORT_SEPARATORS = (',', ':')
LONG_SEPARATORS = (', ', ': ')
INDENT_SEPARATORS = (',', ': ')
# Version Constants.
PY36 = sys.version_info >= (3, 6)

View File

@ -124,8 +124,23 @@ def action(methods=None, detail=None, url_path=None, url_name=None, **kwargs):
"""
Mark a ViewSet method as a routable action.
Set the `detail` boolean to determine if this action should apply to
instance/detail requests or collection/list requests.
`@action`-decorated functions will be endowed with a `mapping` property,
a `MethodMapper` that can be used to add additional method-based behaviors
on the routed action.
:param methods: A list of HTTP method names this action responds to.
Defaults to GET only.
:param detail: Required. Determines whether this action applies to
instance/detail requests or collection/list requests.
:param url_path: Define the URL segment for this action. Defaults to the
name of the method decorated.
:param url_name: Define the internal (`reverse`) URL name for this action.
Defaults to the name of the method decorated with underscores
replaced with dashes.
:param kwargs: Additional properties to set on the view. This can be used
to override viewset-level *_classes settings, equivalent to
how the `@renderer_classes` etc. decorators work for function-
based API views.
"""
methods = ['get'] if (methods is None) else methods
methods = [method.lower() for method in methods]
@ -144,6 +159,10 @@ def action(methods=None, detail=None, url_path=None, url_name=None, **kwargs):
func.detail = detail
func.url_path = url_path if url_path else func.__name__
func.url_name = url_name if url_name else func.__name__.replace('_', '-')
# These kwargs will end up being passed to `ViewSet.as_view()` within
# the router, which eventually delegates to Django's CBV `View`,
# which assigns them as instance attributes for each request.
func.kwargs = kwargs
# Set descriptive arguments for viewsets

View File

@ -1,4 +1,4 @@
from django.conf.urls import include, url
from django.urls import include, path
from rest_framework.renderers import (
CoreJSONRenderer, DocumentationRenderer, SchemaJSRenderer
@ -82,7 +82,7 @@ def include_docs_urls(
permission_classes=permission_classes,
)
urls = [
url(r'^$', docs_view, name='docs-index'),
url(r'^schema.js$', schema_js_view, name='schema-js')
path('', docs_view, name='docs-index'),
path('schema.js', schema_js_view, name='schema-js')
]
return include((urls, 'api-docs'), namespace='api-docs')

View File

@ -20,7 +20,7 @@ def _get_error_details(data, default_code=None):
Descend into a nested data structure, forcing any
lazy translation strings or strings into `ErrorDetail`.
"""
if isinstance(data, list):
if isinstance(data, (list, tuple)):
ret = [
_get_error_details(item, default_code) for item in data
]
@ -73,6 +73,8 @@ class ErrorDetail(str):
def __eq__(self, other):
r = super().__eq__(other)
if r is NotImplemented:
return NotImplemented
try:
return r and self.code == other.code
except AttributeError:
@ -148,7 +150,9 @@ class ValidationError(APIException):
# For validation failures, we may collect many errors together,
# so the details should always be coerced to a list if not already.
if not isinstance(detail, dict) and not isinstance(detail, list):
if isinstance(detail, tuple):
detail = list(detail)
elif not isinstance(detail, dict) and not isinstance(detail, list):
detail = [detail]
self.detail = _get_error_details(detail, code)

View File

@ -5,6 +5,7 @@ import functools
import inspect
import re
import uuid
import warnings
from collections import OrderedDict
from collections.abc import Mapping
@ -13,7 +14,8 @@ from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import ValidationError as DjangoValidationError
from django.core.validators import (
EmailValidator, MaxLengthValidator, MaxValueValidator, MinLengthValidator,
MinValueValidator, RegexValidator, URLValidator, ip_address_validators
MinValueValidator, ProhibitNullCharactersValidator, RegexValidator,
URLValidator, ip_address_validators
)
from django.forms import FilePathField as DjangoFilePathField
from django.forms import ImageField as DjangoImageField
@ -22,19 +24,21 @@ from django.utils.dateparse import (
parse_date, parse_datetime, parse_duration, parse_time
)
from django.utils.duration import duration_string
from django.utils.encoding import is_protected_type, smart_text
from django.utils.encoding import is_protected_type, smart_str
from django.utils.formats import localize_input, sanitize_separators
from django.utils.ipv6 import clean_ipv6_address
from django.utils.timezone import utc
from django.utils.translation import gettext_lazy as _
from pytz.exceptions import InvalidTimeError
from rest_framework import ISO_8601
from rest_framework.compat import ProhibitNullCharactersValidator
from rest_framework import (
ISO_8601, RemovedInDRF313Warning, RemovedInDRF314Warning
)
from rest_framework.exceptions import ErrorDetail, ValidationError
from rest_framework.settings import api_settings
from rest_framework.utils import html, humanize_datetime, json, representation
from rest_framework.utils.formatting import lazy_format
from rest_framework.validators import ProhibitSurrogateCharactersValidator
class empty:
@ -249,19 +253,30 @@ class CreateOnlyDefault:
for create operations, but that do not return any value for update
operations.
"""
requires_context = True
def __init__(self, default):
self.default = default
def set_context(self, serializer_field):
self.is_update = serializer_field.parent.instance is not None
if callable(self.default) and hasattr(self.default, 'set_context') and not self.is_update:
self.default.set_context(serializer_field)
def __call__(self):
if self.is_update:
def __call__(self, serializer_field):
is_update = serializer_field.parent.instance is not None
if is_update:
raise SkipField()
if callable(self.default):
return self.default()
if hasattr(self.default, 'set_context'):
warnings.warn(
"Method `set_context` on defaults is deprecated and will "
"no longer be called starting with 3.13. Instead set "
"`requires_context = True` on the class, and accept the "
"context as an additional argument.",
RemovedInDRF313Warning, stacklevel=2
)
self.default.set_context(self)
if getattr(self.default, 'requires_context', False):
return self.default(serializer_field)
else:
return self.default()
return self.default
def __repr__(self):
@ -269,11 +284,10 @@ class CreateOnlyDefault:
class CurrentUserDefault:
def set_context(self, serializer_field):
self.user = serializer_field.context['request'].user
requires_context = True
def __call__(self):
return self.user
def __call__(self, serializer_field):
return serializer_field.context['request'].user
def __repr__(self):
return '%s()' % self.__class__.__name__
@ -489,8 +503,20 @@ class Field:
raise SkipField()
if callable(self.default):
if hasattr(self.default, 'set_context'):
warnings.warn(
"Method `set_context` on defaults is deprecated and will "
"no longer be called starting with 3.13. Instead set "
"`requires_context = True` on the class, and accept the "
"context as an additional argument.",
RemovedInDRF313Warning, stacklevel=2
)
self.default.set_context(self)
return self.default()
if getattr(self.default, 'requires_context', False):
return self.default(self)
else:
return self.default()
return self.default
def validate_empty_values(self, data):
@ -551,10 +577,20 @@ class Field:
errors = []
for validator in self.validators:
if hasattr(validator, 'set_context'):
warnings.warn(
"Method `set_context` on validators is deprecated and will "
"no longer be called starting with 3.13. Instead set "
"`requires_context = True` on the class, and accept the "
"context as an additional argument.",
RemovedInDRF313Warning, stacklevel=2
)
validator.set_context(self)
try:
validator(value)
if getattr(validator, 'requires_context', False):
validator(value, self)
else:
validator(value)
except ValidationError as exc:
# If the validation error contains a mapping of fields to
# errors then simply raise it immediately rather than
@ -572,8 +608,11 @@ class Field:
Transform the *incoming* primitive data into a native value.
"""
raise NotImplementedError(
'{cls}.to_internal_value() must be implemented.'.format(
cls=self.__class__.__name__
'{cls}.to_internal_value() must be implemented for field '
'{field_name}. If you do not need to support write operations '
'you probably want to subclass `ReadOnlyField` instead.'.format(
cls=self.__class__.__name__,
field_name=self.field_name,
)
)
@ -582,9 +621,7 @@ class Field:
Transform the *outgoing* native value into primitive data.
"""
raise NotImplementedError(
'{cls}.to_representation() must be implemented for field '
'{field_name}. If you do not need to support write operations '
'you probably want to subclass `ReadOnlyField` instead.'.format(
'{cls}.to_representation() must be implemented for field {field_name}.'.format(
cls=self.__class__.__name__,
field_name=self.field_name,
)
@ -705,55 +742,22 @@ class BooleanField(Field):
return bool(value)
class NullBooleanField(Field):
default_error_messages = {
'invalid': _('Must be a valid boolean.')
}
class NullBooleanField(BooleanField):
initial = None
TRUE_VALUES = {
't', 'T',
'y', 'Y', 'yes', 'YES',
'true', 'True', 'TRUE',
'on', 'On', 'ON',
'1', 1,
True
}
FALSE_VALUES = {
'f', 'F',
'n', 'N', 'no', 'NO',
'false', 'False', 'FALSE',
'off', 'Off', 'OFF',
'0', 0, 0.0,
False
}
NULL_VALUES = {'null', 'Null', 'NULL', '', None}
def __init__(self, **kwargs):
warnings.warn(
"The `NullBooleanField` is deprecated and will be removed starting "
"with 3.14. Instead use the `BooleanField` field and set "
"`allow_null=True` which does the same thing.",
RemovedInDRF314Warning, stacklevel=2
)
assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.'
kwargs['allow_null'] = True
super().__init__(**kwargs)
def to_internal_value(self, data):
try:
if data in self.TRUE_VALUES:
return True
elif data in self.FALSE_VALUES:
return False
elif data in self.NULL_VALUES:
return None
except TypeError: # Input is an unhashable type
pass
self.fail('invalid', input=data)
def to_representation(self, value):
if value in self.NULL_VALUES:
return None
if value in self.TRUE_VALUES:
return True
elif value in self.FALSE_VALUES:
return False
return bool(value)
# String types...
@ -781,9 +785,8 @@ 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())
self.validators.append(ProhibitNullCharactersValidator())
self.validators.append(ProhibitSurrogateCharactersValidator())
def run_validation(self, data=empty):
# Test for the empty string here so that it does not get validated,
@ -1049,7 +1052,7 @@ class DecimalField(Field):
instance.
"""
data = smart_text(data).strip()
data = smart_str(data).strip()
if self.localize:
data = sanitize_separators(data)
@ -1062,9 +1065,7 @@ class DecimalField(Field):
except decimal.DecimalException:
self.fail('invalid')
# Check for NaN. It is the only value that isn't equal to itself,
# so we can use this to identify NaN values.
if value != value:
if value.is_nan():
self.fail('invalid')
# Check for infinity and negative infinity.
@ -1757,6 +1758,7 @@ class JSONField(Field):
def __init__(self, *args, **kwargs):
self.binary = kwargs.pop('binary', False)
self.encoder = kwargs.pop('encoder', None)
self.decoder = kwargs.pop('decoder', None)
super().__init__(*args, **kwargs)
def get_value(self, dictionary):
@ -1764,8 +1766,8 @@ class JSONField(Field):
# When HTML form input is used, mark up the input
# as being a JSON string, rather than a JSON primitive.
class JSONString(str):
def __new__(self, value):
ret = str.__new__(self, value)
def __new__(cls, value):
ret = str.__new__(cls, value)
ret.is_json_string = True
return ret
return JSONString(dictionary[self.field_name])
@ -1776,7 +1778,7 @@ class JSONField(Field):
if self.binary or getattr(data, 'is_json_string', False):
if isinstance(data, bytes):
data = data.decode()
return json.loads(data)
return json.loads(data, cls=self.decoder)
else:
json.dumps(data, cls=self.encoder)
except (TypeError, ValueError):
@ -1857,14 +1859,9 @@ class SerializerMethodField(Field):
super().__init__(**kwargs)
def bind(self, field_name, parent):
# In order to enforce a consistent style, we error if a redundant
# 'method_name' argument has been used. For example:
# my_field = serializer.SerializerMethodField(method_name='get_my_field')
default_method_name = 'get_{field_name}'.format(field_name=field_name)
# The method name should default to `get_{field_name}`.
# The method name defaults to `get_{field_name}`.
if self.method_name is None:
self.method_name = default_method_name
self.method_name = 'get_{field_name}'.format(field_name=field_name)
super().bind(field_name, parent)

View File

@ -8,7 +8,6 @@ from functools import reduce
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.db.models.constants import LOOKUP_SEP
from django.db.models.sql.constants import ORDER_PATTERN
from django.template import loader
from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _
@ -86,7 +85,7 @@ class SearchFilter(BaseFilterBackend):
search_field = search_field[1:]
# Annotated fields do not need to be distinct
if isinstance(queryset, models.QuerySet) and search_field in queryset.query.annotations:
return False
continue
parts = search_field.split(LOOKUP_SEP)
for part in parts:
field = opts.get_field(part)
@ -97,6 +96,9 @@ class SearchFilter(BaseFilterBackend):
if any(path.m2m for path in path_info):
# This field is a m2m relation so we know we need to call distinct
return True
else:
# This field has a custom __ query transform but is not a relational field.
break
return False
def filter_queryset(self, request, queryset, view):
@ -256,7 +258,13 @@ class OrderingFilter(BaseFilterBackend):
def remove_invalid_fields(self, queryset, fields, view, request):
valid_fields = [item[0] for item in self.get_valid_fields(queryset, view, {'request': request})]
return [term for term in fields if term.lstrip('-') in valid_fields and ORDER_PATTERN.match(term)]
def term_valid(term):
if term.startswith("-"):
term = term[1:]
return term in valid_fields
return [term for term in fields if term_valid(term)]
def filter_queryset(self, request, queryset, view):
ordering = self.get_ordering(request, queryset, view)

View File

@ -106,7 +106,7 @@ class GenericAPIView(views.APIView):
deserializing input, and for serializing output.
"""
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
kwargs.setdefault('context', self.get_serializer_context())
return serializer_class(*args, **kwargs)
def get_serializer_class(self):

View File

@ -7,13 +7,14 @@
# aymen chaieb <chaieb.aymen1992@gmail.com>, 2017
# Bashar Al-Abdulhadi, 2016-2017
# Eyad Toma <d.eyad.t@gmail.com>, 2015,2017
# zak zak <zakaria.bendifallah@gmail.com>, 2020
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-07-12 16:13+0100\n"
"PO-Revision-Date: 2017-10-18 09:51+0000\n"
"Last-Translator: Andrew Ayoub <andrew.ayoub@connectads.com>\n"
"POT-Creation-Date: 2020-10-13 21:45+0200\n"
"PO-Revision-Date: 2020-10-13 19:45+0000\n"
"Last-Translator: Xavier Ordoquy <xordoquy@linovia.com>\n"
"Language-Team: Arabic (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ar/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -21,40 +22,40 @@ msgstr ""
"Language: ar\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
#: authentication.py:73
#: authentication.py:70
msgid "Invalid basic header. No credentials provided."
msgstr ""
msgstr "رأس أساسي غير صالح, لم تقدم اي بيانات."
#: authentication.py:76
#: authentication.py:73
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr ""
msgstr "رأس أساسي غير صالح, سلسلة البيانات لا يجب أن تحتوي على أي أحرف مسافات"
#: authentication.py:82
#: authentication.py:83
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr ""
msgstr "رأس أساسي غير صالح, البيانات ليست مرمّزة بصحة على أساس64."
#: authentication.py:99
#: authentication.py:101
msgid "Invalid username/password."
msgstr "اسم المستخدم/كلمة السر غير صحيحين."
#: authentication.py:102 authentication.py:198
#: authentication.py:104 authentication.py:206
msgid "User inactive or deleted."
msgstr "المستخدم غير مفعل او تم حذفه."
#: authentication.py:176
#: authentication.py:184
msgid "Invalid token header. No credentials provided."
msgstr ""
msgstr "رمز الراْس المميّز غير صالح, لم تقدم أي بيانات."
#: authentication.py:179
#: authentication.py:187
msgid "Invalid token header. Token string should not contain spaces."
msgstr ""
msgstr "رمز الراْس المميّز غير صالح, سلسلة الرمز المميّز لا يجب أن تحتوي على أي أحرف مسافات."
#: authentication.py:185
#: authentication.py:193
msgid ""
"Invalid token header. Token string should not contain invalid characters."
msgstr ""
msgstr "رمز الراْس المميّز غير صالح, سلسلة الرمز المميّز لا يجب أن تحتوي على أي أحرف غير صالحة."
#: authentication.py:195
#: authentication.py:203
msgid "Invalid token."
msgstr "رمز غير صحيح."
@ -62,382 +63,515 @@ msgstr "رمز غير صحيح."
msgid "Auth Token"
msgstr "رمز التفويض"
#: authtoken/models.py:15
#: authtoken/models.py:13
msgid "Key"
msgstr "المفتاح"
#: authtoken/models.py:18
#: authtoken/models.py:16
msgid "User"
msgstr "المستخدم"
#: authtoken/models.py:20
#: authtoken/models.py:18
msgid "Created"
msgstr "أنشئ"
#: authtoken/models.py:29
#: authtoken/models.py:27 authtoken/serializers.py:19
msgid "Token"
msgstr "الرمز"
#: authtoken/models.py:30
#: authtoken/models.py:28
msgid "Tokens"
msgstr "الرموز"
#: authtoken/serializers.py:8
#: authtoken/serializers.py:9
msgid "Username"
msgstr "اسم المستخدم"
#: authtoken/serializers.py:9
#: authtoken/serializers.py:13
msgid "Password"
msgstr "كلمة المرور"
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "حساب المستخدم غير مفعل."
#: authtoken/serializers.py:23
#: authtoken/serializers.py:35
msgid "Unable to log in with provided credentials."
msgstr "تعذر تسجيل الدخول بالبيانات التي ادخلتها."
#: authtoken/serializers.py:26
#: authtoken/serializers.py:38
msgid "Must include \"username\" and \"password\"."
msgstr "يجب أن تتضمن \"اسم المستخدم\" و \"كلمة المرور\"."
#: exceptions.py:49
#: exceptions.py:102
msgid "A server error occurred."
msgstr "حدث خطأ في المخدم."
#: exceptions.py:84
msgid "Malformed request."
#: exceptions.py:142
msgid "Invalid input."
msgstr ""
#: exceptions.py:89
#: exceptions.py:161
msgid "Malformed request."
msgstr "الطلب صيغ بشكل سيء."
#: exceptions.py:167
msgid "Incorrect authentication credentials."
msgstr "بيانات الدخول غير صحيحة."
#: exceptions.py:94
#: exceptions.py:173
msgid "Authentication credentials were not provided."
msgstr "لم يتم تزويد بيانات الدخول."
#: exceptions.py:99
#: exceptions.py:179
msgid "You do not have permission to perform this action."
msgstr "ليس لديك صلاحية للقيام بهذا الإجراء."
#: exceptions.py:104 views.py:81
#: exceptions.py:185
msgid "Not found."
msgstr "غير موجود."
#: exceptions.py:109
#: exceptions.py:191
#, python-brace-format
msgid "Method \"{method}\" not allowed."
msgstr "طلب غير مسموح به"
msgstr "الطريقة \"{method}\" غير مسموح بها."
#: exceptions.py:120
#: exceptions.py:202
msgid "Could not satisfy the request Accept header."
msgstr ""
msgstr "لم نتمكن من تلبية الرٱس Accept في الطلب."
#: exceptions.py:132
#: exceptions.py:212
#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request."
msgstr ""
msgstr "الوسيط \"{media_type}\" الموجود في الطلب غير معتمد."
#: exceptions.py:145
#: exceptions.py:223
msgid "Request was throttled."
msgstr "تم تقييد الطلب."
#: exceptions.py:224
#, python-brace-format
msgid "Expected available in {wait} second."
msgstr ""
#: fields.py:269 relations.py:206 relations.py:239 validators.py:98
#: validators.py:181
#: exceptions.py:225
#, python-brace-format
msgid "Expected available in {wait} seconds."
msgstr ""
#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
#: validators.py:183
msgid "This field is required."
msgstr "هذا الحقل مطلوب."
#: fields.py:270
#: fields.py:317
msgid "This field may not be null."
msgstr "لا يمكن لهذا الحقل ان يكون فارغاً null."
#: fields.py:608 fields.py:639
msgid "\"{input}\" is not a valid boolean."
msgstr "\"{input}\" ليس قيمة منطقية."
#: fields.py:701
msgid "Must be a valid boolean."
msgstr ""
#: fields.py:674
#: fields.py:766
msgid "Not a valid string."
msgstr ""
#: fields.py:767
msgid "This field may not be blank."
msgstr "لا يمكن لهذا الحقل ان يكون فارغاً."
#: fields.py:675 fields.py:1675
#: fields.py:768 fields.py:1881
#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters."
msgstr "تأكد ان الحقل لا يزيد عن {max_length} محرف."
#: fields.py:676
#: fields.py:769
#, python-brace-format
msgid "Ensure this field has at least {min_length} characters."
msgstr "تأكد ان الحقل {min_length} محرف على الاقل."
#: fields.py:713
#: fields.py:816
msgid "Enter a valid email address."
msgstr "عليك ان تدخل بريد إلكتروني صالح."
#: fields.py:724
#: fields.py:827
msgid "This value does not match the required pattern."
msgstr "هذه القيمة لا تطابق النمط المطلوب."
#: fields.py:735
#: fields.py:838
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "أدخل \"slug\" صالح يحتوي على حروف، أرقام، شُرط سفلية أو واصلات."
#: fields.py:839
msgid ""
"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
"or hyphens."
msgstr ""
#: fields.py:747
#: fields.py:854
msgid "Enter a valid URL."
msgstr "الرجاء إدخال رابط إلكتروني صالح."
#: fields.py:760
msgid "\"{value}\" is not a valid UUID."
#: fields.py:867
msgid "Must be a valid UUID."
msgstr ""
#: fields.py:796
#: fields.py:903
msgid "Enter a valid IPv4 or IPv6 address."
msgstr "برجاء إدخال عنوان IPV4 أو IPV6 صحيح"
msgstr "أدخِل عنوان IPV4 أو IPV6 صحيح."
#: fields.py:821
#: fields.py:931
msgid "A valid integer is required."
msgstr "الرجاء إدخال رقم صحيح صالح."
#: fields.py:822 fields.py:857 fields.py:891
#: fields.py:932 fields.py:969 fields.py:1005 fields.py:1366
#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "تأكد ان القيمة أقل أو تساوي {max_value}."
#: fields.py:823 fields.py:858 fields.py:892
#: fields.py:933 fields.py:970 fields.py:1006 fields.py:1367
#, python-brace-format
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "تأكد ان القيمة أكبر أو تساوي {min_value}."
#: fields.py:824 fields.py:859 fields.py:896
#: fields.py:934 fields.py:971 fields.py:1010
msgid "String value too large."
msgstr "القيمه اكبر من المسموح"
msgstr "السلسلة اطول من القيمة المسموح بها."
#: fields.py:856 fields.py:890
#: fields.py:968 fields.py:1004
msgid "A valid number is required."
msgstr "الرجاء إدخال رقم صالح."
#: fields.py:893
#: fields.py:1007
#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "تأكد ان القيمة لا تحوي أكثر من {max_digits} رقم."
#: fields.py:894
#: fields.py:1008
#, python-brace-format
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr ""
msgstr "تأكد انه لا يوجد اكثر من {max_decimal_places} منازل عشرية."
#: fields.py:895
#: fields.py:1009
#, python-brace-format
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr ""
msgstr "تأكد انه لا يوجد اكثر من {max_whole_digits} أرقام قبل النقطة العشرية."
#: fields.py:1025
#: fields.py:1148
#, python-brace-format
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "صيغة التاريخ و الوقت غير صحيحة. عليك أن تستخدم واحدة من هذه الصيغ التالية: {format}."
#: fields.py:1026
#: fields.py:1149
msgid "Expected a datetime but got a date."
msgstr "متوقع تاريخ و وقت و وجد تاريخ فقط"
#: fields.py:1103
#: fields.py:1150
#, python-brace-format
msgid "Invalid datetime for the timezone \"{timezone}\"."
msgstr ""
#: fields.py:1151
msgid "Datetime value out of range."
msgstr ""
#: fields.py:1236
#, python-brace-format
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "صيغة التاريخ غير صحيحة. عليك أن تستخدم واحدة من هذه الصيغ التالية: {format}."
#: fields.py:1104
#: fields.py:1237
msgid "Expected a date but got a datetime."
msgstr "متوقع تاريخ فقط و وجد تاريخ ووقت"
#: fields.py:1170
#: fields.py:1303
#, python-brace-format
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "صيغة الوقت غير صحيحة. عليك أن تستخدم واحدة من هذه الصيغ التالية: {format}."
#: fields.py:1232
#: fields.py:1365
#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}."
msgstr "صيغة المده غير صحيحه, برجاء إستخدام أحد هذه الصيغ {format}"
msgstr "صيغة المدة غير صحيحه, يرجى إستخدام إحدى هذه الصيغ: {format}."
#: fields.py:1251 fields.py:1300
#: fields.py:1399 fields.py:1456
#, python-brace-format
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" ليست واحدة من الخيارات الصالحة."
#: fields.py:1254 relations.py:71 relations.py:441
#: fields.py:1402
#, python-brace-format
msgid "More than {count} items..."
msgstr "أكثر من {count} عنصر..."
#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524
#: fields.py:1457 fields.py:1603 relations.py:485 serializers.py:570
#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr ""
msgstr "المتوقع وجود قائمة عناصر لكن وجد النوع \"{input_type}\"."
#: fields.py:1302
#: fields.py:1458
msgid "This selection may not be empty."
msgstr ""
msgstr "هذا التحديد لا يجب أن يكون فارغا."
#: fields.py:1339
#: fields.py:1495
#, python-brace-format
msgid "\"{input}\" is not a valid path choice."
msgstr ""
msgstr "{input} كإختيار مسار غير صالح."
#: fields.py:1358
#: fields.py:1514
msgid "No file was submitted."
msgstr "لم يتم إرسال أي ملف."
#: fields.py:1359
#: fields.py:1515
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr ""
msgstr "المعطيات المرسولة ليست ملف. إفحص نوع الترميز في النموذج."
#: fields.py:1360
#: fields.py:1516
msgid "No filename could be determined."
msgstr ""
msgstr "ما من إسم ملف أمكن تحديده."
#: fields.py:1361
#: fields.py:1517
msgid "The submitted file is empty."
msgstr "الملف الذي تم إرساله فارغ."
#: fields.py:1362
#: fields.py:1518
#, python-brace-format
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "تأكد ان اسم الملف لا يحوي أكثر من {max_length} محرف (الإسم المرسل يحوي {length} محرف)."
#: fields.py:1410
#: fields.py:1566
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr ""
msgstr "الرجاء تحميل صورة صالحة. الملف الذي تم تحميله إما لم يكن صورة او انه كان صورة تالفة."
#: fields.py:1449 relations.py:438 serializers.py:525
#: fields.py:1604 relations.py:486 serializers.py:571
msgid "This list may not be empty."
msgstr "القائمة يجب أن لا تكون فارغة."
#: fields.py:1605
#, python-brace-format
msgid "Ensure this field has at least {min_length} elements."
msgstr ""
#: fields.py:1502
#: fields.py:1606
#, python-brace-format
msgid "Ensure this field has no more than {max_length} elements."
msgstr ""
#: fields.py:1682
#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "المتوقع كان قاموس عناصر و لكن النوع المتحصل عليه هو \"{input_type}\"."
#: fields.py:1683
msgid "This dictionary may not be empty."
msgstr ""
#: fields.py:1549
#: fields.py:1755
msgid "Value must be valid JSON."
msgstr ""
msgstr "القيمة يجب أن تكون JSON صالح."
#: filters.py:36 templates/rest_framework/filters/django_filter.html:5
msgid "Submit"
msgstr "أرسل"
#: filters.py:336
msgid "ascending"
msgstr "تصاعدي"
#: filters.py:337
msgid "descending"
msgstr "تنازلي"
#: pagination.py:193
msgid "Invalid page."
msgstr "صفحة غير صحيحة."
#: pagination.py:427
msgid "Invalid cursor"
msgstr ""
#: relations.py:207
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "معرف العنصر \"{pk_value}\" غير صالح - العنصر غير موجود."
#: relations.py:208
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr ""
#: relations.py:240
msgid "Invalid hyperlink - No URL match."
msgstr ""
#: relations.py:241
msgid "Invalid hyperlink - Incorrect URL match."
msgstr ""
#: relations.py:242
msgid "Invalid hyperlink - Object does not exist."
msgstr ""
#: relations.py:243
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr ""
#: relations.py:401
msgid "Object with {slug_name}={value} does not exist."
msgstr ""
#: relations.py:402
msgid "Invalid value."
msgstr "قيمة غير صالحة."
#: serializers.py:326
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr ""
#: templates/rest_framework/admin.html:116
#: templates/rest_framework/base.html:128
msgid "Filters"
msgstr "مرشحات"
#: templates/rest_framework/filters/django_filter.html:2
#: templates/rest_framework/filters/django_filter_crispyforms.html:4
msgid "Field filters"
msgstr "مرشحات الحقول"
#: templates/rest_framework/filters/ordering.html:3
msgid "Ordering"
msgstr "الترتيب"
#: templates/rest_framework/filters/search.html:2
#: filters.py:49 templates/rest_framework/filters/search.html:2
msgid "Search"
msgstr "بحث"
#: templates/rest_framework/horizontal/radio.html:2
#: templates/rest_framework/inline/radio.html:2
#: templates/rest_framework/vertical/radio.html:2
#: filters.py:50
msgid "A search term."
msgstr ""
#: filters.py:180 templates/rest_framework/filters/ordering.html:3
msgid "Ordering"
msgstr "الترتيب"
#: filters.py:181
msgid "Which field to use when ordering the results."
msgstr ""
#: filters.py:287
msgid "ascending"
msgstr "تصاعدي"
#: filters.py:288
msgid "descending"
msgstr "تنازلي"
#: pagination.py:174
msgid "A page number within the paginated result set."
msgstr ""
#: pagination.py:179 pagination.py:372 pagination.py:590
msgid "Number of results to return per page."
msgstr ""
#: pagination.py:189
msgid "Invalid page."
msgstr "صفحة غير صحيحة."
#: pagination.py:374
msgid "The initial index from which to return the results."
msgstr ""
#: pagination.py:581
msgid "The pagination cursor value."
msgstr ""
#: pagination.py:583
msgid "Invalid cursor"
msgstr "مؤشر غير صالح"
#: relations.py:246
#, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "معرف العنصر \"{pk_value}\" غير صالح - العنصر غير موجود."
#: relations.py:247
#, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "نوع خاطئ. المتوقع قيمة من pk، لكن المتحصل عليه {data_type}."
#: relations.py:280
msgid "Invalid hyperlink - No URL match."
msgstr "إرتباط تشعبي غير صالح - لا مطابقة لURL."
#: relations.py:281
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "إرتباط تشعبي غير صالح - مطابقة خاطئة لURL."
#: relations.py:282
msgid "Invalid hyperlink - Object does not exist."
msgstr "إرتباط تشعبي غير صالح - عنصر غير موجود."
#: relations.py:283
#, python-brace-format
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "نوع خاطئ. المتوقع سلسلة URL، لكن المتحصل عليه {data_type}."
#: relations.py:448
#, python-brace-format
msgid "Object with {slug_name}={value} does not exist."
msgstr "عنصر ب {slug_name}={value} غير موجود."
#: relations.py:449
msgid "Invalid value."
msgstr "قيمة غير صالحة."
#: schemas/utils.py:32
msgid "unique integer value"
msgstr ""
#: schemas/utils.py:34
msgid "UUID string"
msgstr ""
#: schemas/utils.py:36
msgid "unique value"
msgstr ""
#: schemas/utils.py:38
#, python-brace-format
msgid "A {value_type} identifying this {name}."
msgstr ""
#: serializers.py:337
#, python-brace-format
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "معطيات غير صالحة. المتوقع هو قاموس، لكن المتحصل عليه {datatype}."
#: templates/rest_framework/admin.html:116
#: templates/rest_framework/base.html:136
msgid "Extra Actions"
msgstr ""
#: templates/rest_framework/admin.html:130
#: templates/rest_framework/base.html:150
msgid "Filters"
msgstr "مرشحات"
#: templates/rest_framework/base.html:37
msgid "navbar"
msgstr ""
#: templates/rest_framework/base.html:75
msgid "content"
msgstr ""
#: templates/rest_framework/base.html:78
msgid "request form"
msgstr ""
#: templates/rest_framework/base.html:157
msgid "main content"
msgstr ""
#: templates/rest_framework/base.html:173
msgid "request info"
msgstr ""
#: templates/rest_framework/base.html:177
msgid "response info"
msgstr ""
#: templates/rest_framework/horizontal/radio.html:4
#: templates/rest_framework/inline/radio.html:3
#: templates/rest_framework/vertical/radio.html:3
msgid "None"
msgstr "لا شيء"
#: templates/rest_framework/horizontal/select_multiple.html:2
#: templates/rest_framework/inline/select_multiple.html:2
#: templates/rest_framework/vertical/select_multiple.html:2
#: templates/rest_framework/horizontal/select_multiple.html:4
#: templates/rest_framework/inline/select_multiple.html:3
#: templates/rest_framework/vertical/select_multiple.html:3
msgid "No items to select."
msgstr ""
msgstr "ما من عناصر للتحديد."
#: validators.py:43
#: validators.py:39
msgid "This field must be unique."
msgstr "هذا الحقل يجب أن يكون فريد"
#: validators.py:97
#: validators.py:89
#, python-brace-format
msgid "The fields {field_names} must make a unique set."
msgstr "الحقول {field_names} يجب أن تشكل مجموعة فريدة."
#: validators.py:171
#, python-brace-format
msgid "Surrogate characters are not allowed: U+{code_point:X}."
msgstr ""
#: validators.py:245
#: validators.py:243
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date."
msgstr ""
msgstr "الحقل يجب ان يكون فريد للتاريخ {date_field}."
#: validators.py:260
#: validators.py:258
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month."
msgstr ""
msgstr "الحقل يجب ان يكون فريد للشهر {date_field}."
#: validators.py:273
#: validators.py:271
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year."
msgstr ""
msgstr "الحقل يجب ان يكون فريد للعام {date_field}."
#: versioning.py:42
#: versioning.py:40
msgid "Invalid version in \"Accept\" header."
msgstr ""
msgstr "إصدار غير صالح في الرٱس \"Accept\"."
#: versioning.py:73
#: versioning.py:71
msgid "Invalid version in URL path."
msgstr ""
msgstr "إصدار غير صالح في المسار URL."
#: versioning.py:115
#: versioning.py:116
msgid "Invalid version in URL path. Does not match any version namespace."
msgstr ""
msgstr " إصدار غير صالح في المسار URL. لا يطابق أي إصدار من مساحة الإسم."
#: versioning.py:147
#: versioning.py:148
msgid "Invalid version in hostname."
msgstr ""
msgstr "إصدار غير صالح في اسم المضيف."
#: versioning.py:169
#: versioning.py:170
msgid "Invalid version in query parameter."
msgstr ""
#: views.py:88
msgid "Permission denied."
msgstr "ليس لديك صلاحية."
msgstr "إصدار غير صالح في معلمة الإستعلام."

Binary file not shown.

View File

@ -0,0 +1,573 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Emin Mastizada <emin@linux.com>, 2020
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-13 21:45+0200\n"
"PO-Revision-Date: 2020-10-13 19:45+0000\n"
"Last-Translator: Xavier Ordoquy <xordoquy@linovia.com>\n"
"Language-Team: Azerbaijani (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/az/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: az\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: authentication.py:70
msgid "Invalid basic header. No credentials provided."
msgstr "Xətalı sadə başlıq. İstifadəçi məlumatları təchiz edilməyib."
#: authentication.py:73
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "Xətalı sadə başlıq. İstifadəçi məlumatlarında boşluq olmamalıdır."
#: authentication.py:83
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "Xətalı sadə başlıq. İstifadəçi məlumatları base64 ilə düzgün şifrələnməyib."
#: authentication.py:101
msgid "Invalid username/password."
msgstr "Xətalı istifadəçi adı/parol."
#: authentication.py:104 authentication.py:206
msgid "User inactive or deleted."
msgstr "İstifadəçi aktiv deyil və ya silinib."
#: authentication.py:184
msgid "Invalid token header. No credentials provided."
msgstr "Xətalı token başlığı. İstifadəçi məlumatları təchiz edilməyib."
#: authentication.py:187
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Xətalı token başlığı. Token mətnində boşluqlar olmamalıdır."
#: authentication.py:193
msgid ""
"Invalid token header. Token string should not contain invalid characters."
msgstr "Xətalı token başlığı. Token mətnində xətalı simvollar olmamalıdır."
#: authentication.py:203
msgid "Invalid token."
msgstr "Xətalı token."
#: authtoken/apps.py:7
msgid "Auth Token"
msgstr "Təsdiqləmə Tokeni"
#: authtoken/models.py:13
msgid "Key"
msgstr "Açar"
#: authtoken/models.py:16
msgid "User"
msgstr "İstifadəçi"
#: authtoken/models.py:18
msgid "Created"
msgstr "Yaradılıb"
#: authtoken/models.py:27 authtoken/serializers.py:19
msgid "Token"
msgstr "Token"
#: authtoken/models.py:28
msgid "Tokens"
msgstr "Tokenlər"
#: authtoken/serializers.py:9
msgid "Username"
msgstr "İstifadəçi adı"
#: authtoken/serializers.py:13
msgid "Password"
msgstr "Parol"
#: authtoken/serializers.py:35
msgid "Unable to log in with provided credentials."
msgstr "Təchiz edilən istifadəçi məlumatları ilə daxil olmaq mümkün olmadı."
#: authtoken/serializers.py:38
msgid "Must include \"username\" and \"password\"."
msgstr "Mütləq \"username\" və \"password\" olmalıdır."
#: exceptions.py:102
msgid "A server error occurred."
msgstr "Server xətası yaşandı."
#: exceptions.py:142
msgid "Invalid input."
msgstr ""
#: exceptions.py:161
msgid "Malformed request."
msgstr "Qüsurlu istək."
#: exceptions.py:167
msgid "Incorrect authentication credentials."
msgstr "Səhv təsdiqləmə məlumatları."
#: exceptions.py:173
msgid "Authentication credentials were not provided."
msgstr "Təsdiqləmə məlumatları təchiz edilməyib."
#: exceptions.py:179
msgid "You do not have permission to perform this action."
msgstr "Bu əməliyyat üçün icazəniz yoxdur."
#: exceptions.py:185
msgid "Not found."
msgstr "Tapılmadı."
#: exceptions.py:191
#, python-brace-format
msgid "Method \"{method}\" not allowed."
msgstr "\"{method}\" yöntəminə icazə verilmir."
#: exceptions.py:202
msgid "Could not satisfy the request Accept header."
msgstr "İstəyin Accept başlığını qane etmək mümkün olmadı."
#: exceptions.py:212
#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "İstəkdə dəstəklənməyən \"{media_type}\" mediya növü."
#: exceptions.py:223
msgid "Request was throttled."
msgstr "İstək nəzərə alınmadı."
#: exceptions.py:224
#, python-brace-format
msgid "Expected available in {wait} second."
msgstr ""
#: exceptions.py:225
#, python-brace-format
msgid "Expected available in {wait} seconds."
msgstr ""
#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
#: validators.py:183
msgid "This field is required."
msgstr "Bu sahə tələb edilir."
#: fields.py:317
msgid "This field may not be null."
msgstr "Bu sahə null ola bilməz."
#: fields.py:701
msgid "Must be a valid boolean."
msgstr ""
#: fields.py:766
msgid "Not a valid string."
msgstr ""
#: fields.py:767
msgid "This field may not be blank."
msgstr "Bu sahə boş ola bilməz."
#: fields.py:768 fields.py:1881
#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Bu sahənin ən çox {max_length} simvolu olduğuna əmin olun."
#: fields.py:769
#, python-brace-format
msgid "Ensure this field has at least {min_length} characters."
msgstr "Bu sahənin ən az {min_length} simvolu olduğuna əmin olun."
#: fields.py:816
msgid "Enter a valid email address."
msgstr "Keçərli e-poçt ünvanı daxil edin."
#: fields.py:827
msgid "This value does not match the required pattern."
msgstr "Bu dəyər tələb edilən formaya uyğun gəlmir."
#: fields.py:838
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Hərf, rəqəm, alt xətt və defislərdən ibarət keçərli \"slug\" daxil edin."
#: fields.py:839
msgid ""
"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
"or hyphens."
msgstr ""
#: fields.py:854
msgid "Enter a valid URL."
msgstr "Keçərli URL daxil edin."
#: fields.py:867
msgid "Must be a valid UUID."
msgstr ""
#: fields.py:903
msgid "Enter a valid IPv4 or IPv6 address."
msgstr "Keçərli IPv4 və ya IPv6 ünvanı daxil edin."
#: fields.py:931
msgid "A valid integer is required."
msgstr "Keçərli tam ədəd tələb edilir."
#: fields.py:932 fields.py:969 fields.py:1005 fields.py:1366
#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Bu dəyərin uzunluğunun ən çox {max_value} olduğuna əmin olun."
#: fields.py:933 fields.py:970 fields.py:1006 fields.py:1367
#, python-brace-format
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Bu dəyərin uzunluğunun ən az {min_value} olduğuna əmin olun."
#: fields.py:934 fields.py:971 fields.py:1010
msgid "String value too large."
msgstr "Yazı dəyəri çox uzundur."
#: fields.py:968 fields.py:1004
msgid "A valid number is required."
msgstr "Keçərli rəqəm tələb edilir."
#: fields.py:1007
#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Ən çox {max_digits} rəqəm olduğuna əmin olun."
#: fields.py:1008
#, python-brace-format
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Ən çox {max_decimal_places} onluq kəsr hissəsi olduğuna əmin olun."
#: fields.py:1009
#, python-brace-format
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Onluq kərsdən əvvəl ən çox {max_whole_digits} rəqəm olduğuna əmin olun."
#: fields.py:1148
#, python-brace-format
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "Datetime dəyəri səhvdir. Əvəzinə bu formatlardan birini işlədin: {format}."
#: fields.py:1149
msgid "Expected a datetime but got a date."
msgstr "Datetime gözlənirdi amma date gəldi."
#: fields.py:1150
#, python-brace-format
msgid "Invalid datetime for the timezone \"{timezone}\"."
msgstr ""
#: fields.py:1151
msgid "Datetime value out of range."
msgstr ""
#: fields.py:1236
#, python-brace-format
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "Date dəyəri səhvdir. Əvəzinə bu formatlardan birini işlədin: {format}."
#: fields.py:1237
msgid "Expected a date but got a datetime."
msgstr "Date gözlənirdi amma datetime gəldi."
#: fields.py:1303
#, python-brace-format
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "Vaxt formatı səhvdir. Əvəzinə bu formatlardan birini işlədin: {format}."
#: fields.py:1365
#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}."
msgstr "Müddət formatı səhvdir. Əvəzinə bu formatlardan birini işlədin: {format}."
#: fields.py:1399 fields.py:1456
#, python-brace-format
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" keçərli seçim deyil."
#: fields.py:1402
#, python-brace-format
msgid "More than {count} items..."
msgstr "{count} elementdən daha çoxdur..."
#: fields.py:1457 fields.py:1603 relations.py:485 serializers.py:570
#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "Elementlər siyahısı gözlənirdi, amma \"{input_type}\" növü gəldi."
#: fields.py:1458
msgid "This selection may not be empty."
msgstr "Bu seçim boş ola bilməz."
#: fields.py:1495
#, python-brace-format
msgid "\"{input}\" is not a valid path choice."
msgstr "\"{input}\" keçərli yol seçimi deyil."
#: fields.py:1514
msgid "No file was submitted."
msgstr "Heç bir fayl göndərilmədi."
#: fields.py:1515
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "Göndərilən məlumat fayl deyildi. Anketin şifrələmə (encoding) növünü yoxlayın."
#: fields.py:1516
msgid "No filename could be determined."
msgstr "Faylın adı təyin edilə bilmədi."
#: fields.py:1517
msgid "The submitted file is empty."
msgstr "Göndərilən fayl boşdur."
#: fields.py:1518
#, python-brace-format
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "Fayl adının ən çox {max_length} simvoldan ibarət olduğuna əmin olun (hazırki: {length})."
#: fields.py:1566
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "Keçərli şəkil yükləyin. Yüklədiyiniz fayl ya şəkil deyil, ya da ola bilsin zədələnib."
#: fields.py:1604 relations.py:486 serializers.py:571
msgid "This list may not be empty."
msgstr "Bu siyahı boş ola bilməz."
#: fields.py:1605
#, python-brace-format
msgid "Ensure this field has at least {min_length} elements."
msgstr ""
#: fields.py:1606
#, python-brace-format
msgid "Ensure this field has no more than {max_length} elements."
msgstr ""
#: fields.py:1682
#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "Elementlərin kitabçası (dictionary) gözlənirdi amma \"{input_type}\" növü gəldi."
#: fields.py:1683
msgid "This dictionary may not be empty."
msgstr ""
#: fields.py:1755
msgid "Value must be valid JSON."
msgstr "Dəyər keçərli JSON olmalıdır."
#: filters.py:49 templates/rest_framework/filters/search.html:2
msgid "Search"
msgstr "Axtarış"
#: filters.py:50
msgid "A search term."
msgstr ""
#: filters.py:180 templates/rest_framework/filters/ordering.html:3
msgid "Ordering"
msgstr "Sıralama"
#: filters.py:181
msgid "Which field to use when ordering the results."
msgstr ""
#: filters.py:287
msgid "ascending"
msgstr "artan"
#: filters.py:288
msgid "descending"
msgstr "azalan"
#: pagination.py:174
msgid "A page number within the paginated result set."
msgstr ""
#: pagination.py:179 pagination.py:372 pagination.py:590
msgid "Number of results to return per page."
msgstr ""
#: pagination.py:189
msgid "Invalid page."
msgstr "Xətalı səhifə."
#: pagination.py:374
msgid "The initial index from which to return the results."
msgstr ""
#: pagination.py:581
msgid "The pagination cursor value."
msgstr ""
#: pagination.py:583
msgid "Invalid cursor"
msgstr "Xətalı kursor"
#: relations.py:246
#, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "Xətalı pk \"{pk_value}\" - obyekt mövcud deyil."
#: relations.py:247
#, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "Xətalı növ. PK dəyəri gözlənirdi, {data_type} alındı."
#: relations.py:280
msgid "Invalid hyperlink - No URL match."
msgstr "Xətalı hiperkeçid - Heç bir URL uyğun gəlmir."
#: relations.py:281
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "Xətalı hiperkeçid - Xətalı URL uyğunluğu."
#: relations.py:282
msgid "Invalid hyperlink - Object does not exist."
msgstr "Xətalı hiperkeçid - Obyekt mövcud deyil."
#: relations.py:283
#, python-brace-format
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "Xətalı növ. URL yazısı gözlənirdi, {data_type} gəldi."
#: relations.py:448
#, python-brace-format
msgid "Object with {slug_name}={value} does not exist."
msgstr "{slug_name}={value} üçün uyğun obyekt mövcud deyil."
#: relations.py:449
msgid "Invalid value."
msgstr "Xətalı dəyər."
#: schemas/utils.py:32
msgid "unique integer value"
msgstr ""
#: schemas/utils.py:34
msgid "UUID string"
msgstr ""
#: schemas/utils.py:36
msgid "unique value"
msgstr ""
#: schemas/utils.py:38
#, python-brace-format
msgid "A {value_type} identifying this {name}."
msgstr ""
#: serializers.py:337
#, python-brace-format
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "Xətalı məlumat. Kitabça (dictionary) gözlənirdi, {datatype} gəldi."
#: templates/rest_framework/admin.html:116
#: templates/rest_framework/base.html:136
msgid "Extra Actions"
msgstr ""
#: templates/rest_framework/admin.html:130
#: templates/rest_framework/base.html:150
msgid "Filters"
msgstr "Filterlər"
#: templates/rest_framework/base.html:37
msgid "navbar"
msgstr ""
#: templates/rest_framework/base.html:75
msgid "content"
msgstr ""
#: templates/rest_framework/base.html:78
msgid "request form"
msgstr ""
#: templates/rest_framework/base.html:157
msgid "main content"
msgstr ""
#: templates/rest_framework/base.html:173
msgid "request info"
msgstr ""
#: templates/rest_framework/base.html:177
msgid "response info"
msgstr ""
#: templates/rest_framework/horizontal/radio.html:4
#: templates/rest_framework/inline/radio.html:3
#: templates/rest_framework/vertical/radio.html:3
msgid "None"
msgstr "Heç nə"
#: templates/rest_framework/horizontal/select_multiple.html:4
#: templates/rest_framework/inline/select_multiple.html:3
#: templates/rest_framework/vertical/select_multiple.html:3
msgid "No items to select."
msgstr "Seçiləcək element yoxdur."
#: validators.py:39
msgid "This field must be unique."
msgstr "Bu sahə unikal olmalıdır."
#: validators.py:89
#, python-brace-format
msgid "The fields {field_names} must make a unique set."
msgstr "{field_names} sahələri birlikdə unikal dəst olmalıdırlar."
#: validators.py:171
#, python-brace-format
msgid "Surrogate characters are not allowed: U+{code_point:X}."
msgstr ""
#: validators.py:243
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "Bu sahə \"{date_field}\" günü üçün unikal olmalıdır."
#: validators.py:258
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "Bu sahə \"{date_field}\" ayı üçün unikal olmalıdır."
#: validators.py:271
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "Bu sahə \"{date_field}\" ili üçün unikal olmalıdır."
#: versioning.py:40
msgid "Invalid version in \"Accept\" header."
msgstr "\"Accept\" başlığında xətalı versiya."
#: versioning.py:71
msgid "Invalid version in URL path."
msgstr "URL yolunda xətalı versiya."
#: versioning.py:116
msgid "Invalid version in URL path. Does not match any version namespace."
msgstr "URL yolunda xətalı versiya. Heç bir versiya namespace-inə uyğun gəlmir."
#: versioning.py:148
msgid "Invalid version in hostname."
msgstr "Hostname-də xətalı versiya."
#: versioning.py:170
msgid "Invalid version in query parameter."
msgstr "Sorğu parametrində xətalı versiya."

Binary file not shown.

View File

@ -0,0 +1,572 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-13 21:45+0200\n"
"PO-Revision-Date: 2020-10-13 19:45+0000\n"
"Last-Translator: Xavier Ordoquy <xordoquy@linovia.com>\n"
"Language-Team: Bulgarian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/bg/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: bg\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: authentication.py:70
msgid "Invalid basic header. No credentials provided."
msgstr "Невалиден header за базово удостоверение (basic authentication). Не се предоставени удостоверения."
#: authentication.py:73
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "Невалиден header за базово удостоверение (basic authentication). Носителите на удостоверение не трябва да съдържат интервали."
#: authentication.py:83
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "Невалиден header за базово удостоверение (basic authentication). Носителите на удостоверение не са кодирани в base64."
#: authentication.py:101
msgid "Invalid username/password."
msgstr "Невалидни потребителско име/парола."
#: authentication.py:104 authentication.py:206
msgid "User inactive or deleted."
msgstr "Потребителят е неактивен или изтрит."
#: authentication.py:184
msgid "Invalid token header. No credentials provided."
msgstr "Невалиден token header. Не са предоставени носители на удостоверение."
#: authentication.py:187
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Невалиден token header. Жетона (token-a) не трябва да съдържа интервали."
#: authentication.py:193
msgid ""
"Invalid token header. Token string should not contain invalid characters."
msgstr "Невалиден token header. Жетона (token-a) не трябва да съдържа невалидни символи."
#: authentication.py:203
msgid "Invalid token."
msgstr "Невалиден жетон."
#: authtoken/apps.py:7
msgid "Auth Token"
msgstr "Жетон за удостоверение"
#: authtoken/models.py:13
msgid "Key"
msgstr "Ключ"
#: authtoken/models.py:16
msgid "User"
msgstr "Потребител"
#: authtoken/models.py:18
msgid "Created"
msgstr "Създаден"
#: authtoken/models.py:27 authtoken/serializers.py:19
msgid "Token"
msgstr "Жетон"
#: authtoken/models.py:28
msgid "Tokens"
msgstr "Жетони"
#: authtoken/serializers.py:9
msgid "Username"
msgstr "Потребителско име"
#: authtoken/serializers.py:13
msgid "Password"
msgstr "Парола"
#: authtoken/serializers.py:35
msgid "Unable to log in with provided credentials."
msgstr "Не е възможен вход с предоставените удостоверения."
#: authtoken/serializers.py:38
msgid "Must include \"username\" and \"password\"."
msgstr "Трябва да съдържа \"username\" (потребителско име) и \"password\" (парола)."
#: exceptions.py:102
msgid "A server error occurred."
msgstr "Сървърна грешка."
#: exceptions.py:142
msgid "Invalid input."
msgstr ""
#: exceptions.py:161
msgid "Malformed request."
msgstr "Неправилно оформена заявка."
#: exceptions.py:167
msgid "Incorrect authentication credentials."
msgstr "Невалидни носители на удостоверение."
#: exceptions.py:173
msgid "Authentication credentials were not provided."
msgstr "Носители на удостоверение не са предоставени."
#: exceptions.py:179
msgid "You do not have permission to perform this action."
msgstr "Нямате права да се направи това действие."
#: exceptions.py:185
msgid "Not found."
msgstr "Обектът не е намерен."
#: exceptions.py:191
#, python-brace-format
msgid "Method \"{method}\" not allowed."
msgstr "Метод \"{method}\" не е позволен."
#: exceptions.py:202
msgid "Could not satisfy the request Accept header."
msgstr "Не може да бъде удовлетворен Accept header."
#: exceptions.py:212
#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "Неподдържан тип на документа \"{media_type}\" в заявката."
#: exceptions.py:223
msgid "Request was throttled."
msgstr "Заявката е ограничена."
#: exceptions.py:224
#, python-brace-format
msgid "Expected available in {wait} second."
msgstr ""
#: exceptions.py:225
#, python-brace-format
msgid "Expected available in {wait} seconds."
msgstr ""
#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
#: validators.py:183
msgid "This field is required."
msgstr "Това поле е задължително."
#: fields.py:317
msgid "This field may not be null."
msgstr "Това поле не може да има празна стойност."
#: fields.py:701
msgid "Must be a valid boolean."
msgstr ""
#: fields.py:766
msgid "Not a valid string."
msgstr ""
#: fields.py:767
msgid "This field may not be blank."
msgstr "Това поле не може да е празно."
#: fields.py:768 fields.py:1881
#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Това поле не трябва да съдържа повече от {max_length} символа."
#: fields.py:769
#, python-brace-format
msgid "Ensure this field has at least {min_length} characters."
msgstr "Това поле трябва да съдържа поде {min_length} символа."
#: fields.py:816
msgid "Enter a valid email address."
msgstr "Въведете валиден имейл адрес."
#: fields.py:827
msgid "This value does not match the required pattern."
msgstr "Тази стойност не съответства на изисквания шаблон."
#: fields.py:838
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Въведете валиден \"slug\" съдържащ латински букви, цифри, долни черти или тирета."
#: fields.py:839
msgid ""
"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
"or hyphens."
msgstr ""
#: fields.py:854
msgid "Enter a valid URL."
msgstr "Въведете валиден URL."
#: fields.py:867
msgid "Must be a valid UUID."
msgstr ""
#: fields.py:903
msgid "Enter a valid IPv4 or IPv6 address."
msgstr "Въведете валиден IPv4 или IPv6 адрес."
#: fields.py:931
msgid "A valid integer is required."
msgstr "Необходимо е валидно цяло число."
#: fields.py:932 fields.py:969 fields.py:1005 fields.py:1366
#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Тази стойност трябва да е не повече от {max_value}."
#: fields.py:933 fields.py:970 fields.py:1006 fields.py:1367
#, python-brace-format
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Тази стойност трябва да е поне {min_value}."
#: fields.py:934 fields.py:971 fields.py:1010
msgid "String value too large."
msgstr "Низът е прекалено голям."
#: fields.py:968 fields.py:1004
msgid "A valid number is required."
msgstr "Необходимо е валидно число."
#: fields.py:1007
#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Допустими са не повече от {max_digits} цифри."
#: fields.py:1008
#, python-brace-format
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Допустими са не повече от {max_decimal_places} цифри след десетичната запетая."
#: fields.py:1009
#, python-brace-format
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Допустими са не повече от {max_whole_digits} цифри преди десетичната запетая."
#: fields.py:1148
#, python-brace-format
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "Датата и часът са в невалиден формат. Валидни са следните формати: {format}."
#: fields.py:1149
msgid "Expected a datetime but got a date."
msgstr "Очаква се дата и час, но е намерена само дата."
#: fields.py:1150
#, python-brace-format
msgid "Invalid datetime for the timezone \"{timezone}\"."
msgstr ""
#: fields.py:1151
msgid "Datetime value out of range."
msgstr ""
#: fields.py:1236
#, python-brace-format
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "Дата е в невалиден формат. Валидни са следните формати: {format}."
#: fields.py:1237
msgid "Expected a date but got a datetime."
msgstr "Очаква се дата, но са подадени дата и час."
#: fields.py:1303
#, python-brace-format
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "Времето е в невалиден формат. Валидни са следните формати: {format}."
#: fields.py:1365
#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}."
msgstr "Отрязъкът от време е в невалиден формат. Валидни са следните формати: {format}."
#: fields.py:1399 fields.py:1456
#, python-brace-format
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" не е валиден избор."
#: fields.py:1402
#, python-brace-format
msgid "More than {count} items..."
msgstr "Повече от {count} неща..."
#: fields.py:1457 fields.py:1603 relations.py:485 serializers.py:570
#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "Очаква се списък от неща, но е получен тип \"{input_type}\"."
#: fields.py:1458
msgid "This selection may not be empty."
msgstr "Този избор не може да е празен."
#: fields.py:1495
#, python-brace-format
msgid "\"{input}\" is not a valid path choice."
msgstr "\"{input}\" не е валиден избор за път."
#: fields.py:1514
msgid "No file was submitted."
msgstr "Не е подаден файл."
#: fields.py:1515
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "Подадените данни не са файл. Проверете кодировката на формата."
#: fields.py:1516
msgid "No filename could be determined."
msgstr "Не може да бъде определено името на файла."
#: fields.py:1517
msgid "The submitted file is empty."
msgstr "Подадения файл е празен."
#: fields.py:1518
#, python-brace-format
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "Името на файла може да е до {max_length} символа (то е {length})."
#: fields.py:1566
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "Невалидно изображение. Подадения файл не е изображение или е повредено изображение."
#: fields.py:1604 relations.py:486 serializers.py:571
msgid "This list may not be empty."
msgstr "Този списък не може да е празен."
#: fields.py:1605
#, python-brace-format
msgid "Ensure this field has at least {min_length} elements."
msgstr ""
#: fields.py:1606
#, python-brace-format
msgid "Ensure this field has no more than {max_length} elements."
msgstr ""
#: fields.py:1682
#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "Очаква се асоциативен масив, но е получен \"{input_type}\"."
#: fields.py:1683
msgid "This dictionary may not be empty."
msgstr ""
#: fields.py:1755
msgid "Value must be valid JSON."
msgstr "Стойността трябва да е валиден JSON."
#: filters.py:49 templates/rest_framework/filters/search.html:2
msgid "Search"
msgstr "Търсене"
#: filters.py:50
msgid "A search term."
msgstr ""
#: filters.py:180 templates/rest_framework/filters/ordering.html:3
msgid "Ordering"
msgstr "Подредба"
#: filters.py:181
msgid "Which field to use when ordering the results."
msgstr ""
#: filters.py:287
msgid "ascending"
msgstr "в нарастващ ред"
#: filters.py:288
msgid "descending"
msgstr "в намаляващ ред"
#: pagination.py:174
msgid "A page number within the paginated result set."
msgstr ""
#: pagination.py:179 pagination.py:372 pagination.py:590
msgid "Number of results to return per page."
msgstr ""
#: pagination.py:189
msgid "Invalid page."
msgstr "Невалидна страница."
#: pagination.py:374
msgid "The initial index from which to return the results."
msgstr ""
#: pagination.py:581
msgid "The pagination cursor value."
msgstr ""
#: pagination.py:583
msgid "Invalid cursor"
msgstr "Невалиден курсор"
#: relations.py:246
#, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "Невалиден идентификатор \"{pk_value}\" - обектът не съществува."
#: relations.py:247
#, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "Неправилен тип. Очакван е тип за основен ключ, получен е {data_type}."
#: relations.py:280
msgid "Invalid hyperlink - No URL match."
msgstr "Невалидна връзка (hyperlink) - няма намерен URL."
#: relations.py:281
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "Невалидна връзка (hyperlink) - неправилно съвпадение на URL."
#: relations.py:282
msgid "Invalid hyperlink - Object does not exist."
msgstr "Невалидна връзка (hyperlink) - обектът не съществува."
#: relations.py:283
#, python-brace-format
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "Невалиден тип. Очаква се URL низ, получен е {data_type}."
#: relations.py:448
#, python-brace-format
msgid "Object with {slug_name}={value} does not exist."
msgstr "Обект с {slug_name}={value} не съществува."
#: relations.py:449
msgid "Invalid value."
msgstr "Невалидна стойност."
#: schemas/utils.py:32
msgid "unique integer value"
msgstr ""
#: schemas/utils.py:34
msgid "UUID string"
msgstr ""
#: schemas/utils.py:36
msgid "unique value"
msgstr ""
#: schemas/utils.py:38
#, python-brace-format
msgid "A {value_type} identifying this {name}."
msgstr ""
#: serializers.py:337
#, python-brace-format
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "Невалидни данни. Очаква се асоциативен масив, но е получен {datatype}."
#: templates/rest_framework/admin.html:116
#: templates/rest_framework/base.html:136
msgid "Extra Actions"
msgstr ""
#: templates/rest_framework/admin.html:130
#: templates/rest_framework/base.html:150
msgid "Filters"
msgstr "Филтри"
#: templates/rest_framework/base.html:37
msgid "navbar"
msgstr ""
#: templates/rest_framework/base.html:75
msgid "content"
msgstr ""
#: templates/rest_framework/base.html:78
msgid "request form"
msgstr ""
#: templates/rest_framework/base.html:157
msgid "main content"
msgstr ""
#: templates/rest_framework/base.html:173
msgid "request info"
msgstr ""
#: templates/rest_framework/base.html:177
msgid "response info"
msgstr ""
#: templates/rest_framework/horizontal/radio.html:4
#: templates/rest_framework/inline/radio.html:3
#: templates/rest_framework/vertical/radio.html:3
msgid "None"
msgstr "Нищо"
#: templates/rest_framework/horizontal/select_multiple.html:4
#: templates/rest_framework/inline/select_multiple.html:3
#: templates/rest_framework/vertical/select_multiple.html:3
msgid "No items to select."
msgstr "Няма неща за избиране."
#: validators.py:39
msgid "This field must be unique."
msgstr "Това поле трябва да е уникално."
#: validators.py:89
#, python-brace-format
msgid "The fields {field_names} must make a unique set."
msgstr "Полетата {field_names} трябва да образуват уникална комбинация."
#: validators.py:171
#, python-brace-format
msgid "Surrogate characters are not allowed: U+{code_point:X}."
msgstr ""
#: validators.py:243
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "Това поле трябва да е уникално за \"{date_field}\" дата."
#: validators.py:258
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "Това поле трябва да е уникално за \"{date_field}\" месец."
#: validators.py:271
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "Това поле трябва да е уникално за \"{date_field}\" година."
#: versioning.py:40
msgid "Invalid version in \"Accept\" header."
msgstr "Невалидна версия в \"Accept\" header."
#: versioning.py:71
msgid "Invalid version in URL path."
msgstr "Невалидна версия в URL пътя."
#: versioning.py:116
msgid "Invalid version in URL path. Does not match any version namespace."
msgstr "Невалидна версия в URL пътя. Няма съвпадение с пространството от имена на версии."
#: versioning.py:148
msgid "Invalid version in hostname."
msgstr "Невалидна версия в името на сървъра (hostname)."
#: versioning.py:170
msgid "Invalid version in query parameter."
msgstr "Невалидна версия в GET параметър."

Some files were not shown because too many files have changed in this diff Show More