Merge branch 'master' into improvement/source-attributes-in-extra_kwargs
10
.github/ISSUE_TEMPLATE/1-issue.md
vendored
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
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
|
@ -1,6 +0,0 @@
|
||||||
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. 💖
|
|
13
.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Keep GitHub Actions up to date with GitHub's Dependabot...
|
||||||
|
# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
|
||||||
|
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: /
|
||||||
|
groups:
|
||||||
|
github-actions:
|
||||||
|
patterns:
|
||||||
|
- "*" # Group all Action updates into a single larger pull request
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
37
.github/workflows/main.yml
vendored
|
@ -9,22 +9,21 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
name: Python ${{ matrix.python-version }}
|
name: Python ${{ matrix.python-version }}
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version:
|
python-version:
|
||||||
- '3.6'
|
|
||||||
- '3.7'
|
|
||||||
- '3.8'
|
|
||||||
- '3.9'
|
- '3.9'
|
||||||
- '3.10'
|
- '3.10'
|
||||||
- '3.11'
|
- '3.11'
|
||||||
|
- '3.12'
|
||||||
|
- '3.13'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
cache: 'pip'
|
cache: 'pip'
|
||||||
|
@ -34,19 +33,10 @@ jobs:
|
||||||
run: python -m pip install --upgrade pip setuptools virtualenv wheel
|
run: python -m pip install --upgrade pip setuptools virtualenv wheel
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: python -m pip install --upgrade codecov tox
|
run: python -m pip install --upgrade tox
|
||||||
|
|
||||||
- name: Install tox-py
|
|
||||||
if: ${{ matrix.python-version == '3.6' }}
|
|
||||||
run: python -m pip install --upgrade tox-py
|
|
||||||
|
|
||||||
- name: Run tox targets for ${{ matrix.python-version }}
|
- name: Run tox targets for ${{ matrix.python-version }}
|
||||||
if: ${{ matrix.python-version != '3.6' }}
|
run: tox run -f py$(echo ${{ matrix.python-version }} | tr -d . | cut -f 1 -d '-')
|
||||||
run: tox run -f py$(echo ${{ matrix.python-version }} | tr -d .)
|
|
||||||
|
|
||||||
- name: Run tox targets for ${{ matrix.python-version }}
|
|
||||||
if: ${{ matrix.python-version == '3.6' }}
|
|
||||||
run: tox --py current
|
|
||||||
|
|
||||||
- name: Run extra tox targets
|
- name: Run extra tox targets
|
||||||
if: ${{ matrix.python-version == '3.9' }}
|
if: ${{ matrix.python-version == '3.9' }}
|
||||||
|
@ -54,18 +44,19 @@ jobs:
|
||||||
tox -e base,dist,docs
|
tox -e base,dist,docs
|
||||||
|
|
||||||
- name: Upload coverage
|
- name: Upload coverage
|
||||||
run: |
|
uses: codecov/codecov-action@v5
|
||||||
codecov -e TOXENV,DJANGO
|
with:
|
||||||
|
env_vars: TOXENV,DJANGO
|
||||||
|
|
||||||
test-docs:
|
test-docs:
|
||||||
name: Test documentation links
|
name: Test documentation links
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.9'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pip install -r requirements/requirements-documentation.txt
|
run: pip install -r requirements/requirements-documentation.txt
|
||||||
|
|
8
.github/workflows/pre-commit.yml
vendored
|
@ -11,14 +11,12 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
|
|
||||||
- uses: pre-commit/action@v3.0.0
|
- uses: pre-commit/action@v3.0.1
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v3.4.0
|
rev: v4.5.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-added-large-files
|
- id: check-added-large-files
|
||||||
- id: check-case-conflict
|
- id: check-case-conflict
|
||||||
|
@ -9,12 +9,31 @@ repos:
|
||||||
- id: check-symlinks
|
- id: check-symlinks
|
||||||
- id: check-toml
|
- id: check-toml
|
||||||
- repo: https://github.com/pycqa/isort
|
- repo: https://github.com/pycqa/isort
|
||||||
rev: 5.12.0
|
rev: 5.13.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: isort
|
- id: isort
|
||||||
- repo: https://github.com/PyCQA/flake8
|
- repo: https://github.com/PyCQA/flake8
|
||||||
rev: 3.9.0
|
rev: 7.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- flake8-tidy-imports
|
- flake8-tidy-imports
|
||||||
|
- repo: https://github.com/adamchainz/blacken-docs
|
||||||
|
rev: 1.16.0
|
||||||
|
hooks:
|
||||||
|
- id: blacken-docs
|
||||||
|
exclude: ^(?!docs).*$
|
||||||
|
additional_dependencies:
|
||||||
|
- black==23.1.0
|
||||||
|
- repo: https://github.com/codespell-project/codespell
|
||||||
|
# Configuration for codespell is in .codespellrc
|
||||||
|
rev: v2.2.6
|
||||||
|
hooks:
|
||||||
|
- id: codespell
|
||||||
|
exclude: locale|kickstarter-announcement.md|coreapi-0.1.1.js
|
||||||
|
|
||||||
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
|
rev: v3.19.1
|
||||||
|
hooks:
|
||||||
|
- id: pyupgrade
|
||||||
|
args: ["--py39-plus", "--keep-percent-format"]
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
# Contributing to REST framework
|
# Contributing to REST framework
|
||||||
|
|
||||||
At this point in it's lifespan we consider Django REST framework to be essentially feature-complete. We may accept pull requests that track the continued development of Django versions, but would prefer not to accept new features or code formatting changes.
|
At this point in its lifespan we consider Django REST framework to be essentially feature-complete. We may accept pull requests that track the continued development of Django versions, but would prefer not to accept new features or code formatting changes.
|
||||||
|
|
||||||
Apart from minor documentation changes, the [GitHub discussions page](https://github.com/encode/django-rest-framework/discussions) should generally be your starting point. Please only raise an issue or pull request if you've been recommended to do so after discussion.
|
|
||||||
|
|
||||||
The [Contributing guide in the documentation](https://www.django-rest-framework.org/community/contributing/) gives some more information on our process and code of conduct.
|
The [Contributing guide in the documentation](https://www.django-rest-framework.org/community/contributing/) gives some more information on our process and code of conduct.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
*Note*: Before submitting this pull request, please review our [contributing guidelines](https://www.django-rest-framework.org/community/contributing/#pull-requests).
|
*Note*: Before submitting a code change, please review our [contributing guidelines](https://www.django-rest-framework.org/community/contributing/#pull-requests).
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
|
|
20
README.md
|
@ -27,8 +27,10 @@ The initial aim is to provide a single full-time position on REST framework.
|
||||||
[![][posthog-img]][posthog-url]
|
[![][posthog-img]][posthog-url]
|
||||||
[![][cryptapi-img]][cryptapi-url]
|
[![][cryptapi-img]][cryptapi-url]
|
||||||
[![][fezto-img]][fezto-url]
|
[![][fezto-img]][fezto-url]
|
||||||
|
[![][svix-img]][svix-url]
|
||||||
|
[![][zuplo-img]][zuplo-url]
|
||||||
|
|
||||||
Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Spacinov][spacinov-url], [Retool][retool-url], [bit.io][bitio-url], [PostHog][posthog-url], [CryptAPI][cryptapi-url], and [FEZTO][fezto-url].
|
Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Spacinov][spacinov-url], [Retool][retool-url], [bit.io][bitio-url], [PostHog][posthog-url], [CryptAPI][cryptapi-url], [FEZTO][fezto-url], [Svix][svix-url], and [Zuplo][zuplo-url].
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -38,14 +40,12 @@ Django REST framework is a powerful and flexible toolkit for building Web APIs.
|
||||||
|
|
||||||
Some reasons you might want to use REST framework:
|
Some reasons you might want to use REST framework:
|
||||||
|
|
||||||
* The [Web browsable API][sandbox] is a huge usability win for your developers.
|
* The Web browsable API is a huge usability win for your developers.
|
||||||
* [Authentication policies][authentication] including optional packages for [OAuth1a][oauth1-section] and [OAuth2][oauth2-section].
|
* [Authentication policies][authentication] including optional packages for [OAuth1a][oauth1-section] and [OAuth2][oauth2-section].
|
||||||
* [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources.
|
* [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].
|
* 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][docs], and [great community support][group].
|
* [Extensive documentation][docs], and [great community support][group].
|
||||||
|
|
||||||
There is a live example API for testing purposes, [available here][sandbox].
|
|
||||||
|
|
||||||
**Below**: *Screenshot from the browsable API*
|
**Below**: *Screenshot from the browsable API*
|
||||||
|
|
||||||
![Screenshot][image]
|
![Screenshot][image]
|
||||||
|
@ -54,8 +54,8 @@ There is a live example API for testing purposes, [available here][sandbox].
|
||||||
|
|
||||||
# Requirements
|
# Requirements
|
||||||
|
|
||||||
* Python 3.6+
|
* Python 3.9+
|
||||||
* Django 4.2, 4.1, 4.0, 3.2, 3.1, 3.0
|
* Django 4.2, 5.0, 5.1, 5.2
|
||||||
|
|
||||||
We **highly recommend** and only officially support the latest patch release of
|
We **highly recommend** and only officially support the latest patch release of
|
||||||
each Python and Django series.
|
each Python and Django series.
|
||||||
|
@ -173,8 +173,6 @@ Full documentation for the project is available at [https://www.django-rest-fram
|
||||||
|
|
||||||
For questions and support, use the [REST framework discussion group][group], or `#restframework` on libera.chat IRC.
|
For questions and support, use the [REST framework discussion group][group], or `#restframework` on libera.chat IRC.
|
||||||
|
|
||||||
You may also want to [follow the author on Twitter][twitter].
|
|
||||||
|
|
||||||
# Security
|
# Security
|
||||||
|
|
||||||
Please see the [security policy][security-policy].
|
Please see the [security policy][security-policy].
|
||||||
|
@ -185,9 +183,7 @@ Please see the [security policy][security-policy].
|
||||||
[codecov]: https://codecov.io/github/encode/django-rest-framework?branch=master
|
[codecov]: https://codecov.io/github/encode/django-rest-framework?branch=master
|
||||||
[pypi-version]: https://img.shields.io/pypi/v/djangorestframework.svg
|
[pypi-version]: https://img.shields.io/pypi/v/djangorestframework.svg
|
||||||
[pypi]: https://pypi.org/project/djangorestframework/
|
[pypi]: https://pypi.org/project/djangorestframework/
|
||||||
[twitter]: https://twitter.com/starletdreaming
|
|
||||||
[group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
|
[group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
|
||||||
[sandbox]: https://restframework.herokuapp.com/
|
|
||||||
|
|
||||||
[funding]: https://fund.django-rest-framework.org/topics/funding/
|
[funding]: https://fund.django-rest-framework.org/topics/funding/
|
||||||
[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors
|
[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors
|
||||||
|
@ -200,6 +196,8 @@ Please see the [security policy][security-policy].
|
||||||
[posthog-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/posthog-readme.png
|
[posthog-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/posthog-readme.png
|
||||||
[cryptapi-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/cryptapi-readme.png
|
[cryptapi-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/cryptapi-readme.png
|
||||||
[fezto-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/fezto-readme.png
|
[fezto-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/fezto-readme.png
|
||||||
|
[svix-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/svix-premium.png
|
||||||
|
[zuplo-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/zuplo-readme.png
|
||||||
|
|
||||||
[sentry-url]: https://getsentry.com/welcome/
|
[sentry-url]: https://getsentry.com/welcome/
|
||||||
[stream-url]: https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage
|
[stream-url]: https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage
|
||||||
|
@ -209,6 +207,8 @@ Please see the [security policy][security-policy].
|
||||||
[posthog-url]: https://posthog.com?utm_source=drf&utm_medium=sponsorship&utm_campaign=open-source-sponsorship
|
[posthog-url]: https://posthog.com?utm_source=drf&utm_medium=sponsorship&utm_campaign=open-source-sponsorship
|
||||||
[cryptapi-url]: https://cryptapi.io
|
[cryptapi-url]: https://cryptapi.io
|
||||||
[fezto-url]: https://www.fezto.xyz/?utm_source=DjangoRESTFramework
|
[fezto-url]: https://www.fezto.xyz/?utm_source=DjangoRESTFramework
|
||||||
|
[svix-url]: https://www.svix.com/?utm_source=django-REST&utm_medium=sponsorship
|
||||||
|
[zuplo-url]: https://zuplo.link/django-gh
|
||||||
|
|
||||||
[oauth1-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth
|
[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
|
[oauth2-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
Security issues are handled under the supervision of the [Django security team](https://www.djangoproject.com/foundation/teams/#security-team).
|
**Please report security issues by emailing security@encode.io**.
|
||||||
|
|
||||||
**Please report security issues by emailing security@djangoproject.com**.
|
|
||||||
|
|
||||||
The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure.
|
The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure.
|
||||||
|
|
|
@ -90,6 +90,12 @@ The kind of response that will be used depends on the authentication scheme. Al
|
||||||
|
|
||||||
Note that when a request may successfully authenticate, but still be denied permission to perform the request, in which case a `403 Permission Denied` response will always be used, regardless of the authentication scheme.
|
Note that when a request may successfully authenticate, but still be denied permission to perform the request, in which case a `403 Permission Denied` response will always be used, regardless of the authentication scheme.
|
||||||
|
|
||||||
|
## Django 5.1+ `LoginRequiredMiddleware`
|
||||||
|
|
||||||
|
If you're running Django 5.1+ and use the [`LoginRequiredMiddleware`][login-required-middleware], please note that all views from DRF are opted-out of this middleware. This is because the authentication in DRF is based authentication and permissions classes, which may be determined after the middleware has been applied. Additionally, when the request is not authenticated, the middleware redirects the user to the login page, which is not suitable for API requests, where it's preferable to return a 401 status code.
|
||||||
|
|
||||||
|
REST framework offers an equivalent mechanism for DRF views via the global settings, `DEFAULT_AUTHENTICATION_CLASSES` and `DEFAULT_PERMISSION_CLASSES`. They should be changed accordingly if you need to enforce that API requests are logged in.
|
||||||
|
|
||||||
## Apache mod_wsgi specific configuration
|
## Apache mod_wsgi specific configuration
|
||||||
|
|
||||||
Note that if deploying to [Apache using mod_wsgi][mod_wsgi_official], the authorization header is not passed through to a WSGI application by default, as it is assumed that authentication will be handled by Apache, rather than at an application level.
|
Note that if deploying to [Apache using mod_wsgi][mod_wsgi_official], the authorization header is not passed through to a WSGI application by default, as it is assumed that authentication will be handled by Apache, rather than at an application level.
|
||||||
|
@ -448,13 +454,19 @@ There are currently two forks of this project.
|
||||||
|
|
||||||
More information can be found in the [Documentation](https://django-rest-durin.readthedocs.io/en/latest/index.html).
|
More information can be found in the [Documentation](https://django-rest-durin.readthedocs.io/en/latest/index.html).
|
||||||
|
|
||||||
|
## django-pyoidc
|
||||||
|
|
||||||
|
[dango-pyoidc][django_pyoidc] adds support for OpenID Connect (OIDC) authentication. This allows you to delegate user management to an Identity Provider, which can be used to implement Single-Sign-On (SSO). It provides support for most uses-cases, such as customizing how token info are mapped to user models, using OIDC audiences for access control, etc.
|
||||||
|
|
||||||
|
More information can be found in the [Documentation](https://django-pyoidc.readthedocs.io/latest/index.html).
|
||||||
|
|
||||||
[cite]: https://jacobian.org/writing/rest-worst-practices/
|
[cite]: https://jacobian.org/writing/rest-worst-practices/
|
||||||
[http401]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2
|
[http401]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2
|
||||||
[http403]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4
|
[http403]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4
|
||||||
[basicauth]: https://tools.ietf.org/html/rfc2617
|
[basicauth]: https://tools.ietf.org/html/rfc2617
|
||||||
[permission]: permissions.md
|
[permission]: permissions.md
|
||||||
[throttling]: throttling.md
|
[throttling]: throttling.md
|
||||||
[csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax
|
[csrf-ajax]: https://docs.djangoproject.com/en/stable/howto/csrf/#using-csrf-protection-with-ajax
|
||||||
[mod_wsgi_official]: https://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIPassAuthorization.html
|
[mod_wsgi_official]: https://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIPassAuthorization.html
|
||||||
[django-oauth-toolkit-getting-started]: https://django-oauth-toolkit.readthedocs.io/en/latest/rest-framework/getting_started.html
|
[django-oauth-toolkit-getting-started]: https://django-oauth-toolkit.readthedocs.io/en/latest/rest-framework/getting_started.html
|
||||||
[django-rest-framework-oauth]: https://jpadilla.github.io/django-rest-framework-oauth/
|
[django-rest-framework-oauth]: https://jpadilla.github.io/django-rest-framework-oauth/
|
||||||
|
@ -484,3 +496,5 @@ More information can be found in the [Documentation](https://django-rest-durin.r
|
||||||
[drfpasswordless]: https://github.com/aaronn/django-rest-framework-passwordless
|
[drfpasswordless]: https://github.com/aaronn/django-rest-framework-passwordless
|
||||||
[django-rest-authemail]: https://github.com/celiao/django-rest-authemail
|
[django-rest-authemail]: https://github.com/celiao/django-rest-authemail
|
||||||
[django-rest-durin]: https://github.com/eshaan7/django-rest-durin
|
[django-rest-durin]: https://github.com/eshaan7/django-rest-durin
|
||||||
|
[login-required-middleware]: https://docs.djangoproject.com/en/stable/ref/middleware/#django.contrib.auth.middleware.LoginRequiredMiddleware
|
||||||
|
[django-pyoidc] : https://github.com/makinacorpus/django_pyoidc
|
||||||
|
|
|
@ -32,7 +32,7 @@ class UserViewSet(viewsets.ViewSet):
|
||||||
@method_decorator(vary_on_cookie)
|
@method_decorator(vary_on_cookie)
|
||||||
def list(self, request, format=None):
|
def list(self, request, format=None):
|
||||||
content = {
|
content = {
|
||||||
'user_feed': request.user.get_user_feed()
|
"user_feed": request.user.get_user_feed(),
|
||||||
}
|
}
|
||||||
return Response(content)
|
return Response(content)
|
||||||
|
|
||||||
|
@ -40,10 +40,10 @@ class UserViewSet(viewsets.ViewSet):
|
||||||
class ProfileView(APIView):
|
class ProfileView(APIView):
|
||||||
# With auth: cache requested url for each user for 2 hours
|
# With auth: cache requested url for each user for 2 hours
|
||||||
@method_decorator(cache_page(60 * 60 * 2))
|
@method_decorator(cache_page(60 * 60 * 2))
|
||||||
@method_decorator(vary_on_headers("Authorization",))
|
@method_decorator(vary_on_headers("Authorization"))
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
content = {
|
content = {
|
||||||
'user_feed': request.user.get_user_feed()
|
"user_feed": request.user.get_user_feed(),
|
||||||
}
|
}
|
||||||
return Response(content)
|
return Response(content)
|
||||||
|
|
||||||
|
@ -53,12 +53,35 @@ class PostView(APIView):
|
||||||
@method_decorator(cache_page(60 * 60 * 2))
|
@method_decorator(cache_page(60 * 60 * 2))
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
content = {
|
content = {
|
||||||
'title': 'Post title',
|
"title": "Post title",
|
||||||
'body': 'Post content'
|
"body": "Post content",
|
||||||
}
|
}
|
||||||
return Response(content)
|
return Response(content)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Using cache with @api_view decorator
|
||||||
|
|
||||||
|
When using @api_view decorator, the Django-provided method-based cache decorators such as [`cache_page`][page],
|
||||||
|
[`vary_on_cookie`][cookie] and [`vary_on_headers`][headers] can be called directly.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from django.views.decorators.cache import cache_page
|
||||||
|
from django.views.decorators.vary import vary_on_cookie
|
||||||
|
|
||||||
|
from rest_framework.decorators import api_view
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
|
||||||
|
@cache_page(60 * 15)
|
||||||
|
@vary_on_cookie
|
||||||
|
@api_view(["GET"])
|
||||||
|
def get_user_list(request):
|
||||||
|
content = {"user_feed": request.user.get_user_feed()}
|
||||||
|
return Response(content)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
**NOTE:** The [`cache_page`][page] decorator only caches the
|
**NOTE:** The [`cache_page`][page] decorator only caches the
|
||||||
`GET` and `HEAD` responses with status 200.
|
`GET` and `HEAD` responses with status 200.
|
||||||
|
|
||||||
|
|
|
@ -68,14 +68,6 @@ When serializing the instance, default will be used if the object attribute or d
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
Notes regarding default value propagation from model to serializer:
|
|
||||||
|
|
||||||
All the default values from model will pass as default to the serializer and the options method.
|
|
||||||
|
|
||||||
If the default is callable then it will be propagated to & evaluated every time in the serializer but not in options method.
|
|
||||||
|
|
||||||
If the value for given field is not given then default value will be present in the serializer and available in serializer's methods. Specified validation on given field will be evaluated on default value as that field will be present in the serializer.
|
|
||||||
|
|
||||||
### `allow_null`
|
### `allow_null`
|
||||||
|
|
||||||
Normally an error will be raised if `None` is passed to a serializer field. Set this keyword argument to `True` if `None` should be considered a valid value.
|
Normally an error will be raised if `None` is passed to a serializer field. Set this keyword argument to `True` if `None` should be considered a valid value.
|
||||||
|
@ -299,11 +291,11 @@ Corresponds to `django.db.models.fields.DecimalField`.
|
||||||
* `max_digits` The maximum number of digits allowed in the number. It must be either `None` or an integer greater than or equal to `decimal_places`.
|
* `max_digits` The maximum number of digits allowed in the number. It must be either `None` or an integer greater than or equal to `decimal_places`.
|
||||||
* `decimal_places` The number of decimal places to store with the number.
|
* `decimal_places` The number of decimal places to store with the number.
|
||||||
* `coerce_to_string` Set to `True` if string values should be returned for the representation, or `False` if `Decimal` objects should be returned. Defaults to the same value as the `COERCE_DECIMAL_TO_STRING` settings key, which will be `True` unless overridden. If `Decimal` objects are returned by the serializer, then the final output format will be determined by the renderer. Note that setting `localize` will force the value to `True`.
|
* `coerce_to_string` Set to `True` if string values should be returned for the representation, or `False` if `Decimal` objects should be returned. Defaults to the same value as the `COERCE_DECIMAL_TO_STRING` settings key, which will be `True` unless overridden. If `Decimal` objects are returned by the serializer, then the final output format will be determined by the renderer. Note that setting `localize` will force the value to `True`.
|
||||||
* `max_value` Validate that the number provided is no greater than this value.
|
* `max_value` Validate that the number provided is no greater than this value. Should be an integer or `Decimal` object.
|
||||||
* `min_value` Validate that the number provided is no less than this value.
|
* `min_value` Validate that the number provided is no less than this value. Should be an integer or `Decimal` object.
|
||||||
* `localize` Set to `True` to enable localization of input and output based on the current locale. This will also force `coerce_to_string` to `True`. Defaults to `False`. Note that data formatting is enabled if you have set `USE_L10N=True` in your settings file.
|
* `localize` Set to `True` to enable localization of input and output based on the current locale. This will also force `coerce_to_string` to `True`. Defaults to `False`. Note that data formatting is enabled if you have set `USE_L10N=True` in your settings file.
|
||||||
* `rounding` Sets the rounding mode used when quantizing to the configured precision. Valid values are [`decimal` module rounding modes][python-decimal-rounding-modes]. Defaults to `None`.
|
* `rounding` Sets the rounding mode used when quantizing to the configured precision. Valid values are [`decimal` module rounding modes][python-decimal-rounding-modes]. Defaults to `None`.
|
||||||
* `normalize_output` Will normalize the decimal value when serialized. This will strip all trailing zeroes and change the value's precision to the minimum required precision to be able to represent the value without loosing data. Defaults to `False`.
|
* `normalize_output` Will normalize the decimal value when serialized. This will strip all trailing zeroes and change the value's precision to the minimum required precision to be able to represent the value without losing data. Defaults to `False`.
|
||||||
|
|
||||||
#### Example usage
|
#### Example usage
|
||||||
|
|
||||||
|
@ -560,7 +552,7 @@ For further examples on `HiddenField` see the [validators](validators.md) docume
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Note:** `HiddenField()` does not appear in `partial=True` serializer (when making `PATCH` request). This behavior might change in future, follow updates on [github discussion](https://github.com/encode/django-rest-framework/discussions/8259).
|
**Note:** `HiddenField()` does not appear in `partial=True` serializer (when making `PATCH` request).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -173,12 +173,11 @@ This permission is suitable if you want to your API to allow read permissions to
|
||||||
|
|
||||||
This permission class ties into Django's standard `django.contrib.auth` [model permissions][contribauth]. This permission must only be applied to views that have a `.queryset` property or `get_queryset()` method. Authorization will only be granted if the user *is authenticated* and has the *relevant model permissions* assigned. The appropriate model is determined by checking `get_queryset().model` or `queryset.model`.
|
This permission class ties into Django's standard `django.contrib.auth` [model permissions][contribauth]. This permission must only be applied to views that have a `.queryset` property or `get_queryset()` method. Authorization will only be granted if the user *is authenticated* and has the *relevant model permissions* assigned. The appropriate model is determined by checking `get_queryset().model` or `queryset.model`.
|
||||||
|
|
||||||
* `GET` requests require the user to have the `view` or `change` permission on the model
|
|
||||||
* `POST` requests require the user to have the `add` permission on the model.
|
* `POST` requests require the user to have the `add` permission on the model.
|
||||||
* `PUT` and `PATCH` requests require the user to have the `change` permission on the model.
|
* `PUT` and `PATCH` requests require the user to have the `change` permission on the model.
|
||||||
* `DELETE` requests require the user to have the `delete` permission on the model.
|
* `DELETE` requests require the user to have the `delete` permission on the model.
|
||||||
|
|
||||||
The default behaviour can also be overridden to support custom model permissions.
|
The default behavior can also be overridden to support custom model permissions. For example, you might want to include a `view` model permission for `GET` requests.
|
||||||
|
|
||||||
To use custom model permissions, override `DjangoModelPermissions` and set the `.perms_map` property. Refer to the source code for details.
|
To use custom model permissions, override `DjangoModelPermissions` and set the `.perms_map` property. Refer to the source code for details.
|
||||||
|
|
||||||
|
|
|
@ -628,12 +628,16 @@ The [drf-nested-routers package][drf-nested-routers] provides routers and relati
|
||||||
|
|
||||||
The [rest-framework-generic-relations][drf-nested-relations] library provides read/write serialization for generic foreign keys.
|
The [rest-framework-generic-relations][drf-nested-relations] library provides read/write serialization for generic foreign keys.
|
||||||
|
|
||||||
|
The [rest-framework-gm2m-relations][drf-gm2m-relations] library provides read/write serialization for [django-gm2m][django-gm2m-field].
|
||||||
|
|
||||||
[cite]: http://users.ece.utexas.edu/~adnan/pike.html
|
[cite]: http://users.ece.utexas.edu/~adnan/pike.html
|
||||||
[reverse-relationships]: https://docs.djangoproject.com/en/stable/topics/db/queries/#following-relationships-backward
|
[reverse-relationships]: https://docs.djangoproject.com/en/stable/topics/db/queries/#following-relationships-backward
|
||||||
[routers]: https://www.django-rest-framework.org/api-guide/routers#defaultrouter
|
[routers]: https://www.django-rest-framework.org/api-guide/routers#defaultrouter
|
||||||
[generic-relations]: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#id1
|
[generic-relations]: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#id1
|
||||||
[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
|
[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
|
||||||
[drf-nested-relations]: https://github.com/Ian-Foote/rest-framework-generic-relations
|
[drf-nested-relations]: https://github.com/Ian-Foote/rest-framework-generic-relations
|
||||||
|
[drf-gm2m-relations]: https://github.com/mojtabaakbari221b/rest-framework-gm2m-relations
|
||||||
|
[django-gm2m-field]: https://github.com/tkhyn/django-gm2m
|
||||||
[django-intermediary-manytomany]: https://docs.djangoproject.com/en/stable/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
|
[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
|
[to_internal_value]: https://www.django-rest-framework.org/api-guide/serializers/#to_internal_valueself-data
|
||||||
|
|
|
@ -283,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.
|
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_text
|
from django.utils.encoding import smart_str
|
||||||
from rest_framework import renderers
|
from rest_framework import renderers
|
||||||
|
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ The following is an example plaintext renderer that will return a response with
|
||||||
format = 'txt'
|
format = 'txt'
|
||||||
|
|
||||||
def render(self, data, accepted_media_type=None, renderer_context=None):
|
def render(self, data, accepted_media_type=None, renderer_context=None):
|
||||||
return smart_text(data, encoding=self.charset)
|
return smart_str(data, encoding=self.charset)
|
||||||
|
|
||||||
## Setting the character set
|
## Setting the character set
|
||||||
|
|
||||||
|
@ -525,7 +525,7 @@ Comma-separated values are a plain-text tabular data format, that can be easily
|
||||||
|
|
||||||
## LaTeX
|
## LaTeX
|
||||||
|
|
||||||
[Rest Framework Latex] provides a renderer that outputs PDFs using Laulatex. It is maintained by [Pebble (S/F Software)][mypebble].
|
[Rest Framework Latex] provides a renderer that outputs PDFs using Lualatex. It is maintained by [Pebble (S/F Software)][mypebble].
|
||||||
|
|
||||||
|
|
||||||
[cite]: https://docs.djangoproject.com/en/stable/ref/template-response/#the-rendering-process
|
[cite]: https://docs.djangoproject.com/en/stable/ref/template-response/#the-rendering-process
|
||||||
|
|
|
@ -142,6 +142,24 @@ The above example would now generate the following URL pattern:
|
||||||
* URL path: `^users/{pk}/change-password/$`
|
* URL path: `^users/{pk}/change-password/$`
|
||||||
* URL name: `'user-change_password'`
|
* URL name: `'user-change_password'`
|
||||||
|
|
||||||
|
### Using Django `path()` with routers
|
||||||
|
|
||||||
|
By default, the URLs created by routers use regular expressions. This behavior can be modified by setting the `use_regex_path` argument to `False` when instantiating the router, in this case [path converters][path-converters-topic-reference] are used. For example:
|
||||||
|
|
||||||
|
router = SimpleRouter(use_regex_path=False)
|
||||||
|
|
||||||
|
The router will match lookup values containing any characters except slashes and period characters. For a more restrictive (or lenient) lookup pattern, set the `lookup_value_regex` attribute on the viewset or `lookup_value_converter` if using path converters. For example, you can limit the lookup to valid UUIDs:
|
||||||
|
|
||||||
|
class MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
|
||||||
|
lookup_field = 'my_model_id'
|
||||||
|
lookup_value_regex = '[0-9a-f]{32}'
|
||||||
|
|
||||||
|
class MyPathModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
|
||||||
|
lookup_field = 'my_model_uuid'
|
||||||
|
lookup_value_converter = 'uuid'
|
||||||
|
|
||||||
|
Note that path converters will be used on all URLs registered in the router, including viewset actions.
|
||||||
|
|
||||||
# API Guide
|
# API Guide
|
||||||
|
|
||||||
## SimpleRouter
|
## SimpleRouter
|
||||||
|
@ -160,30 +178,13 @@ This router includes routes for the standard set of `list`, `create`, `retrieve`
|
||||||
<tr><td>{prefix}/{lookup}/{url_path}/</td><td>GET, or as specified by `methods` argument</td><td>`@action(detail=True)` decorated method</td><td>{basename}-{url_name}</td></tr>
|
<tr><td>{prefix}/{lookup}/{url_path}/</td><td>GET, or as specified by `methods` argument</td><td>`@action(detail=True)` decorated method</td><td>{basename}-{url_name}</td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
By default the URLs created by `SimpleRouter` are appended with a trailing slash.
|
By default, the URLs created by `SimpleRouter` are appended with a trailing slash.
|
||||||
This behavior can be modified by setting the `trailing_slash` argument to `False` when instantiating the router. For example:
|
This behavior can be modified by setting the `trailing_slash` argument to `False` when instantiating the router. For example:
|
||||||
|
|
||||||
router = SimpleRouter(trailing_slash=False)
|
router = SimpleRouter(trailing_slash=False)
|
||||||
|
|
||||||
Trailing slashes are conventional in Django, but are not used by default in some other frameworks such as Rails. Which style you choose to use is largely a matter of preference, although some javascript frameworks may expect a particular routing style.
|
Trailing slashes are conventional in Django, but are not used by default in some other frameworks such as Rails. Which style you choose to use is largely a matter of preference, although some javascript frameworks may expect a particular routing style.
|
||||||
|
|
||||||
By default the URLs created by `SimpleRouter` use regular expressions. This behavior can be modified by setting the `use_regex_path` argument to `False` when instantiating the router, in this case [path converters][path-converters-topic-reference] are used. For example:
|
|
||||||
|
|
||||||
router = SimpleRouter(use_regex_path=False)
|
|
||||||
|
|
||||||
**Note**: `use_regex_path=False` only works with Django 2.x or above, since this feature was introduced in 2.0.0. See [release note][simplified-routing-release-note]
|
|
||||||
|
|
||||||
|
|
||||||
The router will match lookup values containing any characters except slashes and period characters. For a more restrictive (or lenient) lookup pattern, set the `lookup_value_regex` attribute on the viewset or `lookup_value_converter` if using path converters. For example, you can limit the lookup to valid UUIDs:
|
|
||||||
|
|
||||||
class MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
|
|
||||||
lookup_field = 'my_model_id'
|
|
||||||
lookup_value_regex = '[0-9a-f]{32}'
|
|
||||||
|
|
||||||
class MyPathModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
|
|
||||||
lookup_field = 'my_model_uuid'
|
|
||||||
lookup_value_converter = 'uuid'
|
|
||||||
|
|
||||||
## DefaultRouter
|
## DefaultRouter
|
||||||
|
|
||||||
This router is similar to `SimpleRouter` as above, but additionally includes a default API root view, that returns a response containing hyperlinks to all the list views. It also generates routes for optional `.json` style format suffixes.
|
This router is similar to `SimpleRouter` as above, but additionally includes a default API root view, that returns a response containing hyperlinks to all the list views. It also generates routes for optional `.json` style format suffixes.
|
||||||
|
@ -351,5 +352,4 @@ The [`DRF-extensions` package][drf-extensions] provides [routers][drf-extensions
|
||||||
[drf-extensions-customizable-endpoint-names]: https://chibisov.github.io/drf-extensions/docs/#controller-endpoint-name
|
[drf-extensions-customizable-endpoint-names]: https://chibisov.github.io/drf-extensions/docs/#controller-endpoint-name
|
||||||
[url-namespace-docs]: https://docs.djangoproject.com/en/4.0/topics/http/urls/#url-namespaces
|
[url-namespace-docs]: https://docs.djangoproject.com/en/4.0/topics/http/urls/#url-namespaces
|
||||||
[include-api-reference]: https://docs.djangoproject.com/en/4.0/ref/urls/#include
|
[include-api-reference]: https://docs.djangoproject.com/en/4.0/ref/urls/#include
|
||||||
[simplified-routing-release-note]: https://docs.djangoproject.com/en/2.0/releases/2.0/#simplified-url-routing-syntax
|
|
||||||
[path-converters-topic-reference]: https://docs.djangoproject.com/en/2.0/topics/http/urls/#path-converters
|
[path-converters-topic-reference]: https://docs.djangoproject.com/en/2.0/topics/http/urls/#path-converters
|
||||||
|
|
|
@ -56,10 +56,11 @@ The following sections explain more.
|
||||||
|
|
||||||
### Install dependencies
|
### Install dependencies
|
||||||
|
|
||||||
pip install pyyaml uritemplate
|
pip install pyyaml uritemplate inflection
|
||||||
|
|
||||||
* `pyyaml` is used to generate schema into YAML-based OpenAPI format.
|
* `pyyaml` is used to generate schema into YAML-based OpenAPI format.
|
||||||
* `uritemplate` is used internally to get parameters in path.
|
* `uritemplate` is used internally to get parameters in path.
|
||||||
|
* `inflection` is used to pluralize operations more appropriately in the list endpoints.
|
||||||
|
|
||||||
### Generating a static schema with the `generateschema` management command
|
### Generating a static schema with the `generateschema` management command
|
||||||
|
|
||||||
|
@ -94,11 +95,13 @@ urlpatterns = [
|
||||||
# Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
|
# Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
|
||||||
# * `title` and `description` parameters are passed to `SchemaGenerator`.
|
# * `title` and `description` parameters are passed to `SchemaGenerator`.
|
||||||
# * Provide view name for use with `reverse()`.
|
# * Provide view name for use with `reverse()`.
|
||||||
path('openapi', get_schema_view(
|
path(
|
||||||
title="Your Project",
|
"openapi",
|
||||||
description="API for all things …",
|
get_schema_view(
|
||||||
version="1.0.0"
|
title="Your Project", description="API for all things …", version="1.0.0"
|
||||||
), name='openapi-schema'),
|
),
|
||||||
|
name="openapi-schema",
|
||||||
|
),
|
||||||
# ...
|
# ...
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
@ -259,11 +262,13 @@ class CustomSchema(AutoSchema):
|
||||||
"""
|
"""
|
||||||
AutoSchema subclass using schema_extra_info on the view.
|
AutoSchema subclass using schema_extra_info on the view.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
class CustomView(APIView):
|
class CustomView(APIView):
|
||||||
schema = CustomSchema()
|
schema = CustomSchema()
|
||||||
schema_extra_info = ... some extra info ...
|
schema_extra_info = ... # some extra info
|
||||||
```
|
```
|
||||||
|
|
||||||
Here, the `AutoSchema` subclass goes looking for `schema_extra_info` on the
|
Here, the `AutoSchema` subclass goes looking for `schema_extra_info` on the
|
||||||
|
@ -278,10 +283,13 @@ class BaseSchema(AutoSchema):
|
||||||
"""
|
"""
|
||||||
AutoSchema subclass that knows how to use extra_info.
|
AutoSchema subclass that knows how to use extra_info.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
class CustomSchema(BaseSchema):
|
class CustomSchema(BaseSchema):
|
||||||
extra_info = ... some extra info ...
|
extra_info = ... # some extra info
|
||||||
|
|
||||||
|
|
||||||
class CustomView(APIView):
|
class CustomView(APIView):
|
||||||
schema = CustomSchema()
|
schema = CustomSchema()
|
||||||
|
@ -302,10 +310,9 @@ class CustomSchema(BaseSchema):
|
||||||
self.extra_info = kwargs.pop("extra_info")
|
self.extra_info = kwargs.pop("extra_info")
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class CustomView(APIView):
|
class CustomView(APIView):
|
||||||
schema = CustomSchema(
|
schema = CustomSchema(extra_info=...) # some extra info
|
||||||
extra_info=... some extra info ...
|
|
||||||
)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This saves you having to create a custom subclass per-view for a commonly used option.
|
This saves you having to create a custom subclass per-view for a commonly used option.
|
||||||
|
|
|
@ -233,7 +233,7 @@ Serializer classes can also include reusable validators that are applied to the
|
||||||
|
|
||||||
class EventSerializer(serializers.Serializer):
|
class EventSerializer(serializers.Serializer):
|
||||||
name = serializers.CharField()
|
name = serializers.CharField()
|
||||||
room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
|
room_number = serializers.ChoiceField(choices=[101, 102, 103, 201])
|
||||||
date = serializers.DateField()
|
date = serializers.DateField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -861,8 +861,6 @@ Here's an example of how you might choose to implement multiple updates:
|
||||||
class Meta:
|
class Meta:
|
||||||
list_serializer_class = BookListSerializer
|
list_serializer_class = BookListSerializer
|
||||||
|
|
||||||
It is possible that a third party package may be included alongside the 3.1 release that provides some automatic support for multiple update operations, similar to the `allow_add_remove` behavior that was present in REST framework 2.
|
|
||||||
|
|
||||||
#### Customizing ListSerializer initialization
|
#### Customizing ListSerializer initialization
|
||||||
|
|
||||||
When a serializer with `many=True` is instantiated, we need to determine which arguments and keyword arguments should be passed to the `.__init__()` method for both the child `Serializer` class, and for the parent `ListSerializer` class.
|
When a serializer with `many=True` is instantiated, we need to determine which arguments and keyword arguments should be passed to the `.__init__()` method for both the child `Serializer` class, and for the parent `ListSerializer` class.
|
||||||
|
|
|
@ -163,6 +163,12 @@ The string that should used for any versioning parameters, such as in the media
|
||||||
|
|
||||||
Default: `'version'`
|
Default: `'version'`
|
||||||
|
|
||||||
|
#### DEFAULT_VERSIONING_CLASS
|
||||||
|
|
||||||
|
The default versioning scheme to use.
|
||||||
|
|
||||||
|
Default: `None`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Authentication settings
|
## Authentication settings
|
||||||
|
@ -454,4 +460,4 @@ Default: `None`
|
||||||
[cite]: https://www.python.org/dev/peps/pep-0020/
|
[cite]: https://www.python.org/dev/peps/pep-0020/
|
||||||
[rfc4627]: https://www.ietf.org/rfc/rfc4627.txt
|
[rfc4627]: https://www.ietf.org/rfc/rfc4627.txt
|
||||||
[heroku-minified-json]: https://github.com/interagent/http-api-design#keep-json-minified-in-all-responses
|
[heroku-minified-json]: https://github.com/interagent/http-api-design#keep-json-minified-in-all-responses
|
||||||
[strftime]: https://docs.python.org/3/library/time.html#time.strftime
|
[strftime]: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
|
||||||
|
|
|
@ -25,9 +25,12 @@ The `APIRequestFactory` class supports an almost identical API to Django's stand
|
||||||
factory = APIRequestFactory()
|
factory = APIRequestFactory()
|
||||||
request = factory.post('/notes/', {'title': 'new idea'})
|
request = factory.post('/notes/', {'title': 'new idea'})
|
||||||
|
|
||||||
|
# Using the standard RequestFactory API to encode JSON data
|
||||||
|
request = factory.post('/notes/', {'title': 'new idea'}, content_type='application/json')
|
||||||
|
|
||||||
#### Using the `format` argument
|
#### Using the `format` argument
|
||||||
|
|
||||||
Methods which create a request body, such as `post`, `put` and `patch`, include a `format` argument, which make it easy to generate requests using a content type other than multipart form data. For example:
|
Methods which create a request body, such as `post`, `put` and `patch`, include a `format` argument, which make it easy to generate requests using a wide set of request formats. When using this argument, the factory will select an appropriate renderer and its configured `content_type`. For example:
|
||||||
|
|
||||||
# Create a JSON POST request
|
# Create a JSON POST request
|
||||||
factory = APIRequestFactory()
|
factory = APIRequestFactory()
|
||||||
|
@ -41,7 +44,7 @@ To support a wider set of request formats, or change the default format, [see th
|
||||||
|
|
||||||
If you need to explicitly encode the request body, you can do so by setting the `content_type` flag. For example:
|
If you need to explicitly encode the request body, you can do so by setting the `content_type` flag. For example:
|
||||||
|
|
||||||
request = factory.post('/notes/', json.dumps({'title': 'new idea'}), content_type='application/json')
|
request = factory.post('/notes/', yaml.dump({'title': 'new idea'}), content_type='application/yaml')
|
||||||
|
|
||||||
#### PUT and PATCH with form data
|
#### PUT and PATCH with form data
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ The default throttling policy may be set globally, using the `DEFAULT_THROTTLE_C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
The rate descriptions used in `DEFAULT_THROTTLE_RATES` may include `second`, `minute`, `hour` or `day` as the throttle period.
|
The rates used in `DEFAULT_THROTTLE_RATES` can be specified over a period of second, minute, hour or day. The period must be specified after the `/` separator using `s`, `m`, `h` or `d`, respectively. For increased clarity, extended units such as `second`, `minute`, `hour`, `day` or even abbreviations like `sec`, `min`, `hr` are allowed, as only the first character is relevant to identify the rate.
|
||||||
|
|
||||||
You can also set the throttling policy on a per-view or per-viewset basis,
|
You can also set the throttling policy on a per-view or per-viewset basis,
|
||||||
using the `APIView` class-based views.
|
using the `APIView` class-based views.
|
||||||
|
@ -110,7 +110,7 @@ You'll need to remember to also set your custom throttle class in the `'DEFAULT_
|
||||||
|
|
||||||
The built-in throttle implementations are open to [race conditions][race], so under high concurrency they may allow a few extra requests through.
|
The built-in throttle implementations are open to [race conditions][race], so under high concurrency they may allow a few extra requests through.
|
||||||
|
|
||||||
If your project relies on guaranteeing the number of requests during concurrent requests, you will need to implement your own throttle class. See [issue #5181][gh5181] for more details.
|
If your project relies on guaranteeing the number of requests during concurrent requests, you will need to implement your own throttle class.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -220,5 +220,4 @@ The following is an example of a rate throttle, that will randomly throttle 1 in
|
||||||
[identifying-clients]: http://oxpedia.org/wiki/index.php?title=AppSuite:Grizzly#Multiple_Proxies_in_front_of_the_cluster
|
[identifying-clients]: http://oxpedia.org/wiki/index.php?title=AppSuite:Grizzly#Multiple_Proxies_in_front_of_the_cluster
|
||||||
[cache-setting]: https://docs.djangoproject.com/en/stable/ref/settings/#caches
|
[cache-setting]: https://docs.djangoproject.com/en/stable/ref/settings/#caches
|
||||||
[cache-docs]: https://docs.djangoproject.com/en/stable/topics/cache/#setting-up-the-cache
|
[cache-docs]: https://docs.djangoproject.com/en/stable/topics/cache/#setting-up-the-cache
|
||||||
[gh5181]: https://github.com/encode/django-rest-framework/issues/5181
|
|
||||||
[race]: https://en.wikipedia.org/wiki/Race_condition#Data_race
|
[race]: https://en.wikipedia.org/wiki/Race_condition#Data_race
|
||||||
|
|
|
@ -48,7 +48,7 @@ If we open up the Django shell using `manage.py shell` we can now
|
||||||
CustomerReportSerializer():
|
CustomerReportSerializer():
|
||||||
id = IntegerField(label='ID', read_only=True)
|
id = IntegerField(label='ID', read_only=True)
|
||||||
time_raised = DateTimeField(read_only=True)
|
time_raised = DateTimeField(read_only=True)
|
||||||
reference = CharField(max_length=20, validators=[<UniqueValidator(queryset=CustomerReportRecord.objects.all())>])
|
reference = CharField(max_length=20, validators=[UniqueValidator(queryset=CustomerReportRecord.objects.all())])
|
||||||
description = CharField(style={'type': 'textarea'})
|
description = CharField(style={'type': 'textarea'})
|
||||||
|
|
||||||
The interesting bit here is the `reference` field. We can see that the uniqueness constraint is being explicitly enforced by a validator on the serializer field.
|
The interesting bit here is the `reference` field. We can see that the uniqueness constraint is being explicitly enforced by a validator on the serializer field.
|
||||||
|
@ -166,18 +166,16 @@ If you want the date field to be entirely hidden from the user, then use `Hidden
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Note:** `HiddenField()` does not appear in `partial=True` serializer (when making `PATCH` request). This behavior might change in future, follow updates on [github discussion](https://github.com/encode/django-rest-framework/discussions/8259).
|
**Note:** `HiddenField()` does not appear in `partial=True` serializer (when making `PATCH` request).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Advanced field defaults
|
# Advanced field defaults
|
||||||
|
|
||||||
Validators that are applied across multiple fields in the serializer can sometimes require a field input that should not be provided by the API client, but that *is* available as input to the validator.
|
Validators that are applied across multiple fields in the serializer can sometimes require a field input that should not be provided by the API client, but that *is* available as input to the validator.
|
||||||
|
For this purposes use `HiddenField`. This field will be present in `validated_data` but *will not* be used in the serializer output representation.
|
||||||
|
|
||||||
Two patterns that you may want to use for this sort of validation include:
|
**Note:** Using a `read_only=True` field is excluded from writable fields so it won't use a `default=…` argument. Look [3.8 announcement](https://www.django-rest-framework.org/community/3.8-announcement/#altered-the-behaviour-of-read_only-plus-default-on-field).
|
||||||
|
|
||||||
* Using `HiddenField`. This field will be present in `validated_data` but *will not* be used in the serializer output representation.
|
|
||||||
* Using a standard field with `read_only=True`, but that also includes a `default=…` argument. This field *will* be used in the serializer output representation, but cannot be set directly by the user.
|
|
||||||
|
|
||||||
REST framework includes a couple of defaults that may be useful in this context.
|
REST framework includes a couple of defaults that may be useful in this context.
|
||||||
|
|
||||||
|
@ -189,7 +187,7 @@ A default class that can be used to represent the current user. In order to use
|
||||||
default=serializers.CurrentUserDefault()
|
default=serializers.CurrentUserDefault()
|
||||||
)
|
)
|
||||||
|
|
||||||
#### CreateOnlyDefault
|
#### CreateOnlyDefault
|
||||||
|
|
||||||
A default class that can be used to *only set a default argument during create operations*. During updates the field is omitted.
|
A default class that can be used to *only set a default argument during create operations*. During updates the field is omitted.
|
||||||
|
|
||||||
|
|
|
@ -128,6 +128,8 @@ You may inspect these attributes to adjust behavior based on the current action.
|
||||||
permission_classes = [IsAdminUser]
|
permission_classes = [IsAdminUser]
|
||||||
return [permission() for permission in permission_classes]
|
return [permission() for permission in permission_classes]
|
||||||
|
|
||||||
|
**Note**: the `action` attribute is not available in the `get_parsers`, `get_authenticators` and `get_content_negotiator` methods, as it is set _after_ they are called in the framework lifecycle. If you override one of these methods and try to access the `action` attribute in them, you will get an `AttributeError` error.
|
||||||
|
|
||||||
## Marking extra actions for routing
|
## Marking extra actions for routing
|
||||||
|
|
||||||
If you have ad-hoc methods that should be routable, you can mark them as such with the `@action` decorator. Like regular actions, extra actions may be intended for either a single object, or an entire collection. To indicate this, set the `detail` argument to `True` or `False`. The router will configure its URL patterns accordingly. e.g., the `DefaultRouter` will configure detail actions to contain `pk` in their URL patterns.
|
If you have ad-hoc methods that should be routable, you can mark them as such with the `@action` decorator. Like regular actions, extra actions may be intended for either a single object, or an entire collection. To indicate this, set the `detail` argument to `True` or `False`. The router will configure its URL patterns accordingly. e.g., the `DefaultRouter` will configure detail actions to contain `pk` in their URL patterns.
|
||||||
|
@ -201,11 +203,12 @@ To view all extra actions, call the `.get_extra_actions()` method.
|
||||||
Extra actions can map additional HTTP methods to separate `ViewSet` methods. For example, the above password set/unset methods could be consolidated into a single route. Note that additional mappings do not accept arguments.
|
Extra actions can map additional HTTP methods to separate `ViewSet` methods. For example, the above password set/unset methods could be consolidated into a single route. Note that additional mappings do not accept arguments.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@action(detail=True, methods=['put'], name='Change Password')
|
@action(detail=True, methods=["put"], name="Change Password")
|
||||||
def password(self, request, pk=None):
|
def password(self, request, pk=None):
|
||||||
"""Update the user's password."""
|
"""Update the user's password."""
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
@password.mapping.delete
|
@password.mapping.delete
|
||||||
def delete_password(self, request, pk=None):
|
def delete_password(self, request, pk=None):
|
||||||
"""Delete the user's password."""
|
"""Delete the user's password."""
|
||||||
|
@ -220,14 +223,14 @@ Note that the `basename` is provided by the router during `ViewSet` registration
|
||||||
|
|
||||||
Using the example from the previous section:
|
Using the example from the previous section:
|
||||||
|
|
||||||
```python
|
```pycon
|
||||||
>>> view.reverse_action('set-password', args=['1'])
|
>>> view.reverse_action("set-password", args=["1"])
|
||||||
'http://localhost:8000/api/users/1/set_password'
|
'http://localhost:8000/api/users/1/set_password'
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, you can use the `url_name` attribute set by the `@action` decorator.
|
Alternatively, you can use the `url_name` attribute set by the `@action` decorator.
|
||||||
|
|
||||||
```python
|
```pycon
|
||||||
>>> view.reverse_action(view.set_password.url_name, args=['1'])
|
>>> view.reverse_action(view.set_password.url_name, args=['1'])
|
||||||
'http://localhost:8000/api/users/1/set_password'
|
'http://localhost:8000/api/users/1/set_password'
|
||||||
```
|
```
|
||||||
|
@ -310,7 +313,7 @@ You may need to provide custom `ViewSet` classes that do not have the full set o
|
||||||
|
|
||||||
To create a base viewset class that provides `create`, `list` and `retrieve` operations, inherit from `GenericViewSet`, and mixin the required actions:
|
To create a base viewset class that provides `create`, `list` and `retrieve` operations, inherit from `GenericViewSet`, and mixin the required actions:
|
||||||
|
|
||||||
from rest_framework import mixins
|
from rest_framework import mixins, viewsets
|
||||||
|
|
||||||
class CreateListRetrieveViewSet(mixins.CreateModelMixin,
|
class CreateListRetrieveViewSet(mixins.CreateModelMixin,
|
||||||
mixins.ListModelMixin,
|
mixins.ListModelMixin,
|
||||||
|
|
|
@ -41,8 +41,8 @@ update your REST framework settings to include `DEFAULT_SCHEMA_CLASS` explicitly
|
||||||
|
|
||||||
```python
|
```python
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
...
|
...: ...,
|
||||||
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'
|
"DEFAULT_SCHEMA_CLASS": "rest_framework.schemas.coreapi.AutoSchema",
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -74,10 +74,11 @@ urlpatterns = [
|
||||||
# Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
|
# Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
|
||||||
# * `title` and `description` parameters are passed to `SchemaGenerator`.
|
# * `title` and `description` parameters are passed to `SchemaGenerator`.
|
||||||
# * Provide view name for use with `reverse()`.
|
# * Provide view name for use with `reverse()`.
|
||||||
path('openapi', get_schema_view(
|
path(
|
||||||
title="Your Project",
|
"openapi",
|
||||||
description="API for all things …"
|
get_schema_view(title="Your Project", description="API for all things …"),
|
||||||
), name='openapi-schema'),
|
name="openapi-schema",
|
||||||
|
),
|
||||||
# ...
|
# ...
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
|
@ -47,6 +47,7 @@ class DocStringExampleListView(APIView):
|
||||||
get: A description of my GET operation.
|
get: A description of my GET operation.
|
||||||
post: A description of my POST operation.
|
post: A description of my POST operation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
|
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
@ -63,7 +64,7 @@ In some circumstances a Validator class or a Default class may need to access th
|
||||||
* 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.
|
* 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.
|
* 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.
|
Our previous 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.
|
Instead, validators or defaults which require the serializer context, should include a `requires_context = True` attribute on the class.
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ The tags used for a particular view may also be overridden...
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class MyOrders(APIView):
|
class MyOrders(APIView):
|
||||||
schema = AutoSchema(tags=['users', 'orders'])
|
schema = AutoSchema(tags=["users", "orders"])
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -118,10 +118,11 @@ class SitesSearchView(generics.ListAPIView):
|
||||||
by a search against the site name or location. (Location searches are
|
by a search against the site name or location. (Location searches are
|
||||||
matched against the region and country names.)
|
matched against the region and country names.)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
queryset = Sites.objects.all()
|
queryset = Sites.objects.all()
|
||||||
serializer_class = SitesSerializer
|
serializer_class = SitesSerializer
|
||||||
filter_backends = [filters.SearchFilter]
|
filter_backends = [filters.SearchFilter]
|
||||||
search_fields = ['site_name', 'location__region', 'location__country']
|
search_fields = ["site_name", "location__region", "location__country"]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Searches against annotate fields
|
### Searches against annotate fields
|
||||||
|
@ -135,10 +136,11 @@ class PublisherSearchView(generics.ListAPIView):
|
||||||
Search for publishers, optionally filtering the search against the average
|
Search for publishers, optionally filtering the search against the average
|
||||||
rating of all their books.
|
rating of all their books.
|
||||||
"""
|
"""
|
||||||
queryset = Publisher.objects.annotate(avg_rating=Avg('book__rating'))
|
|
||||||
|
queryset = Publisher.objects.annotate(avg_rating=Avg("book__rating"))
|
||||||
serializer_class = PublisherSerializer
|
serializer_class = PublisherSerializer
|
||||||
filter_backends = [filters.SearchFilter]
|
filter_backends = [filters.SearchFilter]
|
||||||
search_fields = ['avg_rating']
|
search_fields = ["avg_rating"]
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
@ -28,10 +28,10 @@ Our requirements are now:
|
||||||
* Python 3.6+
|
* Python 3.6+
|
||||||
* Django 4.1, 4.0, 3.2, 3.1, 3.0
|
* Django 4.1, 4.0, 3.2, 3.1, 3.0
|
||||||
|
|
||||||
## `raise_exceptions` argument for `is_valid` is now keyword-only.
|
## `raise_exception` argument for `is_valid` is now keyword-only.
|
||||||
|
|
||||||
Calling `serializer_instance.is_valid(True)` is no longer acceptable syntax.
|
Calling `serializer_instance.is_valid(True)` is no longer acceptable syntax.
|
||||||
If you'd like to use the `raise_exceptions` argument, you must use it as a
|
If you'd like to use the `raise_exception` argument, you must use it as a
|
||||||
keyword argument.
|
keyword argument.
|
||||||
|
|
||||||
See Pull Request [#7952](https://github.com/encode/django-rest-framework/pull/7952) for more details.
|
See Pull Request [#7952](https://github.com/encode/django-rest-framework/pull/7952) for more details.
|
||||||
|
|
50
docs/community/3.15-announcement.md
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<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.15
|
||||||
|
|
||||||
|
At the Internet, on March 15th, 2024, with 176 commits by 138 authors, we are happy to announce the release of Django REST framework 3.15.
|
||||||
|
|
||||||
|
## Django 5.0 and Python 3.12 support
|
||||||
|
|
||||||
|
The latest release now fully supports Django 5.0 and Python 3.12.
|
||||||
|
|
||||||
|
The current minimum versions of Django still is 3.0 and Python 3.6.
|
||||||
|
|
||||||
|
## Primary Support of UniqueConstraint
|
||||||
|
|
||||||
|
`ModelSerializer` generates validators for [UniqueConstraint](https://docs.djangoproject.com/en/4.0/ref/models/constraints/#uniqueconstraint) (both UniqueValidator and UniqueTogetherValidator)
|
||||||
|
|
||||||
|
## SimpleRouter non-regex matching support
|
||||||
|
|
||||||
|
By default the URLs created by `SimpleRouter` use regular expressions. This behavior can be modified by setting the `use_regex_path` argument to `False` when instantiating the router.
|
||||||
|
|
||||||
|
## ZoneInfo as the primary source of timezone data
|
||||||
|
|
||||||
|
Dependency on pytz has been removed and deprecation warnings have been added, Django will provide ZoneInfo instances as long as USE_DEPRECATED_PYTZ is not enabled. More info on the migration can be found [in this guide](https://pytz-deprecation-shim.readthedocs.io/en/latest/migration.html).
|
||||||
|
|
||||||
|
## Align `SearchFilter` behaviour to `django.contrib.admin` search
|
||||||
|
|
||||||
|
Searches now may contain _quoted phrases_ with spaces, each phrase is considered as a single search term, and it will raise a validation error if any null-character is provided in search. See the [Filtering API guide](../api-guide/filtering.md) for more information.
|
||||||
|
|
||||||
|
## Other fixes and improvements
|
||||||
|
|
||||||
|
There are a number of fixes and minor improvements in this release, ranging from documentation, internal infrastructure (typing, testing, requirements, deprecation, etc.), security and overall behaviour.
|
||||||
|
|
||||||
|
See the [release notes](release-notes.md) page for a complete listing.
|
42
docs/community/3.16-announcement.md
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<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.16
|
||||||
|
|
||||||
|
At the Internet, on March 28th, 2025, we are happy to announce the release of Django REST framework 3.16.
|
||||||
|
|
||||||
|
## Updated Django and Python support
|
||||||
|
|
||||||
|
The latest release now fully supports Django 5.1 and the upcoming 5.2 LTS as well as Python 3.13.
|
||||||
|
|
||||||
|
The current minimum versions of Django is now 4.2 and Python 3.9.
|
||||||
|
|
||||||
|
## Django LoginRequiredMiddleware
|
||||||
|
|
||||||
|
The new `LoginRequiredMiddleware` introduced by Django 5.1 can now be used alongside Django REST Framework, however it is not honored for API views as an equivalent behaviour can be configured via `DEFAULT_AUTHENTICATION_CLASSES`. See [our dedicated section](../api-guide/authentication.md#django-51-loginrequiredmiddleware) in the docs for more information.
|
||||||
|
|
||||||
|
## Improved support for UniqueConstraint
|
||||||
|
|
||||||
|
The generation of validators for [UniqueConstraint](https://docs.djangoproject.com/en/stable/ref/models/constraints/#uniqueconstraint) has been improved to support better nullable fields and constraints with conditions.
|
||||||
|
|
||||||
|
## Other fixes and improvements
|
||||||
|
|
||||||
|
There are a number of fixes and minor improvements in this release, ranging from documentation, internal infrastructure (typing, testing, requirements, deprecation, etc.), security and overall behaviour.
|
||||||
|
|
||||||
|
See the [release notes](release-notes.md) page for a complete listing.
|
|
@ -64,14 +64,10 @@ from rest_framework.schemas import get_schema_view
|
||||||
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer
|
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer
|
||||||
|
|
||||||
schema_view = get_schema_view(
|
schema_view = get_schema_view(
|
||||||
title='Example API',
|
title="Example API", renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer]
|
||||||
renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [path("swagger/", schema_view), ...]
|
||||||
path('swagger/', schema_view),
|
|
||||||
...
|
|
||||||
]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
There have been a large number of fixes to the schema generation. These should
|
There have been a large number of fixes to the schema generation. These should
|
||||||
|
|
|
@ -65,15 +65,12 @@ from rest_framework.renderers import JSONOpenAPIRenderer
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
schema_view = get_schema_view(
|
schema_view = get_schema_view(
|
||||||
title='Server Monitoring API',
|
title="Server Monitoring API",
|
||||||
url='https://www.example.org/api/',
|
url="https://www.example.org/api/",
|
||||||
renderer_classes=[JSONOpenAPIRenderer]
|
renderer_classes=[JSONOpenAPIRenderer],
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [path("schema.json", schema_view), ...]
|
||||||
path('schema.json', schema_view),
|
|
||||||
...
|
|
||||||
]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
And here's how you can use the `generateschema` management command:
|
And here's how you can use the `generateschema` management command:
|
||||||
|
|
|
@ -4,13 +4,9 @@
|
||||||
>
|
>
|
||||||
> — [Tim Berners-Lee][cite]
|
> — [Tim Berners-Lee][cite]
|
||||||
|
|
||||||
There are many ways you can contribute to Django REST framework. We'd like it to be a community-led project, so please get involved and help shape the future of the project.
|
!!! note
|
||||||
|
|
||||||
---
|
At this point in its lifespan we consider Django REST framework to be feature-complete. We focus on pull requests that track the continued development of Django versions, and generally do not accept new features or code formatting changes.
|
||||||
|
|
||||||
**Note**: At this point in it's lifespan we consider Django REST framework to be essentially feature-complete. We may accept pull requests that track the continued development of Django versions, but would prefer not to accept new features or code formatting changes.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
|
@ -32,24 +28,8 @@ The [Django code of conduct][code-of-conduct] gives a fuller set of guidelines f
|
||||||
|
|
||||||
# Issues
|
# Issues
|
||||||
|
|
||||||
Our contribution process is that the [GitHub discussions page](https://github.com/encode/django-rest-framework/discussions) should generally be your starting point. Please only raise an issue or pull request if you've been recommended to do so after discussion.
|
* Django REST framework is considered feature-complete. Please do not file requests to change behavior, unless it is required for security reasons or to maintain compatibility with upcoming Django or Python versions.
|
||||||
|
* Feature requests will typically be closed with a recommendation that they be implemented outside the core REST framework library (e.g. as third-party libraries). This approach allows us to keep down the maintenance overhead of REST framework, so that the focus can be on continued stability and great documentation.
|
||||||
Some tips on good potential issue reporting:
|
|
||||||
|
|
||||||
* When describing issues try to phrase your ticket in terms of the *behavior* you think needs changing rather than the *code* you think need changing.
|
|
||||||
* Search the GitHub project page for related items, and make sure you're running the latest version of REST framework before reporting an issue.
|
|
||||||
* Feature requests will often be closed with a recommendation that they be implemented outside of the core REST framework library. Keeping new feature requests implemented as third party libraries allows us to keep down the maintenance overhead of REST framework, so that the focus can be on continued stability, bugfixes, and great documentation. At this point in it's lifespan we consider Django REST framework to be essentially feature-complete.
|
|
||||||
* Closing an issue doesn't necessarily mean the end of a discussion. If you believe your issue has been closed incorrectly, explain why and we'll consider if it needs to be reopened.
|
|
||||||
|
|
||||||
## Triaging issues
|
|
||||||
|
|
||||||
Getting involved in triaging incoming issues is a good way to start contributing. Every single ticket that comes into the ticket tracker needs to be reviewed in order to determine what the next steps should be. Anyone can help out with this, you just need to be willing to
|
|
||||||
|
|
||||||
* Read through the ticket - does it make sense, is it missing any context that would help explain it better?
|
|
||||||
* Is the ticket reported in the correct place, would it be better suited as a discussion on the discussion group?
|
|
||||||
* If the ticket is a bug report, can you reproduce it? Are you able to write a failing test case that demonstrates the issue and that can be submitted as a pull request?
|
|
||||||
* If the ticket is a feature request, do you agree with it, and could the feature request instead be implemented as a third party package?
|
|
||||||
* If a ticket hasn't had much activity and it addresses something you need, then comment on the ticket and try to find out what's needed to get it moving again.
|
|
||||||
|
|
||||||
# Development
|
# Development
|
||||||
|
|
||||||
|
@ -209,7 +189,6 @@ If you want to draw attention to a note or warning, use a pair of enclosing line
|
||||||
[code-of-conduct]: https://www.djangoproject.com/conduct/
|
[code-of-conduct]: https://www.djangoproject.com/conduct/
|
||||||
[google-group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
|
[google-group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
|
||||||
[so-filter]: https://stackexchange.com/filters/66475/rest-framework
|
[so-filter]: https://stackexchange.com/filters/66475/rest-framework
|
||||||
[issues]: https://github.com/encode/django-rest-framework/issues?state=open
|
|
||||||
[pep-8]: https://www.python.org/dev/peps/pep-0008/
|
[pep-8]: https://www.python.org/dev/peps/pep-0008/
|
||||||
[build-status]: ../img/build-status.png
|
[build-status]: ../img/build-status.png
|
||||||
[pull-requests]: https://help.github.com/articles/using-pull-requests
|
[pull-requests]: https://help.github.com/articles/using-pull-requests
|
||||||
|
|
|
@ -114,7 +114,7 @@ If you use REST framework commercially we strongly encourage you to invest in it
|
||||||
Signing up for a paid plan will:
|
Signing up for a paid plan will:
|
||||||
|
|
||||||
* Directly contribute to faster releases, more features, and higher quality software.
|
* Directly contribute to faster releases, more features, and higher quality software.
|
||||||
* Allow more time to be invested in documentation, issue triage, and community support.
|
* Allow more time to be invested in keeping the package up to date.
|
||||||
* Safeguard the future development of REST framework.
|
* Safeguard the future development of REST framework.
|
||||||
|
|
||||||
REST framework continues to be open-source and permissively licensed, but we firmly believe it is in the commercial best-interest for users of the project to invest in its ongoing development.
|
REST framework continues to be open-source and permissively licensed, but we firmly believe it is in the commercial best-interest for users of the project to invest in its ongoing development.
|
||||||
|
@ -134,18 +134,6 @@ REST framework continues to be open-source and permissively licensed, but we fir
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## What future funding will enable
|
|
||||||
|
|
||||||
* Realtime API support, using WebSockets. This will consist of documentation and support for using REST framework together with Django Channels, plus integrating WebSocket support into the client libraries.
|
|
||||||
* Better authentication defaults, possibly bringing JWT & CORS support into the core package.
|
|
||||||
* Securing the community & operations manager position long-term.
|
|
||||||
* Opening up and securing a part-time position to focus on ticket triage and resolution.
|
|
||||||
* Paying for development time on building API client libraries in a range of programming languages. These would be integrated directly into the upcoming API documentation.
|
|
||||||
|
|
||||||
Sign up for a paid plan today, and help ensure that REST framework becomes a sustainable, full-time funded project.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What our sponsors and users say
|
## What our sponsors and users say
|
||||||
|
|
||||||
> As a developer, Django REST framework feels like an obvious and natural extension to all the great things that make up Django and it's community. Getting started is easy while providing simple abstractions which makes it flexible and customizable. Contributing and supporting Django REST framework helps ensure its future and one way or another it also helps Django, and the Python ecosystem.
|
> As a developer, Django REST framework feels like an obvious and natural extension to all the great things that make up Django and it's community. Getting started is easy while providing simple abstractions which makes it flexible and customizable. Contributing and supporting Django REST framework helps ensure its future and one way or another it also helps Django, and the Python ecosystem.
|
||||||
|
@ -165,6 +153,8 @@ DRF is one of the core reasons why Django is top choice among web frameworks tod
|
||||||
>
|
>
|
||||||
> — Andrew Conti, Django REST framework user
|
> — Andrew Conti, Django REST framework user
|
||||||
|
|
||||||
|
Sign up for a paid plan today, and help ensure that REST framework becomes a sustainable, full-time funded project.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Individual plan
|
## Individual plan
|
||||||
|
|
|
@ -7,6 +7,7 @@ Looking for a new Django REST Framework related role? On this site we provide a
|
||||||
|
|
||||||
* [https://www.djangoproject.com/community/jobs/][djangoproject-website]
|
* [https://www.djangoproject.com/community/jobs/][djangoproject-website]
|
||||||
* [https://www.python.org/jobs/][python-org-jobs]
|
* [https://www.python.org/jobs/][python-org-jobs]
|
||||||
|
* [https://django.on-remote.com][django-on-remote]
|
||||||
* [https://djangogigs.com][django-gigs-com]
|
* [https://djangogigs.com][django-gigs-com]
|
||||||
* [https://djangojobs.net/jobs/][django-jobs-net]
|
* [https://djangojobs.net/jobs/][django-jobs-net]
|
||||||
* [https://findwork.dev/django-rest-framework-jobs][findwork-dev]
|
* [https://findwork.dev/django-rest-framework-jobs][findwork-dev]
|
||||||
|
@ -26,6 +27,7 @@ Wonder how else you can help? One of the best ways you can help Django REST Fram
|
||||||
|
|
||||||
[djangoproject-website]: https://www.djangoproject.com/community/jobs/
|
[djangoproject-website]: https://www.djangoproject.com/community/jobs/
|
||||||
[python-org-jobs]: https://www.python.org/jobs/
|
[python-org-jobs]: https://www.python.org/jobs/
|
||||||
|
[django-on-remote]: https://django.on-remote.com/
|
||||||
[django-gigs-com]: https://djangogigs.com
|
[django-gigs-com]: https://djangogigs.com
|
||||||
[django-jobs-net]: https://djangojobs.net/jobs/
|
[django-jobs-net]: https://djangojobs.net/jobs/
|
||||||
[findwork-dev]: https://findwork.dev/django-rest-framework-jobs
|
[findwork-dev]: https://findwork.dev/django-rest-framework-jobs
|
||||||
|
|
|
@ -13,55 +13,13 @@ The aim is to ensure that the project has a high
|
||||||
|
|
||||||
## Maintenance team
|
## Maintenance team
|
||||||
|
|
||||||
We have a quarterly maintenance cycle where new members may join the maintenance team. We currently cap the size of the team at 5 members, and may encourage folks to step out of the team for a cycle to allow new members to participate.
|
[Participating actively in the REST framework project](contributing.md) **does not require being part of the maintenance team**. Almost every important part of issue triage and project improvement can be actively worked on regardless of your collaborator status on the repository.
|
||||||
|
|
||||||
#### Current team
|
#### Composition
|
||||||
|
|
||||||
The [maintenance team for Q4 2015](https://github.com/encode/django-rest-framework/issues/2190):
|
The composition of the maintenance team is handled by [@tomchristie](https://github.com/encode/). Team members will be added as collaborators to the repository.
|
||||||
|
|
||||||
* [@tomchristie](https://github.com/encode/)
|
#### Responsibilities
|
||||||
* [@xordoquy](https://github.com/xordoquy/) (Release manager.)
|
|
||||||
* [@carltongibson](https://github.com/carltongibson/)
|
|
||||||
* [@kevin-brown](https://github.com/kevin-brown/)
|
|
||||||
* [@jpadilla](https://github.com/jpadilla/)
|
|
||||||
|
|
||||||
#### Maintenance cycles
|
|
||||||
|
|
||||||
Each maintenance cycle is initiated by an issue being opened with the `Process` label.
|
|
||||||
|
|
||||||
* To be considered for a maintainer role simply comment against the issue.
|
|
||||||
* Existing members must explicitly opt-in to the next cycle by check-marking their name.
|
|
||||||
* The final decision on the incoming team will be made by `@tomchristie`.
|
|
||||||
|
|
||||||
Members of the maintenance team will be added as collaborators to the repository.
|
|
||||||
|
|
||||||
The following template should be used for the description of the issue, and serves as the formal process for selecting the team.
|
|
||||||
|
|
||||||
This issue is for determining the maintenance team for the *** period.
|
|
||||||
|
|
||||||
Please see the [Project management](https://www.django-rest-framework.org/topics/project-management/) section of our documentation for more details.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### Renewing existing members.
|
|
||||||
|
|
||||||
The following people are the current maintenance team. Please checkmark your name if you wish to continue to have write permission on the repository for the *** period.
|
|
||||||
|
|
||||||
- [ ] @***
|
|
||||||
- [ ] @***
|
|
||||||
- [ ] @***
|
|
||||||
- [ ] @***
|
|
||||||
- [ ] @***
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### New members.
|
|
||||||
|
|
||||||
If you wish to be considered for this or a future date, please comment against this or subsequent issues.
|
|
||||||
|
|
||||||
To modify this process for future maintenance cycles make a pull request to the [project management](https://www.django-rest-framework.org/topics/project-management/) documentation.
|
|
||||||
|
|
||||||
#### Responsibilities of team members
|
|
||||||
|
|
||||||
Team members have the following responsibilities.
|
Team members have the following responsibilities.
|
||||||
|
|
||||||
|
@ -76,18 +34,13 @@ Further notes for maintainers:
|
||||||
* Code changes should come in the form of a pull request - do not push directly to master.
|
* Code changes should come in the form of a pull request - do not push directly to master.
|
||||||
* Maintainers should typically not merge their own pull requests.
|
* Maintainers should typically not merge their own pull requests.
|
||||||
* Each issue/pull request should have exactly one label once triaged.
|
* Each issue/pull request should have exactly one label once triaged.
|
||||||
* Search for un-triaged issues with [is:open no:label][un-triaged].
|
|
||||||
|
|
||||||
It should be noted that participating actively in the REST framework project clearly **does not require being part of the maintenance team**. Almost every import part of issue triage and project improvement can be actively worked on regardless of your collaborator status on the repository.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Release process
|
## Release process
|
||||||
|
|
||||||
The release manager is selected on every quarterly maintenance cycle.
|
* The release manager is selected by `@tomchristie`.
|
||||||
|
* The release manager will then have the maintainer role added to PyPI package.
|
||||||
* The manager should be selected by `@tomchristie`.
|
|
||||||
* The manager will then have the maintainer role added to PyPI package.
|
|
||||||
* The previous manager will then have the maintainer role removed from the PyPI package.
|
* The previous manager will then have the maintainer role removed from the PyPI package.
|
||||||
|
|
||||||
Our PyPI releases will be handled by either the current release manager, or by `@tomchristie`. Every release should have an open issue tagged with the `Release` label and marked against the appropriate milestone.
|
Our PyPI releases will be handled by either the current release manager, or by `@tomchristie`. Every release should have an open issue tagged with the `Release` label and marked against the appropriate milestone.
|
||||||
|
@ -198,15 +151,12 @@ If `@tomchristie` ceases to participate in the project then `@j4mie` has respons
|
||||||
|
|
||||||
The following issues still need to be addressed:
|
The following issues still need to be addressed:
|
||||||
|
|
||||||
* Ensure `@jamie` has back-up access to the `django-rest-framework.org` domain setup and admin.
|
* Ensure `@j4mie` 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.
|
* Document ownership of the [mailing list][mailing-list] and IRC channel.
|
||||||
* Document ownership and management of the security mailing list.
|
* Document ownership and management of the security mailing list.
|
||||||
|
|
||||||
[bus-factor]: https://en.wikipedia.org/wiki/Bus_factor
|
[bus-factor]: https://en.wikipedia.org/wiki/Bus_factor
|
||||||
[un-triaged]: https://github.com/encode/django-rest-framework/issues?q=is%3Aopen+no%3Alabel
|
|
||||||
[transifex-project]: https://www.transifex.com/projects/p/django-rest-framework/
|
[transifex-project]: https://www.transifex.com/projects/p/django-rest-framework/
|
||||||
[transifex-client]: https://pypi.org/project/transifex-client/
|
[transifex-client]: https://pypi.org/project/transifex-client/
|
||||||
[translation-memory]: http://docs.transifex.com/guides/tm#let-tm-automatically-populate-translations
|
[translation-memory]: http://docs.transifex.com/guides/tm#let-tm-automatically-populate-translations
|
||||||
[sandbox]: https://restframework.herokuapp.com/
|
|
||||||
[mailing-list]: https://groups.google.com/forum/#!forum/django-rest-framework
|
[mailing-list]: https://groups.google.com/forum/#!forum/django-rest-framework
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
|
|
||||||
## Versioning
|
## 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.
|
- **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.
|
||||||
|
|
||||||
Medium version numbers (0.x.0) may include API changes, in line with the [deprecation policy][deprecation-policy]. You should read the release notes carefully before upgrading between medium point releases.
|
- **Medium** version numbers (0.x.0) may include API changes, in line with the [deprecation policy][deprecation-policy]. You should read the release notes carefully before upgrading between medium point releases.
|
||||||
|
|
||||||
Major version numbers (x.0.0) are reserved for substantial project milestones.
|
- **Major** version numbers (x.0.0) are reserved for substantial project milestones.
|
||||||
|
|
||||||
|
As REST Framework is considered feature-complete, most releases are expected to be minor releases.
|
||||||
|
|
||||||
## Deprecation policy
|
## Deprecation policy
|
||||||
|
|
||||||
|
@ -34,6 +36,191 @@ You can determine your currently installed version using `pip show`:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 3.16.x series
|
||||||
|
|
||||||
|
### 3.16.0
|
||||||
|
|
||||||
|
**Date**: 28th March 2025
|
||||||
|
|
||||||
|
This release is considered a significant release to improve upstream support with Django and Python. Some of these may change the behaviour of existing features and pre-existing behaviour. Specifically, some fixes were added to around the support of `UniqueConstraint` with nullable fields which will improve built-in serializer validation.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Add official support for Django 5.1 and its new `LoginRequiredMiddleware` in [#9514](https://github.com/encode/django-rest-framework/pull/9514) and [#9657](https://github.com/encode/django-rest-framework/pull/9657)
|
||||||
|
* Add official Django 5.2a1 support in [#9634](https://github.com/encode/django-rest-framework/pull/9634)
|
||||||
|
* Add support for Python 3.13 in [#9527](https://github.com/encode/django-rest-framework/pull/9527) and [#9556](https://github.com/encode/django-rest-framework/pull/9556)
|
||||||
|
* Support Django 2.1+ test client JSON data automatically serialized in [#6511](https://github.com/encode/django-rest-framework/pull/6511) and fix a regression in [#9615](https://github.com/encode/django-rest-framework/pull/9615)
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
* Fix unique together validator to respect condition's fields from `UniqueConstraint` in [#9360](https://github.com/encode/django-rest-framework/pull/9360)
|
||||||
|
* Fix raising on nullable fields part of `UniqueConstraint` in [#9531](https://github.com/encode/django-rest-framework/pull/9531)
|
||||||
|
* Fix `unique_together` validation with source in [#9482](https://github.com/encode/django-rest-framework/pull/9482)
|
||||||
|
* Added protections to `AttributeError` raised within properties in [#9455](https://github.com/encode/django-rest-framework/pull/9455)
|
||||||
|
* Fix `get_template_context` to handle also lists in [#9467](https://github.com/encode/django-rest-framework/pull/9467)
|
||||||
|
* Fix "Converter is already registered" deprecation warning. in [#9512](https://github.com/encode/django-rest-framework/pull/9512)
|
||||||
|
* Fix noisy warning and accept integers as min/max values of `DecimalField` in [#9515](https://github.com/encode/django-rest-framework/pull/9515)
|
||||||
|
* Fix usages of `open()` in `setup.py` in [#9661](https://github.com/encode/django-rest-framework/pull/9661)
|
||||||
|
|
||||||
|
## Translations
|
||||||
|
|
||||||
|
* Add some missing Chinese translations in [#9505](https://github.com/encode/django-rest-framework/pull/9505)
|
||||||
|
* Fix spelling mistakes in Farsi language were corrected in [#9521](https://github.com/encode/django-rest-framework/pull/9521)
|
||||||
|
* Fixing and adding missing Brazilian Portuguese translations in [#9535](https://github.com/encode/django-rest-framework/pull/9535)
|
||||||
|
|
||||||
|
## Removals
|
||||||
|
|
||||||
|
* Remove support for Python 3.8 in [#9670](https://github.com/encode/django-rest-framework/pull/9670)
|
||||||
|
* Remove long deprecated code from request wrapper in [#9441](https://github.com/encode/django-rest-framework/pull/9441)
|
||||||
|
* Remove deprecated `AutoSchema._get_reference` method in [#9525](https://github.com/encode/django-rest-framework/pull/9525)
|
||||||
|
|
||||||
|
## Documentation and internal changes
|
||||||
|
|
||||||
|
* Provide tests for hashing of `OperandHolder` in [#9437](https://github.com/encode/django-rest-framework/pull/9437)
|
||||||
|
* Update documentation: Add `adrf` third party package in [#9198](https://github.com/encode/django-rest-framework/pull/9198)
|
||||||
|
* Update tutorials links in Community contributions docs in [#9476](https://github.com/encode/django-rest-framework/pull/9476)
|
||||||
|
* Fix usage of deprecated Django function in example from docs in [#9509](https://github.com/encode/django-rest-framework/pull/9509)
|
||||||
|
* Move path converter docs into a separate section in [#9524](https://github.com/encode/django-rest-framework/pull/9524)
|
||||||
|
* Add test covering update view without `queryset` attribute in [#9528](https://github.com/encode/django-rest-framework/pull/9528)
|
||||||
|
* Fix Transifex link in [#9541](https://github.com/encode/django-rest-framework/pull/9541)
|
||||||
|
* Fix example `httpie` call in docs in [#9543](https://github.com/encode/django-rest-framework/pull/9543)
|
||||||
|
* Fix example for serializer field with choices in docs in [#9563](https://github.com/encode/django-rest-framework/pull/9563)
|
||||||
|
* Remove extra `<>` in validators example in [#9590](https://github.com/encode/django-rest-framework/pull/9590)
|
||||||
|
* Update `strftime` link in the docs in [#9624](https://github.com/encode/django-rest-framework/pull/9624)
|
||||||
|
* Switch to codecov GHA in [#9618](https://github.com/encode/django-rest-framework/pull/9618)
|
||||||
|
* Add note regarding availability of the `action` attribute in 'Introspecting ViewSet actions' docs section in [#9633](https://github.com/encode/django-rest-framework/pull/9633)
|
||||||
|
* Improved description of allowed throttling rates in documentation in [#9640](https://github.com/encode/django-rest-framework/pull/9640)
|
||||||
|
* Add `rest-framework-gm2m-relations` package to the list of 3rd party libraries in [#9063](https://github.com/encode/django-rest-framework/pull/9063)
|
||||||
|
* Fix a number of typos in the test suite in the docs in [#9662](https://github.com/encode/django-rest-framework/pull/9662)
|
||||||
|
* Add `django-pyoidc` as a third party authentication library in [#9667](https://github.com/encode/django-rest-framework/pull/9667)
|
||||||
|
|
||||||
|
## New Contributors
|
||||||
|
|
||||||
|
* [`@maerteijn`](https://github.com/maerteijn) made their first contribution in [#9198](https://github.com/encode/django-rest-framework/pull/9198)
|
||||||
|
* [`@FraCata00`](https://github.com/FraCata00) made their first contribution in [#9444](https://github.com/encode/django-rest-framework/pull/9444)
|
||||||
|
* [`@AlvaroVega`](https://github.com/AlvaroVega) made their first contribution in [#9451](https://github.com/encode/django-rest-framework/pull/9451)
|
||||||
|
* [`@james`](https://github.com/james)-mchugh made their first contribution in [#9455](https://github.com/encode/django-rest-framework/pull/9455)
|
||||||
|
* [`@ifeanyidavid`](https://github.com/ifeanyidavid) made their first contribution in [#9479](https://github.com/encode/django-rest-framework/pull/9479)
|
||||||
|
* [`@p`](https://github.com/p)-schlickmann made their first contribution in [#9480](https://github.com/encode/django-rest-framework/pull/9480)
|
||||||
|
* [`@akkuman`](https://github.com/akkuman) made their first contribution in [#9505](https://github.com/encode/django-rest-framework/pull/9505)
|
||||||
|
* [`@rafaelgramoschi`](https://github.com/rafaelgramoschi) made their first contribution in [#9509](https://github.com/encode/django-rest-framework/pull/9509)
|
||||||
|
* [`@Sinaatkd`](https://github.com/Sinaatkd) made their first contribution in [#9521](https://github.com/encode/django-rest-framework/pull/9521)
|
||||||
|
* [`@gtkacz`](https://github.com/gtkacz) made their first contribution in [#9535](https://github.com/encode/django-rest-framework/pull/9535)
|
||||||
|
* [`@sliverc`](https://github.com/sliverc) made their first contribution in [#9556](https://github.com/encode/django-rest-framework/pull/9556)
|
||||||
|
* [`@gabrielromagnoli1987`](https://github.com/gabrielromagnoli1987) made their first contribution in [#9543](https://github.com/encode/django-rest-framework/pull/9543)
|
||||||
|
* [`@cheehong1030`](https://github.com/cheehong1030) made their first contribution in [#9563](https://github.com/encode/django-rest-framework/pull/9563)
|
||||||
|
* [`@amansharma612`](https://github.com/amansharma612) made their first contribution in [#9590](https://github.com/encode/django-rest-framework/pull/9590)
|
||||||
|
* [`@Gluroda`](https://github.com/Gluroda) made their first contribution in [#9616](https://github.com/encode/django-rest-framework/pull/9616)
|
||||||
|
* [`@deepakangadi`](https://github.com/deepakangadi) made their first contribution in [#9624](https://github.com/encode/django-rest-framework/pull/9624)
|
||||||
|
* [`@EXG1O`](https://github.com/EXG1O) made their first contribution in [#9633](https://github.com/encode/django-rest-framework/pull/9633)
|
||||||
|
* [`@decadenza`](https://github.com/decadenza) made their first contribution in [#9640](https://github.com/encode/django-rest-framework/pull/9640)
|
||||||
|
* [`@mojtabaakbari221b`](https://github.com/mojtabaakbari221b) made their first contribution in [#9063](https://github.com/encode/django-rest-framework/pull/9063)
|
||||||
|
* [`@mikemanger`](https://github.com/mikemanger) made their first contribution in [#9661](https://github.com/encode/django-rest-framework/pull/9661)
|
||||||
|
* [`@gbip`](https://github.com/gbip) made their first contribution in [#9667](https://github.com/encode/django-rest-framework/pull/9667)
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/encode/django-rest-framework/compare/3.15.2...3.16.0
|
||||||
|
|
||||||
|
## 3.15.x series
|
||||||
|
|
||||||
|
### 3.15.2
|
||||||
|
|
||||||
|
**Date**: 14th June 2024
|
||||||
|
|
||||||
|
* Fix potential XSS vulnerability in browsable API. [#9435](https://github.com/encode/django-rest-framework/pull/9435)
|
||||||
|
* Revert "Ensure CursorPagination respects nulls in the ordering field". [#9381](https://github.com/encode/django-rest-framework/pull/9381)
|
||||||
|
* Use warnings rather than logging a warning for DecimalField. [#9367](https://github.com/encode/django-rest-framework/pull/9367)
|
||||||
|
* Remove unused code. [#9393](https://github.com/encode/django-rest-framework/pull/9393)
|
||||||
|
* Django < 4.2 and Python < 3.8 no longer supported. [#9393](https://github.com/encode/django-rest-framework/pull/9393)
|
||||||
|
|
||||||
|
### 3.15.1
|
||||||
|
|
||||||
|
Date: 22nd March 2024
|
||||||
|
|
||||||
|
* Fix `SearchFilter` handling of quoted and comma separated strings, when `.get_search_terms` is being called into by a custom class. See [[#9338](https://github.com/encode/django-rest-framework/issues/9338)]
|
||||||
|
* Revert number of 3.15.0 issues which included unintended side-effects. See [[#9331](https://github.com/encode/django-rest-framework/issues/9331)]
|
||||||
|
|
||||||
|
### 3.15.0
|
||||||
|
|
||||||
|
Date: 15th March 2024
|
||||||
|
|
||||||
|
* Django 5.0 and Python 3.12 support [[#9157](https://github.com/encode/django-rest-framework/pull/9157)]
|
||||||
|
* Use POST method instead of GET to perform logout in browsable API [[9208](https://github.com/encode/django-rest-framework/pull/9208)]
|
||||||
|
* Added jQuery 3.7.1 support & dropped previous version [[#9094](https://github.com/encode/django-rest-framework/pull/9094)]
|
||||||
|
* Use str as default path converter [[#9066](https://github.com/encode/django-rest-framework/pull/9066)]
|
||||||
|
* Document support for http.HTTPMethod in the @action decorator added in Python 3.11 [[#9067](https://github.com/encode/django-rest-framework/pull/9067)]
|
||||||
|
* Update exceptions.md [[#9071](https://github.com/encode/django-rest-framework/pull/9071)]
|
||||||
|
* Partial serializer should not have required fields [[#7563](https://github.com/encode/django-rest-framework/pull/7563)]
|
||||||
|
* Propagate 'default' from model field to serializer field. [[#9030](https://github.com/encode/django-rest-framework/pull/9030)]
|
||||||
|
* Allow to override child.run_validation call in ListSerializer [[#8035](https://github.com/encode/django-rest-framework/pull/8035)]
|
||||||
|
* Align SearchFilter behaviour to django.contrib.admin search [[#9017](https://github.com/encode/django-rest-framework/pull/9017)]
|
||||||
|
* Class name added to unknown field error [[#9019](https://github.com/encode/django-rest-framework/pull/9019)]
|
||||||
|
* Fix: Pagination response schemas. [[#9049](https://github.com/encode/django-rest-framework/pull/9049)]
|
||||||
|
* Fix choices in ChoiceField to support IntEnum [[#8955](https://github.com/encode/django-rest-framework/pull/8955)]
|
||||||
|
* Fix `SearchFilter` rendering search field with invalid value [[#9023](https://github.com/encode/django-rest-framework/pull/9023)]
|
||||||
|
* Fix OpenAPI Schema yaml rendering for `timedelta` [[#9007](https://github.com/encode/django-rest-framework/pull/9007)]
|
||||||
|
* Fix `NamespaceVersioning` ignoring `DEFAULT_VERSION` on non-None namespaces [[#7278](https://github.com/encode/django-rest-framework/pull/7278)]
|
||||||
|
* Added Deprecation Warnings for CoreAPI [[#7519](https://github.com/encode/django-rest-framework/pull/7519)]
|
||||||
|
* Removed usage of `field.choices` that triggered full table load [[#8950](https://github.com/encode/django-rest-framework/pull/8950)]
|
||||||
|
* Permit mixed casing of string values for `BooleanField` validation [[#8970](https://github.com/encode/django-rest-framework/pull/8970)]
|
||||||
|
* Fixes `BrowsableAPIRenderer` for usage with `ListSerializer`. [[#7530](https://github.com/encode/django-rest-framework/pull/7530)]
|
||||||
|
* Change semantic of `OR` of two permission classes [[#7522](https://github.com/encode/django-rest-framework/pull/7522)]
|
||||||
|
* Remove dependency on `pytz` [[#8984](https://github.com/encode/django-rest-framework/pull/8984)]
|
||||||
|
* Make set_value a method within `Serializer` [[#8001](https://github.com/encode/django-rest-framework/pull/8001)]
|
||||||
|
* Fix URLPathVersioning reverse fallback [[#7247](https://github.com/encode/django-rest-framework/pull/7247)]
|
||||||
|
* Warn about Decimal type in min_value and max_value arguments of DecimalField [[#8972](https://github.com/encode/django-rest-framework/pull/8972)]
|
||||||
|
* Fix mapping for choice values [[#8968](https://github.com/encode/django-rest-framework/pull/8968)]
|
||||||
|
* Refactor read function to use context manager for file handling [[#8967](https://github.com/encode/django-rest-framework/pull/8967)]
|
||||||
|
* Fix: fallback on CursorPagination ordering if unset on the view [[#8954](https://github.com/encode/django-rest-framework/pull/8954)]
|
||||||
|
* Replaced `OrderedDict` with `dict` [[#8964](https://github.com/encode/django-rest-framework/pull/8964)]
|
||||||
|
* Refactor get_field_info method to include max_digits and decimal_places attributes in SimpleMetadata class [[#8943](https://github.com/encode/django-rest-framework/pull/8943)]
|
||||||
|
* Implement `__eq__` for validators [[#8925](https://github.com/encode/django-rest-framework/pull/8925)]
|
||||||
|
* Ensure CursorPagination respects nulls in the ordering field [[#8912](https://github.com/encode/django-rest-framework/pull/8912)]
|
||||||
|
* Use ZoneInfo as primary source of timezone data [[#8924](https://github.com/encode/django-rest-framework/pull/8924)]
|
||||||
|
* Add username search field for TokenAdmin (#8927) [[#8934](https://github.com/encode/django-rest-framework/pull/8934)]
|
||||||
|
* Handle Nested Relation in SlugRelatedField when many=False [[#8922](https://github.com/encode/django-rest-framework/pull/8922)]
|
||||||
|
* Bump version of jQuery to 3.6.4 & updated ref links [[#8909](https://github.com/encode/django-rest-framework/pull/8909)]
|
||||||
|
* Support UniqueConstraint [[#7438](https://github.com/encode/django-rest-framework/pull/7438)]
|
||||||
|
* Allow Request, Response, Field, and GenericAPIView to be subscriptable. This allows the classes to be made generic for type checking. [[#8825](https://github.com/encode/django-rest-framework/pull/8825)]
|
||||||
|
* Feat: Add some changes to ValidationError to support django style validation errors [[#8863](https://github.com/encode/django-rest-framework/pull/8863)]
|
||||||
|
* Fix Respect `can_read_model` permission in DjangoModelPermissions [[#8009](https://github.com/encode/django-rest-framework/pull/8009)]
|
||||||
|
* Add SimplePathRouter [[#6789](https://github.com/encode/django-rest-framework/pull/6789)]
|
||||||
|
* Re-prefetch related objects after updating [[#8043](https://github.com/encode/django-rest-framework/pull/8043)]
|
||||||
|
* Fix FilePathField required argument [[#8805](https://github.com/encode/django-rest-framework/pull/8805)]
|
||||||
|
* Raise ImproperlyConfigured exception if `basename` is not unique [[#8438](https://github.com/encode/django-rest-framework/pull/8438)]
|
||||||
|
* Use PrimaryKeyRelatedField pkfield in openapi [[#8315](https://github.com/encode/django-rest-framework/pull/8315)]
|
||||||
|
* replace partition with split in BasicAuthentication [[#8790](https://github.com/encode/django-rest-framework/pull/8790)]
|
||||||
|
* Fix BooleanField's allow_null behavior [[#8614](https://github.com/encode/django-rest-framework/pull/8614)]
|
||||||
|
* Handle Django's ValidationErrors in ListField [[#6423](https://github.com/encode/django-rest-framework/pull/6423)]
|
||||||
|
* Remove a bit of inline CSS. Add CSP nonce where it might be required and is available [[#8783](https://github.com/encode/django-rest-framework/pull/8783)]
|
||||||
|
* Use autocomplete widget for user selection in Token admin [[#8534](https://github.com/encode/django-rest-framework/pull/8534)]
|
||||||
|
* Make browsable API compatible with strong CSP [[#8784](https://github.com/encode/django-rest-framework/pull/8784)]
|
||||||
|
* Avoid inline script execution for injecting CSRF token [[#7016](https://github.com/encode/django-rest-framework/pull/7016)]
|
||||||
|
* Mitigate global dependency on inflection [[#8017](https://github.com/encode/django-rest-framework/pull/8017)] [[#8781](https://github.com/encode/django-rest-framework/pull/8781)]
|
||||||
|
* Register Django urls [[#8778](https://github.com/encode/django-rest-framework/pull/8778)]
|
||||||
|
* Implemented Verbose Name Translation for TokenProxy [[#8713](https://github.com/encode/django-rest-framework/pull/8713)]
|
||||||
|
* Properly handle OverflowError in DurationField deserialization [[#8042](https://github.com/encode/django-rest-framework/pull/8042)]
|
||||||
|
* Fix OpenAPI operation name plural appropriately [[#8017](https://github.com/encode/django-rest-framework/pull/8017)]
|
||||||
|
* Represent SafeString as plain string on schema rendering [[#8429](https://github.com/encode/django-rest-framework/pull/8429)]
|
||||||
|
* Fix #8771 - Checking for authentication even if `_ignore_model_permissions = True` [[#8772](https://github.com/encode/django-rest-framework/pull/8772)]
|
||||||
|
* Fix 404 when page query parameter is empty string [[#8578](https://github.com/encode/django-rest-framework/pull/8578)]
|
||||||
|
* Fixes instance check in ListSerializer.to_representation [[#8726](https://github.com/encode/django-rest-framework/pull/8726)] [[#8727](https://github.com/encode/django-rest-framework/pull/8727)]
|
||||||
|
* FloatField will crash if the input is a number that is too big [[#8725](https://github.com/encode/django-rest-framework/pull/8725)]
|
||||||
|
* Add missing DurationField to SimpleMetadata label_lookup [[#8702](https://github.com/encode/django-rest-framework/pull/8702)]
|
||||||
|
* Add support for Python 3.11 [[#8752](https://github.com/encode/django-rest-framework/pull/8752)]
|
||||||
|
* Make request consistently available in pagination classes [[#8764](https://github.com/encode/django-rest-framework/pull/9764)]
|
||||||
|
* Possibility to remove trailing zeros on DecimalFields representation [[#6514](https://github.com/encode/django-rest-framework/pull/6514)]
|
||||||
|
* Add a method for getting serializer field name (OpenAPI) [[#7493](https://github.com/encode/django-rest-framework/pull/7493)]
|
||||||
|
* Add `__eq__` method for `OperandHolder` class [[#8710](https://github.com/encode/django-rest-framework/pull/8710)]
|
||||||
|
* Avoid importing `django.test` package when not testing [[#8699](https://github.com/encode/django-rest-framework/pull/8699)]
|
||||||
|
* Preserve exception messages for wrapped Django exceptions [[#8051](https://github.com/encode/django-rest-framework/pull/8051)]
|
||||||
|
* Include `examples` and `format` to OpenAPI schema of CursorPagination [[#8687](https://github.com/encode/django-rest-framework/pull/8687)] [[#8686](https://github.com/encode/django-rest-framework/pull/8686)]
|
||||||
|
* Fix infinite recursion with deepcopy on Request [[#8684](https://github.com/encode/django-rest-framework/pull/8684)]
|
||||||
|
* Refactor: Replace try/except with contextlib.suppress() [[#8676](https://github.com/encode/django-rest-framework/pull/8676)]
|
||||||
|
* Minor fix to SerializeMethodField docstring [[#8629](https://github.com/encode/django-rest-framework/pull/8629)]
|
||||||
|
* Minor refactor: Unnecessary use of list() function [[#8672](https://github.com/encode/django-rest-framework/pull/8672)]
|
||||||
|
* Unnecessary list comprehension [[#8670](https://github.com/encode/django-rest-framework/pull/8670)]
|
||||||
|
* Use correct class to indicate present deprecation [[#8665](https://github.com/encode/django-rest-framework/pull/8665)]
|
||||||
|
|
||||||
## 3.14.x series
|
## 3.14.x series
|
||||||
|
|
||||||
### 3.14.0
|
### 3.14.0
|
||||||
|
@ -47,7 +234,7 @@ Date: 22nd September 2022
|
||||||
* Stop calling `set_context` on Validators. [[#8589](https://github.com/encode/django-rest-framework/pull/8589)]
|
* Stop calling `set_context` on Validators. [[#8589](https://github.com/encode/django-rest-framework/pull/8589)]
|
||||||
* Return `NotImplemented` from `ErrorDetails.__ne__`. [[#8538](https://github.com/encode/django-rest-framework/pull/8538)]
|
* Return `NotImplemented` from `ErrorDetails.__ne__`. [[#8538](https://github.com/encode/django-rest-framework/pull/8538)]
|
||||||
* Don't evaluate `DateTimeField.default_timezone` when a custom timezone is set. [[#8531](https://github.com/encode/django-rest-framework/pull/8531)]
|
* Don't evaluate `DateTimeField.default_timezone` when a custom timezone is set. [[#8531](https://github.com/encode/django-rest-framework/pull/8531)]
|
||||||
* Make relative URLs clickable in Browseable API. [[#8464](https://github.com/encode/django-rest-framework/pull/8464)]
|
* Make relative URLs clickable in Browsable API. [[#8464](https://github.com/encode/django-rest-framework/pull/8464)]
|
||||||
* Support `ManyRelatedField` falling back to the default value when the attribute specified by dot notation doesn't exist. Matches `ManyRelatedField.get_attribute` to `Field.get_attribute`. [[#7574](https://github.com/encode/django-rest-framework/pull/7574)]
|
* Support `ManyRelatedField` falling back to the default value when the attribute specified by dot notation doesn't exist. Matches `ManyRelatedField.get_attribute` to `Field.get_attribute`. [[#7574](https://github.com/encode/django-rest-framework/pull/7574)]
|
||||||
* Make `schemas.openapi.get_reference` public. [[#7515](https://github.com/encode/django-rest-framework/pull/7515)]
|
* Make `schemas.openapi.get_reference` public. [[#7515](https://github.com/encode/django-rest-framework/pull/7515)]
|
||||||
* Make `ReturnDict` support `dict` union operators on Python 3.9 and later. [[#8302](https://github.com/encode/django-rest-framework/pull/8302)]
|
* Make `ReturnDict` support `dict` union operators on Python 3.9 and later. [[#8302](https://github.com/encode/django-rest-framework/pull/8302)]
|
||||||
|
@ -65,7 +252,7 @@ Date: 15th December 2021
|
||||||
|
|
||||||
Date: 13th December 2021
|
Date: 13th December 2021
|
||||||
|
|
||||||
* Django 4.0 compatability. [#8178]
|
* Django 4.0 compatibility. [#8178]
|
||||||
* Add `max_length` and `min_length` options to `ListSerializer`. [#8165]
|
* Add `max_length` and `min_length` options to `ListSerializer`. [#8165]
|
||||||
* Add `get_request_serializer` and `get_response_serializer` hooks to `AutoSchema`. [#7424]
|
* Add `get_request_serializer` and `get_response_serializer` hooks to `AutoSchema`. [#7424]
|
||||||
* Fix OpenAPI representation of null-able read only fields. [#8116]
|
* Fix OpenAPI representation of null-able read only fields. [#8116]
|
||||||
|
@ -306,7 +493,11 @@ Be sure to upgrade to Python 3 before upgrading to Django REST Framework 3.10.
|
||||||
class NullableCharField(serializers.CharField):
|
class NullableCharField(serializers.CharField):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.validators = [v for v in self.validators if not isinstance(v, ProhibitNullCharactersValidator)]
|
self.validators = [
|
||||||
|
v
|
||||||
|
for v in self.validators
|
||||||
|
if not isinstance(v, ProhibitNullCharactersValidator)
|
||||||
|
]
|
||||||
```
|
```
|
||||||
* Add `OpenAPIRenderer` and `generate_schema` management command. [#6229][gh6229]
|
* Add `OpenAPIRenderer` and `generate_schema` management command. [#6229][gh6229]
|
||||||
* Add OpenAPIRenderer by default, and add schema docs. [#6233][gh6233]
|
* Add OpenAPIRenderer by default, and add schema docs. [#6233][gh6233]
|
||||||
|
@ -321,7 +512,7 @@ Be sure to upgrade to Python 3 before upgrading to Django REST Framework 3.10.
|
||||||
* Allow hashing of ErrorDetail. [#5932][gh5932]
|
* Allow hashing of ErrorDetail. [#5932][gh5932]
|
||||||
* Correct schema parsing for JSONField [#5878][gh5878]
|
* Correct schema parsing for JSONField [#5878][gh5878]
|
||||||
* Render descriptions (from help_text) using safe [#5869][gh5869]
|
* Render descriptions (from help_text) using safe [#5869][gh5869]
|
||||||
* Removed input value from deault_error_message [#5881][gh5881]
|
* Removed input value from default_error_message [#5881][gh5881]
|
||||||
* Added min_value/max_value support in DurationField [#5643][gh5643]
|
* Added min_value/max_value support in DurationField [#5643][gh5643]
|
||||||
* Fixed instance being overwritten in pk-only optimization try/except block [#5747][gh5747]
|
* Fixed instance being overwritten in pk-only optimization try/except block [#5747][gh5747]
|
||||||
* Fixed AttributeError from items filter when value is None [#5981][gh5981]
|
* Fixed AttributeError from items filter when value is None [#5981][gh5981]
|
||||||
|
@ -942,7 +1133,7 @@ See the [release announcement][3.6-release].
|
||||||
* description.py codes and tests removal. ([#4153][gh4153])
|
* description.py codes and tests removal. ([#4153][gh4153])
|
||||||
* Wrap guardian.VERSION in tuple. ([#4149][gh4149])
|
* Wrap guardian.VERSION in tuple. ([#4149][gh4149])
|
||||||
* Refine validator for fields with <source=> kwargs. ([#4146][gh4146])
|
* Refine validator for fields with <source=> kwargs. ([#4146][gh4146])
|
||||||
* Fix None values representation in childs of ListField, DictField. ([#4118][gh4118])
|
* Fix None values representation in children of ListField, DictField. ([#4118][gh4118])
|
||||||
* Resolve TimeField representation for midnight value. ([#4107][gh4107])
|
* Resolve TimeField representation for midnight value. ([#4107][gh4107])
|
||||||
* Set proper status code in AdminRenderer for the redirection after POST/DELETE requests. ([#4106][gh4106])
|
* Set proper status code in AdminRenderer for the redirection after POST/DELETE requests. ([#4106][gh4106])
|
||||||
* TimeField render returns None instead of 00:00:00. ([#4105][gh4105])
|
* TimeField render returns None instead of 00:00:00. ([#4105][gh4105])
|
||||||
|
@ -950,7 +1141,7 @@ See the [release announcement][3.6-release].
|
||||||
* Prevent raising exception when limit is 0. ([#4098][gh4098])
|
* Prevent raising exception when limit is 0. ([#4098][gh4098])
|
||||||
* TokenAuthentication: Allow custom keyword in the header. ([#4097][gh4097])
|
* TokenAuthentication: Allow custom keyword in the header. ([#4097][gh4097])
|
||||||
* Handle incorrectly padded HTTP basic auth header. ([#4090][gh4090])
|
* Handle incorrectly padded HTTP basic auth header. ([#4090][gh4090])
|
||||||
* LimitOffset pagination crashes Browseable API when limit=0. ([#4079][gh4079])
|
* LimitOffset pagination crashes Browsable API when limit=0. ([#4079][gh4079])
|
||||||
* Fixed DecimalField arbitrary precision support. ([#4075][gh4075])
|
* Fixed DecimalField arbitrary precision support. ([#4075][gh4075])
|
||||||
* Added support for custom CSRF cookie names. ([#4049][gh4049])
|
* Added support for custom CSRF cookie names. ([#4049][gh4049])
|
||||||
* Fix regression introduced by #4035. ([#4041][gh4041])
|
* Fix regression introduced by #4035. ([#4041][gh4041])
|
||||||
|
|
|
@ -32,7 +32,7 @@ We suggest adding your package to the [REST Framework][rest-framework-grid] grid
|
||||||
|
|
||||||
#### Adding to the Django REST framework docs
|
#### Adding to the Django REST framework docs
|
||||||
|
|
||||||
Create a [Pull Request][drf-create-pr] or [Issue][drf-create-issue] on GitHub, and we'll add a link to it from the main REST framework documentation. You can add your package under **Third party packages** of the API Guide section that best applies, like [Authentication][authentication] or [Permissions][permissions]. You can also link your package under the [Third Party Packages][third-party-packages] section.
|
Create a [Pull Request][drf-create-pr] on GitHub, and we'll add a link to it from the main REST framework documentation. You can add your package under **Third party packages** of the API Guide section that best applies, like [Authentication][authentication] or [Permissions][permissions]. You can also link your package under the [Third Party Packages][third-party-packages] section.
|
||||||
|
|
||||||
#### Announce on the discussion group.
|
#### Announce on the discussion group.
|
||||||
|
|
||||||
|
@ -44,7 +44,11 @@ Django REST Framework has a growing community of developers, packages, and resou
|
||||||
|
|
||||||
Check out a grid detailing all the packages and ecosystem around Django REST Framework at [Django Packages][rest-framework-grid].
|
Check out a grid detailing all the packages and ecosystem around Django REST Framework at [Django Packages][rest-framework-grid].
|
||||||
|
|
||||||
To submit new content, [open an issue][drf-create-issue] or [create a pull request][drf-create-pr].
|
To submit new content, [create a pull request][drf-create-pr].
|
||||||
|
|
||||||
|
## Async Support
|
||||||
|
|
||||||
|
* [adrf](https://github.com/em1208/adrf) - Async support, provides async Views, ViewSets, and Serializers.
|
||||||
|
|
||||||
### Authentication
|
### Authentication
|
||||||
|
|
||||||
|
@ -58,6 +62,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
|
||||||
* [drf-oidc-auth][drf-oidc-auth] - Implements OpenID Connect token authentication for DRF.
|
* [drf-oidc-auth][drf-oidc-auth] - Implements OpenID Connect token authentication for DRF.
|
||||||
* [drfpasswordless][drfpasswordless] - Adds (Medium, Square Cash inspired) passwordless logins and signups via email and mobile numbers.
|
* [drfpasswordless][drfpasswordless] - Adds (Medium, Square Cash inspired) passwordless logins and signups via email and mobile numbers.
|
||||||
* [django-rest-authemail][django-rest-authemail] - Provides a RESTful API for user signup and authentication using email addresses.
|
* [django-rest-authemail][django-rest-authemail] - Provides a RESTful API for user signup and authentication using email addresses.
|
||||||
|
* [dango-pyoidc][django-pyoidc] adds support for OpenID Connect (OIDC) authentication.
|
||||||
|
|
||||||
### Permissions
|
### Permissions
|
||||||
|
|
||||||
|
@ -125,6 +130,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
|
||||||
|
|
||||||
### Misc
|
### Misc
|
||||||
|
|
||||||
|
* [drf-sendables][drf-sendables] - User messages for Django REST Framework
|
||||||
* [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.
|
* [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 serializer that can can be used to alter relationships via hyperlinks, but otherwise like a hyperlink model serializer.
|
* [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.
|
* [django-rest-framework-proxy][django-rest-framework-proxy] - Proxy to redirect incoming request to another API server.
|
||||||
|
@ -152,6 +158,13 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
|
||||||
* [drf-standardized-errors][drf-standardized-errors] - DRF exception handler to standardize error responses for all API endpoints.
|
* [drf-standardized-errors][drf-standardized-errors] - DRF exception handler to standardize error responses for all API endpoints.
|
||||||
* [drf-api-action][drf-api-action] - uses the power of DRF also as a library functions
|
* [drf-api-action][drf-api-action] - uses the power of DRF also as a library functions
|
||||||
|
|
||||||
|
### Customization
|
||||||
|
|
||||||
|
* [drf-restwind][drf-restwind] - a modern re-imagining of the Django REST Framework utilizes TailwindCSS and DaisyUI to provide flexible and customizable UI solutions with minimal coding effort.
|
||||||
|
* [drf-redesign][drf-redesign] - A project that gives a fresh look to the browse-able API using Bootstrap 5.
|
||||||
|
* [drf-material][drf-material] - A project that gives a sleek and elegant look to the browsable API using Material Design.
|
||||||
|
|
||||||
|
[drf-sendables]: https://github.com/amikrop/drf-sendables
|
||||||
[cite]: http://www.software-ecosystems.com/Software_Ecosystems/Ecosystems.html
|
[cite]: http://www.software-ecosystems.com/Software_Ecosystems/Ecosystems.html
|
||||||
[cookiecutter]: https://github.com/jpadilla/cookiecutter-django-rest-framework
|
[cookiecutter]: https://github.com/jpadilla/cookiecutter-django-rest-framework
|
||||||
[new-repo]: https://github.com/new
|
[new-repo]: https://github.com/new
|
||||||
|
@ -162,7 +175,6 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
|
||||||
[drf-compat]: https://github.com/encode/django-rest-framework/blob/master/rest_framework/compat.py
|
[drf-compat]: https://github.com/encode/django-rest-framework/blob/master/rest_framework/compat.py
|
||||||
[rest-framework-grid]: https://www.djangopackages.com/grids/g/django-rest-framework/
|
[rest-framework-grid]: https://www.djangopackages.com/grids/g/django-rest-framework/
|
||||||
[drf-create-pr]: https://github.com/encode/django-rest-framework/compare
|
[drf-create-pr]: https://github.com/encode/django-rest-framework/compare
|
||||||
[drf-create-issue]: https://github.com/encode/django-rest-framework/issues/new
|
|
||||||
[authentication]: ../api-guide/authentication.md
|
[authentication]: ../api-guide/authentication.md
|
||||||
[permissions]: ../api-guide/permissions.md
|
[permissions]: ../api-guide/permissions.md
|
||||||
[third-party-packages]: ../topics/third-party-packages/#existing-third-party-packages
|
[third-party-packages]: ../topics/third-party-packages/#existing-third-party-packages
|
||||||
|
@ -243,3 +255,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
|
||||||
[django-requestlogs]: https://github.com/Raekkeri/django-requestlogs
|
[django-requestlogs]: https://github.com/Raekkeri/django-requestlogs
|
||||||
[drf-standardized-errors]: https://github.com/ghazi-git/drf-standardized-errors
|
[drf-standardized-errors]: https://github.com/ghazi-git/drf-standardized-errors
|
||||||
[drf-api-action]: https://github.com/Ori-Roza/drf-api-action
|
[drf-api-action]: https://github.com/Ori-Roza/drf-api-action
|
||||||
|
[drf-restwind]: https://github.com/youzarsiph/drf-restwind
|
||||||
|
[drf-redesign]: https://github.com/youzarsiph/drf-redesign
|
||||||
|
[drf-material]: https://github.com/youzarsiph/drf-material
|
||||||
|
[django-pyoidc]: https://github.com/makinacorpus/django_pyoidc
|
||||||
|
|
|
@ -12,19 +12,22 @@ There are a wide range of resources available for learning and using Django REST
|
||||||
<img src="../../img/books/tsd-cover.png"/>
|
<img src="../../img/books/tsd-cover.png"/>
|
||||||
</a>
|
</a>
|
||||||
<a class="book-cover" href="https://djangoforapis.com">
|
<a class="book-cover" href="https://djangoforapis.com">
|
||||||
<img src="../../img/books/dfa-cover.jpg"/>
|
<img src="../../img/books/dfa-40-cover.jpg"/>
|
||||||
</a>
|
</a>
|
||||||
<a class="book-cover" href="https://books.agiliq.com/projects/django-api-polls-tutorial/en/latest/">
|
<a class="book-cover" href="https://books.agiliq.com/projects/django-api-polls-tutorial/en/latest/">
|
||||||
<img src="../../img/books/bda-cover.png"/>
|
<img src="../../img/books/bda-cover.png"/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
## Courses
|
||||||
|
|
||||||
|
* [Developing RESTful APIs with Django REST Framework][developing-restful-apis-with-django-rest-framework]
|
||||||
|
|
||||||
## Tutorials
|
## Tutorials
|
||||||
|
|
||||||
* [Beginner's Guide to the Django REST Framework][beginners-guide-to-the-django-rest-framework]
|
* [Beginner's Guide to the Django REST Framework][beginners-guide-to-the-django-rest-framework]
|
||||||
* [Django REST Framework - An Introduction][drf-an-intro]
|
* [Django REST Framework - An Introduction][drf-an-intro]
|
||||||
* [Django REST Framework Tutorial][drf-tutorial]
|
* [Django REST Framework Tutorial][drf-tutorial]
|
||||||
* [Django REST Framework Course][django-rest-framework-course]
|
|
||||||
* [Building a RESTful API with Django REST Framework][building-a-restful-api-with-drf]
|
* [Building a RESTful API with Django REST Framework][building-a-restful-api-with-drf]
|
||||||
* [Getting Started with Django REST Framework and AngularJS][getting-started-with-django-rest-framework-and-angularjs]
|
* [Getting Started with Django REST Framework and AngularJS][getting-started-with-django-rest-framework-and-angularjs]
|
||||||
* [End to End Web App with Django REST Framework & AngularJS][end-to-end-web-app-with-django-rest-framework-angularjs]
|
* [End to End Web App with Django REST Framework & AngularJS][end-to-end-web-app-with-django-rest-framework-angularjs]
|
||||||
|
@ -35,8 +38,10 @@ There are a wide range of resources available for learning and using Django REST
|
||||||
* [Check Credentials Using Django REST Framework][check-credentials-using-django-rest-framework]
|
* [Check Credentials Using Django REST Framework][check-credentials-using-django-rest-framework]
|
||||||
* [Creating a Production Ready API with Python and Django REST Framework – Part 1][creating-a-production-ready-api-with-python-and-drf-part1]
|
* [Creating a Production Ready API with Python and Django REST Framework – Part 1][creating-a-production-ready-api-with-python-and-drf-part1]
|
||||||
* [Creating a Production Ready API with Python and Django REST Framework – Part 2][creating-a-production-ready-api-with-python-and-drf-part2]
|
* [Creating a Production Ready API with Python and Django REST Framework – Part 2][creating-a-production-ready-api-with-python-and-drf-part2]
|
||||||
* [Django REST Framework Tutorial - Build a Blog API][django-rest-framework-tutorial-build-a-blog]
|
* [Creating a Production Ready API with Python and Django REST Framework – Part 3][creating-a-production-ready-api-with-python-and-drf-part3]
|
||||||
* [Django REST Framework & React Tutorial - Build a Todo List API][django-rest-framework-react-tutorial-build-a-todo-list]
|
* [Creating a Production Ready API with Python and Django REST Framework – Part 4][creating-a-production-ready-api-with-python-and-drf-part4]
|
||||||
|
* [Django Polls Tutorial API][django-polls-api]
|
||||||
|
* [Django REST Framework Tutorial: Todo API][django-rest-framework-todo-api]
|
||||||
* [Tutorial: Django REST with React (Django 2.0)][django-rest-react-valentinog]
|
* [Tutorial: Django REST with React (Django 2.0)][django-rest-react-valentinog]
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,11 +50,11 @@ There are a wide range of resources available for learning and using Django REST
|
||||||
### Talks
|
### Talks
|
||||||
|
|
||||||
* [Level Up! Rethinking the Web API Framework][pycon-us-2017]
|
* [Level Up! Rethinking the Web API Framework][pycon-us-2017]
|
||||||
* [How to Make a Full Fledged REST API with Django OAuth Toolkit][full-fledged-rest-api-with-django-oauth-tookit]
|
* [How to Make a Full Fledged REST API with Django OAuth Toolkit][full-fledged-rest-api-with-django-oauth-toolkit]
|
||||||
* [Django REST API - So Easy You Can Learn It in 25 Minutes][django-rest-api-so-easy]
|
* [Django REST API - So Easy You Can Learn It in 25 Minutes][django-rest-api-so-easy]
|
||||||
* [Tom Christie about Django Rest Framework at Django: Under The Hood][django-under-hood-2014]
|
* [Tom Christie about Django Rest Framework at Django: Under The Hood][django-under-hood-2014]
|
||||||
* [Django REST Framework: Schemas, Hypermedia & Client Libraries][pycon-uk-2016]
|
* [Django REST Framework: Schemas, Hypermedia & Client Libraries][pycon-uk-2016]
|
||||||
|
* [Finally Understand Authentication in Django REST Framework][django-con-2018]
|
||||||
|
|
||||||
### Tutorials
|
### Tutorials
|
||||||
|
|
||||||
|
@ -99,7 +104,6 @@ Want your Django REST Framework talk/tutorial/article to be added to our website
|
||||||
[api-development-with-django-and-django-rest-framework]: https://bnotions.com/news-and-insights/api-development-with-django-and-django-rest-framework/
|
[api-development-with-django-and-django-rest-framework]: https://bnotions.com/news-and-insights/api-development-with-django-and-django-rest-framework/
|
||||||
[cdrf.co]:http://www.cdrf.co
|
[cdrf.co]:http://www.cdrf.co
|
||||||
[medium-django-rest-framework]: https://medium.com/django-rest-framework
|
[medium-django-rest-framework]: https://medium.com/django-rest-framework
|
||||||
[django-rest-framework-course]: https://teamtreehouse.com/library/django-rest-framework
|
|
||||||
[pycon-uk-2016]: https://www.youtube.com/watch?v=FjmiGh7OqVg
|
[pycon-uk-2016]: https://www.youtube.com/watch?v=FjmiGh7OqVg
|
||||||
[django-under-hood-2014]: https://www.youtube.com/watch?v=3cSsbe-tA0E
|
[django-under-hood-2014]: https://www.youtube.com/watch?v=3cSsbe-tA0E
|
||||||
[integrating-pandas-drf-and-bokeh]: https://web.archive.org/web/20180104205117/http://machinalis.com/blog/pandas-django-rest-framework-bokeh/
|
[integrating-pandas-drf-and-bokeh]: https://web.archive.org/web/20180104205117/http://machinalis.com/blog/pandas-django-rest-framework-bokeh/
|
||||||
|
@ -111,12 +115,14 @@ Want your Django REST Framework talk/tutorial/article to be added to our website
|
||||||
[chatbot-using-drf-part1]: https://chatbotslife.com/chatbot-using-django-rest-framework-api-ai-slack-part-1-3-69c7e38b7b1e#.g2aceuncf
|
[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/
|
[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/
|
[drf-schema]: https://drf-schema-adapter.readthedocs.io/en/latest/
|
||||||
[creating-a-production-ready-api-with-python-and-drf-part1]: https://www.andreagrandi.it/2016/09/28/creating-production-ready-api-python-django-rest-framework-part-1/
|
[creating-a-production-ready-api-with-python-and-drf-part1]: https://www.andreagrandi.it/posts/creating-production-ready-api-python-django-rest-framework-part-1/
|
||||||
[creating-a-production-ready-api-with-python-and-drf-part2]: https://www.andreagrandi.it/2016/10/01/creating-a-production-ready-api-with-python-and-django-rest-framework-part-2/
|
[creating-a-production-ready-api-with-python-and-drf-part2]: https://www.andreagrandi.it/posts/creating-a-production-ready-api-with-python-and-django-rest-framework-part-2/
|
||||||
[django-rest-framework-tutorial-build-a-blog]: https://wsvincent.com/django-rest-framework-tutorial/
|
[creating-a-production-ready-api-with-python-and-drf-part3]: https://www.andreagrandi.it/posts/creating-a-production-ready-api-with-python-and-django-rest-framework-part-3/
|
||||||
[django-rest-framework-react-tutorial-build-a-todo-list]: https://wsvincent.com/django-rest-framework-react-tutorial/
|
[creating-a-production-ready-api-with-python-and-drf-part4]: https://www.andreagrandi.it/posts/creating-a-production-ready-api-with-python-and-django-rest-framework-part-4/
|
||||||
|
[django-polls-api]: https://learndjango.com/tutorials/django-polls-tutorial-api
|
||||||
|
[django-rest-framework-todo-api]: https://learndjango.com/tutorials/django-rest-framework-tutorial-todo-api
|
||||||
[django-rest-api-so-easy]: https://www.youtube.com/watch?v=cqP758k1BaQ
|
[django-rest-api-so-easy]: https://www.youtube.com/watch?v=cqP758k1BaQ
|
||||||
[full-fledged-rest-api-with-django-oauth-tookit]: https://www.youtube.com/watch?v=M6Ud3qC2tTk
|
[full-fledged-rest-api-with-django-oauth-toolkit]: https://www.youtube.com/watch?v=M6Ud3qC2tTk
|
||||||
[drf-in-your-pjs]: https://www.youtube.com/watch?v=xMtHsWa72Ww
|
[drf-in-your-pjs]: https://www.youtube.com/watch?v=xMtHsWa72Ww
|
||||||
[building-a-rest-api-using-django-and-drf]: https://www.youtube.com/watch?v=PwssEec3IRw
|
[building-a-rest-api-using-django-and-drf]: https://www.youtube.com/watch?v=PwssEec3IRw
|
||||||
[drf-tutorials]: https://www.youtube.com/watch?v=axRCBgbOJp8&list=PLJtp8Jm8EDzjgVg9vVyIUMoGyqtegj7FH
|
[drf-tutorials]: https://www.youtube.com/watch?v=axRCBgbOJp8&list=PLJtp8Jm8EDzjgVg9vVyIUMoGyqtegj7FH
|
||||||
|
@ -130,3 +136,5 @@ Want your Django REST Framework talk/tutorial/article to be added to our website
|
||||||
[pycon-us-2017]: https://www.youtube.com/watch?v=Rk6MHZdust4
|
[pycon-us-2017]: https://www.youtube.com/watch?v=Rk6MHZdust4
|
||||||
[django-rest-react-valentinog]: https://www.valentinog.com/blog/tutorial-api-django-rest-react/
|
[django-rest-react-valentinog]: https://www.valentinog.com/blog/tutorial-api-django-rest-react/
|
||||||
[doordash-implementing-rest-apis]: https://doordash.engineering/2013/10/07/implementing-rest-apis-with-embedded-privacy/
|
[doordash-implementing-rest-apis]: https://doordash.engineering/2013/10/07/implementing-rest-apis-with-embedded-privacy/
|
||||||
|
[developing-restful-apis-with-django-rest-framework]: https://testdriven.io/courses/django-rest-framework/
|
||||||
|
[django-con-2018]: https://youtu.be/pY-oje5b5Qk?si=AOU6tLi0IL1_pVzq
|
BIN
docs/img/books/dfa-40-cover.jpg
Normal file
After Width: | Height: | Size: 755 KiB |
BIN
docs/img/drf-m-api-root.png
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
docs/img/drf-m-detail-view.png
Normal file
After Width: | Height: | Size: 121 KiB |
BIN
docs/img/drf-m-list-view.png
Normal file
After Width: | Height: | Size: 123 KiB |
BIN
docs/img/drf-r-api-root.png
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
docs/img/drf-r-detail-view.png
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
docs/img/drf-r-list-view.png
Normal file
After Width: | Height: | Size: 127 KiB |
BIN
docs/img/drf-rw-api-root.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
docs/img/drf-rw-detail-view.png
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
docs/img/drf-rw-list-view.png
Normal file
After Width: | Height: | Size: 100 KiB |
BIN
docs/img/premium/svix-premium.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
docs/img/premium/zuplo-readme.png
Normal file
After Width: | Height: | Size: 12 KiB |
|
@ -48,7 +48,7 @@ Django REST framework is a powerful and flexible toolkit for building Web APIs.
|
||||||
|
|
||||||
Some reasons you might want to use REST framework:
|
Some reasons you might want to use REST framework:
|
||||||
|
|
||||||
* The [Web browsable API][sandbox] is a huge usability win for your developers.
|
* The Web browsable API is a huge usability win for your developers.
|
||||||
* [Authentication policies][authentication] including packages for [OAuth1a][oauth1-section] and [OAuth2][oauth2-section].
|
* [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.
|
* [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].
|
* 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].
|
||||||
|
@ -74,10 +74,12 @@ continued development by **[signing up for a paid plan][funding]**.
|
||||||
<li><a href="https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/135996800-d49fe024-32d9-441a-98d9-4c7596287a67.png)">PostHog</a></li>
|
<li><a href="https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/135996800-d49fe024-32d9-441a-98d9-4c7596287a67.png)">PostHog</a></li>
|
||||||
<li><a href="https://cryptapi.io" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/cryptapi.png)">CryptAPI</a></li>
|
<li><a href="https://cryptapi.io" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/cryptapi.png)">CryptAPI</a></li>
|
||||||
<li><a href="https://www.fezto.xyz/?utm_source=DjangoRESTFramework" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/fezto.png)">FEZTO</a></li>
|
<li><a href="https://www.fezto.xyz/?utm_source=DjangoRESTFramework" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/fezto.png)">FEZTO</a></li>
|
||||||
|
<li><a href="https://www.svix.com/?utm_source=django-REST&utm_medium=sponsorship" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/svix.png)">Svix</a></li>
|
||||||
|
<li><a href="https://zuplo.link/django-web" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/zuplo.png)">Zuplo</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div style="clear: both; padding-bottom: 20px;"></div>
|
<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=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage), [Spacinov](https://www.spacinov.com/), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [PostHog](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [CryptAPI](https://cryptapi.io), and [FEZTO](https://www.fezto.xyz/?utm_source=DjangoRESTFramework).*
|
*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=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage), [Spacinov](https://www.spacinov.com/), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [PostHog](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [CryptAPI](https://cryptapi.io), [FEZTO](https://www.fezto.xyz/?utm_source=DjangoRESTFramework), [Svix](https://www.svix.com/?utm_source=django-REST&utm_medium=sponsorship), , and [Zuplo](https://zuplo.link/django-web).*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -85,8 +87,8 @@ continued development by **[signing up for a paid plan][funding]**.
|
||||||
|
|
||||||
REST framework requires the following:
|
REST framework requires the following:
|
||||||
|
|
||||||
* Python (3.6, 3.7, 3.8, 3.9, 3.10, 3.11)
|
* Django (4.2, 5.0, 5.1, 5.2)
|
||||||
* Django (3.0, 3.1, 3.2, 4.0, 4.1)
|
* Python (3.9, 3.10, 3.11, 3.12, 3.13)
|
||||||
|
|
||||||
We **highly recommend** and only officially support the latest patch release of
|
We **highly recommend** and only officially support the latest patch release of
|
||||||
each Python and Django series.
|
each Python and Django series.
|
||||||
|
@ -94,8 +96,8 @@ each Python and Django series.
|
||||||
The following packages are optional:
|
The following packages are optional:
|
||||||
|
|
||||||
* [PyYAML][pyyaml], [uritemplate][uriteemplate] (5.1+, 3.0.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.
|
* [Markdown][markdown] (3.3.0+) - Markdown support for the browsable API.
|
||||||
* [Pygments][pygments] (2.4.0+) - Add syntax highlighting to Markdown processing.
|
* [Pygments][pygments] (2.7.0+) - Add syntax highlighting to Markdown processing.
|
||||||
* [django-filter][django-filter] (1.0.1+) - Filtering support.
|
* [django-filter][django-filter] (1.0.1+) - Filtering support.
|
||||||
* [django-guardian][django-guardian] (1.1.1+) - Object level permissions support.
|
* [django-guardian][django-guardian] (1.1.1+) - Object level permissions support.
|
||||||
|
|
||||||
|
@ -183,7 +185,7 @@ Can't wait to get started? The [quickstart guide][quickstart] is the fastest way
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
See the [Contribution guidelines][contributing] for information on how to clone
|
See the [Contribution guidelines][contributing] for information on how to clone
|
||||||
the repository, run the test suite and contribute changes back to REST
|
the repository, run the test suite and help maintain the code base of REST
|
||||||
Framework.
|
Framework.
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
@ -194,9 +196,7 @@ For priority support please sign up for a [professional or premium sponsorship p
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
Security issues are handled under the supervision of the [Django security team](https://www.djangoproject.com/foundation/teams/#security-team).
|
**Please report security issues by emailing security@encode.io**.
|
||||||
|
|
||||||
**Please report security issues by emailing security@djangoproject.com**.
|
|
||||||
|
|
||||||
The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure.
|
The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure.
|
||||||
|
|
||||||
|
@ -246,7 +246,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[serializer-section]: api-guide/serializers#serializers
|
[serializer-section]: api-guide/serializers#serializers
|
||||||
[modelserializer-section]: api-guide/serializers#modelserializer
|
[modelserializer-section]: api-guide/serializers#modelserializer
|
||||||
[functionview-section]: api-guide/views#function-based-views
|
[functionview-section]: api-guide/views#function-based-views
|
||||||
[sandbox]: https://restframework.herokuapp.com/
|
|
||||||
[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors
|
[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors
|
||||||
|
|
||||||
[quickstart]: tutorial/quickstart.md
|
[quickstart]: tutorial/quickstart.md
|
||||||
|
|
|
@ -35,7 +35,7 @@ The best way to deal with CORS in REST framework is to add the required response
|
||||||
|
|
||||||
[cite]: https://blog.codinghorror.com/preventing-csrf-and-xsrf-attacks/
|
[cite]: https://blog.codinghorror.com/preventing-csrf-and-xsrf-attacks/
|
||||||
[csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
|
[csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
|
||||||
[csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax
|
[csrf-ajax]: https://docs.djangoproject.com/en/stable/howto/csrf/#using-csrf-protection-with-ajax
|
||||||
[cors]: https://www.w3.org/TR/cors/
|
[cors]: https://www.w3.org/TR/cors/
|
||||||
[adamchainz]: https://github.com/adamchainz
|
[adamchainz]: https://github.com/adamchainz
|
||||||
[django-cors-headers]: https://github.com/adamchainz/django-cors-headers
|
[django-cors-headers]: https://github.com/adamchainz/django-cors-headers
|
||||||
|
|
|
@ -15,6 +15,20 @@ If you include fully-qualified URLs in your resource output, they will be 'urliz
|
||||||
|
|
||||||
By default, the API will return the format specified by the headers, which in the case of the browser is HTML. The format can be specified using `?format=` in the request, so you can look at the raw JSON response in a browser by adding `?format=json` to the URL. There are helpful extensions for viewing JSON in [Firefox][ffjsonview] and [Chrome][chromejsonview].
|
By default, the API will return the format specified by the headers, which in the case of the browser is HTML. The format can be specified using `?format=` in the request, so you can look at the raw JSON response in a browser by adding `?format=json` to the URL. There are helpful extensions for viewing JSON in [Firefox][ffjsonview] and [Chrome][chromejsonview].
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
To quickly add authentication to the browesable api, add a routes named `"login"` and `"logout"` under the namespace `"rest_framework"`. DRF provides default routes for this which you can add to your urlconf:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from django.urls import include, path
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
# ...
|
||||||
|
path("api-auth/", include("rest_framework.urls", namespace="rest_framework"))
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Customizing
|
## Customizing
|
||||||
|
|
||||||
The browsable API is built with [Twitter's Bootstrap][bootstrap] (v 3.4.1), making it easy to customize the look-and-feel.
|
The browsable API is built with [Twitter's Bootstrap][bootstrap] (v 3.4.1), making it easy to customize the look-and-feel.
|
||||||
|
@ -65,6 +79,48 @@ For more specific CSS tweaks than simply overriding the default bootstrap theme
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Third party packages for customization
|
||||||
|
|
||||||
|
You can use a third party package for customization, rather than doing it by yourself. Here is 3 packages for customizing the API:
|
||||||
|
|
||||||
|
* [drf-restwind][drf-restwind] - a modern re-imagining of the Django REST Framework utilizes TailwindCSS and DaisyUI to provide flexible and customizable UI solutions with minimal coding effort.
|
||||||
|
* [drf-redesign][drf-redesign] - A package for customizing the API using Bootstrap 5. Modern and sleek design, it comes with the support for dark mode.
|
||||||
|
* [drf-material][drf-material] - Material design for Django REST Framework.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
![API Root][drf-rw-api-root]
|
||||||
|
|
||||||
|
![List View][drf-rw-list-view]
|
||||||
|
|
||||||
|
![Detail View][drf-rw-detail-view]
|
||||||
|
|
||||||
|
*Screenshots of the drf-restwind*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
![API Root][drf-r-api-root]
|
||||||
|
|
||||||
|
![List View][drf-r-list-view]
|
||||||
|
|
||||||
|
![Detail View][drf-r-detail-view]
|
||||||
|
|
||||||
|
*Screenshot of the drf-redesign*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
![API Root][drf-m-api-root]
|
||||||
|
|
||||||
|
![List View][drf-m-api-root]
|
||||||
|
|
||||||
|
![Detail View][drf-m-api-root]
|
||||||
|
|
||||||
|
*Screenshot of the drf-material*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Blocks
|
### Blocks
|
||||||
|
|
||||||
All of the blocks available in the browsable API base template that can be used in your `api.html`.
|
All of the blocks available in the browsable API base template that can be used in your `api.html`.
|
||||||
|
@ -162,3 +218,15 @@ There are [a variety of packages for autocomplete widgets][autocomplete-packages
|
||||||
[bcomponentsnav]: https://getbootstrap.com/2.3.2/components.html#navbar
|
[bcomponentsnav]: https://getbootstrap.com/2.3.2/components.html#navbar
|
||||||
[autocomplete-packages]: https://www.djangopackages.com/grids/g/auto-complete/
|
[autocomplete-packages]: https://www.djangopackages.com/grids/g/auto-complete/
|
||||||
[django-autocomplete-light]: https://github.com/yourlabs/django-autocomplete-light
|
[django-autocomplete-light]: https://github.com/yourlabs/django-autocomplete-light
|
||||||
|
[drf-restwind]: https://github.com/youzarsiph/drf-restwind
|
||||||
|
[drf-rw-api-root]: ../img/drf-rw-api-root.png
|
||||||
|
[drf-rw-list-view]: ../img/drf-rw-list-view.png
|
||||||
|
[drf-rw-detail-view]: ../img/drf-rw-detail-view.png
|
||||||
|
[drf-redesign]: https://github.com/youzarsiph/drf-redesign
|
||||||
|
[drf-r-api-root]: ../img/drf-r-api-root.png
|
||||||
|
[drf-r-list-view]: ../img/drf-r-list-view.png
|
||||||
|
[drf-r-detail-view]: ../img/drf-r-detail-view.png
|
||||||
|
[drf-material]: https://github.com/youzarsiph/drf-material
|
||||||
|
[drf-m-api-root]: ../img/drf-m-api-root.png
|
||||||
|
[drf-m-list-view]: ../img/drf-m-list-view.png
|
||||||
|
[drf-m-detail-view]: ../img/drf-m-detail-view.png
|
||||||
|
|
|
@ -96,10 +96,14 @@ urlpatterns = [
|
||||||
# ...
|
# ...
|
||||||
# Route TemplateView to serve Swagger UI template.
|
# Route TemplateView to serve Swagger UI template.
|
||||||
# * Provide `extra_context` with view name of `SchemaView`.
|
# * Provide `extra_context` with view name of `SchemaView`.
|
||||||
path('swagger-ui/', TemplateView.as_view(
|
path(
|
||||||
template_name='swagger-ui.html',
|
"swagger-ui/",
|
||||||
extra_context={'schema_url':'openapi-schema'}
|
TemplateView.as_view(
|
||||||
), name='swagger-ui'),
|
template_name="swagger-ui.html",
|
||||||
|
extra_context={"schema_url": "openapi-schema"},
|
||||||
|
),
|
||||||
|
name="swagger-ui",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -145,10 +149,13 @@ urlpatterns = [
|
||||||
# ...
|
# ...
|
||||||
# Route TemplateView to serve the ReDoc template.
|
# Route TemplateView to serve the ReDoc template.
|
||||||
# * Provide `extra_context` with view name of `SchemaView`.
|
# * Provide `extra_context` with view name of `SchemaView`.
|
||||||
path('redoc/', TemplateView.as_view(
|
path(
|
||||||
template_name='redoc.html',
|
"redoc/",
|
||||||
extra_context={'schema_url':'openapi-schema'}
|
TemplateView.as_view(
|
||||||
), name='redoc'),
|
template_name="redoc.html", extra_context={"schema_url": "openapi-schema"}
|
||||||
|
),
|
||||||
|
name="redoc",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ For API clients the most appropriate of these will typically be to use the `Acce
|
||||||
[cite]: https://youtu.be/Wa0VfS2q94Y
|
[cite]: https://youtu.be/Wa0VfS2q94Y
|
||||||
[django-translation]: https://docs.djangoproject.com/en/stable/topics/i18n/translation
|
[django-translation]: https://docs.djangoproject.com/en/stable/topics/i18n/translation
|
||||||
[custom-exception-handler]: ../api-guide/exceptions.md#custom-exception-handling
|
[custom-exception-handler]: ../api-guide/exceptions.md#custom-exception-handling
|
||||||
[transifex-project]: https://www.transifex.com/projects/p/django-rest-framework/
|
[transifex-project]: https://explore.transifex.com/django-rest-framework-1/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-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/stable/topics/i18n/translation/#how-django-discovers-language-preference
|
[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-paths]: https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-LOCALE_PATHS
|
||||||
|
|
|
@ -8,7 +8,7 @@ The tutorial is fairly in-depth, so you should probably get a cookie and a cup o
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Note**: The code for this tutorial is available in the [encode/rest-framework-tutorial][repo] repository on GitHub. The completed implementation is also online as a sandbox version for testing, [available here][sandbox].
|
**Note**: The code for this tutorial is available in the [encode/rest-framework-tutorial][repo] repository on GitHub. Feel free to clone the repository and see the code in action.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ Deserialization is similar. First we parse a stream into Python native datatype
|
||||||
serializer.is_valid()
|
serializer.is_valid()
|
||||||
# True
|
# True
|
||||||
serializer.validated_data
|
serializer.validated_data
|
||||||
# OrderedDict([('title', ''), ('code', 'print("hello, world")\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
|
# {'title': '', 'code': 'print("hello, world")', 'linenos': False, 'language': 'python', 'style': 'friendly'}
|
||||||
serializer.save()
|
serializer.save()
|
||||||
# <Snippet: Snippet object>
|
# <Snippet: Snippet object>
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ We can also serialize querysets instead of model instances. To do so we simply
|
||||||
|
|
||||||
serializer = SnippetSerializer(Snippet.objects.all(), many=True)
|
serializer = SnippetSerializer(Snippet.objects.all(), many=True)
|
||||||
serializer.data
|
serializer.data
|
||||||
# [OrderedDict([('id', 1), ('title', ''), ('code', 'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print("hello, world")\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', ''), ('code', 'print("hello, world")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]
|
# [{'id': 1, 'title': '', 'code': 'foo = "bar"\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}, {'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}, {'id': 3, 'title': '', 'code': 'print("hello, world")', 'linenos': False, 'language': 'python', 'style': 'friendly'}]
|
||||||
|
|
||||||
## Using ModelSerializers
|
## Using ModelSerializers
|
||||||
|
|
||||||
|
@ -307,7 +307,7 @@ Quit out of the shell...
|
||||||
Validating models...
|
Validating models...
|
||||||
|
|
||||||
0 errors found
|
0 errors found
|
||||||
Django version 4.0, using settings 'tutorial.settings'
|
Django version 5.0, using settings 'tutorial.settings'
|
||||||
Starting Development server at http://127.0.0.1:8000/
|
Starting Development server at http://127.0.0.1:8000/
|
||||||
Quit the server with CONTROL-C.
|
Quit the server with CONTROL-C.
|
||||||
|
|
||||||
|
@ -321,7 +321,7 @@ You can install httpie using pip:
|
||||||
|
|
||||||
Finally, we can get a list of all of the snippets:
|
Finally, we can get a list of all of the snippets:
|
||||||
|
|
||||||
http http://127.0.0.1:8000/snippets/
|
http GET http://127.0.0.1:8000/snippets/ --unsorted
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
...
|
...
|
||||||
|
@ -341,12 +341,20 @@ Finally, we can get a list of all of the snippets:
|
||||||
"linenos": false,
|
"linenos": false,
|
||||||
"language": "python",
|
"language": "python",
|
||||||
"style": "friendly"
|
"style": "friendly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"title": "",
|
||||||
|
"code": "print(\"hello, world\")",
|
||||||
|
"linenos": false,
|
||||||
|
"language": "python",
|
||||||
|
"style": "friendly"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
Or we can get a particular snippet by referencing its id:
|
Or we can get a particular snippet by referencing its id:
|
||||||
|
|
||||||
http http://127.0.0.1:8000/snippets/2/
|
http GET http://127.0.0.1:8000/snippets/2/ --unsorted
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
...
|
...
|
||||||
|
@ -371,7 +379,6 @@ We'll see how we can start to improve things in [part 2 of the tutorial][tut-2].
|
||||||
|
|
||||||
[quickstart]: quickstart.md
|
[quickstart]: quickstart.md
|
||||||
[repo]: https://github.com/encode/rest-framework-tutorial
|
[repo]: https://github.com/encode/rest-framework-tutorial
|
||||||
[sandbox]: https://restframework.herokuapp.com/
|
|
||||||
[venv]: https://docs.python.org/3/library/venv.html
|
[venv]: https://docs.python.org/3/library/venv.html
|
||||||
[tut-2]: 2-requests-and-responses.md
|
[tut-2]: 2-requests-and-responses.md
|
||||||
[httpie]: https://github.com/httpie/httpie#installation
|
[httpie]: https://github.com/httpie/httpie#installation
|
||||||
|
|
|
@ -10,7 +10,7 @@ A `ViewSet` class is only bound to a set of method handlers at the last moment,
|
||||||
|
|
||||||
Let's take our current set of views, and refactor them into view sets.
|
Let's take our current set of views, and refactor them into view sets.
|
||||||
|
|
||||||
First of all let's refactor our `UserList` and `UserDetail` classes into a single `UserViewSet` class. We can remove the two view classes, and replace them with a single ViewSet class:
|
First of all let's refactor our `UserList` and `UserDetail` classes into a single `UserViewSet` class. In the `snippets/views.py` file, we can remove the two view classes and replace them with a single ViewSet class:
|
||||||
|
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ Create a new Django project named `tutorial`, then start a new app called `quick
|
||||||
source env/bin/activate # On Windows use `env\Scripts\activate`
|
source env/bin/activate # On Windows use `env\Scripts\activate`
|
||||||
|
|
||||||
# Install Django and Django REST framework into the virtual environment
|
# Install Django and Django REST framework into the virtual environment
|
||||||
pip install django
|
|
||||||
pip install djangorestframework
|
pip install djangorestframework
|
||||||
|
|
||||||
# Set up a new project with a single application
|
# Set up a new project with a single application
|
||||||
|
@ -105,7 +104,7 @@ Right, we'd better write some views then. Open `tutorial/quickstart/views.py` a
|
||||||
"""
|
"""
|
||||||
API endpoint that allows groups to be viewed or edited.
|
API endpoint that allows groups to be viewed or edited.
|
||||||
"""
|
"""
|
||||||
queryset = Group.objects.all()
|
queryset = Group.objects.all().order_by('name')
|
||||||
serializer_class = GroupSerializer
|
serializer_class = GroupSerializer
|
||||||
permission_classes = [permissions.IsAuthenticated]
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
@ -133,8 +132,6 @@ Okay, now let's wire up the API URLs. On to `tutorial/urls.py`...
|
||||||
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
|
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns += router.urls
|
|
||||||
|
|
||||||
Because we're using viewsets instead of views, we can automatically generate the URL conf for our API, by simply registering the viewsets with a router class.
|
Because we're using viewsets instead of views, we can automatically generate the URL conf for our API, by simply registering the viewsets with a router class.
|
||||||
|
|
||||||
Again, if we need more control over the API URLs we can simply drop down to using regular class-based views, and writing the URL conf explicitly.
|
Again, if we need more control over the API URLs we can simply drop down to using regular class-based views, and writing the URL conf explicitly.
|
||||||
|
|
|
@ -439,3 +439,17 @@ ul.sponsor {
|
||||||
display: inline-block !important;
|
display: inline-block !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* admonition */
|
||||||
|
.admonition {
|
||||||
|
border: .075rem solid #448aff;
|
||||||
|
border-radius: .2rem;
|
||||||
|
margin: 1.5625em 0;
|
||||||
|
padding: 0 .6rem;
|
||||||
|
}
|
||||||
|
.admonition-title {
|
||||||
|
background: #448aff1a;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0 -.6rem 1em;
|
||||||
|
padding: 0.4rem 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ theme:
|
||||||
custom_dir: docs_theme
|
custom_dir: docs_theme
|
||||||
|
|
||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
|
- admonition
|
||||||
- toc:
|
- toc:
|
||||||
anchorlink: True
|
anchorlink: True
|
||||||
|
|
||||||
|
@ -65,6 +66,8 @@ nav:
|
||||||
- 'Contributing to REST framework': 'community/contributing.md'
|
- 'Contributing to REST framework': 'community/contributing.md'
|
||||||
- 'Project management': 'community/project-management.md'
|
- 'Project management': 'community/project-management.md'
|
||||||
- 'Release Notes': 'community/release-notes.md'
|
- 'Release Notes': 'community/release-notes.md'
|
||||||
|
- '3.16 Announcement': 'community/3.16-announcement.md'
|
||||||
|
- '3.15 Announcement': 'community/3.15-announcement.md'
|
||||||
- '3.14 Announcement': 'community/3.14-announcement.md'
|
- '3.14 Announcement': 'community/3.14-announcement.md'
|
||||||
- '3.13 Announcement': 'community/3.13-announcement.md'
|
- '3.13 Announcement': 'community/3.13-announcement.md'
|
||||||
- '3.12 Announcement': 'community/3.12-announcement.md'
|
- '3.12 Announcement': 'community/3.12-announcement.md'
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# MkDocs to build our documentation.
|
# MkDocs to build our documentation.
|
||||||
mkdocs>=1.1.2,<1.2
|
mkdocs==1.6.0
|
||||||
jinja2>=2.10,<3.1.0 # contextfilter has been renamed
|
|
||||||
|
|
||||||
# pylinkvalidator to check for broken links in documentation.
|
# pylinkvalidator to check for broken links in documentation.
|
||||||
pylinkvalidator==0.3
|
pylinkvalidator==0.3
|
||||||
|
|
|
@ -4,7 +4,7 @@ coreschema==0.0.4
|
||||||
django-filter
|
django-filter
|
||||||
django-guardian>=2.4.0,<2.5
|
django-guardian>=2.4.0,<2.5
|
||||||
inflection==0.5.1
|
inflection==0.5.1
|
||||||
markdown==3.3
|
markdown>=3.3.7
|
||||||
psycopg2-binary>=2.9.5,<2.10
|
psycopg2-binary>=2.9.5,<2.10
|
||||||
pygments==2.12
|
pygments~=2.17.0
|
||||||
pyyaml>=5.3.1,<5.4
|
pyyaml>=5.3.1,<5.4
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pytest for running the tests.
|
# Pytest for running the tests.
|
||||||
pytest>=6.2.0,<8.0
|
pytest>=7.0.1,<8.0
|
||||||
pytest-cov>=4.0.0,<5.0
|
pytest-cov>=4.0.0,<5.0
|
||||||
pytest-django>=4.5.2,<5.0
|
pytest-django>=4.5.2,<5.0
|
||||||
importlib-metadata<5.0
|
importlib-metadata<5.0
|
||||||
|
|
|
@ -7,10 +7,8 @@ ______ _____ _____ _____ __
|
||||||
\_| \_\____/\____/ \_/ |_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_|
|
\_| \_\____/\____/ \_/ |_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import django
|
|
||||||
|
|
||||||
__title__ = 'Django REST framework'
|
__title__ = 'Django REST framework'
|
||||||
__version__ = '3.14.0'
|
__version__ = '3.16.0'
|
||||||
__author__ = 'Tom Christie'
|
__author__ = 'Tom Christie'
|
||||||
__license__ = 'BSD 3-Clause'
|
__license__ = 'BSD 3-Clause'
|
||||||
__copyright__ = 'Copyright 2011-2023 Encode OSS Ltd'
|
__copyright__ = 'Copyright 2011-2023 Encode OSS Ltd'
|
||||||
|
@ -25,13 +23,5 @@ HTTP_HEADER_ENCODING = 'iso-8859-1'
|
||||||
ISO_8601 = 'iso-8601'
|
ISO_8601 = 'iso-8601'
|
||||||
|
|
||||||
|
|
||||||
if django.VERSION < (3, 2):
|
|
||||||
default_app_config = 'rest_framework.apps.RestFrameworkConfig'
|
|
||||||
|
|
||||||
|
|
||||||
class RemovedInDRF315Warning(DeprecationWarning):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class RemovedInDRF317Warning(PendingDeprecationWarning):
|
class RemovedInDRF317Warning(PendingDeprecationWarning):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
import django
|
|
||||||
|
|
||||||
if django.VERSION < (3, 2):
|
|
||||||
default_app_config = 'rest_framework.authtoken.apps.AuthTokenConfig'
|
|
|
@ -28,7 +28,6 @@ class TokenAdmin(admin.ModelAdmin):
|
||||||
search_help_text = _('Username')
|
search_help_text = _('Username')
|
||||||
ordering = ('-created',)
|
ordering = ('-created',)
|
||||||
actions = None # Actions not compatible with mapped IDs.
|
actions = None # Actions not compatible with mapped IDs.
|
||||||
autocomplete_fields = ("user",)
|
|
||||||
|
|
||||||
def get_changelist(self, request, **kwargs):
|
def get_changelist(self, request, **kwargs):
|
||||||
return TokenChangeList
|
return TokenChangeList
|
||||||
|
|
|
@ -42,4 +42,4 @@ class Command(BaseCommand):
|
||||||
username)
|
username)
|
||||||
)
|
)
|
||||||
self.stdout.write(
|
self.stdout.write(
|
||||||
'Generated token {} for user {}'.format(token.key, username))
|
f'Generated token {token.key} for user {username}')
|
||||||
|
|
|
@ -3,6 +3,9 @@ The `compat` module provides support for backwards compatibility with older
|
||||||
versions of Django/Python, and compatibility wrappers around optional packages.
|
versions of Django/Python, and compatibility wrappers around optional packages.
|
||||||
"""
|
"""
|
||||||
import django
|
import django
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models.constants import LOOKUP_SEP
|
||||||
|
from django.db.models.sql.query import Node
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,6 +49,12 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
yaml = None
|
yaml = None
|
||||||
|
|
||||||
|
# inflection is optional
|
||||||
|
try:
|
||||||
|
import inflection
|
||||||
|
except ImportError:
|
||||||
|
inflection = None
|
||||||
|
|
||||||
|
|
||||||
# requests is optional
|
# requests is optional
|
||||||
try:
|
try:
|
||||||
|
@ -145,27 +154,51 @@ else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
if django.VERSION >= (4, 2):
|
if django.VERSION >= (5, 1):
|
||||||
# Django 4.2+: use the stock parse_header_parameters function
|
# Django 5.1+: use the stock ip_address_validators function
|
||||||
# Note: Django 4.1 also has an implementation of parse_header_parameters
|
# Note: Before Django 5.1, ip_address_validators returns a tuple containing
|
||||||
# which is slightly different from the one in 4.2, it needs
|
# 1) the list of validators and 2) the error message. Starting from
|
||||||
# the compatibility shim as well.
|
# Django 5.1 ip_address_validators only returns the list of validators
|
||||||
from django.utils.http import parse_header_parameters
|
from django.core.validators import ip_address_validators
|
||||||
else:
|
|
||||||
# Django <= 4.1: create a compatibility shim for parse_header_parameters
|
|
||||||
from django.http.multipartparser import parse_header
|
|
||||||
|
|
||||||
def parse_header_parameters(line):
|
def get_referenced_base_fields_from_q(q):
|
||||||
# parse_header works with bytes, but parse_header_parameters
|
return q.referenced_base_fields
|
||||||
# works with strings. Call encode to convert the line to bytes.
|
|
||||||
main_value_pair, params = parse_header(line.encode())
|
else:
|
||||||
return main_value_pair, {
|
# Django <= 5.1: create a compatibility shim for ip_address_validators
|
||||||
# parse_header will convert *some* values to string.
|
from django.core.validators import \
|
||||||
# parse_header_parameters converts *all* values to string.
|
ip_address_validators as _ip_address_validators
|
||||||
# Make sure all values are converted by calling decode on
|
|
||||||
# any remaining non-string values.
|
def ip_address_validators(protocol, unpack_ipv4):
|
||||||
k: v if isinstance(v, str) else v.decode()
|
return _ip_address_validators(protocol, unpack_ipv4)[0]
|
||||||
for k, v in params.items()
|
|
||||||
|
# Django < 5.1: create a compatibility shim for Q.referenced_base_fields
|
||||||
|
# https://github.com/django/django/blob/5.1a1/django/db/models/query_utils.py#L179
|
||||||
|
def _get_paths_from_expression(expr):
|
||||||
|
if isinstance(expr, models.F):
|
||||||
|
yield expr.name
|
||||||
|
elif hasattr(expr, 'flatten'):
|
||||||
|
for child in expr.flatten():
|
||||||
|
if isinstance(child, models.F):
|
||||||
|
yield child.name
|
||||||
|
elif isinstance(child, models.Q):
|
||||||
|
yield from _get_children_from_q(child)
|
||||||
|
|
||||||
|
def _get_children_from_q(q):
|
||||||
|
for child in q.children:
|
||||||
|
if isinstance(child, Node):
|
||||||
|
yield from _get_children_from_q(child)
|
||||||
|
elif isinstance(child, tuple):
|
||||||
|
lhs, rhs = child
|
||||||
|
yield lhs
|
||||||
|
if hasattr(rhs, 'resolve_expression'):
|
||||||
|
yield from _get_paths_from_expression(rhs)
|
||||||
|
elif hasattr(child, 'resolve_expression'):
|
||||||
|
yield from _get_paths_from_expression(child)
|
||||||
|
|
||||||
|
def get_referenced_base_fields_from_q(q):
|
||||||
|
return {
|
||||||
|
child.split(LOOKUP_SEP, 1)[0] for child in _get_children_from_q(q)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ def api_view(http_method_names=None):
|
||||||
# WrappedAPIView.__doc__ = func.doc <--- Not possible to do this
|
# WrappedAPIView.__doc__ = func.doc <--- Not possible to do this
|
||||||
|
|
||||||
# api_view applied without (method_names)
|
# api_view applied without (method_names)
|
||||||
assert not(isinstance(http_method_names, types.FunctionType)), \
|
assert not isinstance(http_method_names, types.FunctionType), \
|
||||||
'@api_view missing list of allowed HTTP methods'
|
'@api_view missing list of allowed HTTP methods'
|
||||||
|
|
||||||
# api_view applied with eg. string instead of list of strings
|
# api_view applied with eg. string instead of list of strings
|
||||||
|
|
|
@ -144,30 +144,17 @@ class ValidationError(APIException):
|
||||||
status_code = status.HTTP_400_BAD_REQUEST
|
status_code = status.HTTP_400_BAD_REQUEST
|
||||||
default_detail = _('Invalid input.')
|
default_detail = _('Invalid input.')
|
||||||
default_code = 'invalid'
|
default_code = 'invalid'
|
||||||
default_params = {}
|
|
||||||
|
|
||||||
def __init__(self, detail=None, code=None, params=None):
|
def __init__(self, detail=None, code=None):
|
||||||
if detail is None:
|
if detail is None:
|
||||||
detail = self.default_detail
|
detail = self.default_detail
|
||||||
if code is None:
|
if code is None:
|
||||||
code = self.default_code
|
code = self.default_code
|
||||||
if params is None:
|
|
||||||
params = self.default_params
|
|
||||||
|
|
||||||
# For validation failures, we may collect many errors together,
|
# For validation failures, we may collect many errors together,
|
||||||
# so the details should always be coerced to a list if not already.
|
# so the details should always be coerced to a list if not already.
|
||||||
if isinstance(detail, str):
|
if isinstance(detail, tuple):
|
||||||
detail = [detail % params]
|
detail = list(detail)
|
||||||
elif isinstance(detail, ValidationError):
|
|
||||||
detail = detail.detail
|
|
||||||
elif isinstance(detail, (list, tuple)):
|
|
||||||
final_detail = []
|
|
||||||
for detail_item in detail:
|
|
||||||
if isinstance(detail_item, ValidationError):
|
|
||||||
final_detail += detail_item.detail
|
|
||||||
else:
|
|
||||||
final_detail += [detail_item % params if isinstance(detail_item, str) else detail_item]
|
|
||||||
detail = final_detail
|
|
||||||
elif not isinstance(detail, dict) and not isinstance(detail, list):
|
elif not isinstance(detail, dict) and not isinstance(detail, list):
|
||||||
detail = [detail]
|
detail = [detail]
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ import datetime
|
||||||
import decimal
|
import decimal
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
|
||||||
import re
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
|
import warnings
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ from django.core.exceptions import ValidationError as DjangoValidationError
|
||||||
from django.core.validators import (
|
from django.core.validators import (
|
||||||
EmailValidator, MaxLengthValidator, MaxValueValidator, MinLengthValidator,
|
EmailValidator, MaxLengthValidator, MaxValueValidator, MinLengthValidator,
|
||||||
MinValueValidator, ProhibitNullCharactersValidator, RegexValidator,
|
MinValueValidator, ProhibitNullCharactersValidator, RegexValidator,
|
||||||
URLValidator, ip_address_validators
|
URLValidator
|
||||||
)
|
)
|
||||||
from django.forms import FilePathField as DjangoFilePathField
|
from django.forms import FilePathField as DjangoFilePathField
|
||||||
from django.forms import ImageField as DjangoImageField
|
from django.forms import ImageField as DjangoImageField
|
||||||
|
@ -36,6 +36,7 @@ except ImportError:
|
||||||
pytz = None
|
pytz = None
|
||||||
|
|
||||||
from rest_framework import ISO_8601
|
from rest_framework import ISO_8601
|
||||||
|
from rest_framework.compat import ip_address_validators
|
||||||
from rest_framework.exceptions import ErrorDetail, ValidationError
|
from rest_framework.exceptions import ErrorDetail, ValidationError
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
from rest_framework.utils import html, humanize_datetime, json, representation
|
from rest_framework.utils import html, humanize_datetime, json, representation
|
||||||
|
@ -43,8 +44,6 @@ from rest_framework.utils.formatting import lazy_format
|
||||||
from rest_framework.utils.timezone import valid_datetime
|
from rest_framework.utils.timezone import valid_datetime
|
||||||
from rest_framework.validators import ProhibitSurrogateCharactersValidator
|
from rest_framework.validators import ProhibitSurrogateCharactersValidator
|
||||||
|
|
||||||
logger = logging.getLogger("rest_framework.fields")
|
|
||||||
|
|
||||||
|
|
||||||
class empty:
|
class empty:
|
||||||
"""
|
"""
|
||||||
|
@ -112,7 +111,7 @@ def get_attribute(instance, attrs):
|
||||||
# If we raised an Attribute or KeyError here it'd get treated
|
# If we raised an Attribute or KeyError here it'd get treated
|
||||||
# as an omitted field in `Field.get_attribute()`. Instead we
|
# as an omitted field in `Field.get_attribute()`. Instead we
|
||||||
# raise a ValueError to ensure the exception is not masked.
|
# raise a ValueError to ensure the exception is not masked.
|
||||||
raise ValueError('Exception raised in callable attribute "{}"; original exception was: {}'.format(attr, exc))
|
raise ValueError(f'Exception raised in callable attribute "{attr}"; original exception was: {exc}')
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
@ -866,7 +865,7 @@ class IPAddressField(CharField):
|
||||||
self.protocol = protocol.lower()
|
self.protocol = protocol.lower()
|
||||||
self.unpack_ipv4 = (self.protocol == 'both')
|
self.unpack_ipv4 = (self.protocol == 'both')
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
validators, error_message = ip_address_validators(protocol, self.unpack_ipv4)
|
validators = ip_address_validators(protocol, self.unpack_ipv4)
|
||||||
self.validators.extend(validators)
|
self.validators.extend(validators)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
|
@ -987,10 +986,10 @@ class DecimalField(Field):
|
||||||
self.max_value = max_value
|
self.max_value = max_value
|
||||||
self.min_value = min_value
|
self.min_value = min_value
|
||||||
|
|
||||||
if self.max_value is not None and not isinstance(self.max_value, decimal.Decimal):
|
if self.max_value is not None and not isinstance(self.max_value, (int, decimal.Decimal)):
|
||||||
logger.warning("max_value in DecimalField should be Decimal type.")
|
warnings.warn("max_value should be an integer or Decimal instance.")
|
||||||
if self.min_value is not None and not isinstance(self.min_value, decimal.Decimal):
|
if self.min_value is not None and not isinstance(self.min_value, (int, decimal.Decimal)):
|
||||||
logger.warning("min_value in DecimalField should be Decimal type.")
|
warnings.warn("min_value should be an integer or Decimal instance.")
|
||||||
|
|
||||||
if self.max_digits is not None and self.decimal_places is not None:
|
if self.max_digits is not None and self.decimal_places is not None:
|
||||||
self.max_whole_digits = self.max_digits - self.decimal_places
|
self.max_whole_digits = self.max_digits - self.decimal_places
|
||||||
|
@ -1104,7 +1103,7 @@ class DecimalField(Field):
|
||||||
if self.localize:
|
if self.localize:
|
||||||
return localize_input(quantized)
|
return localize_input(quantized)
|
||||||
|
|
||||||
return '{:f}'.format(quantized)
|
return f'{quantized:f}'
|
||||||
|
|
||||||
def quantize(self, value):
|
def quantize(self, value):
|
||||||
"""
|
"""
|
||||||
|
@ -1862,7 +1861,7 @@ class SerializerMethodField(Field):
|
||||||
def bind(self, field_name, parent):
|
def bind(self, field_name, parent):
|
||||||
# The method name defaults to `get_{field_name}`.
|
# The method name defaults to `get_{field_name}`.
|
||||||
if self.method_name is None:
|
if self.method_name is None:
|
||||||
self.method_name = 'get_{field_name}'.format(field_name=field_name)
|
self.method_name = f'get_{field_name}'
|
||||||
|
|
||||||
super().bind(field_name, parent)
|
super().bind(field_name, parent)
|
||||||
|
|
||||||
|
|
|
@ -21,18 +21,20 @@ from rest_framework.settings import api_settings
|
||||||
|
|
||||||
|
|
||||||
def search_smart_split(search_terms):
|
def search_smart_split(search_terms):
|
||||||
"""generator that first splits string by spaces, leaving quoted phrases togheter,
|
"""Returns sanitized search terms as a list."""
|
||||||
then it splits non-quoted phrases by commas.
|
split_terms = []
|
||||||
"""
|
|
||||||
for term in smart_split(search_terms):
|
for term in smart_split(search_terms):
|
||||||
# trim commas to avoid bad matching for quoted phrases
|
# trim commas to avoid bad matching for quoted phrases
|
||||||
term = term.strip(',')
|
term = term.strip(',')
|
||||||
if term.startswith(('"', "'")) and term[0] == term[-1]:
|
if term.startswith(('"', "'")) and term[0] == term[-1]:
|
||||||
# quoted phrases are kept togheter without any other split
|
# quoted phrases are kept together without any other split
|
||||||
yield unescape_string_literal(term)
|
split_terms.append(unescape_string_literal(term))
|
||||||
else:
|
else:
|
||||||
# non-quoted tokens are split by comma, keeping only non-empty ones
|
# non-quoted tokens are split by comma, keeping only non-empty ones
|
||||||
yield from (sub_term.strip() for sub_term in term.split(',') if sub_term)
|
for sub_term in term.split(','):
|
||||||
|
if sub_term:
|
||||||
|
split_terms.append(sub_term.strip())
|
||||||
|
return split_terms
|
||||||
|
|
||||||
|
|
||||||
class BaseFilterBackend:
|
class BaseFilterBackend:
|
||||||
|
@ -85,7 +87,8 @@ class SearchFilter(BaseFilterBackend):
|
||||||
"""
|
"""
|
||||||
value = request.query_params.get(self.search_param, '')
|
value = request.query_params.get(self.search_param, '')
|
||||||
field = CharField(trim_whitespace=False, allow_blank=True)
|
field = CharField(trim_whitespace=False, allow_blank=True)
|
||||||
return field.run_validation(value)
|
cleaned_value = field.run_validation(value)
|
||||||
|
return search_smart_split(cleaned_value)
|
||||||
|
|
||||||
def construct_search(self, field_name, queryset):
|
def construct_search(self, field_name, queryset):
|
||||||
lookup = self.lookup_prefixes.get(field_name[0])
|
lookup = self.lookup_prefixes.get(field_name[0])
|
||||||
|
@ -111,10 +114,6 @@ class SearchFilter(BaseFilterBackend):
|
||||||
if hasattr(field, "path_infos"):
|
if hasattr(field, "path_infos"):
|
||||||
# Update opts to follow the relation.
|
# Update opts to follow the relation.
|
||||||
opts = field.path_infos[-1].to_opts
|
opts = field.path_infos[-1].to_opts
|
||||||
# django < 4.1
|
|
||||||
elif hasattr(field, 'get_path_info'):
|
|
||||||
# Update opts to follow the relation.
|
|
||||||
opts = field.get_path_info()[-1].to_opts
|
|
||||||
# Otherwise, use the field with icontains.
|
# Otherwise, use the field with icontains.
|
||||||
lookup = 'icontains'
|
lookup = 'icontains'
|
||||||
return LOOKUP_SEP.join([field_name, lookup])
|
return LOOKUP_SEP.join([field_name, lookup])
|
||||||
|
@ -163,7 +162,7 @@ class SearchFilter(BaseFilterBackend):
|
||||||
reduce(
|
reduce(
|
||||||
operator.or_,
|
operator.or_,
|
||||||
(models.Q(**{orm_lookup: term}) for orm_lookup in orm_lookups)
|
(models.Q(**{orm_lookup: term}) for orm_lookup in orm_lookups)
|
||||||
) for term in search_smart_split(search_terms)
|
) for term in search_terms
|
||||||
)
|
)
|
||||||
queryset = queryset.filter(reduce(operator.and_, conditions))
|
queryset = queryset.filter(reduce(operator.and_, conditions))
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
# Bashar Al-Abdulhadi, 2016-2017
|
# Bashar Al-Abdulhadi, 2016-2017
|
||||||
# Eyad Toma <d.eyad.t@gmail.com>, 2015,2017
|
# Eyad Toma <d.eyad.t@gmail.com>, 2015,2017
|
||||||
# zak zak <zakaria.bendifallah@gmail.com>, 2020
|
# zak zak <zakaria.bendifallah@gmail.com>, 2020
|
||||||
|
# Salman Saeed Albukhaitan <ssyb2014@gmail.com>, 2024
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Django REST framework\n"
|
"Project-Id-Version: Django REST framework\n"
|
||||||
|
@ -24,19 +25,19 @@ msgstr ""
|
||||||
|
|
||||||
#: authentication.py:70
|
#: authentication.py:70
|
||||||
msgid "Invalid basic header. No credentials provided."
|
msgid "Invalid basic header. No credentials provided."
|
||||||
msgstr "رأس أساسي غير صالح, لم تقدم اي بيانات."
|
msgstr "ترويسة أساسية غير صالحة. لم تقدم أي بيانات تفويض."
|
||||||
|
|
||||||
#: authentication.py:73
|
#: authentication.py:73
|
||||||
msgid "Invalid basic header. Credentials string should not contain spaces."
|
msgid "Invalid basic header. Credentials string should not contain spaces."
|
||||||
msgstr "رأس أساسي غير صالح, سلسلة البيانات لا يجب أن تحتوي على أي أحرف مسافات"
|
msgstr "ترويسة أساسية غير صالحة. يجب أن لا تحتوي سلسلة بيانات التفويض على مسافات."
|
||||||
|
|
||||||
#: authentication.py:83
|
#: authentication.py:83
|
||||||
msgid "Invalid basic header. Credentials not correctly base64 encoded."
|
msgid "Invalid basic header. Credentials not correctly base64 encoded."
|
||||||
msgstr "رأس أساسي غير صالح, البيانات ليست مرمّزة بصحة على أساس64."
|
msgstr "ترويسة أساسية غير صالحة. بيانات التفويض لم تُشفر بشكل صحيح بنظام أساس64."
|
||||||
|
|
||||||
#: authentication.py:101
|
#: authentication.py:101
|
||||||
msgid "Invalid username/password."
|
msgid "Invalid username/password."
|
||||||
msgstr "اسم المستخدم/كلمة السر غير صحيحين."
|
msgstr "اسم المستخدم/كلمة المرور غير صحيحة."
|
||||||
|
|
||||||
#: authentication.py:104 authentication.py:206
|
#: authentication.py:104 authentication.py:206
|
||||||
msgid "User inactive or deleted."
|
msgid "User inactive or deleted."
|
||||||
|
@ -93,7 +94,7 @@ msgstr "كلمة المرور"
|
||||||
|
|
||||||
#: authtoken/serializers.py:35
|
#: authtoken/serializers.py:35
|
||||||
msgid "Unable to log in with provided credentials."
|
msgid "Unable to log in with provided credentials."
|
||||||
msgstr "تعذر تسجيل الدخول بالبيانات التي ادخلتها."
|
msgstr "تعذر تسجيل الدخول بالبيانات المدخلة."
|
||||||
|
|
||||||
#: authtoken/serializers.py:38
|
#: authtoken/serializers.py:38
|
||||||
msgid "Must include \"username\" and \"password\"."
|
msgid "Must include \"username\" and \"password\"."
|
||||||
|
@ -101,11 +102,11 @@ msgstr "يجب أن تتضمن \"اسم المستخدم\" و \"كلمة الم
|
||||||
|
|
||||||
#: exceptions.py:102
|
#: exceptions.py:102
|
||||||
msgid "A server error occurred."
|
msgid "A server error occurred."
|
||||||
msgstr "حدث خطأ في المخدم."
|
msgstr "حدث خطأ في الخادم."
|
||||||
|
|
||||||
#: exceptions.py:142
|
#: exceptions.py:142
|
||||||
msgid "Invalid input."
|
msgid "Invalid input."
|
||||||
msgstr ""
|
msgstr "مدخل غير صالح."
|
||||||
|
|
||||||
#: exceptions.py:161
|
#: exceptions.py:161
|
||||||
msgid "Malformed request."
|
msgid "Malformed request."
|
||||||
|
@ -130,11 +131,11 @@ msgstr "غير موجود."
|
||||||
#: exceptions.py:191
|
#: exceptions.py:191
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Method \"{method}\" not allowed."
|
msgid "Method \"{method}\" not allowed."
|
||||||
msgstr "الطريقة \"{method}\" غير مسموح بها."
|
msgstr "طريقة \"{method}\" غير مسموح بها."
|
||||||
|
|
||||||
#: exceptions.py:202
|
#: exceptions.py:202
|
||||||
msgid "Could not satisfy the request Accept header."
|
msgid "Could not satisfy the request Accept header."
|
||||||
msgstr "لم نتمكن من تلبية الرٱس Accept في الطلب."
|
msgstr "تعذر تلبية ترويسة Accept في الطلب."
|
||||||
|
|
||||||
#: exceptions.py:212
|
#: exceptions.py:212
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -143,17 +144,17 @@ msgstr "الوسيط \"{media_type}\" الموجود في الطلب غير مع
|
||||||
|
|
||||||
#: exceptions.py:223
|
#: exceptions.py:223
|
||||||
msgid "Request was throttled."
|
msgid "Request was throttled."
|
||||||
msgstr "تم تقييد الطلب."
|
msgstr "تم حد الطلب."
|
||||||
|
|
||||||
#: exceptions.py:224
|
#: exceptions.py:224
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected available in {wait} second."
|
msgid "Expected available in {wait} second."
|
||||||
msgstr ""
|
msgstr "متوقع التوفر خلال {wait} ثانية."
|
||||||
|
|
||||||
#: exceptions.py:225
|
#: exceptions.py:225
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected available in {wait} seconds."
|
msgid "Expected available in {wait} seconds."
|
||||||
msgstr ""
|
msgstr "متوقع التوفر خلال {wait} ثواني."
|
||||||
|
|
||||||
#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
|
#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
|
||||||
#: validators.py:183
|
#: validators.py:183
|
||||||
|
@ -166,11 +167,11 @@ msgstr "لا يمكن لهذا الحقل ان يكون فارغاً null."
|
||||||
|
|
||||||
#: fields.py:701
|
#: fields.py:701
|
||||||
msgid "Must be a valid boolean."
|
msgid "Must be a valid boolean."
|
||||||
msgstr ""
|
msgstr "يجب أن يكون قيمة منطقية صالحة."
|
||||||
|
|
||||||
#: fields.py:766
|
#: fields.py:766
|
||||||
msgid "Not a valid string."
|
msgid "Not a valid string."
|
||||||
msgstr ""
|
msgstr "ليس نصاً صالحاً."
|
||||||
|
|
||||||
#: fields.py:767
|
#: fields.py:767
|
||||||
msgid "This field may not be blank."
|
msgid "This field may not be blank."
|
||||||
|
@ -179,16 +180,16 @@ msgstr "لا يمكن لهذا الحقل ان يكون فارغاً."
|
||||||
#: fields.py:768 fields.py:1881
|
#: fields.py:768 fields.py:1881
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has no more than {max_length} characters."
|
msgid "Ensure this field has no more than {max_length} characters."
|
||||||
msgstr "تأكد ان الحقل لا يزيد عن {max_length} محرف."
|
msgstr "تأكد ان عدد الحروف في هذا الحقل لا تتجاوز {max_length}."
|
||||||
|
|
||||||
#: fields.py:769
|
#: fields.py:769
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has at least {min_length} characters."
|
msgid "Ensure this field has at least {min_length} characters."
|
||||||
msgstr "تأكد ان الحقل {min_length} محرف على الاقل."
|
msgstr "تأكد ان عدد الحروف في هذا الحقل لا يقل عن {min_length}."
|
||||||
|
|
||||||
#: fields.py:816
|
#: fields.py:816
|
||||||
msgid "Enter a valid email address."
|
msgid "Enter a valid email address."
|
||||||
msgstr "عليك ان تدخل بريد إلكتروني صالح."
|
msgstr "يرجى إدخال عنوان بريد إلكتروني صحيح."
|
||||||
|
|
||||||
#: fields.py:827
|
#: fields.py:827
|
||||||
msgid "This value does not match the required pattern."
|
msgid "This value does not match the required pattern."
|
||||||
|
@ -204,7 +205,7 @@ msgstr "أدخل \"slug\" صالح يحتوي على حروف، أرقام، ش
|
||||||
msgid ""
|
msgid ""
|
||||||
"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
|
"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
|
||||||
"or hyphens."
|
"or hyphens."
|
||||||
msgstr ""
|
msgstr "أدخل \"slug\" صالح يحتوي على حروف يونيكود، أرقام، شُرط سفلية أو واصلات."
|
||||||
|
|
||||||
#: fields.py:854
|
#: fields.py:854
|
||||||
msgid "Enter a valid URL."
|
msgid "Enter a valid URL."
|
||||||
|
@ -212,7 +213,7 @@ msgstr "الرجاء إدخال رابط إلكتروني صالح."
|
||||||
|
|
||||||
#: fields.py:867
|
#: fields.py:867
|
||||||
msgid "Must be a valid UUID."
|
msgid "Must be a valid UUID."
|
||||||
msgstr ""
|
msgstr "يجب أن يكون معرف UUID صالح."
|
||||||
|
|
||||||
#: fields.py:903
|
#: fields.py:903
|
||||||
msgid "Enter a valid IPv4 or IPv6 address."
|
msgid "Enter a valid IPv4 or IPv6 address."
|
||||||
|
@ -225,16 +226,16 @@ msgstr "الرجاء إدخال رقم صحيح صالح."
|
||||||
#: fields.py:932 fields.py:969 fields.py:1005 fields.py:1366
|
#: fields.py:932 fields.py:969 fields.py:1005 fields.py:1366
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this value is less than or equal to {max_value}."
|
msgid "Ensure this value is less than or equal to {max_value}."
|
||||||
msgstr "تأكد ان القيمة أقل أو تساوي {max_value}."
|
msgstr "تأكد ان القيمة أقل من أو تساوي {max_value}."
|
||||||
|
|
||||||
#: fields.py:933 fields.py:970 fields.py:1006 fields.py:1367
|
#: fields.py:933 fields.py:970 fields.py:1006 fields.py:1367
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this value is greater than or equal to {min_value}."
|
msgid "Ensure this value is greater than or equal to {min_value}."
|
||||||
msgstr "تأكد ان القيمة أكبر أو تساوي {min_value}."
|
msgstr "تأكد ان القيمة أكبر من أو تساوي {min_value}."
|
||||||
|
|
||||||
#: fields.py:934 fields.py:971 fields.py:1010
|
#: fields.py:934 fields.py:971 fields.py:1010
|
||||||
msgid "String value too large."
|
msgid "String value too large."
|
||||||
msgstr "السلسلة اطول من القيمة المسموح بها."
|
msgstr "النص طويل جداً."
|
||||||
|
|
||||||
#: fields.py:968 fields.py:1004
|
#: fields.py:968 fields.py:1004
|
||||||
msgid "A valid number is required."
|
msgid "A valid number is required."
|
||||||
|
@ -249,7 +250,7 @@ msgstr "تأكد ان القيمة لا تحوي أكثر من {max_digits} رق
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Ensure that there are no more than {max_decimal_places} decimal places."
|
"Ensure that there are no more than {max_decimal_places} decimal places."
|
||||||
msgstr "تأكد انه لا يوجد اكثر من {max_decimal_places} منازل عشرية."
|
msgstr "تأكد انه لا يوجد اكثر من {max_decimal_places} أرقام عشرية."
|
||||||
|
|
||||||
#: fields.py:1009
|
#: fields.py:1009
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -261,7 +262,7 @@ msgstr "تأكد انه لا يوجد اكثر من {max_whole_digits} أرقا
|
||||||
#: fields.py:1148
|
#: fields.py:1148
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
|
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
|
||||||
msgstr "صيغة التاريخ و الوقت غير صحيحة. عليك أن تستخدم واحدة من هذه الصيغ التالية: {format}."
|
msgstr "صيغة التاريخ و الوقت غير صحيحة. عليك أن تستخدم احد الصيغ التالية: {format}."
|
||||||
|
|
||||||
#: fields.py:1149
|
#: fields.py:1149
|
||||||
msgid "Expected a datetime but got a date."
|
msgid "Expected a datetime but got a date."
|
||||||
|
@ -270,11 +271,11 @@ msgstr "متوقع تاريخ و وقت و وجد تاريخ فقط"
|
||||||
#: fields.py:1150
|
#: fields.py:1150
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Invalid datetime for the timezone \"{timezone}\"."
|
msgid "Invalid datetime for the timezone \"{timezone}\"."
|
||||||
msgstr ""
|
msgstr "تاريخ و وقت غير صالح للمنطقة الزمنية \"{timezone}\"."
|
||||||
|
|
||||||
#: fields.py:1151
|
#: fields.py:1151
|
||||||
msgid "Datetime value out of range."
|
msgid "Datetime value out of range."
|
||||||
msgstr ""
|
msgstr "قيمة التاريخ و الوقت خارج النطاق."
|
||||||
|
|
||||||
#: fields.py:1236
|
#: fields.py:1236
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -293,12 +294,12 @@ msgstr "صيغة الوقت غير صحيحة. عليك أن تستخدم واح
|
||||||
#: fields.py:1365
|
#: fields.py:1365
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Duration has wrong format. Use one of these formats instead: {format}."
|
msgid "Duration has wrong format. Use one of these formats instead: {format}."
|
||||||
msgstr "صيغة المدة غير صحيحه, يرجى إستخدام إحدى هذه الصيغ: {format}."
|
msgstr "صيغة المدة غير صحيحة. يرجى استخدام احد الصيغ التالية: {format}."
|
||||||
|
|
||||||
#: fields.py:1399 fields.py:1456
|
#: fields.py:1399 fields.py:1456
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "\"{input}\" is not a valid choice."
|
msgid "\"{input}\" is not a valid choice."
|
||||||
msgstr "\"{input}\" ليست واحدة من الخيارات الصالحة."
|
msgstr "\"{input}\" ليس خياراً صالحاً."
|
||||||
|
|
||||||
#: fields.py:1402
|
#: fields.py:1402
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -317,7 +318,7 @@ msgstr "هذا التحديد لا يجب أن يكون فارغا."
|
||||||
#: fields.py:1495
|
#: fields.py:1495
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "\"{input}\" is not a valid path choice."
|
msgid "\"{input}\" is not a valid path choice."
|
||||||
msgstr "{input} كإختيار مسار غير صالح."
|
msgstr "{input} ليس خيار مسار صالح."
|
||||||
|
|
||||||
#: fields.py:1514
|
#: fields.py:1514
|
||||||
msgid "No file was submitted."
|
msgid "No file was submitted."
|
||||||
|
@ -326,41 +327,41 @@ msgstr "لم يتم إرسال أي ملف."
|
||||||
#: fields.py:1515
|
#: fields.py:1515
|
||||||
msgid ""
|
msgid ""
|
||||||
"The submitted data was not a file. Check the encoding type on the form."
|
"The submitted data was not a file. Check the encoding type on the form."
|
||||||
msgstr "المعطيات المرسولة ليست ملف. إفحص نوع الترميز في النموذج."
|
msgstr "البيانات المرسلة ليست ملف. تأكد من نوع الترميز في النموذج."
|
||||||
|
|
||||||
#: fields.py:1516
|
#: fields.py:1516
|
||||||
msgid "No filename could be determined."
|
msgid "No filename could be determined."
|
||||||
msgstr "ما من إسم ملف أمكن تحديده."
|
msgstr "تعذر تحديد اسم الملف."
|
||||||
|
|
||||||
#: fields.py:1517
|
#: fields.py:1517
|
||||||
msgid "The submitted file is empty."
|
msgid "The submitted file is empty."
|
||||||
msgstr "الملف الذي تم إرساله فارغ."
|
msgstr "الملف المرسل فارغ."
|
||||||
|
|
||||||
#: fields.py:1518
|
#: fields.py:1518
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Ensure this filename has at most {max_length} characters (it has {length})."
|
"Ensure this filename has at most {max_length} characters (it has {length})."
|
||||||
msgstr "تأكد ان اسم الملف لا يحوي أكثر من {max_length} محرف (الإسم المرسل يحوي {length} محرف)."
|
msgstr "تأكد ان طول إسم الملف لا يتجاوز {max_length} حرف (عدد الحروف الحالي {length})."
|
||||||
|
|
||||||
#: fields.py:1566
|
#: fields.py:1566
|
||||||
msgid ""
|
msgid ""
|
||||||
"Upload a valid image. The file you uploaded was either not an image or a "
|
"Upload a valid image. The file you uploaded was either not an image or a "
|
||||||
"corrupted image."
|
"corrupted image."
|
||||||
msgstr "الرجاء تحميل صورة صالحة. الملف الذي تم تحميله إما لم يكن صورة او انه كان صورة تالفة."
|
msgstr "يرجى رفع صورة صالحة. الملف الذي قمت برفعه ليس صورة أو أنه ملف تالف."
|
||||||
|
|
||||||
#: fields.py:1604 relations.py:486 serializers.py:571
|
#: fields.py:1604 relations.py:486 serializers.py:571
|
||||||
msgid "This list may not be empty."
|
msgid "This list may not be empty."
|
||||||
msgstr "القائمة يجب أن لا تكون فارغة."
|
msgstr "يجب أن لا تكون القائمة فارغة."
|
||||||
|
|
||||||
#: fields.py:1605
|
#: fields.py:1605
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has at least {min_length} elements."
|
msgid "Ensure this field has at least {min_length} elements."
|
||||||
msgstr ""
|
msgstr "تأكد ان عدد العناصر في هذا الحقل لا يقل عن {min_length}."
|
||||||
|
|
||||||
#: fields.py:1606
|
#: fields.py:1606
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has no more than {max_length} elements."
|
msgid "Ensure this field has no more than {max_length} elements."
|
||||||
msgstr ""
|
msgstr "تأكد ان عدد العناصر في هذا الحقل لا يتجاوز {max_length}."
|
||||||
|
|
||||||
#: fields.py:1682
|
#: fields.py:1682
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -369,7 +370,7 @@ msgstr "المتوقع كان قاموس عناصر و لكن النوع الم
|
||||||
|
|
||||||
#: fields.py:1683
|
#: fields.py:1683
|
||||||
msgid "This dictionary may not be empty."
|
msgid "This dictionary may not be empty."
|
||||||
msgstr ""
|
msgstr "يجب أن لا يكون القاموس فارغاً."
|
||||||
|
|
||||||
#: fields.py:1755
|
#: fields.py:1755
|
||||||
msgid "Value must be valid JSON."
|
msgid "Value must be valid JSON."
|
||||||
|
@ -381,7 +382,7 @@ msgstr "بحث"
|
||||||
|
|
||||||
#: filters.py:50
|
#: filters.py:50
|
||||||
msgid "A search term."
|
msgid "A search term."
|
||||||
msgstr ""
|
msgstr "مصطلح البحث."
|
||||||
|
|
||||||
#: filters.py:180 templates/rest_framework/filters/ordering.html:3
|
#: filters.py:180 templates/rest_framework/filters/ordering.html:3
|
||||||
msgid "Ordering"
|
msgid "Ordering"
|
||||||
|
@ -389,7 +390,7 @@ msgstr "الترتيب"
|
||||||
|
|
||||||
#: filters.py:181
|
#: filters.py:181
|
||||||
msgid "Which field to use when ordering the results."
|
msgid "Which field to use when ordering the results."
|
||||||
msgstr ""
|
msgstr "أي حقل يجب استخدامه عند ترتيب النتائج."
|
||||||
|
|
||||||
#: filters.py:287
|
#: filters.py:287
|
||||||
msgid "ascending"
|
msgid "ascending"
|
||||||
|
@ -401,11 +402,11 @@ msgstr "تنازلي"
|
||||||
|
|
||||||
#: pagination.py:174
|
#: pagination.py:174
|
||||||
msgid "A page number within the paginated result set."
|
msgid "A page number within the paginated result set."
|
||||||
msgstr ""
|
msgstr "رقم الصفحة ضمن النتائج المقسمة."
|
||||||
|
|
||||||
#: pagination.py:179 pagination.py:372 pagination.py:590
|
#: pagination.py:179 pagination.py:372 pagination.py:590
|
||||||
msgid "Number of results to return per page."
|
msgid "Number of results to return per page."
|
||||||
msgstr ""
|
msgstr "عدد النتائج التي يجب إرجاعها في كل صفحة."
|
||||||
|
|
||||||
#: pagination.py:189
|
#: pagination.py:189
|
||||||
msgid "Invalid page."
|
msgid "Invalid page."
|
||||||
|
@ -413,11 +414,11 @@ msgstr "صفحة غير صحيحة."
|
||||||
|
|
||||||
#: pagination.py:374
|
#: pagination.py:374
|
||||||
msgid "The initial index from which to return the results."
|
msgid "The initial index from which to return the results."
|
||||||
msgstr ""
|
msgstr "الفهرس الأولي الذي يجب البدء منه لإرجاع النتائج."
|
||||||
|
|
||||||
#: pagination.py:581
|
#: pagination.py:581
|
||||||
msgid "The pagination cursor value."
|
msgid "The pagination cursor value."
|
||||||
msgstr ""
|
msgstr "قيمة المؤشر للتقسيم."
|
||||||
|
|
||||||
#: pagination.py:583
|
#: pagination.py:583
|
||||||
msgid "Invalid cursor"
|
msgid "Invalid cursor"
|
||||||
|
@ -431,24 +432,24 @@ msgstr "معرف العنصر \"{pk_value}\" غير صالح - العنصر غ
|
||||||
#: relations.py:247
|
#: relations.py:247
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Incorrect type. Expected pk value, received {data_type}."
|
msgid "Incorrect type. Expected pk value, received {data_type}."
|
||||||
msgstr "نوع خاطئ. المتوقع قيمة من pk، لكن المتحصل عليه {data_type}."
|
msgstr "نوع غير صحيح. يتوقع قيمة معرف العنصر، بينما حصل على {data_type}."
|
||||||
|
|
||||||
#: relations.py:280
|
#: relations.py:280
|
||||||
msgid "Invalid hyperlink - No URL match."
|
msgid "Invalid hyperlink - No URL match."
|
||||||
msgstr "إرتباط تشعبي غير صالح - لا مطابقة لURL."
|
msgstr "رابط تشعبي غير صالح - لا يوجد تطابق URL."
|
||||||
|
|
||||||
#: relations.py:281
|
#: relations.py:281
|
||||||
msgid "Invalid hyperlink - Incorrect URL match."
|
msgid "Invalid hyperlink - Incorrect URL match."
|
||||||
msgstr "إرتباط تشعبي غير صالح - مطابقة خاطئة لURL."
|
msgstr "رابط تشعبي غير صالح - تطابق URL غير صحيح."
|
||||||
|
|
||||||
#: relations.py:282
|
#: relations.py:282
|
||||||
msgid "Invalid hyperlink - Object does not exist."
|
msgid "Invalid hyperlink - Object does not exist."
|
||||||
msgstr "إرتباط تشعبي غير صالح - عنصر غير موجود."
|
msgstr "رابط تشعبي غير صالح - العنصر غير موجود."
|
||||||
|
|
||||||
#: relations.py:283
|
#: relations.py:283
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Incorrect type. Expected URL string, received {data_type}."
|
msgid "Incorrect type. Expected URL string, received {data_type}."
|
||||||
msgstr "نوع خاطئ. المتوقع سلسلة URL، لكن المتحصل عليه {data_type}."
|
msgstr "نوع غير صحيح. المتوقع هو رابط URL، ولكن تم الحصول على {data_type}."
|
||||||
|
|
||||||
#: relations.py:448
|
#: relations.py:448
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -461,20 +462,20 @@ msgstr "قيمة غير صالحة."
|
||||||
|
|
||||||
#: schemas/utils.py:32
|
#: schemas/utils.py:32
|
||||||
msgid "unique integer value"
|
msgid "unique integer value"
|
||||||
msgstr ""
|
msgstr "رقم صحيح فريد"
|
||||||
|
|
||||||
#: schemas/utils.py:34
|
#: schemas/utils.py:34
|
||||||
msgid "UUID string"
|
msgid "UUID string"
|
||||||
msgstr ""
|
msgstr "نص UUID"
|
||||||
|
|
||||||
#: schemas/utils.py:36
|
#: schemas/utils.py:36
|
||||||
msgid "unique value"
|
msgid "unique value"
|
||||||
msgstr ""
|
msgstr "قيمة فريدة"
|
||||||
|
|
||||||
#: schemas/utils.py:38
|
#: schemas/utils.py:38
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "A {value_type} identifying this {name}."
|
msgid "A {value_type} identifying this {name}."
|
||||||
msgstr ""
|
msgstr "نوع {value_type} يحدد هذا {name}."
|
||||||
|
|
||||||
#: serializers.py:337
|
#: serializers.py:337
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -484,7 +485,7 @@ msgstr "معطيات غير صالحة. المتوقع هو قاموس، لكن
|
||||||
#: templates/rest_framework/admin.html:116
|
#: templates/rest_framework/admin.html:116
|
||||||
#: templates/rest_framework/base.html:136
|
#: templates/rest_framework/base.html:136
|
||||||
msgid "Extra Actions"
|
msgid "Extra Actions"
|
||||||
msgstr ""
|
msgstr "إجراءات إضافية"
|
||||||
|
|
||||||
#: templates/rest_framework/admin.html:130
|
#: templates/rest_framework/admin.html:130
|
||||||
#: templates/rest_framework/base.html:150
|
#: templates/rest_framework/base.html:150
|
||||||
|
@ -493,27 +494,27 @@ msgstr "مرشحات"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:37
|
#: templates/rest_framework/base.html:37
|
||||||
msgid "navbar"
|
msgid "navbar"
|
||||||
msgstr ""
|
msgstr "شريط التنقل"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:75
|
#: templates/rest_framework/base.html:75
|
||||||
msgid "content"
|
msgid "content"
|
||||||
msgstr ""
|
msgstr "المحتوى"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:78
|
#: templates/rest_framework/base.html:78
|
||||||
msgid "request form"
|
msgid "request form"
|
||||||
msgstr ""
|
msgstr "نموذج الطلب"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:157
|
#: templates/rest_framework/base.html:157
|
||||||
msgid "main content"
|
msgid "main content"
|
||||||
msgstr ""
|
msgstr "المحتوى الرئيسي"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:173
|
#: templates/rest_framework/base.html:173
|
||||||
msgid "request info"
|
msgid "request info"
|
||||||
msgstr ""
|
msgstr "معلومات الطلب"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:177
|
#: templates/rest_framework/base.html:177
|
||||||
msgid "response info"
|
msgid "response info"
|
||||||
msgstr ""
|
msgstr "معلومات الرد"
|
||||||
|
|
||||||
#: templates/rest_framework/horizontal/radio.html:4
|
#: templates/rest_framework/horizontal/radio.html:4
|
||||||
#: templates/rest_framework/inline/radio.html:3
|
#: templates/rest_framework/inline/radio.html:3
|
||||||
|
@ -525,7 +526,7 @@ msgstr "لا شيء"
|
||||||
#: templates/rest_framework/inline/select_multiple.html:3
|
#: templates/rest_framework/inline/select_multiple.html:3
|
||||||
#: templates/rest_framework/vertical/select_multiple.html:3
|
#: templates/rest_framework/vertical/select_multiple.html:3
|
||||||
msgid "No items to select."
|
msgid "No items to select."
|
||||||
msgstr "ما من عناصر للتحديد."
|
msgstr "لا يوجد عناصر لتحديدها."
|
||||||
|
|
||||||
#: validators.py:39
|
#: validators.py:39
|
||||||
msgid "This field must be unique."
|
msgid "This field must be unique."
|
||||||
|
@ -539,7 +540,7 @@ msgstr "الحقول {field_names} يجب أن تشكل مجموعة فريدة.
|
||||||
#: validators.py:171
|
#: validators.py:171
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Surrogate characters are not allowed: U+{code_point:X}."
|
msgid "Surrogate characters are not allowed: U+{code_point:X}."
|
||||||
msgstr ""
|
msgstr "لا يُسمح بالحروف البديلة: U+{code_point:X}."
|
||||||
|
|
||||||
#: validators.py:243
|
#: validators.py:243
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -558,11 +559,11 @@ msgstr "الحقل يجب ان يكون فريد للعام {date_field}."
|
||||||
|
|
||||||
#: versioning.py:40
|
#: versioning.py:40
|
||||||
msgid "Invalid version in \"Accept\" header."
|
msgid "Invalid version in \"Accept\" header."
|
||||||
msgstr "إصدار غير صالح في الرٱس \"Accept\"."
|
msgstr "إصدار غير صالح في ترويسة \"Accept\"."
|
||||||
|
|
||||||
#: versioning.py:71
|
#: versioning.py:71
|
||||||
msgid "Invalid version in URL path."
|
msgid "Invalid version in URL path."
|
||||||
msgstr "إصدار غير صالح في المسار URL."
|
msgstr "نسخة غير صالحة في مسار URL."
|
||||||
|
|
||||||
#: versioning.py:116
|
#: versioning.py:116
|
||||||
msgid "Invalid version in URL path. Does not match any version namespace."
|
msgid "Invalid version in URL path. Does not match any version namespace."
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
# Thomas Tanner, 2015
|
# Thomas Tanner, 2015
|
||||||
# Tom Jaster <futur3.tom@googlemail.com>, 2015
|
# Tom Jaster <futur3.tom@googlemail.com>, 2015
|
||||||
# Xavier Ordoquy <xordoquy@linovia.com>, 2015
|
# Xavier Ordoquy <xordoquy@linovia.com>, 2015
|
||||||
|
# stefan6419846, 2025
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Django REST framework\n"
|
"Project-Id-Version: Django REST framework\n"
|
||||||
|
@ -27,19 +28,19 @@ msgstr ""
|
||||||
|
|
||||||
#: authentication.py:70
|
#: authentication.py:70
|
||||||
msgid "Invalid basic header. No credentials provided."
|
msgid "Invalid basic header. No credentials provided."
|
||||||
msgstr "Ungültiger basic header. Keine Zugangsdaten angegeben."
|
msgstr "Ungültiger Basic Header. Keine Zugangsdaten angegeben."
|
||||||
|
|
||||||
#: authentication.py:73
|
#: authentication.py:73
|
||||||
msgid "Invalid basic header. Credentials string should not contain spaces."
|
msgid "Invalid basic header. Credentials string should not contain spaces."
|
||||||
msgstr "Ungültiger basic header. Zugangsdaten sollen keine Leerzeichen enthalten."
|
msgstr "Ungültiger Basic Header. Zugangsdaten sollen keine Leerzeichen enthalten."
|
||||||
|
|
||||||
#: authentication.py:83
|
#: authentication.py:83
|
||||||
msgid "Invalid basic header. Credentials not correctly base64 encoded."
|
msgid "Invalid basic header. Credentials not correctly base64 encoded."
|
||||||
msgstr "Ungültiger basic header. Zugangsdaten sind nicht korrekt mit base64 kodiert."
|
msgstr "Ungültiger Basic Header. Zugangsdaten sind nicht korrekt mit base64 kodiert."
|
||||||
|
|
||||||
#: authentication.py:101
|
#: authentication.py:101
|
||||||
msgid "Invalid username/password."
|
msgid "Invalid username/password."
|
||||||
msgstr "Ungültiger Benutzername/Passwort"
|
msgstr "Ungültiger Benutzername/Passwort."
|
||||||
|
|
||||||
#: authentication.py:104 authentication.py:206
|
#: authentication.py:104 authentication.py:206
|
||||||
msgid "User inactive or deleted."
|
msgid "User inactive or deleted."
|
||||||
|
@ -47,16 +48,16 @@ msgstr "Benutzer inaktiv oder gelöscht."
|
||||||
|
|
||||||
#: authentication.py:184
|
#: authentication.py:184
|
||||||
msgid "Invalid token header. No credentials provided."
|
msgid "Invalid token header. No credentials provided."
|
||||||
msgstr "Ungültiger token header. Keine Zugangsdaten angegeben."
|
msgstr "Ungültiger Token Header. Keine Zugangsdaten angegeben."
|
||||||
|
|
||||||
#: authentication.py:187
|
#: authentication.py:187
|
||||||
msgid "Invalid token header. Token string should not contain spaces."
|
msgid "Invalid token header. Token string should not contain spaces."
|
||||||
msgstr "Ungültiger token header. Zugangsdaten sollen keine Leerzeichen enthalten."
|
msgstr "Ungültiger Token Header. Zugangsdaten sollen keine Leerzeichen enthalten."
|
||||||
|
|
||||||
#: authentication.py:193
|
#: authentication.py:193
|
||||||
msgid ""
|
msgid ""
|
||||||
"Invalid token header. Token string should not contain invalid characters."
|
"Invalid token header. Token string should not contain invalid characters."
|
||||||
msgstr "Ungültiger Token Header. Tokens dürfen keine ungültigen Zeichen enthalten."
|
msgstr "Ungültiger Token Header. Zugangsdaten dürfen keine ungültigen Zeichen enthalten."
|
||||||
|
|
||||||
#: authentication.py:203
|
#: authentication.py:203
|
||||||
msgid "Invalid token."
|
msgid "Invalid token."
|
||||||
|
@ -108,7 +109,7 @@ msgstr "Ein Serverfehler ist aufgetreten."
|
||||||
|
|
||||||
#: exceptions.py:142
|
#: exceptions.py:142
|
||||||
msgid "Invalid input."
|
msgid "Invalid input."
|
||||||
msgstr ""
|
msgstr "Ungültige Eingabe."
|
||||||
|
|
||||||
#: exceptions.py:161
|
#: exceptions.py:161
|
||||||
msgid "Malformed request."
|
msgid "Malformed request."
|
||||||
|
@ -124,7 +125,7 @@ msgstr "Anmeldedaten fehlen."
|
||||||
|
|
||||||
#: exceptions.py:179
|
#: exceptions.py:179
|
||||||
msgid "You do not have permission to perform this action."
|
msgid "You do not have permission to perform this action."
|
||||||
msgstr "Sie sind nicht berechtigt diese Aktion durchzuführen."
|
msgstr "Sie sind nicht berechtigt, diese Aktion durchzuführen."
|
||||||
|
|
||||||
#: exceptions.py:185
|
#: exceptions.py:185
|
||||||
msgid "Not found."
|
msgid "Not found."
|
||||||
|
@ -151,17 +152,17 @@ msgstr "Die Anfrage wurde gedrosselt."
|
||||||
#: exceptions.py:224
|
#: exceptions.py:224
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected available in {wait} second."
|
msgid "Expected available in {wait} second."
|
||||||
msgstr ""
|
msgstr "Erwarte Verfügbarkeit in {wait} Sekunde."
|
||||||
|
|
||||||
#: exceptions.py:225
|
#: exceptions.py:225
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected available in {wait} seconds."
|
msgid "Expected available in {wait} seconds."
|
||||||
msgstr ""
|
msgstr "Erwarte Verfügbarkeit in {wait} Sekunden."
|
||||||
|
|
||||||
#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
|
#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
|
||||||
#: validators.py:183
|
#: validators.py:183
|
||||||
msgid "This field is required."
|
msgid "This field is required."
|
||||||
msgstr "Dieses Feld ist erforderlich."
|
msgstr "Dieses Feld ist zwingend erforderlich."
|
||||||
|
|
||||||
#: fields.py:317
|
#: fields.py:317
|
||||||
msgid "This field may not be null."
|
msgid "This field may not be null."
|
||||||
|
@ -169,11 +170,11 @@ msgstr "Dieses Feld darf nicht null sein."
|
||||||
|
|
||||||
#: fields.py:701
|
#: fields.py:701
|
||||||
msgid "Must be a valid boolean."
|
msgid "Must be a valid boolean."
|
||||||
msgstr ""
|
msgstr "Muss ein gültiger Wahrheitswert sein."
|
||||||
|
|
||||||
#: fields.py:766
|
#: fields.py:766
|
||||||
msgid "Not a valid string."
|
msgid "Not a valid string."
|
||||||
msgstr ""
|
msgstr "Kein gültiger String."
|
||||||
|
|
||||||
#: fields.py:767
|
#: fields.py:767
|
||||||
msgid "This field may not be blank."
|
msgid "This field may not be blank."
|
||||||
|
@ -207,7 +208,7 @@ msgstr "Gib ein gültiges \"slug\" aus Buchstaben, Ziffern, Unterstrichen und Mi
|
||||||
msgid ""
|
msgid ""
|
||||||
"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
|
"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
|
||||||
"or hyphens."
|
"or hyphens."
|
||||||
msgstr ""
|
msgstr "Gib ein gültiges \"slug\" aus Unicode-Buchstaben, Ziffern, Unterstrichen und Minuszeichen ein."
|
||||||
|
|
||||||
#: fields.py:854
|
#: fields.py:854
|
||||||
msgid "Enter a valid URL."
|
msgid "Enter a valid URL."
|
||||||
|
@ -215,11 +216,11 @@ msgstr "Gib eine gültige URL ein."
|
||||||
|
|
||||||
#: fields.py:867
|
#: fields.py:867
|
||||||
msgid "Must be a valid UUID."
|
msgid "Must be a valid UUID."
|
||||||
msgstr ""
|
msgstr "Muss eine gültige UUID sein."
|
||||||
|
|
||||||
#: fields.py:903
|
#: fields.py:903
|
||||||
msgid "Enter a valid IPv4 or IPv6 address."
|
msgid "Enter a valid IPv4 or IPv6 address."
|
||||||
msgstr "Geben Sie eine gültige IPv4 oder IPv6 Adresse an"
|
msgstr "Geben Sie eine gültige IPv4 oder IPv6 Adresse an."
|
||||||
|
|
||||||
#: fields.py:931
|
#: fields.py:931
|
||||||
msgid "A valid integer is required."
|
msgid "A valid integer is required."
|
||||||
|
@ -273,11 +274,11 @@ msgstr "Erwarte eine Datums- und Zeitangabe, erhielt aber ein Datum."
|
||||||
#: fields.py:1150
|
#: fields.py:1150
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Invalid datetime for the timezone \"{timezone}\"."
|
msgid "Invalid datetime for the timezone \"{timezone}\"."
|
||||||
msgstr ""
|
msgstr "Ungültige Datumsangabe für die Zeitzone \"{timezone}\"."
|
||||||
|
|
||||||
#: fields.py:1151
|
#: fields.py:1151
|
||||||
msgid "Datetime value out of range."
|
msgid "Datetime value out of range."
|
||||||
msgstr ""
|
msgstr "Datumsangabe außerhalb des Bereichs."
|
||||||
|
|
||||||
#: fields.py:1236
|
#: fields.py:1236
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -358,12 +359,12 @@ msgstr "Diese Liste darf nicht leer sein."
|
||||||
#: fields.py:1605
|
#: fields.py:1605
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has at least {min_length} elements."
|
msgid "Ensure this field has at least {min_length} elements."
|
||||||
msgstr ""
|
msgstr "Dieses Feld muss mindestens {min_length} Einträge enthalten."
|
||||||
|
|
||||||
#: fields.py:1606
|
#: fields.py:1606
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has no more than {max_length} elements."
|
msgid "Ensure this field has no more than {max_length} elements."
|
||||||
msgstr ""
|
msgstr "Dieses Feld darf nicht mehr als {max_length} Einträge enthalten."
|
||||||
|
|
||||||
#: fields.py:1682
|
#: fields.py:1682
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -372,7 +373,7 @@ msgstr "Erwartete ein Dictionary mit Elementen, erhielt aber den Typ \"{input_ty
|
||||||
|
|
||||||
#: fields.py:1683
|
#: fields.py:1683
|
||||||
msgid "This dictionary may not be empty."
|
msgid "This dictionary may not be empty."
|
||||||
msgstr ""
|
msgstr "Dieses Dictionary darf nicht leer sein."
|
||||||
|
|
||||||
#: fields.py:1755
|
#: fields.py:1755
|
||||||
msgid "Value must be valid JSON."
|
msgid "Value must be valid JSON."
|
||||||
|
@ -384,7 +385,7 @@ msgstr "Suche"
|
||||||
|
|
||||||
#: filters.py:50
|
#: filters.py:50
|
||||||
msgid "A search term."
|
msgid "A search term."
|
||||||
msgstr ""
|
msgstr "Ein Suchbegriff."
|
||||||
|
|
||||||
#: filters.py:180 templates/rest_framework/filters/ordering.html:3
|
#: filters.py:180 templates/rest_framework/filters/ordering.html:3
|
||||||
msgid "Ordering"
|
msgid "Ordering"
|
||||||
|
@ -392,7 +393,7 @@ msgstr "Sortierung"
|
||||||
|
|
||||||
#: filters.py:181
|
#: filters.py:181
|
||||||
msgid "Which field to use when ordering the results."
|
msgid "Which field to use when ordering the results."
|
||||||
msgstr ""
|
msgstr "Feld, das zum Sortieren der Ergebnisse verwendet werden soll."
|
||||||
|
|
||||||
#: filters.py:287
|
#: filters.py:287
|
||||||
msgid "ascending"
|
msgid "ascending"
|
||||||
|
@ -404,11 +405,11 @@ msgstr "Absteigend"
|
||||||
|
|
||||||
#: pagination.py:174
|
#: pagination.py:174
|
||||||
msgid "A page number within the paginated result set."
|
msgid "A page number within the paginated result set."
|
||||||
msgstr ""
|
msgstr "Eine Seitenzahl in der paginierten Ergebnismenge."
|
||||||
|
|
||||||
#: pagination.py:179 pagination.py:372 pagination.py:590
|
#: pagination.py:179 pagination.py:372 pagination.py:590
|
||||||
msgid "Number of results to return per page."
|
msgid "Number of results to return per page."
|
||||||
msgstr ""
|
msgstr "Anzahl der pro Seite zurückzugebenden Ergebnisse."
|
||||||
|
|
||||||
#: pagination.py:189
|
#: pagination.py:189
|
||||||
msgid "Invalid page."
|
msgid "Invalid page."
|
||||||
|
@ -416,11 +417,11 @@ msgstr "Ungültige Seite."
|
||||||
|
|
||||||
#: pagination.py:374
|
#: pagination.py:374
|
||||||
msgid "The initial index from which to return the results."
|
msgid "The initial index from which to return the results."
|
||||||
msgstr ""
|
msgstr "Der initiale Index, von dem die Ergebnisse zurückgegeben werden sollen."
|
||||||
|
|
||||||
#: pagination.py:581
|
#: pagination.py:581
|
||||||
msgid "The pagination cursor value."
|
msgid "The pagination cursor value."
|
||||||
msgstr ""
|
msgstr "Der Zeigerwert für die Paginierung"
|
||||||
|
|
||||||
#: pagination.py:583
|
#: pagination.py:583
|
||||||
msgid "Invalid cursor"
|
msgid "Invalid cursor"
|
||||||
|
@ -434,7 +435,7 @@ msgstr "Ungültiger pk \"{pk_value}\" - Object existiert nicht."
|
||||||
#: relations.py:247
|
#: relations.py:247
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Incorrect type. Expected pk value, received {data_type}."
|
msgid "Incorrect type. Expected pk value, received {data_type}."
|
||||||
msgstr "Falscher Typ. Erwarte pk Wert, erhielt aber {data_type}."
|
msgstr "Falscher Typ. Erwarte pk-Wert, erhielt aber {data_type}."
|
||||||
|
|
||||||
#: relations.py:280
|
#: relations.py:280
|
||||||
msgid "Invalid hyperlink - No URL match."
|
msgid "Invalid hyperlink - No URL match."
|
||||||
|
@ -451,7 +452,7 @@ msgstr "Ungültiger Hyperlink - Objekt existiert nicht."
|
||||||
#: relations.py:283
|
#: relations.py:283
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Incorrect type. Expected URL string, received {data_type}."
|
msgid "Incorrect type. Expected URL string, received {data_type}."
|
||||||
msgstr "Falscher Typ. Erwarte URL Zeichenkette, erhielt aber {data_type}."
|
msgstr "Falscher Typ. Erwarte URL-Zeichenkette, erhielt aber {data_type}."
|
||||||
|
|
||||||
#: relations.py:448
|
#: relations.py:448
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -464,20 +465,20 @@ msgstr "Ungültiger Wert."
|
||||||
|
|
||||||
#: schemas/utils.py:32
|
#: schemas/utils.py:32
|
||||||
msgid "unique integer value"
|
msgid "unique integer value"
|
||||||
msgstr ""
|
msgstr "eindeutiger Ganzzahl-Wert"
|
||||||
|
|
||||||
#: schemas/utils.py:34
|
#: schemas/utils.py:34
|
||||||
msgid "UUID string"
|
msgid "UUID string"
|
||||||
msgstr ""
|
msgstr "UUID-String"
|
||||||
|
|
||||||
#: schemas/utils.py:36
|
#: schemas/utils.py:36
|
||||||
msgid "unique value"
|
msgid "unique value"
|
||||||
msgstr ""
|
msgstr "eindeutiger Wert"
|
||||||
|
|
||||||
#: schemas/utils.py:38
|
#: schemas/utils.py:38
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "A {value_type} identifying this {name}."
|
msgid "A {value_type} identifying this {name}."
|
||||||
msgstr ""
|
msgstr "Ein {value_type}, der {name} identifiziert."
|
||||||
|
|
||||||
#: serializers.py:337
|
#: serializers.py:337
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -487,7 +488,7 @@ msgstr "Ungültige Daten. Dictionary erwartet, aber {datatype} erhalten."
|
||||||
#: templates/rest_framework/admin.html:116
|
#: templates/rest_framework/admin.html:116
|
||||||
#: templates/rest_framework/base.html:136
|
#: templates/rest_framework/base.html:136
|
||||||
msgid "Extra Actions"
|
msgid "Extra Actions"
|
||||||
msgstr ""
|
msgstr "Zusätzliche Aktionen"
|
||||||
|
|
||||||
#: templates/rest_framework/admin.html:130
|
#: templates/rest_framework/admin.html:130
|
||||||
#: templates/rest_framework/base.html:150
|
#: templates/rest_framework/base.html:150
|
||||||
|
@ -496,27 +497,27 @@ msgstr "Filter"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:37
|
#: templates/rest_framework/base.html:37
|
||||||
msgid "navbar"
|
msgid "navbar"
|
||||||
msgstr ""
|
msgstr "Navigation"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:75
|
#: templates/rest_framework/base.html:75
|
||||||
msgid "content"
|
msgid "content"
|
||||||
msgstr ""
|
msgstr "Inhalt"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:78
|
#: templates/rest_framework/base.html:78
|
||||||
msgid "request form"
|
msgid "request form"
|
||||||
msgstr ""
|
msgstr "Anfrage-Formular"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:157
|
#: templates/rest_framework/base.html:157
|
||||||
msgid "main content"
|
msgid "main content"
|
||||||
msgstr ""
|
msgstr "Hauptteil"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:173
|
#: templates/rest_framework/base.html:173
|
||||||
msgid "request info"
|
msgid "request info"
|
||||||
msgstr ""
|
msgstr "Anfrage-Informationen"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:177
|
#: templates/rest_framework/base.html:177
|
||||||
msgid "response info"
|
msgid "response info"
|
||||||
msgstr ""
|
msgstr "Antwort-Informationen"
|
||||||
|
|
||||||
#: templates/rest_framework/horizontal/radio.html:4
|
#: templates/rest_framework/horizontal/radio.html:4
|
||||||
#: templates/rest_framework/inline/radio.html:3
|
#: templates/rest_framework/inline/radio.html:3
|
||||||
|
@ -542,7 +543,7 @@ msgstr "Die Felder {field_names} müssen eine eindeutige Menge bilden."
|
||||||
#: validators.py:171
|
#: validators.py:171
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Surrogate characters are not allowed: U+{code_point:X}."
|
msgid "Surrogate characters are not allowed: U+{code_point:X}."
|
||||||
msgstr ""
|
msgstr "Ersatzzeichen sind nicht erlaubt: U+{code_point:X}."
|
||||||
|
|
||||||
#: validators.py:243
|
#: validators.py:243
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -565,7 +566,7 @@ msgstr "Ungültige Version in der \"Accept\" Kopfzeile."
|
||||||
|
|
||||||
#: versioning.py:71
|
#: versioning.py:71
|
||||||
msgid "Invalid version in URL path."
|
msgid "Invalid version in URL path."
|
||||||
msgstr "Ungültige Version im URL Pfad."
|
msgstr "Ungültige Version im URL-Pfad."
|
||||||
|
|
||||||
#: versioning.py:116
|
#: versioning.py:116
|
||||||
msgid "Invalid version in URL path. Does not match any version namespace."
|
msgid "Invalid version in URL path. Does not match any version namespace."
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
# Aryan Baghi <ar.baghi.ce@gmail.com>, 2020
|
# Aryan Baghi <ar.baghi.ce@gmail.com>, 2020
|
||||||
# Omid Zarin <zarinpy@gmail.com>, 2019
|
# Omid Zarin <zarinpy@gmail.com>, 2019
|
||||||
# Xavier Ordoquy <xordoquy@linovia.com>, 2020
|
# Xavier Ordoquy <xordoquy@linovia.com>, 2020
|
||||||
|
# Sina Amini <general.sina.amini.20@gmail.com>, 2024
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Django REST framework\n"
|
"Project-Id-Version: Django REST framework\n"
|
||||||
|
@ -104,7 +105,7 @@ msgstr "خطای سمت سرور رخ داده است."
|
||||||
|
|
||||||
#: exceptions.py:142
|
#: exceptions.py:142
|
||||||
msgid "Invalid input."
|
msgid "Invalid input."
|
||||||
msgstr ""
|
msgstr "ورودی نامعتبر"
|
||||||
|
|
||||||
#: exceptions.py:161
|
#: exceptions.py:161
|
||||||
msgid "Malformed request."
|
msgid "Malformed request."
|
||||||
|
@ -147,12 +148,12 @@ msgstr "تعداد درخواستهای شما محدود شده است."
|
||||||
#: exceptions.py:224
|
#: exceptions.py:224
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected available in {wait} second."
|
msgid "Expected available in {wait} second."
|
||||||
msgstr ""
|
msgstr "انتظار میرود در {wait} ثانیه در دسترس باشد."
|
||||||
|
|
||||||
#: exceptions.py:225
|
#: exceptions.py:225
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected available in {wait} seconds."
|
msgid "Expected available in {wait} seconds."
|
||||||
msgstr ""
|
msgstr "انتظار میرود در {wait} ثانیه در دسترس باشد."
|
||||||
|
|
||||||
#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
|
#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
|
||||||
#: validators.py:183
|
#: validators.py:183
|
||||||
|
@ -165,11 +166,11 @@ msgstr "این مقدار نباید توهی باشد."
|
||||||
|
|
||||||
#: fields.py:701
|
#: fields.py:701
|
||||||
msgid "Must be a valid boolean."
|
msgid "Must be a valid boolean."
|
||||||
msgstr ""
|
msgstr "باید یک مقدار منطقی(بولی) معتبر باشد."
|
||||||
|
|
||||||
#: fields.py:766
|
#: fields.py:766
|
||||||
msgid "Not a valid string."
|
msgid "Not a valid string."
|
||||||
msgstr ""
|
msgstr "یک رشته معتبر نیست."
|
||||||
|
|
||||||
#: fields.py:767
|
#: fields.py:767
|
||||||
msgid "This field may not be blank."
|
msgid "This field may not be blank."
|
||||||
|
@ -187,7 +188,7 @@ msgstr "مطمعن شوید طول این مقدار حداقل {min_length} ا
|
||||||
|
|
||||||
#: fields.py:816
|
#: fields.py:816
|
||||||
msgid "Enter a valid email address."
|
msgid "Enter a valid email address."
|
||||||
msgstr "پست الکترونیکی صحبح وارد کنید."
|
msgstr "پست الکترونیکی صحیح وارد کنید."
|
||||||
|
|
||||||
#: fields.py:827
|
#: fields.py:827
|
||||||
msgid "This value does not match the required pattern."
|
msgid "This value does not match the required pattern."
|
||||||
|
@ -211,7 +212,7 @@ msgstr "یک URL معتبر وارد کنید"
|
||||||
|
|
||||||
#: fields.py:867
|
#: fields.py:867
|
||||||
msgid "Must be a valid UUID."
|
msgid "Must be a valid UUID."
|
||||||
msgstr ""
|
msgstr "باید یک UUID معتبر باشد."
|
||||||
|
|
||||||
#: fields.py:903
|
#: fields.py:903
|
||||||
msgid "Enter a valid IPv4 or IPv6 address."
|
msgid "Enter a valid IPv4 or IPv6 address."
|
||||||
|
@ -269,11 +270,11 @@ msgstr "باید datetime باشد اما date دریافت شد."
|
||||||
#: fields.py:1150
|
#: fields.py:1150
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Invalid datetime for the timezone \"{timezone}\"."
|
msgid "Invalid datetime for the timezone \"{timezone}\"."
|
||||||
msgstr ""
|
msgstr "تاریخ و زمان برای منطقه زمانی \"{timezone}\" نامعتبر است."
|
||||||
|
|
||||||
#: fields.py:1151
|
#: fields.py:1151
|
||||||
msgid "Datetime value out of range."
|
msgid "Datetime value out of range."
|
||||||
msgstr ""
|
msgstr "مقدار تاریخ و زمان خارج از محدوده است."
|
||||||
|
|
||||||
#: fields.py:1236
|
#: fields.py:1236
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -354,12 +355,12 @@ msgstr "این لیست نمی تواند خالی باشد"
|
||||||
#: fields.py:1605
|
#: fields.py:1605
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has at least {min_length} elements."
|
msgid "Ensure this field has at least {min_length} elements."
|
||||||
msgstr ""
|
msgstr "اطمینان حاصل کنید که این فیلد حداقل {min_length} عنصر دارد."
|
||||||
|
|
||||||
#: fields.py:1606
|
#: fields.py:1606
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has no more than {max_length} elements."
|
msgid "Ensure this field has no more than {max_length} elements."
|
||||||
msgstr ""
|
msgstr "اطمینان حاصل کنید که این فیلد بیش از {max_length} عنصر ندارد."
|
||||||
|
|
||||||
#: fields.py:1682
|
#: fields.py:1682
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -368,7 +369,7 @@ msgstr "باید دیکشنری از آیتم ها ارسال می شد، اما
|
||||||
|
|
||||||
#: fields.py:1683
|
#: fields.py:1683
|
||||||
msgid "This dictionary may not be empty."
|
msgid "This dictionary may not be empty."
|
||||||
msgstr ""
|
msgstr "این دیکشنری نمیتواند خالی باشد."
|
||||||
|
|
||||||
#: fields.py:1755
|
#: fields.py:1755
|
||||||
msgid "Value must be valid JSON."
|
msgid "Value must be valid JSON."
|
||||||
|
@ -380,7 +381,7 @@ msgstr "جستجو"
|
||||||
|
|
||||||
#: filters.py:50
|
#: filters.py:50
|
||||||
msgid "A search term."
|
msgid "A search term."
|
||||||
msgstr ""
|
msgstr "یک عبارت جستجو."
|
||||||
|
|
||||||
#: filters.py:180 templates/rest_framework/filters/ordering.html:3
|
#: filters.py:180 templates/rest_framework/filters/ordering.html:3
|
||||||
msgid "Ordering"
|
msgid "Ordering"
|
||||||
|
@ -388,7 +389,7 @@ msgstr "ترتیب"
|
||||||
|
|
||||||
#: filters.py:181
|
#: filters.py:181
|
||||||
msgid "Which field to use when ordering the results."
|
msgid "Which field to use when ordering the results."
|
||||||
msgstr ""
|
msgstr "کدام فیلد باید هنگام مرتبسازی نتایج استفاده شود."
|
||||||
|
|
||||||
#: filters.py:287
|
#: filters.py:287
|
||||||
msgid "ascending"
|
msgid "ascending"
|
||||||
|
@ -400,11 +401,11 @@ msgstr "نزولی"
|
||||||
|
|
||||||
#: pagination.py:174
|
#: pagination.py:174
|
||||||
msgid "A page number within the paginated result set."
|
msgid "A page number within the paginated result set."
|
||||||
msgstr ""
|
msgstr "یک شماره صفحه در مجموعه نتایج صفحهبندی شده."
|
||||||
|
|
||||||
#: pagination.py:179 pagination.py:372 pagination.py:590
|
#: pagination.py:179 pagination.py:372 pagination.py:590
|
||||||
msgid "Number of results to return per page."
|
msgid "Number of results to return per page."
|
||||||
msgstr ""
|
msgstr "تعداد نتایج برای نمایش در هر صفحه."
|
||||||
|
|
||||||
#: pagination.py:189
|
#: pagination.py:189
|
||||||
msgid "Invalid page."
|
msgid "Invalid page."
|
||||||
|
@ -412,11 +413,11 @@ msgstr "صفحه نامعتبر"
|
||||||
|
|
||||||
#: pagination.py:374
|
#: pagination.py:374
|
||||||
msgid "The initial index from which to return the results."
|
msgid "The initial index from which to return the results."
|
||||||
msgstr ""
|
msgstr "ایندکس اولیهای که از آن نتایج بازگردانده میشود."
|
||||||
|
|
||||||
#: pagination.py:581
|
#: pagination.py:581
|
||||||
msgid "The pagination cursor value."
|
msgid "The pagination cursor value."
|
||||||
msgstr ""
|
msgstr "مقدار نشانگر صفحهبندی."
|
||||||
|
|
||||||
#: pagination.py:583
|
#: pagination.py:583
|
||||||
msgid "Invalid cursor"
|
msgid "Invalid cursor"
|
||||||
|
@ -460,20 +461,20 @@ msgstr "مقدار نامعتبر."
|
||||||
|
|
||||||
#: schemas/utils.py:32
|
#: schemas/utils.py:32
|
||||||
msgid "unique integer value"
|
msgid "unique integer value"
|
||||||
msgstr ""
|
msgstr "مقداد عدد یکتا"
|
||||||
|
|
||||||
#: schemas/utils.py:34
|
#: schemas/utils.py:34
|
||||||
msgid "UUID string"
|
msgid "UUID string"
|
||||||
msgstr ""
|
msgstr "رشته UUID"
|
||||||
|
|
||||||
#: schemas/utils.py:36
|
#: schemas/utils.py:36
|
||||||
msgid "unique value"
|
msgid "unique value"
|
||||||
msgstr ""
|
msgstr "مقدار یکتا"
|
||||||
|
|
||||||
#: schemas/utils.py:38
|
#: schemas/utils.py:38
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "A {value_type} identifying this {name}."
|
msgid "A {value_type} identifying this {name}."
|
||||||
msgstr ""
|
msgstr "یک {value_type} که این {name} را شناسایی میکند."
|
||||||
|
|
||||||
#: serializers.py:337
|
#: serializers.py:337
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -483,7 +484,7 @@ msgstr "داده نامعتبر. باید دیکشنری ارسال می شد،
|
||||||
#: templates/rest_framework/admin.html:116
|
#: templates/rest_framework/admin.html:116
|
||||||
#: templates/rest_framework/base.html:136
|
#: templates/rest_framework/base.html:136
|
||||||
msgid "Extra Actions"
|
msgid "Extra Actions"
|
||||||
msgstr ""
|
msgstr "اقدامات اضافی"
|
||||||
|
|
||||||
#: templates/rest_framework/admin.html:130
|
#: templates/rest_framework/admin.html:130
|
||||||
#: templates/rest_framework/base.html:150
|
#: templates/rest_framework/base.html:150
|
||||||
|
@ -492,27 +493,27 @@ msgstr "فیلترها"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:37
|
#: templates/rest_framework/base.html:37
|
||||||
msgid "navbar"
|
msgid "navbar"
|
||||||
msgstr ""
|
msgstr "نوار ناوبری"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:75
|
#: templates/rest_framework/base.html:75
|
||||||
msgid "content"
|
msgid "content"
|
||||||
msgstr ""
|
msgstr "محتوا"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:78
|
#: templates/rest_framework/base.html:78
|
||||||
msgid "request form"
|
msgid "request form"
|
||||||
msgstr ""
|
msgstr "فرم درخواست"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:157
|
#: templates/rest_framework/base.html:157
|
||||||
msgid "main content"
|
msgid "main content"
|
||||||
msgstr ""
|
msgstr "محتوای اصلی"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:173
|
#: templates/rest_framework/base.html:173
|
||||||
msgid "request info"
|
msgid "request info"
|
||||||
msgstr ""
|
msgstr "اطلاعات درخواست"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:177
|
#: templates/rest_framework/base.html:177
|
||||||
msgid "response info"
|
msgid "response info"
|
||||||
msgstr ""
|
msgstr "اطلاعات پاسخ"
|
||||||
|
|
||||||
#: templates/rest_framework/horizontal/radio.html:4
|
#: templates/rest_framework/horizontal/radio.html:4
|
||||||
#: templates/rest_framework/inline/radio.html:3
|
#: templates/rest_framework/inline/radio.html:3
|
||||||
|
@ -538,7 +539,7 @@ msgstr "فیلدهای {field_names} باید یک مجموعه یکتا باش
|
||||||
#: validators.py:171
|
#: validators.py:171
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Surrogate characters are not allowed: U+{code_point:X}."
|
msgid "Surrogate characters are not allowed: U+{code_point:X}."
|
||||||
msgstr ""
|
msgstr "کاراکترهای جایگزین مجاز نیستند: U+{code_point:X}."
|
||||||
|
|
||||||
#: validators.py:243
|
#: validators.py:243
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
# Aryan Baghi <ar.baghi.ce@gmail.com>, 2020
|
# Aryan Baghi <ar.baghi.ce@gmail.com>, 2020
|
||||||
# Omid Zarin <zarinpy@gmail.com>, 2019
|
# Omid Zarin <zarinpy@gmail.com>, 2019
|
||||||
# Xavier Ordoquy <xordoquy@linovia.com>, 2020
|
# Xavier Ordoquy <xordoquy@linovia.com>, 2020
|
||||||
|
# Sina Amini <general.sina.amini.20@gmail.com>, 2024
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Django REST framework\n"
|
"Project-Id-Version: Django REST framework\n"
|
||||||
|
@ -187,7 +188,7 @@ msgstr "مطمعن شوید طول این مقدار حداقل {min_length} ا
|
||||||
|
|
||||||
#: fields.py:816
|
#: fields.py:816
|
||||||
msgid "Enter a valid email address."
|
msgid "Enter a valid email address."
|
||||||
msgstr "پست الکترونیکی صحبح وارد کنید."
|
msgstr "پست الکترونیکی صحیح وارد کنید."
|
||||||
|
|
||||||
#: fields.py:827
|
#: fields.py:827
|
||||||
msgid "This value does not match the required pattern."
|
msgid "This value does not match the required pattern."
|
||||||
|
|
|
@ -3,35 +3,35 @@
|
||||||
# This file is distributed under the same license as the PACKAGE package.
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
#
|
#
|
||||||
# Translators:
|
# Translators:
|
||||||
# GarakdongBigBoy <novelview9@gmail.com>, 2017
|
# JAEGYUN JUNG <twicegoddessana1229@gmail.com>, 2024
|
||||||
# Hochul Kwak <rhkrghcjf@gmail.com>, 2018
|
# Hochul Kwak <rhkrghcjf@gmail.com>, 2018
|
||||||
|
# GarakdongBigBoy <novelview9@gmail.com>, 2017
|
||||||
# Joon Hwan 김준환 <xncbf12@gmail.com>, 2017
|
# Joon Hwan 김준환 <xncbf12@gmail.com>, 2017
|
||||||
# SUN CHOI <best2378@gmail.com>, 2015
|
# SUN CHOI <best2378@gmail.com>, 2015
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Django REST framework\n"
|
"Project-Id-Version: Django REST framework\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-10-13 21:45+0200\n"
|
"POT-Creation-Date: 2024-10-22 16:13+0900\n"
|
||||||
"PO-Revision-Date: 2020-10-13 19:45+0000\n"
|
"PO-Revision-Date: 2020-10-13 19:45+0000\n"
|
||||||
"Last-Translator: Xavier Ordoquy <xordoquy@linovia.com>\n"
|
"Last-Translator: JAEGYUN JUNG <twicegoddessana1229@gmail.com>\n"
|
||||||
"Language-Team: Korean (Korea) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ko_KR/)\n"
|
"Language: ko_KR\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Language: ko_KR\n"
|
|
||||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
|
|
||||||
#: authentication.py:70
|
#: authentication.py:70
|
||||||
msgid "Invalid basic header. No credentials provided."
|
msgid "Invalid basic header. No credentials provided."
|
||||||
msgstr "기본 헤더(basic header)가 유효하지 않습니다. 인증데이터(credentials)가 제공되지 않았습니다."
|
msgstr "기본 헤더가 유효하지 않습니다. 인증 데이터가 제공되지 않았습니다."
|
||||||
|
|
||||||
#: authentication.py:73
|
#: authentication.py:73
|
||||||
msgid "Invalid basic header. Credentials string should not contain spaces."
|
msgid "Invalid basic header. Credentials string should not contain spaces."
|
||||||
msgstr "기본 헤더(basic header)가 유효하지 않습니다. 인증데이터(credentials) 문자열은 빈칸(spaces)을 포함하지 않아야 합니다."
|
msgstr "기본 헤더가 유효하지 않습니다. 인증 데이터 문자열은 공백을 포함하지 않아야 합니다."
|
||||||
|
|
||||||
#: authentication.py:83
|
#: authentication.py:84
|
||||||
msgid "Invalid basic header. Credentials not correctly base64 encoded."
|
msgid "Invalid basic header. Credentials not correctly base64 encoded."
|
||||||
msgstr "기본 헤더(basic header)가 유효하지 않습니다. 인증데이터(credentials)가 base64로 적절히 부호화(encode)되지 않았습니다."
|
msgstr "기본 헤더가 유효하지 않습니다. 인증 데이터가 올바르게 base64 인코딩되지 않았습니다."
|
||||||
|
|
||||||
#: authentication.py:101
|
#: authentication.py:101
|
||||||
msgid "Invalid username/password."
|
msgid "Invalid username/password."
|
||||||
|
@ -43,11 +43,11 @@ msgstr "계정이 중지되었거나 삭제되었습니다."
|
||||||
|
|
||||||
#: authentication.py:184
|
#: authentication.py:184
|
||||||
msgid "Invalid token header. No credentials provided."
|
msgid "Invalid token header. No credentials provided."
|
||||||
msgstr "토큰 헤더가 유효하지 않습니다. 인증데이터(credentials)가 제공되지 않았습니다."
|
msgstr "토큰 헤더가 유효하지 않습니다. 인증 데이터가 제공되지 않았습니다."
|
||||||
|
|
||||||
#: authentication.py:187
|
#: authentication.py:187
|
||||||
msgid "Invalid token header. Token string should not contain spaces."
|
msgid "Invalid token header. Token string should not contain spaces."
|
||||||
msgstr "토큰 헤더가 유효하지 않습니다. 토큰 문자열은 빈칸(spaces)을 포함하지 않아야 합니다."
|
msgstr "토큰 헤더가 유효하지 않습니다. 토큰 문자열은 공백을 포함하지 않아야 합니다."
|
||||||
|
|
||||||
#: authentication.py:193
|
#: authentication.py:193
|
||||||
msgid ""
|
msgid ""
|
||||||
|
@ -58,6 +58,10 @@ msgstr "토큰 헤더가 유효하지 않습니다. 토큰 문자열은 유효
|
||||||
msgid "Invalid token."
|
msgid "Invalid token."
|
||||||
msgstr "토큰이 유효하지 않습니다."
|
msgstr "토큰이 유효하지 않습니다."
|
||||||
|
|
||||||
|
#: authtoken/admin.py:28 authtoken/serializers.py:9
|
||||||
|
msgid "Username"
|
||||||
|
msgstr "사용자 이름"
|
||||||
|
|
||||||
#: authtoken/apps.py:7
|
#: authtoken/apps.py:7
|
||||||
msgid "Auth Token"
|
msgid "Auth Token"
|
||||||
msgstr "인증 토큰"
|
msgstr "인증 토큰"
|
||||||
|
@ -68,23 +72,19 @@ msgstr "키"
|
||||||
|
|
||||||
#: authtoken/models.py:16
|
#: authtoken/models.py:16
|
||||||
msgid "User"
|
msgid "User"
|
||||||
msgstr "유저"
|
msgstr "사용자"
|
||||||
|
|
||||||
#: authtoken/models.py:18
|
#: authtoken/models.py:18
|
||||||
msgid "Created"
|
msgid "Created"
|
||||||
msgstr "생성됨"
|
msgstr "생성일시"
|
||||||
|
|
||||||
#: authtoken/models.py:27 authtoken/serializers.py:19
|
#: authtoken/models.py:27 authtoken/models.py:54 authtoken/serializers.py:19
|
||||||
msgid "Token"
|
msgid "Token"
|
||||||
msgstr "토큰"
|
msgstr "토큰"
|
||||||
|
|
||||||
#: authtoken/models.py:28
|
#: authtoken/models.py:28 authtoken/models.py:55
|
||||||
msgid "Tokens"
|
msgid "Tokens"
|
||||||
msgstr ""
|
msgstr "토큰(들)"
|
||||||
|
|
||||||
#: authtoken/serializers.py:9
|
|
||||||
msgid "Username"
|
|
||||||
msgstr "유저이름"
|
|
||||||
|
|
||||||
#: authtoken/serializers.py:13
|
#: authtoken/serializers.py:13
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
|
@ -92,390 +92,398 @@ msgstr "비밀번호"
|
||||||
|
|
||||||
#: authtoken/serializers.py:35
|
#: authtoken/serializers.py:35
|
||||||
msgid "Unable to log in with provided credentials."
|
msgid "Unable to log in with provided credentials."
|
||||||
msgstr "제공된 인증데이터(credentials)로는 로그인할 수 없습니다."
|
msgstr "제공된 인증 데이터로는 로그인할 수 없습니다."
|
||||||
|
|
||||||
#: authtoken/serializers.py:38
|
#: authtoken/serializers.py:38
|
||||||
msgid "Must include \"username\" and \"password\"."
|
msgid "Must include \"username\" and \"password\"."
|
||||||
msgstr "\"아이디\"와 \"비밀번호\"를 포함해야 합니다."
|
msgstr "\"아이디\"와 \"비밀번호\"를 포함해야 합니다."
|
||||||
|
|
||||||
#: exceptions.py:102
|
#: exceptions.py:105
|
||||||
msgid "A server error occurred."
|
msgid "A server error occurred."
|
||||||
msgstr "서버 장애가 발생했습니다."
|
msgstr "서버 장애가 발생했습니다."
|
||||||
|
|
||||||
#: exceptions.py:142
|
#: exceptions.py:145
|
||||||
msgid "Invalid input."
|
msgid "Invalid input."
|
||||||
msgstr ""
|
msgstr "유효하지 않은 입력입니다."
|
||||||
|
|
||||||
#: exceptions.py:161
|
#: exceptions.py:166
|
||||||
msgid "Malformed request."
|
msgid "Malformed request."
|
||||||
msgstr "잘못된 요청입니다."
|
msgstr "잘못된 요청입니다."
|
||||||
|
|
||||||
#: exceptions.py:167
|
#: exceptions.py:172
|
||||||
msgid "Incorrect authentication credentials."
|
msgid "Incorrect authentication credentials."
|
||||||
msgstr "자격 인증데이터(authentication credentials)가 정확하지 않습니다."
|
msgstr "자격 인증 데이터가 올바르지 않습니다."
|
||||||
|
|
||||||
#: exceptions.py:173
|
#: exceptions.py:178
|
||||||
msgid "Authentication credentials were not provided."
|
msgid "Authentication credentials were not provided."
|
||||||
msgstr "자격 인증데이터(authentication credentials)가 제공되지 않았습니다."
|
msgstr "자격 인증 데이터가 제공되지 않았습니다."
|
||||||
|
|
||||||
#: exceptions.py:179
|
#: exceptions.py:184
|
||||||
msgid "You do not have permission to perform this action."
|
msgid "You do not have permission to perform this action."
|
||||||
msgstr "이 작업을 수행할 권한(permission)이 없습니다."
|
msgstr "이 작업을 수행할 권한이 없습니다."
|
||||||
|
|
||||||
#: exceptions.py:185
|
#: exceptions.py:190
|
||||||
msgid "Not found."
|
msgid "Not found."
|
||||||
msgstr "찾을 수 없습니다."
|
msgstr "찾을 수 없습니다."
|
||||||
|
|
||||||
#: exceptions.py:191
|
#: exceptions.py:196
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Method \"{method}\" not allowed."
|
msgid "Method \"{method}\" not allowed."
|
||||||
msgstr "메소드(Method) \"{method}\"는 허용되지 않습니다."
|
msgstr "메서드 \"{method}\"는 허용되지 않습니다."
|
||||||
|
|
||||||
#: exceptions.py:202
|
#: exceptions.py:207
|
||||||
msgid "Could not satisfy the request Accept header."
|
msgid "Could not satisfy the request Accept header."
|
||||||
msgstr "Accept header 요청을 만족할 수 없습니다."
|
msgstr "요청 Accept 헤더를 만족시킬 수 없습니다."
|
||||||
|
|
||||||
#: exceptions.py:212
|
#: exceptions.py:217
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Unsupported media type \"{media_type}\" in request."
|
msgid "Unsupported media type \"{media_type}\" in request."
|
||||||
msgstr "요청된 \"{media_type}\"가 지원되지 않는 미디어 형태입니다."
|
msgstr "요청된 \"{media_type}\"가 지원되지 않는 미디어 형태입니다."
|
||||||
|
|
||||||
#: exceptions.py:223
|
#: exceptions.py:228
|
||||||
msgid "Request was throttled."
|
msgid "Request was throttled."
|
||||||
msgstr "요청이 지연(throttled)되었습니다."
|
msgstr "요청이 제한되었습니다."
|
||||||
|
|
||||||
#: exceptions.py:224
|
#: exceptions.py:229
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected available in {wait} second."
|
msgid "Expected available in {wait} second."
|
||||||
msgstr ""
|
msgstr "{wait} 초 후에 사용 가능합니다."
|
||||||
|
|
||||||
#: exceptions.py:225
|
#: exceptions.py:230
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected available in {wait} seconds."
|
msgid "Expected available in {wait} seconds."
|
||||||
msgstr ""
|
msgstr "{wait} 초 후에 사용 가능합니다."
|
||||||
|
|
||||||
#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
|
#: fields.py:292 relations.py:240 relations.py:276 validators.py:99
|
||||||
#: validators.py:183
|
#: validators.py:219
|
||||||
msgid "This field is required."
|
msgid "This field is required."
|
||||||
msgstr "이 필드는 필수 항목입니다."
|
msgstr "이 필드는 필수 항목입니다."
|
||||||
|
|
||||||
#: fields.py:317
|
#: fields.py:293
|
||||||
msgid "This field may not be null."
|
msgid "This field may not be null."
|
||||||
msgstr "이 필드는 null일 수 없습니다."
|
msgstr "이 필드는 null일 수 없습니다."
|
||||||
|
|
||||||
#: fields.py:701
|
#: fields.py:661
|
||||||
msgid "Must be a valid boolean."
|
msgid "Must be a valid boolean."
|
||||||
msgstr ""
|
msgstr "유효한 불리언이어야 합니다."
|
||||||
|
|
||||||
#: fields.py:766
|
#: fields.py:724
|
||||||
msgid "Not a valid string."
|
msgid "Not a valid string."
|
||||||
msgstr ""
|
msgstr "유효한 문자열이 아닙니다."
|
||||||
|
|
||||||
#: fields.py:767
|
#: fields.py:725
|
||||||
msgid "This field may not be blank."
|
msgid "This field may not be blank."
|
||||||
msgstr "이 필드는 blank일 수 없습니다."
|
msgstr "이 필드는 blank일 수 없습니다."
|
||||||
|
|
||||||
#: fields.py:768 fields.py:1881
|
#: fields.py:726 fields.py:1881
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has no more than {max_length} characters."
|
msgid "Ensure this field has no more than {max_length} characters."
|
||||||
msgstr "이 필드의 글자 수가 {max_length} 이하인지 확인하십시오."
|
msgstr "이 필드의 글자 수가 {max_length} 이하인지 확인하세요."
|
||||||
|
|
||||||
#: fields.py:769
|
#: fields.py:727
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has at least {min_length} characters."
|
msgid "Ensure this field has at least {min_length} characters."
|
||||||
msgstr "이 필드의 글자 수가 적어도 {min_length} 이상인지 확인하십시오."
|
msgstr "이 필드의 글자 수가 적어도 {min_length} 이상인지 확인하세요."
|
||||||
|
|
||||||
#: fields.py:816
|
#: fields.py:774
|
||||||
msgid "Enter a valid email address."
|
msgid "Enter a valid email address."
|
||||||
msgstr "유효한 이메일 주소를 입력하십시오."
|
msgstr "유효한 이메일 주소를 입력하세요."
|
||||||
|
|
||||||
#: fields.py:827
|
#: fields.py:785
|
||||||
msgid "This value does not match the required pattern."
|
msgid "This value does not match the required pattern."
|
||||||
msgstr "형식에 맞지 않는 값입니다."
|
msgstr "이 값은 요구되는 패턴과 일치하지 않습니다."
|
||||||
|
|
||||||
#: fields.py:838
|
#: fields.py:796
|
||||||
msgid ""
|
msgid ""
|
||||||
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
|
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
|
||||||
"hyphens."
|
"hyphens."
|
||||||
msgstr "문자, 숫자, 밑줄( _ ) 또는 하이픈( - )으로 이루어진 유효한 \"slug\"를 입력하십시오."
|
msgstr "문자, 숫자, 밑줄( _ ) 또는 하이픈( - )으로 이루어진 유효한 \"slug\"를 입력하세요."
|
||||||
|
|
||||||
#: fields.py:839
|
#: fields.py:797
|
||||||
msgid ""
|
msgid ""
|
||||||
"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
|
"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
|
||||||
"or hyphens."
|
"or hyphens."
|
||||||
msgstr ""
|
msgstr "유니코드 문자, 숫자, 밑줄( _ ) 또는 하이픈( - )으로 이루어진 유효한 \"slug\"를 입력하세요."
|
||||||
|
|
||||||
#: fields.py:854
|
#: fields.py:812
|
||||||
msgid "Enter a valid URL."
|
msgid "Enter a valid URL."
|
||||||
msgstr "유효한 URL을 입력하십시오."
|
msgstr "유효한 URL을 입력하세요."
|
||||||
|
|
||||||
#: fields.py:867
|
#: fields.py:825
|
||||||
msgid "Must be a valid UUID."
|
msgid "Must be a valid UUID."
|
||||||
msgstr ""
|
msgstr "유효한 UUID 이어야 합니다."
|
||||||
|
|
||||||
#: fields.py:903
|
#: fields.py:861
|
||||||
msgid "Enter a valid IPv4 or IPv6 address."
|
msgid "Enter a valid IPv4 or IPv6 address."
|
||||||
msgstr "유효한 IPv4 또는 IPv6 주소를 입력하십시오."
|
msgstr "유효한 IPv4 또는 IPv6 주소를 입력하세요."
|
||||||
|
|
||||||
#: fields.py:931
|
#: fields.py:889
|
||||||
msgid "A valid integer is required."
|
msgid "A valid integer is required."
|
||||||
msgstr "유효한 정수(integer)를 넣어주세요."
|
msgstr "유효한 정수를 입력하세요."
|
||||||
|
|
||||||
#: fields.py:932 fields.py:969 fields.py:1005 fields.py:1366
|
#: fields.py:890 fields.py:927 fields.py:966 fields.py:1349
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this value is less than or equal to {max_value}."
|
msgid "Ensure this value is less than or equal to {max_value}."
|
||||||
msgstr "이 값이 {max_value}보다 작거나 같은지 확인하십시오."
|
msgstr "이 값이 {max_value}보다 작거나 같은지 확인하세요."
|
||||||
|
|
||||||
#: fields.py:933 fields.py:970 fields.py:1006 fields.py:1367
|
#: fields.py:891 fields.py:928 fields.py:967 fields.py:1350
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this value is greater than or equal to {min_value}."
|
msgid "Ensure this value is greater than or equal to {min_value}."
|
||||||
msgstr "이 값이 {min_value}보다 크거나 같은지 확인하십시오."
|
msgstr "이 값이 {min_value}보다 크거나 같은지 확인하세요."
|
||||||
|
|
||||||
#: fields.py:934 fields.py:971 fields.py:1010
|
#: fields.py:892 fields.py:929 fields.py:971
|
||||||
msgid "String value too large."
|
msgid "String value too large."
|
||||||
msgstr "문자열 값이 너무 큽니다."
|
msgstr "문자열 값이 너무 깁니다."
|
||||||
|
|
||||||
#: fields.py:968 fields.py:1004
|
#: fields.py:926 fields.py:965
|
||||||
msgid "A valid number is required."
|
msgid "A valid number is required."
|
||||||
msgstr "유효한 숫자를 넣어주세요."
|
msgstr "유효한 숫자를 입력하세요."
|
||||||
|
|
||||||
#: fields.py:1007
|
#: fields.py:930
|
||||||
|
msgid "Integer value too large to convert to float"
|
||||||
|
msgstr "정수 값이 너무 커서 부동 소수점으로 변환할 수 없습니다."
|
||||||
|
|
||||||
|
#: fields.py:968
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure that there are no more than {max_digits} digits in total."
|
msgid "Ensure that there are no more than {max_digits} digits in total."
|
||||||
msgstr "전체 숫자(digits)가 {max_digits} 이하인지 확인하십시오."
|
msgstr "총 자릿수가 {max_digits}을(를) 초과하지 않는지 확인하세요."
|
||||||
|
|
||||||
#: fields.py:1008
|
#: fields.py:969
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid ""
|
msgid "Ensure that there are no more than {max_decimal_places} decimal places."
|
||||||
"Ensure that there are no more than {max_decimal_places} decimal places."
|
msgstr "소수점 이하 자릿수가 {max_decimal_places}을(를) 초과하지 않는지 확인하세요."
|
||||||
msgstr "소수점 자릿수가 {max_decimal_places} 이하인지 확인하십시오."
|
|
||||||
|
|
||||||
#: fields.py:1009
|
#: fields.py:970
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Ensure that there are no more than {max_whole_digits} digits before the "
|
"Ensure that there are no more than {max_whole_digits} digits before the "
|
||||||
"decimal point."
|
"decimal point."
|
||||||
msgstr "소수점 자리 앞에 숫자(digits)가 {max_whole_digits} 이하인지 확인하십시오."
|
msgstr "소수점 앞 자릿수가 {max_whole_digits}을(를) 초과하지 않는지 확인하세요."
|
||||||
|
|
||||||
#: fields.py:1148
|
#: fields.py:1129
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
|
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
|
||||||
msgstr "Datetime의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}."
|
msgstr "Datetime의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}."
|
||||||
|
|
||||||
#: fields.py:1149
|
#: fields.py:1130
|
||||||
msgid "Expected a datetime but got a date."
|
msgid "Expected a datetime but got a date."
|
||||||
msgstr "예상된 datatime 대신 date를 받았습니다."
|
msgstr "datatime이 예상되었지만 date를 받았습니다."
|
||||||
|
|
||||||
#: fields.py:1150
|
#: fields.py:1131
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Invalid datetime for the timezone \"{timezone}\"."
|
msgid "Invalid datetime for the timezone \"{timezone}\"."
|
||||||
msgstr ""
|
msgstr "\"{timezone}\" 시간대에 대한 유효하지 않은 datetime 입니다."
|
||||||
|
|
||||||
#: fields.py:1151
|
#: fields.py:1132
|
||||||
msgid "Datetime value out of range."
|
msgid "Datetime value out of range."
|
||||||
msgstr ""
|
msgstr "Datetime 값이 범위를 벗어났습니다."
|
||||||
|
|
||||||
#: fields.py:1236
|
#: fields.py:1219
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Date has wrong format. Use one of these formats instead: {format}."
|
msgid "Date has wrong format. Use one of these formats instead: {format}."
|
||||||
msgstr "Date의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}."
|
msgstr "Date의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}."
|
||||||
|
|
||||||
#: fields.py:1237
|
#: fields.py:1220
|
||||||
msgid "Expected a date but got a datetime."
|
msgid "Expected a date but got a datetime."
|
||||||
msgstr "예상된 date 대신 datetime을 받았습니다."
|
msgstr "예상된 date 대신 datetime을 받았습니다."
|
||||||
|
|
||||||
#: fields.py:1303
|
#: fields.py:1286
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Time has wrong format. Use one of these formats instead: {format}."
|
msgid "Time has wrong format. Use one of these formats instead: {format}."
|
||||||
msgstr "Time의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}."
|
msgstr "Time의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}."
|
||||||
|
|
||||||
#: fields.py:1365
|
#: fields.py:1348
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Duration has wrong format. Use one of these formats instead: {format}."
|
msgid "Duration has wrong format. Use one of these formats instead: {format}."
|
||||||
msgstr "Duration의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}."
|
msgstr "Duration의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}."
|
||||||
|
|
||||||
#: fields.py:1399 fields.py:1456
|
#: fields.py:1351
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "The number of days must be between {min_days} and {max_days}."
|
||||||
|
msgstr "일수는 {min_days} 이상 {max_days} 이하이어야 합니다."
|
||||||
|
|
||||||
|
#: fields.py:1386 fields.py:1446
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "\"{input}\" is not a valid choice."
|
msgid "\"{input}\" is not a valid choice."
|
||||||
msgstr "\"{input}\"이 유효하지 않은 선택(choice)입니다."
|
msgstr "\"{input}\"은 유효하지 않은 선택입니다."
|
||||||
|
|
||||||
#: fields.py:1402
|
#: fields.py:1389
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "More than {count} items..."
|
msgid "More than {count} items..."
|
||||||
msgstr ""
|
msgstr "{count}개 이상의 아이템이 있습니다..."
|
||||||
|
|
||||||
#: fields.py:1457 fields.py:1603 relations.py:485 serializers.py:570
|
#: fields.py:1447 fields.py:1596 relations.py:486 serializers.py:593
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected a list of items but got type \"{input_type}\"."
|
msgid "Expected a list of items but got type \"{input_type}\"."
|
||||||
msgstr "아이템 리스트가 예상되었으나 \"{input_type}\"를 받았습니다."
|
msgstr "아이템 리스트가 예상되었으나 \"{input_type}\"를 받았습니다."
|
||||||
|
|
||||||
#: fields.py:1458
|
#: fields.py:1448
|
||||||
msgid "This selection may not be empty."
|
msgid "This selection may not be empty."
|
||||||
msgstr "이 선택 항목은 비워 둘 수 없습니다."
|
msgstr "이 선택 항목은 비워 둘 수 없습니다."
|
||||||
|
|
||||||
#: fields.py:1495
|
#: fields.py:1487
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "\"{input}\" is not a valid path choice."
|
msgid "\"{input}\" is not a valid path choice."
|
||||||
msgstr "\"{input}\"이 유효하지 않은 경로 선택입니다."
|
msgstr "\"{input}\"은 유효하지 않은 경로 선택입니다."
|
||||||
|
|
||||||
#: fields.py:1514
|
#: fields.py:1507
|
||||||
msgid "No file was submitted."
|
msgid "No file was submitted."
|
||||||
msgstr "파일이 제출되지 않았습니다."
|
msgstr "파일이 제출되지 않았습니다."
|
||||||
|
|
||||||
#: fields.py:1515
|
#: fields.py:1508
|
||||||
msgid ""
|
msgid "The submitted data was not a file. Check the encoding type on the form."
|
||||||
"The submitted data was not a file. Check the encoding type on the form."
|
|
||||||
msgstr "제출된 데이터는 파일이 아닙니다. 제출된 서식의 인코딩 형식을 확인하세요."
|
msgstr "제출된 데이터는 파일이 아닙니다. 제출된 서식의 인코딩 형식을 확인하세요."
|
||||||
|
|
||||||
#: fields.py:1516
|
#: fields.py:1509
|
||||||
msgid "No filename could be determined."
|
msgid "No filename could be determined."
|
||||||
msgstr "파일명을 알 수 없습니다."
|
msgstr "파일명을 알 수 없습니다."
|
||||||
|
|
||||||
#: fields.py:1517
|
#: fields.py:1510
|
||||||
msgid "The submitted file is empty."
|
msgid "The submitted file is empty."
|
||||||
msgstr "제출한 파일이 비어있습니다."
|
msgstr "제출한 파일이 비어있습니다."
|
||||||
|
|
||||||
#: fields.py:1518
|
#: fields.py:1511
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Ensure this filename has at most {max_length} characters (it has {length})."
|
"Ensure this filename has at most {max_length} characters (it has {length})."
|
||||||
msgstr "이 파일명의 글자수가 최대 {max_length}를 넘지 않는지 확인하십시오. (이것은 {length}가 있습니다)."
|
msgstr "이 파일명의 글자수가 최대 {max_length}자를 넘지 않는지 확인하세요. (현재 {length}자입니다)."
|
||||||
|
|
||||||
#: fields.py:1566
|
#: fields.py:1559
|
||||||
msgid ""
|
msgid ""
|
||||||
"Upload a valid image. The file you uploaded was either not an image or a "
|
"Upload a valid image. The file you uploaded was either not an image or a "
|
||||||
"corrupted image."
|
"corrupted image."
|
||||||
msgstr "유효한 이미지 파일을 업로드 하십시오. 업로드 하신 파일은 이미지 파일이 아니거나 손상된 이미지 파일입니다."
|
msgstr "유효한 이미지 파일을 업로드하세요. 업로드하신 파일은 이미지 파일이 아니거나 손상된 이미지 파일입니다."
|
||||||
|
|
||||||
#: fields.py:1604 relations.py:486 serializers.py:571
|
#: fields.py:1597 relations.py:487 serializers.py:594
|
||||||
msgid "This list may not be empty."
|
msgid "This list may not be empty."
|
||||||
msgstr "이 리스트는 비워 둘 수 없습니다."
|
msgstr "이 리스트는 비워 둘 수 없습니다."
|
||||||
|
|
||||||
#: fields.py:1605
|
#: fields.py:1598 serializers.py:596
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has at least {min_length} elements."
|
msgid "Ensure this field has at least {min_length} elements."
|
||||||
msgstr ""
|
msgstr "이 필드가 최소 {min_length} 개의 요소를 가지는지 확인하세요."
|
||||||
|
|
||||||
#: fields.py:1606
|
#: fields.py:1599 serializers.py:595
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has no more than {max_length} elements."
|
msgid "Ensure this field has no more than {max_length} elements."
|
||||||
msgstr ""
|
msgstr "이 필드가 최대 {max_length} 개의 요소를 가지는지 확인하세요."
|
||||||
|
|
||||||
#: fields.py:1682
|
#: fields.py:1677
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected a dictionary of items but got type \"{input_type}\"."
|
msgid "Expected a dictionary of items but got type \"{input_type}\"."
|
||||||
msgstr "아이템 딕셔너리가 예상되었으나 \"{input_type}\" 타입을 받았습니다."
|
msgstr "아이템 딕셔너리가 예상되었으나 \"{input_type}\" 타입을 받았습니다."
|
||||||
|
|
||||||
#: fields.py:1683
|
#: fields.py:1678
|
||||||
msgid "This dictionary may not be empty."
|
msgid "This dictionary may not be empty."
|
||||||
msgstr ""
|
msgstr "이 딕셔너리는 비어있을 수 없습니다."
|
||||||
|
|
||||||
#: fields.py:1755
|
#: fields.py:1750
|
||||||
msgid "Value must be valid JSON."
|
msgid "Value must be valid JSON."
|
||||||
msgstr "Value 는 유효한 JSON형식이어야 합니다."
|
msgstr "유효한 JSON 값이어야 합니다."
|
||||||
|
|
||||||
#: filters.py:49 templates/rest_framework/filters/search.html:2
|
#: filters.py:72 templates/rest_framework/filters/search.html:2
|
||||||
|
#: templates/rest_framework/filters/search.html:8
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "검색"
|
msgstr "검색"
|
||||||
|
|
||||||
#: filters.py:50
|
#: filters.py:73
|
||||||
msgid "A search term."
|
msgid "A search term."
|
||||||
msgstr ""
|
msgstr "검색어."
|
||||||
|
|
||||||
#: filters.py:180 templates/rest_framework/filters/ordering.html:3
|
#: filters.py:224 templates/rest_framework/filters/ordering.html:3
|
||||||
msgid "Ordering"
|
msgid "Ordering"
|
||||||
msgstr "순서"
|
msgstr "순서"
|
||||||
|
|
||||||
#: filters.py:181
|
#: filters.py:225
|
||||||
msgid "Which field to use when ordering the results."
|
msgid "Which field to use when ordering the results."
|
||||||
msgstr ""
|
msgstr "결과 정렬 시 사용할 필드."
|
||||||
|
|
||||||
#: filters.py:287
|
#: filters.py:341
|
||||||
msgid "ascending"
|
msgid "ascending"
|
||||||
msgstr "오름차순"
|
msgstr "오름차순"
|
||||||
|
|
||||||
#: filters.py:288
|
#: filters.py:342
|
||||||
msgid "descending"
|
msgid "descending"
|
||||||
msgstr "내림차순"
|
msgstr "내림차순"
|
||||||
|
|
||||||
#: pagination.py:174
|
#: pagination.py:180
|
||||||
msgid "A page number within the paginated result set."
|
msgid "A page number within the paginated result set."
|
||||||
msgstr ""
|
msgstr "페이지네이션된 결과 집합 내의 페이지 번호."
|
||||||
|
|
||||||
#: pagination.py:179 pagination.py:372 pagination.py:590
|
#: pagination.py:185 pagination.py:382 pagination.py:599
|
||||||
msgid "Number of results to return per page."
|
msgid "Number of results to return per page."
|
||||||
msgstr ""
|
msgstr "페이지당 반환할 결과 수."
|
||||||
|
|
||||||
#: pagination.py:189
|
#: pagination.py:195
|
||||||
msgid "Invalid page."
|
msgid "Invalid page."
|
||||||
msgstr "페이지가 유효하지 않습니다."
|
msgstr "페이지가 유효하지 않습니다."
|
||||||
|
|
||||||
#: pagination.py:374
|
#: pagination.py:384
|
||||||
msgid "The initial index from which to return the results."
|
msgid "The initial index from which to return the results."
|
||||||
msgstr ""
|
msgstr "결과를 반환할 초기 인덱스."
|
||||||
|
|
||||||
#: pagination.py:581
|
#: pagination.py:590
|
||||||
msgid "The pagination cursor value."
|
msgid "The pagination cursor value."
|
||||||
msgstr ""
|
msgstr "페이지네이션 커서 값."
|
||||||
|
|
||||||
#: pagination.py:583
|
#: pagination.py:592
|
||||||
msgid "Invalid cursor"
|
msgid "Invalid cursor"
|
||||||
msgstr "커서(cursor)가 유효하지 않습니다."
|
msgstr "커서가 유효하지 않습니다."
|
||||||
|
|
||||||
#: relations.py:246
|
#: relations.py:241
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Invalid pk \"{pk_value}\" - object does not exist."
|
msgid "Invalid pk \"{pk_value}\" - object does not exist."
|
||||||
msgstr "유효하지 않은 pk \"{pk_value}\" - 객체가 존재하지 않습니다."
|
msgstr "유효하지 않은 pk \"{pk_value}\" - 객체가 존재하지 않습니다."
|
||||||
|
|
||||||
#: relations.py:247
|
#: relations.py:242
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Incorrect type. Expected pk value, received {data_type}."
|
msgid "Incorrect type. Expected pk value, received {data_type}."
|
||||||
msgstr "잘못된 형식입니다. pk 값 대신 {data_type}를 받았습니다."
|
msgstr "잘못된 형식입니다. pk 값이 예상되었지만, {data_type}을(를) 받았습니다."
|
||||||
|
|
||||||
#: relations.py:280
|
#: relations.py:277
|
||||||
msgid "Invalid hyperlink - No URL match."
|
msgid "Invalid hyperlink - No URL match."
|
||||||
msgstr "유효하지 않은 하이퍼링크 - 일치하는 URL이 없습니다."
|
msgstr "유효하지 않은 하이퍼링크 - 일치하는 URL이 없습니다."
|
||||||
|
|
||||||
#: relations.py:281
|
#: relations.py:278
|
||||||
msgid "Invalid hyperlink - Incorrect URL match."
|
msgid "Invalid hyperlink - Incorrect URL match."
|
||||||
msgstr "유효하지 않은 하이퍼링크 - URL이 일치하지 않습니다."
|
msgstr "유효하지 않은 하이퍼링크 - URL이 일치하지 않습니다."
|
||||||
|
|
||||||
#: relations.py:282
|
#: relations.py:279
|
||||||
msgid "Invalid hyperlink - Object does not exist."
|
msgid "Invalid hyperlink - Object does not exist."
|
||||||
msgstr "유효하지 않은 하이퍼링크 - 객체가 존재하지 않습니다."
|
msgstr "유효하지 않은 하이퍼링크 - 객체가 존재하지 않습니다."
|
||||||
|
|
||||||
#: relations.py:283
|
#: relations.py:280
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Incorrect type. Expected URL string, received {data_type}."
|
msgid "Incorrect type. Expected URL string, received {data_type}."
|
||||||
msgstr "잘못된 형식입니다. URL 문자열을 예상했으나 {data_type}을 받았습니다."
|
msgstr "잘못된 형식입니다. URL 문자열을 예상했으나 {data_type}을 받았습니다."
|
||||||
|
|
||||||
#: relations.py:448
|
#: relations.py:445
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Object with {slug_name}={value} does not exist."
|
msgid "Object with {slug_name}={value} does not exist."
|
||||||
msgstr "{slug_name}={value} 객체가 존재하지 않습니다."
|
msgstr "{slug_name}={value} 객체가 존재하지 않습니다."
|
||||||
|
|
||||||
#: relations.py:449
|
#: relations.py:446
|
||||||
msgid "Invalid value."
|
msgid "Invalid value."
|
||||||
msgstr "값이 유효하지 않습니다."
|
msgstr "값이 유효하지 않습니다."
|
||||||
|
|
||||||
#: schemas/utils.py:32
|
#: schemas/utils.py:32
|
||||||
msgid "unique integer value"
|
msgid "unique integer value"
|
||||||
msgstr ""
|
msgstr "고유한 정수 값"
|
||||||
|
|
||||||
#: schemas/utils.py:34
|
#: schemas/utils.py:34
|
||||||
msgid "UUID string"
|
msgid "UUID string"
|
||||||
msgstr ""
|
msgstr "UUID 문자열"
|
||||||
|
|
||||||
#: schemas/utils.py:36
|
#: schemas/utils.py:36
|
||||||
msgid "unique value"
|
msgid "unique value"
|
||||||
msgstr ""
|
msgstr "고유한 값"
|
||||||
|
|
||||||
#: schemas/utils.py:38
|
#: schemas/utils.py:38
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "A {value_type} identifying this {name}."
|
msgid "A {value_type} identifying this {name}."
|
||||||
msgstr ""
|
msgstr "{name}을 식별하는 {value_type}."
|
||||||
|
|
||||||
#: serializers.py:337
|
#: serializers.py:340
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Invalid data. Expected a dictionary, but got {datatype}."
|
msgid "Invalid data. Expected a dictionary, but got {datatype}."
|
||||||
msgstr "유효하지 않은 데이터. 딕셔너리(dictionary)대신 {datatype}를 받았습니다."
|
msgstr "유효하지 않은 데이터. 딕셔너리(dictionary)대신 {datatype}를 받았습니다."
|
||||||
|
@ -483,7 +491,7 @@ msgstr "유효하지 않은 데이터. 딕셔너리(dictionary)대신 {datatype}
|
||||||
#: templates/rest_framework/admin.html:116
|
#: templates/rest_framework/admin.html:116
|
||||||
#: templates/rest_framework/base.html:136
|
#: templates/rest_framework/base.html:136
|
||||||
msgid "Extra Actions"
|
msgid "Extra Actions"
|
||||||
msgstr ""
|
msgstr "추가 Action들"
|
||||||
|
|
||||||
#: templates/rest_framework/admin.html:130
|
#: templates/rest_framework/admin.html:130
|
||||||
#: templates/rest_framework/base.html:150
|
#: templates/rest_framework/base.html:150
|
||||||
|
@ -492,33 +500,33 @@ msgstr "필터"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:37
|
#: templates/rest_framework/base.html:37
|
||||||
msgid "navbar"
|
msgid "navbar"
|
||||||
msgstr ""
|
msgstr "네비게이션 바"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:75
|
#: templates/rest_framework/base.html:75
|
||||||
msgid "content"
|
msgid "content"
|
||||||
msgstr ""
|
msgstr "콘텐츠"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:78
|
#: templates/rest_framework/base.html:78
|
||||||
msgid "request form"
|
msgid "request form"
|
||||||
msgstr ""
|
msgstr "요청 폼"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:157
|
#: templates/rest_framework/base.html:157
|
||||||
msgid "main content"
|
msgid "main content"
|
||||||
msgstr ""
|
msgstr "메인 콘텐츠"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:173
|
#: templates/rest_framework/base.html:173
|
||||||
msgid "request info"
|
msgid "request info"
|
||||||
msgstr ""
|
msgstr "요청 정보"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:177
|
#: templates/rest_framework/base.html:177
|
||||||
msgid "response info"
|
msgid "response info"
|
||||||
msgstr ""
|
msgstr "응답 정보"
|
||||||
|
|
||||||
#: templates/rest_framework/horizontal/radio.html:4
|
#: templates/rest_framework/horizontal/radio.html:4
|
||||||
#: templates/rest_framework/inline/radio.html:3
|
#: templates/rest_framework/inline/radio.html:3
|
||||||
#: templates/rest_framework/vertical/radio.html:3
|
#: templates/rest_framework/vertical/radio.html:3
|
||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr ""
|
msgstr "없음"
|
||||||
|
|
||||||
#: templates/rest_framework/horizontal/select_multiple.html:4
|
#: templates/rest_framework/horizontal/select_multiple.html:4
|
||||||
#: templates/rest_framework/inline/select_multiple.html:3
|
#: templates/rest_framework/inline/select_multiple.html:3
|
||||||
|
@ -528,49 +536,49 @@ msgstr "선택할 아이템이 없습니다."
|
||||||
|
|
||||||
#: validators.py:39
|
#: validators.py:39
|
||||||
msgid "This field must be unique."
|
msgid "This field must be unique."
|
||||||
msgstr "이 필드는 반드시 고유(unique)해야 합니다."
|
msgstr "이 필드는 반드시 고유해야 합니다."
|
||||||
|
|
||||||
#: validators.py:89
|
#: validators.py:98
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "The fields {field_names} must make a unique set."
|
msgid "The fields {field_names} must make a unique set."
|
||||||
msgstr "필드 {field_names} 는 반드시 고유(unique)해야 합니다."
|
msgstr "필드 {field_names} 는 반드시 고유해야 합니다."
|
||||||
|
|
||||||
#: validators.py:171
|
#: validators.py:200
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Surrogate characters are not allowed: U+{code_point:X}."
|
msgid "Surrogate characters are not allowed: U+{code_point:X}."
|
||||||
msgstr ""
|
msgstr "대체(surrogate) 문자는 허용되지 않습니다: U+{code_point:X}."
|
||||||
|
|
||||||
#: validators.py:243
|
#: validators.py:290
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "This field must be unique for the \"{date_field}\" date."
|
msgid "This field must be unique for the \"{date_field}\" date."
|
||||||
msgstr "이 필드는 고유(unique)한 \"{date_field}\" 날짜를 갖습니다."
|
msgstr "이 필드는 \"{date_field}\" 날짜에 대해 고유해야 합니다."
|
||||||
|
|
||||||
#: validators.py:258
|
#: validators.py:305
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "This field must be unique for the \"{date_field}\" month."
|
msgid "This field must be unique for the \"{date_field}\" month."
|
||||||
msgstr "이 필드는 고유(unique)한 \"{date_field}\" 월을 갖습니다. "
|
msgstr "이 필드는 \"{date_field}\" 월에 대해 고유해야 합니다."
|
||||||
|
|
||||||
#: validators.py:271
|
#: validators.py:318
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "This field must be unique for the \"{date_field}\" year."
|
msgid "This field must be unique for the \"{date_field}\" year."
|
||||||
msgstr "이 필드는 고유(unique)한 \"{date_field}\" 년을 갖습니다. "
|
msgstr "이 필드는 \"{date_field}\" 연도에 대해 고유해야 합니다."
|
||||||
|
|
||||||
#: versioning.py:40
|
#: versioning.py:40
|
||||||
msgid "Invalid version in \"Accept\" header."
|
msgid "Invalid version in \"Accept\" header."
|
||||||
msgstr "\"Accept\" 헤더(header)의 버전이 유효하지 않습니다."
|
msgstr "\"Accept\" 헤더의 버전이 유효하지 않습니다."
|
||||||
|
|
||||||
#: versioning.py:71
|
#: versioning.py:71
|
||||||
msgid "Invalid version in URL path."
|
msgid "Invalid version in URL path."
|
||||||
msgstr "URL path의 버전이 유효하지 않습니다."
|
msgstr "URL 경로의 버전이 유효하지 않습니다."
|
||||||
|
|
||||||
#: versioning.py:116
|
#: versioning.py:118
|
||||||
msgid "Invalid version in URL path. Does not match any version namespace."
|
msgid "Invalid version in URL path. Does not match any version namespace."
|
||||||
msgstr "URL 경로에 유효하지 않은 버전이 있습니다. 버전 네임스페이스와 일치하지 않습니다."
|
msgstr "URL 경로에 유효하지 않은 버전이 있습니다. 버전 네임스페이스와 일치하지 않습니다."
|
||||||
|
|
||||||
#: versioning.py:148
|
#: versioning.py:150
|
||||||
msgid "Invalid version in hostname."
|
msgid "Invalid version in hostname."
|
||||||
msgstr "hostname 내 버전이 유효하지 않습니다."
|
msgstr "hostname 내 버전이 유효하지 않습니다."
|
||||||
|
|
||||||
#: versioning.py:170
|
#: versioning.py:172
|
||||||
msgid "Invalid version in query parameter."
|
msgid "Invalid version in query parameter."
|
||||||
msgstr "쿼리 파라메터 내 버전이 유효하지 않습니다."
|
msgstr "쿼리 파라메터 내 버전이 유효하지 않습니다."
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
# Filipe Rinaldi <filipe.rinaldi@gmail.com>, 2015
|
# Filipe Rinaldi <filipe.rinaldi@gmail.com>, 2015
|
||||||
# Hugo Leonardo Chalhoub Mendonça <hugoleonardocm@live.com>, 2015
|
# Hugo Leonardo Chalhoub Mendonça <hugoleonardocm@live.com>, 2015
|
||||||
# Jonatas Baldin <jonatas.baldin@gmail.com>, 2017
|
# Jonatas Baldin <jonatas.baldin@gmail.com>, 2017
|
||||||
|
# Gabriel Mitelman Tkacz <gmtkacz@proton.me>, 2024
|
||||||
|
# Matheus Oliveira <moliveiracdev@gmail.com>, 2025
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Django REST framework\n"
|
"Project-Id-Version: Django REST framework\n"
|
||||||
|
@ -106,11 +108,11 @@ msgstr "Ocorreu um erro de servidor."
|
||||||
|
|
||||||
#: exceptions.py:142
|
#: exceptions.py:142
|
||||||
msgid "Invalid input."
|
msgid "Invalid input."
|
||||||
msgstr ""
|
msgstr "Entrada inválida"
|
||||||
|
|
||||||
#: exceptions.py:161
|
#: exceptions.py:161
|
||||||
msgid "Malformed request."
|
msgid "Malformed request."
|
||||||
msgstr "Pedido malformado."
|
msgstr "Requisição malformada."
|
||||||
|
|
||||||
#: exceptions.py:167
|
#: exceptions.py:167
|
||||||
msgid "Incorrect authentication credentials."
|
msgid "Incorrect authentication credentials."
|
||||||
|
@ -149,12 +151,12 @@ msgstr "Pedido foi limitado."
|
||||||
#: exceptions.py:224
|
#: exceptions.py:224
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected available in {wait} second."
|
msgid "Expected available in {wait} second."
|
||||||
msgstr ""
|
msgstr "Disponível em {wait} segundo."
|
||||||
|
|
||||||
#: exceptions.py:225
|
#: exceptions.py:225
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected available in {wait} seconds."
|
msgid "Expected available in {wait} seconds."
|
||||||
msgstr ""
|
msgstr "Disponível em {wait} segundos."
|
||||||
|
|
||||||
#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
|
#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
|
||||||
#: validators.py:183
|
#: validators.py:183
|
||||||
|
@ -167,15 +169,15 @@ msgstr "Este campo não pode ser nulo."
|
||||||
|
|
||||||
#: fields.py:701
|
#: fields.py:701
|
||||||
msgid "Must be a valid boolean."
|
msgid "Must be a valid boolean."
|
||||||
msgstr ""
|
msgstr "Deve ser um valor booleano válido."
|
||||||
|
|
||||||
#: fields.py:766
|
#: fields.py:766
|
||||||
msgid "Not a valid string."
|
msgid "Not a valid string."
|
||||||
msgstr ""
|
msgstr "Não é uma string válida."
|
||||||
|
|
||||||
#: fields.py:767
|
#: fields.py:767
|
||||||
msgid "This field may not be blank."
|
msgid "This field may not be blank."
|
||||||
msgstr "Este campo não pode ser em branco."
|
msgstr "Este campo não pode estar em branco."
|
||||||
|
|
||||||
#: fields.py:768 fields.py:1881
|
#: fields.py:768 fields.py:1881
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -199,21 +201,21 @@ msgstr "Este valor não corresponde ao padrão exigido."
|
||||||
msgid ""
|
msgid ""
|
||||||
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
|
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
|
||||||
"hyphens."
|
"hyphens."
|
||||||
msgstr "Entrar um \"slug\" válido que consista de letras, números, sublinhados ou hífens."
|
msgstr "Insira um \"slug\" válido que consista de letras, números, sublinhados ou hífens."
|
||||||
|
|
||||||
#: fields.py:839
|
#: fields.py:839
|
||||||
msgid ""
|
msgid ""
|
||||||
"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
|
"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
|
||||||
"or hyphens."
|
"or hyphens."
|
||||||
msgstr ""
|
msgstr "Insira um \"slug\" válido que consista de letras Unicode, números, sublinhados ou hífens."
|
||||||
|
|
||||||
#: fields.py:854
|
#: fields.py:854
|
||||||
msgid "Enter a valid URL."
|
msgid "Enter a valid URL."
|
||||||
msgstr "Entrar um URL válido."
|
msgstr "Insira um URL válido."
|
||||||
|
|
||||||
#: fields.py:867
|
#: fields.py:867
|
||||||
msgid "Must be a valid UUID."
|
msgid "Must be a valid UUID."
|
||||||
msgstr ""
|
msgstr "Deve ser um UUID válido."
|
||||||
|
|
||||||
#: fields.py:903
|
#: fields.py:903
|
||||||
msgid "Enter a valid IPv4 or IPv6 address."
|
msgid "Enter a valid IPv4 or IPv6 address."
|
||||||
|
@ -271,11 +273,11 @@ msgstr "Necessário uma data e hora mas recebeu uma data."
|
||||||
#: fields.py:1150
|
#: fields.py:1150
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Invalid datetime for the timezone \"{timezone}\"."
|
msgid "Invalid datetime for the timezone \"{timezone}\"."
|
||||||
msgstr ""
|
msgstr "Data e hora inválidas para o fuso horário \"{timezone}\"."
|
||||||
|
|
||||||
#: fields.py:1151
|
#: fields.py:1151
|
||||||
msgid "Datetime value out of range."
|
msgid "Datetime value out of range."
|
||||||
msgstr ""
|
msgstr "Valor de data e hora fora do intervalo."
|
||||||
|
|
||||||
#: fields.py:1236
|
#: fields.py:1236
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -289,17 +291,17 @@ msgstr "Necessário uma data mas recebeu uma data e hora."
|
||||||
#: fields.py:1303
|
#: fields.py:1303
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Time has wrong format. Use one of these formats instead: {format}."
|
msgid "Time has wrong format. Use one of these formats instead: {format}."
|
||||||
msgstr "Formato inválido para Tempo. Use um dos formatos a seguir: {format}."
|
msgstr "Formato inválido para tempo. Use um dos formatos a seguir: {format}."
|
||||||
|
|
||||||
#: fields.py:1365
|
#: fields.py:1365
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Duration has wrong format. Use one of these formats instead: {format}."
|
msgid "Duration has wrong format. Use one of these formats instead: {format}."
|
||||||
msgstr "Formato inválido para Duração. Use um dos formatos a seguir: {format}."
|
msgstr "Formato inválido para duração. Use um dos formatos a seguir: {format}."
|
||||||
|
|
||||||
#: fields.py:1399 fields.py:1456
|
#: fields.py:1399 fields.py:1456
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "\"{input}\" is not a valid choice."
|
msgid "\"{input}\" is not a valid choice."
|
||||||
msgstr "\"{input}\" não é um escolha válido."
|
msgstr "\"{input}\" não é um escolha válida."
|
||||||
|
|
||||||
#: fields.py:1402
|
#: fields.py:1402
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -309,7 +311,7 @@ msgstr "Mais de {count} itens..."
|
||||||
#: fields.py:1457 fields.py:1603 relations.py:485 serializers.py:570
|
#: fields.py:1457 fields.py:1603 relations.py:485 serializers.py:570
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected a list of items but got type \"{input_type}\"."
|
msgid "Expected a list of items but got type \"{input_type}\"."
|
||||||
msgstr "Necessário uma lista de itens, mas recebeu tipo \"{input_type}\"."
|
msgstr "Esperava uma lista de itens, mas recebeu tipo \"{input_type}\"."
|
||||||
|
|
||||||
#: fields.py:1458
|
#: fields.py:1458
|
||||||
msgid "This selection may not be empty."
|
msgid "This selection may not be empty."
|
||||||
|
@ -347,7 +349,7 @@ msgstr "Certifique-se de que o nome do arquivo tem menos de {max_length} caracte
|
||||||
msgid ""
|
msgid ""
|
||||||
"Upload a valid image. The file you uploaded was either not an image or a "
|
"Upload a valid image. The file you uploaded was either not an image or a "
|
||||||
"corrupted image."
|
"corrupted image."
|
||||||
msgstr "Fazer upload de uma imagem válida. O arquivo enviado não é um arquivo de imagem ou está corrompido."
|
msgstr "Faça upload de uma imagem válida. O arquivo enviado não é um arquivo de imagem ou está corrompido."
|
||||||
|
|
||||||
#: fields.py:1604 relations.py:486 serializers.py:571
|
#: fields.py:1604 relations.py:486 serializers.py:571
|
||||||
msgid "This list may not be empty."
|
msgid "This list may not be empty."
|
||||||
|
@ -356,25 +358,25 @@ msgstr "Esta lista não pode estar vazia."
|
||||||
#: fields.py:1605
|
#: fields.py:1605
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has at least {min_length} elements."
|
msgid "Ensure this field has at least {min_length} elements."
|
||||||
msgstr ""
|
msgstr "Certifique-se de que este campo tenha pelo menos {min_length} elementos."
|
||||||
|
|
||||||
#: fields.py:1606
|
#: fields.py:1606
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has no more than {max_length} elements."
|
msgid "Ensure this field has no more than {max_length} elements."
|
||||||
msgstr ""
|
msgstr "Certifique-se de que este campo não tenha mais que {max_length} elementos."
|
||||||
|
|
||||||
#: fields.py:1682
|
#: fields.py:1682
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected a dictionary of items but got type \"{input_type}\"."
|
msgid "Expected a dictionary of items but got type \"{input_type}\"."
|
||||||
msgstr "Esperado um dicionário de itens mas recebeu tipo \"{input_type}\"."
|
msgstr "Esperava um dicionário de itens mas recebeu tipo \"{input_type}\"."
|
||||||
|
|
||||||
#: fields.py:1683
|
#: fields.py:1683
|
||||||
msgid "This dictionary may not be empty."
|
msgid "This dictionary may not be empty."
|
||||||
msgstr ""
|
msgstr "Este dicionário não pode estar vazio."
|
||||||
|
|
||||||
#: fields.py:1755
|
#: fields.py:1755
|
||||||
msgid "Value must be valid JSON."
|
msgid "Value must be valid JSON."
|
||||||
msgstr "Valor devo ser JSON válido."
|
msgstr "Valor deve ser JSON válido."
|
||||||
|
|
||||||
#: filters.py:49 templates/rest_framework/filters/search.html:2
|
#: filters.py:49 templates/rest_framework/filters/search.html:2
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
|
@ -382,7 +384,7 @@ msgstr "Buscar"
|
||||||
|
|
||||||
#: filters.py:50
|
#: filters.py:50
|
||||||
msgid "A search term."
|
msgid "A search term."
|
||||||
msgstr ""
|
msgstr "Um termo de busca."
|
||||||
|
|
||||||
#: filters.py:180 templates/rest_framework/filters/ordering.html:3
|
#: filters.py:180 templates/rest_framework/filters/ordering.html:3
|
||||||
msgid "Ordering"
|
msgid "Ordering"
|
||||||
|
@ -390,23 +392,23 @@ msgstr "Ordenando"
|
||||||
|
|
||||||
#: filters.py:181
|
#: filters.py:181
|
||||||
msgid "Which field to use when ordering the results."
|
msgid "Which field to use when ordering the results."
|
||||||
msgstr ""
|
msgstr "Qual campo usar ao ordenar os resultados."
|
||||||
|
|
||||||
#: filters.py:287
|
#: filters.py:287
|
||||||
msgid "ascending"
|
msgid "ascending"
|
||||||
msgstr "ascendente"
|
msgstr "crescente"
|
||||||
|
|
||||||
#: filters.py:288
|
#: filters.py:288
|
||||||
msgid "descending"
|
msgid "descending"
|
||||||
msgstr "descendente"
|
msgstr "decrescente"
|
||||||
|
|
||||||
#: pagination.py:174
|
#: pagination.py:174
|
||||||
msgid "A page number within the paginated result set."
|
msgid "A page number within the paginated result set."
|
||||||
msgstr ""
|
msgstr "Um número de página dentro do conjunto de resultados paginado."
|
||||||
|
|
||||||
#: pagination.py:179 pagination.py:372 pagination.py:590
|
#: pagination.py:179 pagination.py:372 pagination.py:590
|
||||||
msgid "Number of results to return per page."
|
msgid "Number of results to return per page."
|
||||||
msgstr ""
|
msgstr "Número de resultados a serem retornados por página."
|
||||||
|
|
||||||
#: pagination.py:189
|
#: pagination.py:189
|
||||||
msgid "Invalid page."
|
msgid "Invalid page."
|
||||||
|
@ -414,11 +416,11 @@ msgstr "Página inválida."
|
||||||
|
|
||||||
#: pagination.py:374
|
#: pagination.py:374
|
||||||
msgid "The initial index from which to return the results."
|
msgid "The initial index from which to return the results."
|
||||||
msgstr ""
|
msgstr "O índice inicial a partir do qual retornar os resultados."
|
||||||
|
|
||||||
#: pagination.py:581
|
#: pagination.py:581
|
||||||
msgid "The pagination cursor value."
|
msgid "The pagination cursor value."
|
||||||
msgstr ""
|
msgstr "O valor do cursor de paginação."
|
||||||
|
|
||||||
#: pagination.py:583
|
#: pagination.py:583
|
||||||
msgid "Invalid cursor"
|
msgid "Invalid cursor"
|
||||||
|
@ -432,7 +434,7 @@ msgstr "Pk inválido \"{pk_value}\" - objeto não existe."
|
||||||
#: relations.py:247
|
#: relations.py:247
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Incorrect type. Expected pk value, received {data_type}."
|
msgid "Incorrect type. Expected pk value, received {data_type}."
|
||||||
msgstr "Tipo incorreto. Esperado valor pk, recebeu {data_type}."
|
msgstr "Tipo incorreto. Esperava valor pk, recebeu {data_type}."
|
||||||
|
|
||||||
#: relations.py:280
|
#: relations.py:280
|
||||||
msgid "Invalid hyperlink - No URL match."
|
msgid "Invalid hyperlink - No URL match."
|
||||||
|
@ -462,20 +464,20 @@ msgstr "Valor inválido."
|
||||||
|
|
||||||
#: schemas/utils.py:32
|
#: schemas/utils.py:32
|
||||||
msgid "unique integer value"
|
msgid "unique integer value"
|
||||||
msgstr ""
|
msgstr "valor inteiro único"
|
||||||
|
|
||||||
#: schemas/utils.py:34
|
#: schemas/utils.py:34
|
||||||
msgid "UUID string"
|
msgid "UUID string"
|
||||||
msgstr ""
|
msgstr "string UUID"
|
||||||
|
|
||||||
#: schemas/utils.py:36
|
#: schemas/utils.py:36
|
||||||
msgid "unique value"
|
msgid "unique value"
|
||||||
msgstr ""
|
msgstr "valor único"
|
||||||
|
|
||||||
#: schemas/utils.py:38
|
#: schemas/utils.py:38
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "A {value_type} identifying this {name}."
|
msgid "A {value_type} identifying this {name}."
|
||||||
msgstr ""
|
msgstr "Um {value_type} que identifica este {name}."
|
||||||
|
|
||||||
#: serializers.py:337
|
#: serializers.py:337
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -485,7 +487,7 @@ msgstr "Dado inválido. Necessário um dicionário mas recebeu {datatype}."
|
||||||
#: templates/rest_framework/admin.html:116
|
#: templates/rest_framework/admin.html:116
|
||||||
#: templates/rest_framework/base.html:136
|
#: templates/rest_framework/base.html:136
|
||||||
msgid "Extra Actions"
|
msgid "Extra Actions"
|
||||||
msgstr ""
|
msgstr "Ações Extras"
|
||||||
|
|
||||||
#: templates/rest_framework/admin.html:130
|
#: templates/rest_framework/admin.html:130
|
||||||
#: templates/rest_framework/base.html:150
|
#: templates/rest_framework/base.html:150
|
||||||
|
@ -540,7 +542,7 @@ msgstr "Os campos {field_names} devem criar um set único."
|
||||||
#: validators.py:171
|
#: validators.py:171
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Surrogate characters are not allowed: U+{code_point:X}."
|
msgid "Surrogate characters are not allowed: U+{code_point:X}."
|
||||||
msgstr ""
|
msgstr "Caracteres substitutos não são permitidos: U+{code_point:X}."
|
||||||
|
|
||||||
#: validators.py:243
|
#: validators.py:243
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
|
|
@ -353,12 +353,12 @@ msgstr "列表字段不能为空值。"
|
||||||
#: fields.py:1605
|
#: fields.py:1605
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has at least {min_length} elements."
|
msgid "Ensure this field has at least {min_length} elements."
|
||||||
msgstr ""
|
msgstr "请确保这个字段至少包含 {min_length} 个元素。"
|
||||||
|
|
||||||
#: fields.py:1606
|
#: fields.py:1606
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has no more than {max_length} elements."
|
msgid "Ensure this field has no more than {max_length} elements."
|
||||||
msgstr ""
|
msgstr "请确保这个字段不能超过 {max_length} 个元素。"
|
||||||
|
|
||||||
#: fields.py:1682
|
#: fields.py:1682
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -367,7 +367,7 @@ msgstr "期望是包含类目的字典,得到类型为 “{input_type}”。"
|
||||||
|
|
||||||
#: fields.py:1683
|
#: fields.py:1683
|
||||||
msgid "This dictionary may not be empty."
|
msgid "This dictionary may not be empty."
|
||||||
msgstr ""
|
msgstr "这个字典可能不是空的。"
|
||||||
|
|
||||||
#: fields.py:1755
|
#: fields.py:1755
|
||||||
msgid "Value must be valid JSON."
|
msgid "Value must be valid JSON."
|
||||||
|
|
|
@ -104,7 +104,7 @@ msgstr "服务器出现了错误。"
|
||||||
|
|
||||||
#: exceptions.py:142
|
#: exceptions.py:142
|
||||||
msgid "Invalid input."
|
msgid "Invalid input."
|
||||||
msgstr ""
|
msgstr "无效的输入。"
|
||||||
|
|
||||||
#: exceptions.py:161
|
#: exceptions.py:161
|
||||||
msgid "Malformed request."
|
msgid "Malformed request."
|
||||||
|
@ -142,17 +142,17 @@ msgstr "不支持请求中的媒体类型 “{media_type}”。"
|
||||||
|
|
||||||
#: exceptions.py:223
|
#: exceptions.py:223
|
||||||
msgid "Request was throttled."
|
msgid "Request was throttled."
|
||||||
msgstr "请求超过了限速。"
|
msgstr "请求已被限流。"
|
||||||
|
|
||||||
#: exceptions.py:224
|
#: exceptions.py:224
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected available in {wait} second."
|
msgid "Expected available in {wait} second."
|
||||||
msgstr ""
|
msgstr "预计 {wait} 秒后可用。"
|
||||||
|
|
||||||
#: exceptions.py:225
|
#: exceptions.py:225
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Expected available in {wait} seconds."
|
msgid "Expected available in {wait} seconds."
|
||||||
msgstr ""
|
msgstr "预计 {wait} 秒后可用。"
|
||||||
|
|
||||||
#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
|
#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
|
||||||
#: validators.py:183
|
#: validators.py:183
|
||||||
|
@ -165,11 +165,11 @@ msgstr "该字段不能为 null。"
|
||||||
|
|
||||||
#: fields.py:701
|
#: fields.py:701
|
||||||
msgid "Must be a valid boolean."
|
msgid "Must be a valid boolean."
|
||||||
msgstr ""
|
msgstr "必须是有效的布尔值。"
|
||||||
|
|
||||||
#: fields.py:766
|
#: fields.py:766
|
||||||
msgid "Not a valid string."
|
msgid "Not a valid string."
|
||||||
msgstr ""
|
msgstr "不是有效的字符串。"
|
||||||
|
|
||||||
#: fields.py:767
|
#: fields.py:767
|
||||||
msgid "This field may not be blank."
|
msgid "This field may not be blank."
|
||||||
|
@ -203,7 +203,7 @@ msgstr "请输入合法的“短语“,只能包含字母,数字,下划线
|
||||||
msgid ""
|
msgid ""
|
||||||
"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
|
"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
|
||||||
"or hyphens."
|
"or hyphens."
|
||||||
msgstr ""
|
msgstr "请输入有效的“slug”,由 Unicode 字母、数字、下划线或连字符组成。"
|
||||||
|
|
||||||
#: fields.py:854
|
#: fields.py:854
|
||||||
msgid "Enter a valid URL."
|
msgid "Enter a valid URL."
|
||||||
|
@ -211,7 +211,7 @@ msgstr "请输入合法的URL。"
|
||||||
|
|
||||||
#: fields.py:867
|
#: fields.py:867
|
||||||
msgid "Must be a valid UUID."
|
msgid "Must be a valid UUID."
|
||||||
msgstr ""
|
msgstr "必须是有效的 UUID。"
|
||||||
|
|
||||||
#: fields.py:903
|
#: fields.py:903
|
||||||
msgid "Enter a valid IPv4 or IPv6 address."
|
msgid "Enter a valid IPv4 or IPv6 address."
|
||||||
|
@ -269,11 +269,11 @@ msgstr "期望为日期时间,获得的是日期。"
|
||||||
#: fields.py:1150
|
#: fields.py:1150
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Invalid datetime for the timezone \"{timezone}\"."
|
msgid "Invalid datetime for the timezone \"{timezone}\"."
|
||||||
msgstr ""
|
msgstr "时区“{timezone}”的时间格式无效。"
|
||||||
|
|
||||||
#: fields.py:1151
|
#: fields.py:1151
|
||||||
msgid "Datetime value out of range."
|
msgid "Datetime value out of range."
|
||||||
msgstr ""
|
msgstr "时间数值超出有效范围。"
|
||||||
|
|
||||||
#: fields.py:1236
|
#: fields.py:1236
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -354,12 +354,12 @@ msgstr "列表不能为空。"
|
||||||
#: fields.py:1605
|
#: fields.py:1605
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has at least {min_length} elements."
|
msgid "Ensure this field has at least {min_length} elements."
|
||||||
msgstr ""
|
msgstr "该字段必须包含至少 {min_length} 个元素。"
|
||||||
|
|
||||||
#: fields.py:1606
|
#: fields.py:1606
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Ensure this field has no more than {max_length} elements."
|
msgid "Ensure this field has no more than {max_length} elements."
|
||||||
msgstr ""
|
msgstr "该字段不能超过 {max_length} 个元素。"
|
||||||
|
|
||||||
#: fields.py:1682
|
#: fields.py:1682
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -368,7 +368,7 @@ msgstr "期望是包含类目的字典,得到类型为 “{input_type}”。"
|
||||||
|
|
||||||
#: fields.py:1683
|
#: fields.py:1683
|
||||||
msgid "This dictionary may not be empty."
|
msgid "This dictionary may not be empty."
|
||||||
msgstr ""
|
msgstr "该字典不能为空。"
|
||||||
|
|
||||||
#: fields.py:1755
|
#: fields.py:1755
|
||||||
msgid "Value must be valid JSON."
|
msgid "Value must be valid JSON."
|
||||||
|
@ -380,7 +380,7 @@ msgstr " 搜索"
|
||||||
|
|
||||||
#: filters.py:50
|
#: filters.py:50
|
||||||
msgid "A search term."
|
msgid "A search term."
|
||||||
msgstr ""
|
msgstr "搜索关键词。"
|
||||||
|
|
||||||
#: filters.py:180 templates/rest_framework/filters/ordering.html:3
|
#: filters.py:180 templates/rest_framework/filters/ordering.html:3
|
||||||
msgid "Ordering"
|
msgid "Ordering"
|
||||||
|
@ -388,7 +388,7 @@ msgstr "排序"
|
||||||
|
|
||||||
#: filters.py:181
|
#: filters.py:181
|
||||||
msgid "Which field to use when ordering the results."
|
msgid "Which field to use when ordering the results."
|
||||||
msgstr ""
|
msgstr "用于排序结果的字段。"
|
||||||
|
|
||||||
#: filters.py:287
|
#: filters.py:287
|
||||||
msgid "ascending"
|
msgid "ascending"
|
||||||
|
@ -400,11 +400,11 @@ msgstr "倒排序"
|
||||||
|
|
||||||
#: pagination.py:174
|
#: pagination.py:174
|
||||||
msgid "A page number within the paginated result set."
|
msgid "A page number within the paginated result set."
|
||||||
msgstr ""
|
msgstr "分页结果集中的页码。"
|
||||||
|
|
||||||
#: pagination.py:179 pagination.py:372 pagination.py:590
|
#: pagination.py:179 pagination.py:372 pagination.py:590
|
||||||
msgid "Number of results to return per page."
|
msgid "Number of results to return per page."
|
||||||
msgstr ""
|
msgstr "每页返回的结果数量。"
|
||||||
|
|
||||||
#: pagination.py:189
|
#: pagination.py:189
|
||||||
msgid "Invalid page."
|
msgid "Invalid page."
|
||||||
|
@ -412,11 +412,11 @@ msgstr "无效页面。"
|
||||||
|
|
||||||
#: pagination.py:374
|
#: pagination.py:374
|
||||||
msgid "The initial index from which to return the results."
|
msgid "The initial index from which to return the results."
|
||||||
msgstr ""
|
msgstr "返回结果的起始索引位置。"
|
||||||
|
|
||||||
#: pagination.py:581
|
#: pagination.py:581
|
||||||
msgid "The pagination cursor value."
|
msgid "The pagination cursor value."
|
||||||
msgstr ""
|
msgstr "分页游标值"
|
||||||
|
|
||||||
#: pagination.py:583
|
#: pagination.py:583
|
||||||
msgid "Invalid cursor"
|
msgid "Invalid cursor"
|
||||||
|
@ -460,20 +460,20 @@ msgstr "无效值。"
|
||||||
|
|
||||||
#: schemas/utils.py:32
|
#: schemas/utils.py:32
|
||||||
msgid "unique integer value"
|
msgid "unique integer value"
|
||||||
msgstr ""
|
msgstr "唯一整数值"
|
||||||
|
|
||||||
#: schemas/utils.py:34
|
#: schemas/utils.py:34
|
||||||
msgid "UUID string"
|
msgid "UUID string"
|
||||||
msgstr ""
|
msgstr "UUID 字符串"
|
||||||
|
|
||||||
#: schemas/utils.py:36
|
#: schemas/utils.py:36
|
||||||
msgid "unique value"
|
msgid "unique value"
|
||||||
msgstr ""
|
msgstr "唯一值"
|
||||||
|
|
||||||
#: schemas/utils.py:38
|
#: schemas/utils.py:38
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "A {value_type} identifying this {name}."
|
msgid "A {value_type} identifying this {name}."
|
||||||
msgstr ""
|
msgstr "标识此 {name} 的 {value_type}。"
|
||||||
|
|
||||||
#: serializers.py:337
|
#: serializers.py:337
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
@ -483,7 +483,7 @@ msgstr "无效数据。期待为字典类型,得到的是 {datatype} 。"
|
||||||
#: templates/rest_framework/admin.html:116
|
#: templates/rest_framework/admin.html:116
|
||||||
#: templates/rest_framework/base.html:136
|
#: templates/rest_framework/base.html:136
|
||||||
msgid "Extra Actions"
|
msgid "Extra Actions"
|
||||||
msgstr ""
|
msgstr "扩展操作"
|
||||||
|
|
||||||
#: templates/rest_framework/admin.html:130
|
#: templates/rest_framework/admin.html:130
|
||||||
#: templates/rest_framework/base.html:150
|
#: templates/rest_framework/base.html:150
|
||||||
|
@ -492,27 +492,27 @@ msgstr "过滤器"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:37
|
#: templates/rest_framework/base.html:37
|
||||||
msgid "navbar"
|
msgid "navbar"
|
||||||
msgstr ""
|
msgstr "导航栏"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:75
|
#: templates/rest_framework/base.html:75
|
||||||
msgid "content"
|
msgid "content"
|
||||||
msgstr ""
|
msgstr "内容主体"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:78
|
#: templates/rest_framework/base.html:78
|
||||||
msgid "request form"
|
msgid "request form"
|
||||||
msgstr ""
|
msgstr "请求表单"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:157
|
#: templates/rest_framework/base.html:157
|
||||||
msgid "main content"
|
msgid "main content"
|
||||||
msgstr ""
|
msgstr "主要内容区"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:173
|
#: templates/rest_framework/base.html:173
|
||||||
msgid "request info"
|
msgid "request info"
|
||||||
msgstr ""
|
msgstr "请求信息"
|
||||||
|
|
||||||
#: templates/rest_framework/base.html:177
|
#: templates/rest_framework/base.html:177
|
||||||
msgid "response info"
|
msgid "response info"
|
||||||
msgstr ""
|
msgstr "响应信息"
|
||||||
|
|
||||||
#: templates/rest_framework/horizontal/radio.html:4
|
#: templates/rest_framework/horizontal/radio.html:4
|
||||||
#: templates/rest_framework/inline/radio.html:3
|
#: templates/rest_framework/inline/radio.html:3
|
||||||
|
@ -538,7 +538,7 @@ msgstr "字段 {field_names} 必须能构成唯一集合。"
|
||||||
#: validators.py:171
|
#: validators.py:171
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Surrogate characters are not allowed: U+{code_point:X}."
|
msgid "Surrogate characters are not allowed: U+{code_point:X}."
|
||||||
msgstr ""
|
msgstr "不允许使用代理字符: U+{code_point:X}。"
|
||||||
|
|
||||||
#: validators.py:243
|
#: validators.py:243
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
|
|
@ -11,7 +11,6 @@ from django.http import Http404
|
||||||
from django.utils.encoding import force_str
|
from django.utils.encoding import force_str
|
||||||
|
|
||||||
from rest_framework import exceptions, serializers
|
from rest_framework import exceptions, serializers
|
||||||
from rest_framework.fields import empty
|
|
||||||
from rest_framework.request import clone_request
|
from rest_framework.request import clone_request
|
||||||
from rest_framework.utils.field_mapping import ClassLookupDict
|
from rest_framework.utils.field_mapping import ClassLookupDict
|
||||||
|
|
||||||
|
@ -150,7 +149,4 @@ class SimpleMetadata(BaseMetadata):
|
||||||
for choice_value, choice_name in field.choices.items()
|
for choice_value, choice_name in field.choices.items()
|
||||||
]
|
]
|
||||||
|
|
||||||
if getattr(field, 'default', None) and field.default != empty and not callable(field.default):
|
|
||||||
field_info['default'] = field.default
|
|
||||||
|
|
||||||
return field_info
|
return field_info
|
||||||
|
|
|
@ -4,8 +4,6 @@ Basic building blocks for generic class based views.
|
||||||
We don't bind behaviour to http method handlers yet,
|
We don't bind behaviour to http method handlers yet,
|
||||||
which allows mixin classes to be composed in interesting ways.
|
which allows mixin classes to be composed in interesting ways.
|
||||||
"""
|
"""
|
||||||
from django.db.models.query import prefetch_related_objects
|
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
|
@ -69,13 +67,10 @@ class UpdateModelMixin:
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
self.perform_update(serializer)
|
self.perform_update(serializer)
|
||||||
|
|
||||||
queryset = self.filter_queryset(self.get_queryset())
|
if getattr(instance, '_prefetched_objects_cache', None):
|
||||||
if queryset._prefetch_related_lookups:
|
|
||||||
# If 'prefetch_related' has been applied to a queryset, we need to
|
# If 'prefetch_related' has been applied to a queryset, we need to
|
||||||
# forcibly invalidate the prefetch cache on the instance,
|
# forcibly invalidate the prefetch cache on the instance.
|
||||||
# and then re-prefetch related objects
|
|
||||||
instance._prefetched_objects_cache = {}
|
instance._prefetched_objects_cache = {}
|
||||||
prefetch_related_objects([instance], *queryset._prefetch_related_lookups)
|
|
||||||
|
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ class DefaultContentNegotiation(BaseContentNegotiation):
|
||||||
full_media_type = ';'.join(
|
full_media_type = ';'.join(
|
||||||
(renderer.media_type,) +
|
(renderer.media_type,) +
|
||||||
tuple(
|
tuple(
|
||||||
'{}={}'.format(key, value)
|
f'{key}={value}'
|
||||||
for key, value in media_type_wrapper.params.items()
|
for key, value in media_type_wrapper.params.items()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,7 +11,6 @@ from urllib import parse
|
||||||
|
|
||||||
from django.core.paginator import InvalidPage
|
from django.core.paginator import InvalidPage
|
||||||
from django.core.paginator import Paginator as DjangoPaginator
|
from django.core.paginator import Paginator as DjangoPaginator
|
||||||
from django.db.models import Q
|
|
||||||
from django.template import loader
|
from django.template import loader
|
||||||
from django.utils.encoding import force_str
|
from django.utils.encoding import force_str
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
@ -631,7 +630,7 @@ class CursorPagination(BasePagination):
|
||||||
queryset = queryset.order_by(*self.ordering)
|
queryset = queryset.order_by(*self.ordering)
|
||||||
|
|
||||||
# If we have a cursor with a fixed position then filter by that.
|
# If we have a cursor with a fixed position then filter by that.
|
||||||
if str(current_position) != 'None':
|
if current_position is not None:
|
||||||
order = self.ordering[0]
|
order = self.ordering[0]
|
||||||
is_reversed = order.startswith('-')
|
is_reversed = order.startswith('-')
|
||||||
order_attr = order.lstrip('-')
|
order_attr = order.lstrip('-')
|
||||||
|
@ -642,12 +641,7 @@ class CursorPagination(BasePagination):
|
||||||
else:
|
else:
|
||||||
kwargs = {order_attr + '__gt': current_position}
|
kwargs = {order_attr + '__gt': current_position}
|
||||||
|
|
||||||
filter_query = Q(**kwargs)
|
queryset = queryset.filter(**kwargs)
|
||||||
# If some records contain a null for the ordering field, don't lose them.
|
|
||||||
# When reverse ordering, nulls will come last and need to be included.
|
|
||||||
if (reverse and not is_reversed) or is_reversed:
|
|
||||||
filter_query |= Q(**{order_attr + '__isnull': True})
|
|
||||||
queryset = queryset.filter(filter_query)
|
|
||||||
|
|
||||||
# If we have an offset cursor then offset the entire page by that amount.
|
# If we have an offset cursor then offset the entire page by that amount.
|
||||||
# We also always fetch an extra item in order to determine if there is a
|
# We also always fetch an extra item in order to determine if there is a
|
||||||
|
@ -720,7 +714,7 @@ class CursorPagination(BasePagination):
|
||||||
# The item in this position and the item following it
|
# The item in this position and the item following it
|
||||||
# have different positions. We can use this position as
|
# have different positions. We can use this position as
|
||||||
# our marker.
|
# our marker.
|
||||||
has_item_with_unique_position = position is not None
|
has_item_with_unique_position = True
|
||||||
break
|
break
|
||||||
|
|
||||||
# The item in this position has the same position as the item
|
# The item in this position has the same position as the item
|
||||||
|
@ -773,7 +767,7 @@ class CursorPagination(BasePagination):
|
||||||
# The item in this position and the item following it
|
# The item in this position and the item following it
|
||||||
# have different positions. We can use this position as
|
# have different positions. We can use this position as
|
||||||
# our marker.
|
# our marker.
|
||||||
has_item_with_unique_position = position is not None
|
has_item_with_unique_position = True
|
||||||
break
|
break
|
||||||
|
|
||||||
# The item in this position has the same position as the item
|
# The item in this position has the same position as the item
|
||||||
|
@ -896,7 +890,7 @@ class CursorPagination(BasePagination):
|
||||||
attr = instance[field_name]
|
attr = instance[field_name]
|
||||||
else:
|
else:
|
||||||
attr = getattr(instance, field_name)
|
attr = getattr(instance, field_name)
|
||||||
return None if attr is None else str(attr)
|
return str(attr)
|
||||||
|
|
||||||
def get_paginated_response(self, data):
|
def get_paginated_response(self, data):
|
||||||
return Response({
|
return Response({
|
||||||
|
|
|
@ -15,9 +15,9 @@ from django.http.multipartparser import ChunkIter
|
||||||
from django.http.multipartparser import \
|
from django.http.multipartparser import \
|
||||||
MultiPartParser as DjangoMultiPartParser
|
MultiPartParser as DjangoMultiPartParser
|
||||||
from django.http.multipartparser import MultiPartParserError
|
from django.http.multipartparser import MultiPartParserError
|
||||||
|
from django.utils.http import parse_header_parameters
|
||||||
|
|
||||||
from rest_framework import renderers
|
from rest_framework import renderers
|
||||||
from rest_framework.compat import parse_header_parameters
|
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
from rest_framework.utils import json
|
from rest_framework.utils import json
|
||||||
|
|
|
@ -54,6 +54,9 @@ class OperandHolder(OperationHolderMixin):
|
||||||
self.op2_class == other.op2_class
|
self.op2_class == other.op2_class
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((self.operator_class, self.op1_class, self.op2_class))
|
||||||
|
|
||||||
|
|
||||||
class AND:
|
class AND:
|
||||||
def __init__(self, op1, op2):
|
def __init__(self, op1, op2):
|
||||||
|
@ -186,9 +189,9 @@ class DjangoModelPermissions(BasePermission):
|
||||||
# Override this if you need to also provide 'view' permissions,
|
# Override this if you need to also provide 'view' permissions,
|
||||||
# or if you want to provide custom permission codes.
|
# or if you want to provide custom permission codes.
|
||||||
perms_map = {
|
perms_map = {
|
||||||
'GET': ['%(app_label)s.view_%(model_name)s'],
|
'GET': [],
|
||||||
'OPTIONS': [],
|
'OPTIONS': [],
|
||||||
'HEAD': ['%(app_label)s.view_%(model_name)s'],
|
'HEAD': [],
|
||||||
'POST': ['%(app_label)s.add_%(model_name)s'],
|
'POST': ['%(app_label)s.add_%(model_name)s'],
|
||||||
'PUT': ['%(app_label)s.change_%(model_name)s'],
|
'PUT': ['%(app_label)s.change_%(model_name)s'],
|
||||||
'PATCH': ['%(app_label)s.change_%(model_name)s'],
|
'PATCH': ['%(app_label)s.change_%(model_name)s'],
|
||||||
|
@ -222,7 +225,7 @@ class DjangoModelPermissions(BasePermission):
|
||||||
if hasattr(view, 'get_queryset'):
|
if hasattr(view, 'get_queryset'):
|
||||||
queryset = view.get_queryset()
|
queryset = view.get_queryset()
|
||||||
assert queryset is not None, (
|
assert queryset is not None, (
|
||||||
'{}.get_queryset() returned None'.format(view.__class__.__name__)
|
f'{view.__class__.__name__}.get_queryset() returned None'
|
||||||
)
|
)
|
||||||
return queryset
|
return queryset
|
||||||
return view.queryset
|
return view.queryset
|
||||||
|
@ -239,13 +242,8 @@ class DjangoModelPermissions(BasePermission):
|
||||||
|
|
||||||
queryset = self._queryset(view)
|
queryset = self._queryset(view)
|
||||||
perms = self.get_required_permissions(request.method, queryset.model)
|
perms = self.get_required_permissions(request.method, queryset.model)
|
||||||
change_perm = self.get_required_permissions('PUT', queryset.model)
|
|
||||||
|
|
||||||
user = request.user
|
return request.user.has_perms(perms)
|
||||||
if request.method == 'GET':
|
|
||||||
return user.has_perms(perms) or user.has_perms(change_perm)
|
|
||||||
|
|
||||||
return user.has_perms(perms)
|
|
||||||
|
|
||||||
|
|
||||||
class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions):
|
class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions):
|
||||||
|
|
|
@ -19,12 +19,13 @@ from django.core.paginator import Page
|
||||||
from django.template import engines, loader
|
from django.template import engines, loader
|
||||||
from django.urls import NoReverseMatch
|
from django.urls import NoReverseMatch
|
||||||
from django.utils.html import mark_safe
|
from django.utils.html import mark_safe
|
||||||
|
from django.utils.http import parse_header_parameters
|
||||||
from django.utils.safestring import SafeString
|
from django.utils.safestring import SafeString
|
||||||
|
|
||||||
from rest_framework import VERSION, exceptions, serializers, status
|
from rest_framework import VERSION, exceptions, serializers, status
|
||||||
from rest_framework.compat import (
|
from rest_framework.compat import (
|
||||||
INDENT_SEPARATORS, LONG_SEPARATORS, SHORT_SEPARATORS, coreapi, coreschema,
|
INDENT_SEPARATORS, LONG_SEPARATORS, SHORT_SEPARATORS, coreapi, coreschema,
|
||||||
parse_header_parameters, pygments_css, yaml
|
pygments_css, yaml
|
||||||
)
|
)
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
from rest_framework.request import is_form_media_type, override_method
|
from rest_framework.request import is_form_media_type, override_method
|
||||||
|
@ -170,6 +171,10 @@ class TemplateHTMLRenderer(BaseRenderer):
|
||||||
|
|
||||||
def get_template_context(self, data, renderer_context):
|
def get_template_context(self, data, renderer_context):
|
||||||
response = renderer_context['response']
|
response = renderer_context['response']
|
||||||
|
# in case a ValidationError is caught the data parameter may be a list
|
||||||
|
# see rest_framework.views.exception_handler
|
||||||
|
if isinstance(data, list):
|
||||||
|
return {'details': data, 'status_code': response.status_code}
|
||||||
if response.exception:
|
if response.exception:
|
||||||
data['status_code'] = response.status_code
|
data['status_code'] = response.status_code
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -16,9 +16,9 @@ from django.conf import settings
|
||||||
from django.http import HttpRequest, QueryDict
|
from django.http import HttpRequest, QueryDict
|
||||||
from django.http.request import RawPostDataException
|
from django.http.request import RawPostDataException
|
||||||
from django.utils.datastructures import MultiValueDict
|
from django.utils.datastructures import MultiValueDict
|
||||||
|
from django.utils.http import parse_header_parameters
|
||||||
|
|
||||||
from rest_framework import exceptions
|
from rest_framework import exceptions
|
||||||
from rest_framework.compat import parse_header_parameters
|
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
|
|
||||||
|
|
||||||
|
@ -217,6 +217,7 @@ class Request:
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
if not _hasattr(self, '_full_data'):
|
if not _hasattr(self, '_full_data'):
|
||||||
|
with wrap_attributeerrors():
|
||||||
self._load_data_and_files()
|
self._load_data_and_files()
|
||||||
return self._full_data
|
return self._full_data
|
||||||
|
|
||||||
|
@ -420,19 +421,13 @@ class Request:
|
||||||
_request = self.__getattribute__("_request")
|
_request = self.__getattribute__("_request")
|
||||||
return getattr(_request, attr)
|
return getattr(_request, attr)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return self.__getattribute__(attr)
|
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{attr}'")
|
||||||
|
|
||||||
@property
|
|
||||||
def DATA(self):
|
|
||||||
raise NotImplementedError(
|
|
||||||
'`request.DATA` has been deprecated in favor of `request.data` '
|
|
||||||
'since version 3.0, and has been fully removed as of version 3.2.'
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def POST(self):
|
def POST(self):
|
||||||
# Ensure that request.POST uses our request parsing.
|
# Ensure that request.POST uses our request parsing.
|
||||||
if not _hasattr(self, '_data'):
|
if not _hasattr(self, '_data'):
|
||||||
|
with wrap_attributeerrors():
|
||||||
self._load_data_and_files()
|
self._load_data_and_files()
|
||||||
if is_form_media_type(self.content_type):
|
if is_form_media_type(self.content_type):
|
||||||
return self._data
|
return self._data
|
||||||
|
@ -444,16 +439,10 @@ class Request:
|
||||||
# Different from the other two cases, which are not valid property
|
# Different from the other two cases, which are not valid property
|
||||||
# names on the WSGIRequest class.
|
# names on the WSGIRequest class.
|
||||||
if not _hasattr(self, '_files'):
|
if not _hasattr(self, '_files'):
|
||||||
|
with wrap_attributeerrors():
|
||||||
self._load_data_and_files()
|
self._load_data_and_files()
|
||||||
return self._files
|
return self._files
|
||||||
|
|
||||||
@property
|
|
||||||
def QUERY_PARAMS(self):
|
|
||||||
raise NotImplementedError(
|
|
||||||
'`request.QUERY_PARAMS` has been deprecated in favor of `request.query_params` '
|
|
||||||
'since version 3.0, and has been fully removed as of version 3.2.'
|
|
||||||
)
|
|
||||||
|
|
||||||
def force_plaintext_errors(self, value):
|
def force_plaintext_errors(self, value):
|
||||||
# Hack to allow our exception handler to force choice of
|
# Hack to allow our exception handler to force choice of
|
||||||
# plaintext or html error responses.
|
# plaintext or html error responses.
|
||||||
|
|
|
@ -66,7 +66,7 @@ class Response(SimpleTemplateResponse):
|
||||||
content_type = self.content_type
|
content_type = self.content_type
|
||||||
|
|
||||||
if content_type is None and charset is not None:
|
if content_type is None and charset is not None:
|
||||||
content_type = "{}; charset={}".format(media_type, charset)
|
content_type = f"{media_type}; charset={charset}"
|
||||||
elif content_type is None:
|
elif content_type is None:
|
||||||
content_type = media_type
|
content_type = media_type
|
||||||
self['Content-Type'] = content_type
|
self['Content-Type'] = content_type
|
||||||
|
|
|
@ -144,7 +144,7 @@ class SimpleRouter(BaseRouter):
|
||||||
self._url_conf = re_path
|
self._url_conf = re_path
|
||||||
else:
|
else:
|
||||||
self._base_pattern = '<{lookup_value}:{lookup_prefix}{lookup_url_kwarg}>'
|
self._base_pattern = '<{lookup_value}:{lookup_prefix}{lookup_url_kwarg}>'
|
||||||
self._default_value_pattern = 'path'
|
self._default_value_pattern = 'str'
|
||||||
self._url_conf = path
|
self._url_conf = path
|
||||||
# remove regex characters from routes
|
# remove regex characters from routes
|
||||||
_routes = []
|
_routes = []
|
||||||
|
|
|
@ -68,7 +68,7 @@ class LinkNode(dict):
|
||||||
current_val = self.methods_counter[preferred_key]
|
current_val = self.methods_counter[preferred_key]
|
||||||
self.methods_counter[preferred_key] += 1
|
self.methods_counter[preferred_key] += 1
|
||||||
|
|
||||||
key = '{}_{}'.format(preferred_key, current_val)
|
key = f'{preferred_key}_{current_val}'
|
||||||
if key not in self:
|
if key not in self:
|
||||||
return key
|
return key
|
||||||
|
|
||||||
|
|
|
@ -102,12 +102,12 @@ class EndpointEnumerator:
|
||||||
Given a URL conf regex, return a URI template string.
|
Given a URL conf regex, return a URI template string.
|
||||||
"""
|
"""
|
||||||
# ???: Would it be feasible to adjust this such that we generate the
|
# ???: Would it be feasible to adjust this such that we generate the
|
||||||
# path, plus the kwargs, plus the type from the convertor, such that we
|
# path, plus the kwargs, plus the type from the converter, such that we
|
||||||
# could feed that straight into the parameter schema object?
|
# could feed that straight into the parameter schema object?
|
||||||
|
|
||||||
path = simplify_regex(path_regex)
|
path = simplify_regex(path_regex)
|
||||||
|
|
||||||
# Strip Django 2.0 convertors as they are incompatible with uritemplate format
|
# Strip Django 2.0 converters as they are incompatible with uritemplate format
|
||||||
return re.sub(_PATH_PARAMETER_COMPONENT_RE, r'{\g<parameter>}', path)
|
return re.sub(_PATH_PARAMETER_COMPONENT_RE, r'{\g<parameter>}', path)
|
||||||
|
|
||||||
def should_include_endpoint(self, path, callback):
|
def should_include_endpoint(self, path, callback):
|
||||||
|
|