From ed7f3c55f7647b19ce2358ebce861a0cee8944d5 Mon Sep 17 00:00:00 2001
From: Max Wittig
Date: Mon, 29 Jul 2019 14:20:11 +0200
Subject: [PATCH 001/303] docs(tutorial): add missing permission import in
viewsets
---
docs/tutorial/6-viewsets-and-routers.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md
index 11e24448f..3719bbf18 100644
--- a/docs/tutorial/6-viewsets-and-routers.md
+++ b/docs/tutorial/6-viewsets-and-routers.md
@@ -27,6 +27,7 @@ Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighl
from rest_framework.decorators import action
from rest_framework.response import Response
+ from rest_framework import permissions
class SnippetViewSet(viewsets.ModelViewSet):
"""
From 505a69cf432a1b9684462848059f731ea381ea6c Mon Sep 17 00:00:00 2001
From: Nikhil Kumar
Date: Mon, 1 Jun 2020 13:49:43 -0400
Subject: [PATCH 002/303] Fixed heading markdown
---
docs/api-guide/serializers.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md
index 4f566ff59..6323ceacd 100644
--- a/docs/api-guide/serializers.md
+++ b/docs/api-guide/serializers.md
@@ -161,7 +161,7 @@ Each key in the dictionary will be the field name, and the values will be lists
When deserializing a list of items, errors will be returned as a list of dictionaries representing each of the deserialized items.
-#### Raising an exception on invalid data
+#### Raising an exception on invalid data
The `.is_valid()` method takes an optional `raise_exception` flag that will cause it to raise a `serializers.ValidationError` exception if there are validation errors.
From 9dc7021770ecb5689302cb97b282dd85fb7ed108 Mon Sep 17 00:00:00 2001
From: Nikhil Kumar
Date: Mon, 1 Jun 2020 14:03:10 -0400
Subject: [PATCH 003/303] Fixed h4 rendering
---
docs/api-guide/fields.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md
index b2bdd50c8..e309c9d91 100644
--- a/docs/api-guide/fields.md
+++ b/docs/api-guide/fields.md
@@ -371,7 +371,7 @@ Corresponds to `django.db.models.fields.TimeField`
* `format` - A string representing the output format. If not specified, this defaults to the same value as the `TIME_FORMAT` settings key, which will be `'iso-8601'` unless set. Setting to a format string indicates that `to_representation` return values should be coerced to string output. Format strings are described below. Setting this value to `None` indicates that Python `time` objects should be returned by `to_representation`. In this case the time encoding will be determined by the renderer.
* `input_formats` - A list of strings representing the input formats which may be used to parse the date. If not specified, the `TIME_INPUT_FORMATS` setting will be used, which defaults to `['iso-8601']`.
-#### `TimeField` format strings
+#### `TimeField` format strings
Format strings may either be [Python strftime formats][strftime] which explicitly specify the format, or the special string `'iso-8601'`, which indicates that [ISO 8601][iso8601] style times should be used. (eg `'12:34:56.000000'`)
From 30ca04df835e2d41f32a0ecad3a23bcd06533658 Mon Sep 17 00:00:00 2001
From: Mark Story
Date: Wed, 29 Jul 2020 09:58:53 -0400
Subject: [PATCH 004/303] Fix broken links in documenting API section
The changed links were not working on the documentation site. I'm not 100% sure that these changes are correct but they generate working links in the GitHub previews.
---
docs/topics/documenting-your-api.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md
index 5c5872650..e86a6cc77 100644
--- a/docs/topics/documenting-your-api.md
+++ b/docs/topics/documenting-your-api.md
@@ -209,8 +209,8 @@ To implement a hypermedia API you'll need to decide on an appropriate media type
[cite]: https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
[hypermedia-docs]: rest-hypermedia-hateoas.md
-[metadata-docs]: ../api-guide/metadata/
-[schemas-examples]: ../api-guide/schemas/#examples
+[metadata-docs]: ../api-guide/metadata.md
+[schemas-examples]: ../api-guide/schemas.md#examples
[image-drf-yasg]: ../img/drf-yasg.png
[image-self-describing-api]: ../img/self-describing.png
From 48c327c681da481b7c36c674307bd58adfa4286c Mon Sep 17 00:00:00 2001
From: Justin Duke
Date: Mon, 24 Aug 2020 03:44:34 -0700
Subject: [PATCH 005/303] Fix schema typo (#7491)
---
rest_framework/schemas/openapi.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py
index 9774a94c7..8a8e267e0 100644
--- a/rest_framework/schemas/openapi.py
+++ b/rest_framework/schemas/openapi.py
@@ -50,7 +50,7 @@ class SchemaGenerator(BaseSchemaGenerator):
'You have a duplicated operationId in your OpenAPI schema: {operation_id}\n'
'\tRoute: {route1}, Method: {method1}\n'
'\tRoute: {route2}, Method: {method2}\n'
- '\tAn operationId has to be unique accros your schema. Your schema may not work in other tools.'
+ '\tAn operationId has to be unique across your schema. Your schema may not work in other tools.'
.format(
route1=ids[operation_id]['route'],
method1=ids[operation_id]['method'],
From e215db206a6a80432c4f2a5cb711fc858e9069b3 Mon Sep 17 00:00:00 2001
From: Vlad <38493865+VladSaichenko@users.noreply.github.com>
Date: Tue, 25 Aug 2020 18:50:02 +0700
Subject: [PATCH 006/303] Updated url()'s with path() and re_path() (#7492)
---
rest_framework/documentation.py | 6 +++---
rest_framework/routers.py | 7 +++----
rest_framework/urlpatterns.py | 7 +++----
rest_framework/urls.py | 6 +++---
4 files changed, 12 insertions(+), 14 deletions(-)
diff --git a/rest_framework/documentation.py b/rest_framework/documentation.py
index ce61fa6bf..53e5ab551 100644
--- a/rest_framework/documentation.py
+++ b/rest_framework/documentation.py
@@ -1,4 +1,4 @@
-from django.conf.urls import include, url
+from django.urls import include, path
from rest_framework.renderers import (
CoreJSONRenderer, DocumentationRenderer, SchemaJSRenderer
@@ -82,7 +82,7 @@ def include_docs_urls(
permission_classes=permission_classes,
)
urls = [
- url(r'^$', docs_view, name='docs-index'),
- url(r'^schema.js$', schema_js_view, name='schema-js')
+ path('', docs_view, name='docs-index'),
+ path('schema.js', schema_js_view, name='schema-js')
]
return include((urls, 'api-docs'), namespace='api-docs')
diff --git a/rest_framework/routers.py b/rest_framework/routers.py
index 657ad67bc..e2afa573f 100644
--- a/rest_framework/routers.py
+++ b/rest_framework/routers.py
@@ -16,9 +16,8 @@ For example, you might have a `urls.py` that looks something like this:
import itertools
from collections import OrderedDict, namedtuple
-from django.conf.urls import url
from django.core.exceptions import ImproperlyConfigured
-from django.urls import NoReverseMatch
+from django.urls import NoReverseMatch, re_path
from rest_framework import views
from rest_framework.response import Response
@@ -265,7 +264,7 @@ class SimpleRouter(BaseRouter):
view = viewset.as_view(mapping, **initkwargs)
name = route.name.format(basename=basename)
- ret.append(url(regex, view, name=name))
+ ret.append(re_path(regex, view, name=name))
return ret
@@ -340,7 +339,7 @@ class DefaultRouter(SimpleRouter):
if self.include_root_view:
view = self.get_api_root_view(api_urls=urls)
- root_url = url(r'^$', view, name=self.root_view_name)
+ root_url = re_path(r'^$', view, name=self.root_view_name)
urls.append(root_url)
if self.include_format_suffixes:
diff --git a/rest_framework/urlpatterns.py b/rest_framework/urlpatterns.py
index efcfd8401..bed5708eb 100644
--- a/rest_framework/urlpatterns.py
+++ b/rest_framework/urlpatterns.py
@@ -1,5 +1,4 @@
-from django.conf.urls import include, url
-from django.urls import URLResolver, path, register_converter
+from django.urls import URLResolver, include, path, re_path, register_converter
from django.urls.resolvers import RoutePattern
from rest_framework.settings import api_settings
@@ -52,7 +51,7 @@ def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_r
route = str(urlpattern.pattern)
new_pattern = path(route, include((patterns, app_name), namespace), kwargs)
else:
- new_pattern = url(regex, include((patterns, app_name), namespace), kwargs)
+ new_pattern = re_path(regex, include((patterns, app_name), namespace), kwargs)
ret.append(new_pattern)
else:
@@ -72,7 +71,7 @@ def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_r
route = str(urlpattern.pattern).rstrip('$').rstrip('/') + suffix_route
new_pattern = path(route, view, kwargs, name)
else:
- new_pattern = url(regex, view, kwargs, name)
+ new_pattern = re_path(regex, view, kwargs, name)
ret.append(new_pattern)
diff --git a/rest_framework/urls.py b/rest_framework/urls.py
index 482a0a364..d9b858ebc 100644
--- a/rest_framework/urls.py
+++ b/rest_framework/urls.py
@@ -11,11 +11,11 @@ your API requires authentication:
You should make sure your authentication settings include `SessionAuthentication`.
"""
-from django.conf.urls import url
from django.contrib.auth import views
+from django.urls import path
app_name = 'rest_framework'
urlpatterns = [
- url(r'^login/$', views.LoginView.as_view(template_name='rest_framework/login.html'), name='login'),
- url(r'^logout/$', views.LogoutView.as_view(), name='logout'),
+ path('login/', views.LoginView.as_view(template_name='rest_framework/login.html'), name='login'),
+ path('logout/', views.LogoutView.as_view(), name='logout'),
]
From 7f3a3557a050147dd2420aa41d1bf7ddd7f9818e Mon Sep 17 00:00:00 2001
From: johnthagen
Date: Tue, 25 Aug 2020 18:15:17 -0400
Subject: [PATCH 007/303] Add drf_ujson2 reference (#7494)
---
docs/api-guide/renderers.md | 5 +++--
docs/community/third-party-packages.md | 4 ++--
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md
index a508a9ff9..ca3a29b82 100644
--- a/docs/api-guide/renderers.md
+++ b/docs/api-guide/renderers.md
@@ -503,7 +503,7 @@ Comma-separated values are a plain-text tabular data format, that can be easily
## UltraJSON
-[UltraJSON][ultrajson] is an optimized C JSON encoder which can give significantly faster JSON rendering. [Jacob Haslehurst][hzy] maintains the [drf-ujson-renderer][drf-ujson-renderer] package which implements JSON rendering using the UJSON package.
+[UltraJSON][ultrajson] is an optimized C JSON encoder which can give significantly faster JSON rendering. [Adam Mertz][Amertz08] maintains [drf_ujson2][drf_ujson2], a fork of the now unmaintained [drf-ujson-renderer][drf-ujson-renderer], which implements JSON rendering using the UJSON package.
## CamelCase JSON
@@ -547,8 +547,9 @@ Comma-separated values are a plain-text tabular data format, that can be easily
[djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack
[djangorestframework-csv]: https://github.com/mjumbewu/django-rest-framework-csv
[ultrajson]: https://github.com/esnme/ultrajson
-[hzy]: https://github.com/hzy
+[Amertz08]: https://github.com/Amertz08
[drf-ujson-renderer]: https://github.com/gizmag/drf-ujson-renderer
+[drf_ujson2]: https://github.com/Amertz08/drf_ujson2
[djangorestframework-camel-case]: https://github.com/vbabiy/djangorestframework-camel-case
[Django REST Pandas]: https://github.com/wq/django-rest-pandas
[Pandas]: https://pandas.pydata.org/
diff --git a/docs/community/third-party-packages.md b/docs/community/third-party-packages.md
index b47dc098f..79cb56e01 100644
--- a/docs/community/third-party-packages.md
+++ b/docs/community/third-party-packages.md
@@ -240,7 +240,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
* [djangorestframework-csv][djangorestframework-csv] - Provides CSV renderer support.
* [djangorestframework-jsonapi][djangorestframework-jsonapi] - Provides a parser, renderer, serializers, and other tools to help build an API that is compliant with the jsonapi.org spec.
-* [drf_ujson][drf_ujson] - Implements JSON rendering using the UJSON package.
+* [drf_ujson2][drf_ujson2] - Implements JSON rendering using the UJSON package.
* [rest-pandas][rest-pandas] - Pandas DataFrame-powered renderers including Excel, CSV, and SVG formats.
* [djangorestframework-rapidjson][djangorestframework-rapidjson] - Provides rapidjson support with parser and renderer.
@@ -313,7 +313,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
[djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack
[djangorestframework-camel-case]: https://github.com/vbabiy/djangorestframework-camel-case
[djangorestframework-csv]: https://github.com/mjumbewu/django-rest-framework-csv
-[drf_ujson]: https://github.com/gizmag/drf-ujson-renderer
+[drf_ujson2]: https://github.com/Amertz08/drf_ujson2
[rest-pandas]: https://github.com/wq/django-rest-pandas
[djangorestframework-rapidjson]: https://github.com/allisson/django-rest-framework-rapidjson
[djangorestframework-chain]: https://github.com/philipn/django-rest-framework-chain
From b3e02592d019fc4c66b5f4b11a462691e5cc64f1 Mon Sep 17 00:00:00 2001
From: Bob Thomas
Date: Thu, 3 Sep 2020 06:47:11 -0400
Subject: [PATCH 008/303] Add support for Django 3.1 JSONField (#7467)
Django 3.1 adds a new generic JSONField to replace the PostgreSQL-specific one. This adds support for the new field type, which should behave the same as the existing PostgreSQL field.
Django's new JSONField also includes support for a custom "decoder", so add support for that in the serializer field.
---
rest_framework/fields.py | 3 ++-
rest_framework/serializers.py | 9 +++++++--
rest_framework/utils/field_mapping.py | 3 ++-
tests/test_model_serializer.py | 26 ++++++++++++++++++++++++++
4 files changed, 37 insertions(+), 4 deletions(-)
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index da2dd54be..f218713f1 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -1758,6 +1758,7 @@ class JSONField(Field):
def __init__(self, *args, **kwargs):
self.binary = kwargs.pop('binary', False)
self.encoder = kwargs.pop('encoder', None)
+ self.decoder = kwargs.pop('decoder', None)
super().__init__(*args, **kwargs)
def get_value(self, dictionary):
@@ -1777,7 +1778,7 @@ class JSONField(Field):
if self.binary or getattr(data, 'is_json_string', False):
if isinstance(data, bytes):
data = data.decode()
- return json.loads(data)
+ return json.loads(data, cls=self.decoder)
else:
json.dumps(data, cls=self.encoder)
except (TypeError, ValueError):
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 916f8bec4..439220b34 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -884,6 +884,8 @@ class ModelSerializer(Serializer):
models.GenericIPAddressField: IPAddressField,
models.FilePathField: FilePathField,
}
+ if hasattr(models, 'JSONField'):
+ serializer_field_mapping[models.JSONField] = JSONField
if postgres_fields:
serializer_field_mapping[postgres_fields.HStoreField] = HStoreField
serializer_field_mapping[postgres_fields.ArrayField] = ListField
@@ -1242,10 +1244,13 @@ class ModelSerializer(Serializer):
# `allow_blank` is only valid for textual fields.
field_kwargs.pop('allow_blank', None)
- if postgres_fields and isinstance(model_field, postgres_fields.JSONField):
+ is_django_jsonfield = hasattr(models, 'JSONField') and isinstance(model_field, models.JSONField)
+ if (postgres_fields and isinstance(model_field, postgres_fields.JSONField)) or is_django_jsonfield:
# Populate the `encoder` argument of `JSONField` instances generated
- # for the PostgreSQL specific `JSONField`.
+ # for the model `JSONField`.
field_kwargs['encoder'] = getattr(model_field, 'encoder', None)
+ if is_django_jsonfield:
+ field_kwargs['decoder'] = getattr(model_field, 'decoder', None)
if postgres_fields and isinstance(model_field, postgres_fields.ArrayField):
# Populate the `child` argument on `ListField` instances generated
diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py
index ed270be5e..c008495cc 100644
--- a/rest_framework/utils/field_mapping.py
+++ b/rest_framework/utils/field_mapping.py
@@ -92,7 +92,8 @@ def get_field_kwargs(field_name, model_field):
kwargs['allow_unicode'] = model_field.allow_unicode
if isinstance(model_field, models.TextField) and not model_field.choices or \
- (postgres_fields and isinstance(model_field, postgres_fields.JSONField)):
+ (postgres_fields and isinstance(model_field, postgres_fields.JSONField)) or \
+ (hasattr(models, 'JSONField') and isinstance(model_field, models.JSONField)):
kwargs['style'] = {'base_template': 'textarea.html'}
if isinstance(model_field, models.AutoField) or not model_field.editable:
diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py
index 51b8f2e22..1733930a6 100644
--- a/tests/test_model_serializer.py
+++ b/tests/test_model_serializer.py
@@ -7,6 +7,7 @@ an appropriate set of serializer fields for each case.
"""
import datetime
import decimal
+import json # noqa
import sys
import tempfile
from collections import OrderedDict
@@ -478,6 +479,7 @@ class TestPosgresFieldsMapping(TestCase):
""")
self.assertEqual(repr(TestSerializer()), expected)
+ @pytest.mark.skipif(hasattr(models, 'JSONField'), reason='has models.JSONField')
def test_json_field(self):
class JSONFieldModel(models.Model):
json_field = postgres_fields.JSONField()
@@ -496,6 +498,30 @@ class TestPosgresFieldsMapping(TestCase):
self.assertEqual(repr(TestSerializer()), expected)
+class CustomJSONDecoder(json.JSONDecoder):
+ pass
+
+
+@pytest.mark.skipif(not hasattr(models, 'JSONField'), reason='no models.JSONField')
+class TestDjangoJSONFieldMapping(TestCase):
+ def test_json_field(self):
+ class JSONFieldModel(models.Model):
+ json_field = models.JSONField()
+ json_field_with_encoder = models.JSONField(encoder=DjangoJSONEncoder, decoder=CustomJSONDecoder)
+
+ class TestSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = JSONFieldModel
+ fields = ['json_field', 'json_field_with_encoder']
+
+ expected = dedent("""
+ TestSerializer():
+ json_field = JSONField(decoder=None, encoder=None, style={'base_template': 'textarea.html'})
+ json_field_with_encoder = JSONField(decoder=, encoder=, style={'base_template': 'textarea.html'})
+ """)
+ self.assertEqual(repr(TestSerializer()), expected)
+
+
# Tests for relational field mappings.
# ------------------------------------
From f323049ecc24b48b8453867d0d8952e8e0c1c003 Mon Sep 17 00:00:00 2001
From: Ryan P Kilby
Date: Thu, 3 Sep 2020 03:49:15 -0700
Subject: [PATCH 009/303] Fix pk-only optimization for properties (#7142)
* Add callable/prop tests for pk-only optimization
* Fix related field pk-only optimization for props
---
rest_framework/relations.py | 9 +++++--
tests/models.py | 9 +++++++
tests/test_relations_pk.py | 47 +++++++++++++++++++++++++++++++++++++
3 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/rest_framework/relations.py b/rest_framework/relations.py
index 3a2a8fb4b..3cd46379d 100644
--- a/rest_framework/relations.py
+++ b/rest_framework/relations.py
@@ -175,8 +175,13 @@ class RelatedField(Field):
value = attribute_instance.serializable_value(self.source_attrs[-1])
if is_simple_callable(value):
# Handle edge case where the relationship `source` argument
- # points to a `get_relationship()` method on the model
- value = value().pk
+ # points to a `get_relationship()` method on the model.
+ value = value()
+
+ # Handle edge case where relationship `source` argument points
+ # to an instance instead of a pk (e.g., a `@property`).
+ value = getattr(value, 'pk', value)
+
return PKOnlyObject(pk=value)
except AttributeError:
pass
diff --git a/tests/models.py b/tests/models.py
index 2c340a3e6..afe649760 100644
--- a/tests/models.py
+++ b/tests/models.py
@@ -37,6 +37,15 @@ class ManyToManySource(RESTFrameworkModel):
class ForeignKeyTarget(RESTFrameworkModel):
name = models.CharField(max_length=100)
+ def get_first_source(self):
+ """Used for testing related field against a callable."""
+ return self.sources.all().order_by('pk')[0]
+
+ @property
+ def first_source(self):
+ """Used for testing related field against a property."""
+ return self.sources.all().order_by('pk')[0]
+
class UUIDForeignKeyTarget(RESTFrameworkModel):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4)
diff --git a/tests/test_relations_pk.py b/tests/test_relations_pk.py
index 0da9da890..7a4878a2b 100644
--- a/tests/test_relations_pk.py
+++ b/tests/test_relations_pk.py
@@ -30,6 +30,25 @@ class ForeignKeyTargetSerializer(serializers.ModelSerializer):
fields = ('id', 'name', 'sources')
+class ForeignKeyTargetCallableSourceSerializer(serializers.ModelSerializer):
+ first_source = serializers.PrimaryKeyRelatedField(
+ source='get_first_source',
+ read_only=True,
+ )
+
+ class Meta:
+ model = ForeignKeyTarget
+ fields = ('id', 'name', 'first_source')
+
+
+class ForeignKeyTargetPropertySourceSerializer(serializers.ModelSerializer):
+ first_source = serializers.PrimaryKeyRelatedField(read_only=True)
+
+ class Meta:
+ model = ForeignKeyTarget
+ fields = ('id', 'name', 'first_source')
+
+
class ForeignKeySourceSerializer(serializers.ModelSerializer):
class Meta:
model = ForeignKeySource
@@ -389,6 +408,34 @@ class PKForeignKeyTests(TestCase):
assert len(queryset) == 1
+class PKRelationTests(TestCase):
+
+ def setUp(self):
+ self.target = ForeignKeyTarget.objects.create(name='target-1')
+ ForeignKeySource.objects.create(name='source-1', target=self.target)
+ ForeignKeySource.objects.create(name='source-2', target=self.target)
+
+ def test_relation_field_callable_source(self):
+ serializer = ForeignKeyTargetCallableSourceSerializer(self.target)
+ expected = {
+ 'id': 1,
+ 'name': 'target-1',
+ 'first_source': 1,
+ }
+ with self.assertNumQueries(1):
+ self.assertEqual(serializer.data, expected)
+
+ def test_relation_field_property_source(self):
+ serializer = ForeignKeyTargetPropertySourceSerializer(self.target)
+ expected = {
+ 'id': 1,
+ 'name': 'target-1',
+ 'first_source': 1,
+ }
+ with self.assertNumQueries(1):
+ self.assertEqual(serializer.data, expected)
+
+
class PKNullableForeignKeyTests(TestCase):
def setUp(self):
target = ForeignKeyTarget(name='target-1')
From 35c0abf24ea4a6c38e6fc2ee22b4aabe73776783 Mon Sep 17 00:00:00 2001
From: Charles
Date: Thu, 3 Sep 2020 19:51:03 +0900
Subject: [PATCH 010/303] Change Token.generate_key to a Classmethod (#7502)
* adds classmethod decorator to generate_key and test to confirm change is acceptable
* self -> cls
---
rest_framework/authtoken/models.py | 3 ++-
tests/authentication/test_authentication.py | 4 ++++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/rest_framework/authtoken/models.py b/rest_framework/authtoken/models.py
index f8a871bf1..fd8a50e0e 100644
--- a/rest_framework/authtoken/models.py
+++ b/rest_framework/authtoken/models.py
@@ -32,7 +32,8 @@ class Token(models.Model):
self.key = self.generate_key()
return super().save(*args, **kwargs)
- def generate_key(self):
+ @classmethod
+ def generate_key(cls):
return binascii.hexlify(os.urandom(20)).decode()
def __str__(self):
diff --git a/tests/authentication/test_authentication.py b/tests/authentication/test_authentication.py
index 4760ea319..e872fa744 100644
--- a/tests/authentication/test_authentication.py
+++ b/tests/authentication/test_authentication.py
@@ -397,6 +397,10 @@ class TokenAuthTests(BaseTokenAuthTests, TestCase):
key = token.generate_key()
assert isinstance(key, str)
+ def test_generate_key_accessible_as_classmethod(self):
+ key = self.model.generate_key()
+ assert isinstance(key, str)
+
def test_token_login_json(self):
"""Ensure token login view using JSON POST works."""
client = APIClient(enforce_csrf_checks=True)
From b8ab30683a03f8c6af2dda27ba893920ea83bc92 Mon Sep 17 00:00:00 2001
From: David Smith
Date: Thu, 13 Aug 2020 21:33:25 +0100
Subject: [PATCH 011/303] default_app_config application variable is deprecated
in Django 3.2
---
rest_framework/__init__.py | 6 +++++-
rest_framework/authtoken/__init__.py | 5 ++++-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py
index 8f2bc4466..483276e14 100644
--- a/rest_framework/__init__.py
+++ b/rest_framework/__init__.py
@@ -7,6 +7,8 @@ ______ _____ _____ _____ __
\_| \_\____/\____/ \_/ |_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_|
"""
+import django
+
__title__ = 'Django REST framework'
__version__ = '3.11.0'
__author__ = 'Tom Christie'
@@ -22,7 +24,9 @@ HTTP_HEADER_ENCODING = 'iso-8859-1'
# Default datetime input and output formats
ISO_8601 = 'iso-8601'
-default_app_config = 'rest_framework.apps.RestFrameworkConfig'
+
+if django.VERSION < (3, 2):
+ default_app_config = 'rest_framework.apps.RestFrameworkConfig'
class RemovedInDRF313Warning(DeprecationWarning):
diff --git a/rest_framework/authtoken/__init__.py b/rest_framework/authtoken/__init__.py
index 82f5b9171..285fe15c6 100644
--- a/rest_framework/authtoken/__init__.py
+++ b/rest_framework/authtoken/__init__.py
@@ -1 +1,4 @@
-default_app_config = 'rest_framework.authtoken.apps.AuthTokenConfig'
+import django
+
+if django.VERSION < (3, 2):
+ default_app_config = 'rest_framework.authtoken.apps.AuthTokenConfig'
From d5461e93fea2367fb0a5c7ef28d49c6be150273c Mon Sep 17 00:00:00 2001
From: David Smith <39445562+smithdc1@users.noreply.github.com>
Date: Sat, 5 Sep 2020 09:02:27 +0100
Subject: [PATCH 012/303] Bump flake8 to 3.8.3 (#7521)
---
requirements/requirements-codestyle.txt | 4 ++--
rest_framework/renderers.py | 4 ++--
rest_framework/utils/field_mapping.py | 1 -
tests/test_fields.py | 2 +-
4 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/requirements/requirements-codestyle.txt b/requirements/requirements-codestyle.txt
index 434fc41af..4f54d6e77 100644
--- a/requirements/requirements-codestyle.txt
+++ b/requirements/requirements-codestyle.txt
@@ -1,7 +1,7 @@
# PEP8 code linting, which we run on all commits.
-flake8==3.7.9
+flake8==3.8.3
flake8-tidy-imports==4.1.0
-pycodestyle==2.5.0
+pycodestyle==2.6.0
# Sort and lint imports
isort==5.4.2
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index a96fa6e65..c790879b9 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -856,8 +856,8 @@ class DocumentationRenderer(BaseRenderer):
return {
'document': data,
'langs': self.languages,
- 'lang_htmls': ["rest_framework/docs/langs/%s.html" % l for l in self.languages],
- 'lang_intro_htmls': ["rest_framework/docs/langs/%s-intro.html" % l for l in self.languages],
+ 'lang_htmls': ["rest_framework/docs/langs/%s.html" % language for language in self.languages],
+ 'lang_intro_htmls': ["rest_framework/docs/langs/%s-intro.html" % language for language in self.languages],
'code_style': pygments_css(self.code_style),
'request': request
}
diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py
index c008495cc..4f8a4f192 100644
--- a/rest_framework/utils/field_mapping.py
+++ b/rest_framework/utils/field_mapping.py
@@ -58,7 +58,6 @@ def get_detail_view_name(model):
that refer to instances of the model.
"""
return '%(model_name)s-detail' % {
- 'app_label': model._meta.app_label,
'model_name': model._meta.object_name.lower()
}
diff --git a/tests/test_fields.py b/tests/test_fields.py
index b1ad1dc66..506e75905 100644
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -669,7 +669,7 @@ class TestBooleanField(FieldValues):
for input_value in inputs:
with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation(input_value)
- expected = ['Must be a valid boolean.'.format(input_value)]
+ expected = ['Must be a valid boolean.']
assert exc_info.value.detail == expected
From 9990b5928139072c8e010c8b5c86616f0d40ef9d Mon Sep 17 00:00:00 2001
From: David Smith <39445562+smithdc1@users.noreply.github.com>
Date: Mon, 7 Sep 2020 19:00:17 +0100
Subject: [PATCH 013/303] Dropped test compatibility shims for Django <2.2.
(#7523)
---
tests/conftest.py | 29 +++++++++++++----------------
tests/schemas/test_coreapi.py | 1 -
tests/test_model_serializer.py | 22 +---------------------
tests/test_permissions.py | 7 -------
tests/test_urlpatterns.py | 9 ---------
5 files changed, 14 insertions(+), 54 deletions(-)
diff --git a/tests/conftest.py b/tests/conftest.py
index d28edeb8a..ac29e4a42 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -67,22 +67,19 @@ def pytest_configure(config):
)
# guardian is optional
- # Note that for the test cases we're installing a version of django-guardian
- # that's only compatible with Django 2.0+.
- if django.VERSION >= (2, 0, 0):
- try:
- import guardian # NOQA
- except ImportError:
- pass
- else:
- settings.ANONYMOUS_USER_ID = -1
- settings.AUTHENTICATION_BACKENDS = (
- 'django.contrib.auth.backends.ModelBackend',
- 'guardian.backends.ObjectPermissionBackend',
- )
- settings.INSTALLED_APPS += (
- 'guardian',
- )
+ try:
+ import guardian # NOQA
+ except ImportError:
+ pass
+ else:
+ settings.ANONYMOUS_USER_ID = -1
+ settings.AUTHENTICATION_BACKENDS = (
+ 'django.contrib.auth.backends.ModelBackend',
+ 'guardian.backends.ObjectPermissionBackend',
+ )
+ settings.INSTALLED_APPS += (
+ 'guardian',
+ )
if config.getoption('--no-pkgroot'):
sys.path.pop(0)
diff --git a/tests/schemas/test_coreapi.py b/tests/schemas/test_coreapi.py
index 403b3b634..5f646258b 100644
--- a/tests/schemas/test_coreapi.py
+++ b/tests/schemas/test_coreapi.py
@@ -456,7 +456,6 @@ class TestSchemaGenerator(TestCase):
@unittest.skipUnless(coreapi, 'coreapi is not installed')
-@unittest.skipUnless(path, 'needs Django 2')
@override_settings(REST_FRAMEWORK={'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema'})
class TestSchemaGeneratorDjango2(TestCase):
def setUp(self):
diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py
index 1733930a6..7da1b41ae 100644
--- a/tests/test_model_serializer.py
+++ b/tests/test_model_serializer.py
@@ -12,7 +12,6 @@ import sys
import tempfile
from collections import OrderedDict
-import django
import pytest
from django.core.exceptions import ImproperlyConfigured
from django.core.serializers.json import DjangoJSONEncoder
@@ -63,7 +62,7 @@ class RegularFieldsModel(models.Model):
email_field = models.EmailField(max_length=100)
float_field = models.FloatField()
integer_field = models.IntegerField()
- null_boolean_field = models.NullBooleanField()
+ null_boolean_field = models.BooleanField(null=True, default=False)
positive_integer_field = models.PositiveIntegerField()
positive_small_integer_field = models.PositiveSmallIntegerField()
slug_field = models.SlugField(max_length=100)
@@ -218,25 +217,6 @@ class TestRegularFieldMappings(TestCase):
""")
self.assertEqual(repr(TestSerializer()), expected)
- # merge this into test_regular_fields / RegularFieldsModel when
- # Django 2.1 is the minimum supported version
- @pytest.mark.skipif(django.VERSION < (2, 1), reason='Django version < 2.1')
- def test_nullable_boolean_field(self):
- class NullableBooleanModel(models.Model):
- field = models.BooleanField(null=True, default=False)
-
- class NullableBooleanSerializer(serializers.ModelSerializer):
- class Meta:
- model = NullableBooleanModel
- fields = ['field']
-
- expected = dedent("""
- NullableBooleanSerializer():
- field = BooleanField(allow_null=True, required=False)
- """)
-
- self.assertEqual(repr(NullableBooleanSerializer()), expected)
-
def test_nullable_boolean_field_choices(self):
class NullableBooleanChoicesModel(models.Model):
CHECKLIST_OPTIONS = (
diff --git a/tests/test_permissions.py b/tests/test_permissions.py
index 232c72dd2..4e6cae4b8 100644
--- a/tests/test_permissions.py
+++ b/tests/test_permissions.py
@@ -2,7 +2,6 @@ import base64
import unittest
from unittest import mock
-import django
from django.conf import settings
from django.contrib.auth.models import AnonymousUser, Group, Permission, User
from django.db import models
@@ -248,12 +247,6 @@ class BasicPermModel(models.Model):
class Meta:
app_label = 'tests'
- if django.VERSION < (2, 1):
- permissions = (
- ('view_basicpermmodel', 'Can view basic perm model'),
- # add, change, delete built in to django
- )
-
class BasicPermSerializer(serializers.ModelSerializer):
class Meta:
diff --git a/tests/test_urlpatterns.py b/tests/test_urlpatterns.py
index ec19494b0..90820b532 100644
--- a/tests/test_urlpatterns.py
+++ b/tests/test_urlpatterns.py
@@ -1,4 +1,3 @@
-import unittest
from collections import namedtuple
from django.conf.urls import include, url
@@ -66,7 +65,6 @@ class FormatSuffixTests(TestCase):
]
self._test_trailing_slash(urlpatterns)
- @unittest.skipUnless(path, 'needs Django 2')
def test_trailing_slash_django2(self):
urlpatterns = [
path('test/', dummy_view),
@@ -87,14 +85,12 @@ class FormatSuffixTests(TestCase):
]
self._test_format_suffix(urlpatterns)
- @unittest.skipUnless(path, 'needs Django 2')
def test_format_suffix_django2(self):
urlpatterns = [
path('test', dummy_view),
]
self._test_format_suffix(urlpatterns)
- @unittest.skipUnless(path, 'needs Django 2')
def test_format_suffix_django2_args(self):
urlpatterns = [
path('convtest/', dummy_view),
@@ -124,7 +120,6 @@ class FormatSuffixTests(TestCase):
]
self._test_default_args(urlpatterns)
- @unittest.skipUnless(path, 'needs Django 2')
def test_default_args_django2(self):
urlpatterns = [
path('test', dummy_view, {'foo': 'bar'}),
@@ -148,7 +143,6 @@ class FormatSuffixTests(TestCase):
]
self._test_included_urls(urlpatterns)
- @unittest.skipUnless(path, 'needs Django 2')
def test_included_urls_django2(self):
nested_patterns = [
path('path', dummy_view)
@@ -158,7 +152,6 @@ class FormatSuffixTests(TestCase):
]
self._test_included_urls(urlpatterns)
- @unittest.skipUnless(path, 'needs Django 2')
def test_included_urls_django2_mixed(self):
nested_patterns = [
path('path', dummy_view)
@@ -168,7 +161,6 @@ class FormatSuffixTests(TestCase):
]
self._test_included_urls(urlpatterns)
- @unittest.skipUnless(path, 'needs Django 2')
def test_included_urls_django2_mixed_args(self):
nested_patterns = [
path('path/', dummy_view),
@@ -216,7 +208,6 @@ class FormatSuffixTests(TestCase):
]
self._test_allowed_formats(urlpatterns)
- @unittest.skipUnless(path, 'needs Django 2')
def test_allowed_formats_django2(self):
urlpatterns = [
path('test', dummy_view),
From 410575dace9d64a53ec771ea5e21b40525ed0e95 Mon Sep 17 00:00:00 2001
From: Adam Johnson
Date: Tue, 8 Sep 2020 15:32:27 +0100
Subject: [PATCH 014/303] Replace all url() calls with path() or re_path()
(#7512)
* url() is deprecated in Django 3.1
* update given feedbacks on url() is deprecated in Django 3.1
* Fix test_urlpatterns.py to continue testing mixed re_path() and path()
* Fix one missed reference
Co-authored-by: sanjusci
---
docs/api-guide/authentication.md | 4 +-
docs/api-guide/filtering.md | 2 +-
docs/api-guide/format-suffixes.md | 6 +-
docs/api-guide/generic-views.md | 2 +-
docs/api-guide/parsers.md | 2 +-
docs/api-guide/routers.md | 14 ++---
docs/api-guide/schemas.md | 2 +-
docs/community/3.5-announcement.md | 6 +-
docs/community/3.6-announcement.md | 2 +-
docs/community/3.9-announcement.md | 3 +-
docs/coreapi/from-documenting-your-api.md | 4 +-
docs/coreapi/schemas.md | 9 +--
docs/index.md | 2 +-
docs/topics/api-clients.md | 2 +-
.../4-authentication-and-permissions.md | 2 +-
.../templates/rest_framework/docs/error.html | 2 +-
rest_framework/urls.py | 2 +-
rest_framework/versioning.py | 12 ++--
tests/authentication/test_authentication.py | 30 ++++-----
tests/browsable_api/auth_urls.py | 6 +-
tests/browsable_api/no_auth_urls.py | 4 +-
.../test_browsable_nested_api.py | 4 +-
tests/schemas/test_coreapi.py | 61 +++++++++---------
tests/schemas/test_managementcommand.py | 4 +-
tests/schemas/test_openapi.py | 40 ++++++------
tests/test_api_client.py | 16 ++---
tests/test_atomic_requests.py | 4 +-
tests/test_fields.py | 2 +-
tests/test_htmlrenderer.py | 8 +--
tests/test_lazy_hyperlinks.py | 4 +-
tests/test_metadata.py | 3 +-
tests/test_middleware.py | 6 +-
tests/test_relations.py | 4 +-
tests/test_relations_hyperlink.py | 18 +++---
tests/test_renderers.py | 20 +++---
tests/test_request.py | 8 +--
tests/test_requests_client.py | 10 +--
tests/test_response.py | 20 +++---
tests/test_reverse.py | 5 +-
tests/test_routers.py | 23 ++++---
tests/test_testing.py | 12 ++--
tests/test_urlpatterns.py | 63 +++++++------------
tests/test_utils.py | 14 ++---
tests/test_versioning.py | 30 ++++-----
tests/test_viewsets.py | 4 +-
tests/urls.py | 4 +-
46 files changed, 243 insertions(+), 262 deletions(-)
diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md
index ebb0ab4d6..5878040a4 100644
--- a/docs/api-guide/authentication.md
+++ b/docs/api-guide/authentication.md
@@ -199,7 +199,7 @@ When using `TokenAuthentication`, you may want to provide a mechanism for client
from rest_framework.authtoken import views
urlpatterns += [
- url(r'^api-token-auth/', views.obtain_auth_token)
+ path('api-token-auth/', views.obtain_auth_token)
]
Note that the URL part of the pattern can be whatever you want to use.
@@ -238,7 +238,7 @@ For example, you may return additional user information beyond the `token` value
And in your `urls.py`:
urlpatterns += [
- url(r'^api-token-auth/', CustomAuthToken.as_view())
+ path('api-token-auth/', CustomAuthToken.as_view())
]
diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md
index 8e3bd9ef5..41c1341dd 100644
--- a/docs/api-guide/filtering.md
+++ b/docs/api-guide/filtering.md
@@ -45,7 +45,7 @@ Another style of filtering might involve restricting the queryset based on some
For example if your URL config contained an entry like this:
- url('^purchases/(?P.+)/$', PurchaseList.as_view()),
+ re_path('^purchases/(?P.+)/$', PurchaseList.as_view()),
You could then write a view that returned a purchase queryset filtered by the username portion of the URL:
diff --git a/docs/api-guide/format-suffixes.md b/docs/api-guide/format-suffixes.md
index 04467b3d3..dfdf24953 100644
--- a/docs/api-guide/format-suffixes.md
+++ b/docs/api-guide/format-suffixes.md
@@ -32,9 +32,9 @@ Example:
from blog import views
urlpatterns = [
- url(r'^/$', views.apt_root),
- url(r'^comments/$', views.comment_list),
- url(r'^comments/(?P[0-9]+)/$', views.comment_detail)
+ path('', views.apt_root),
+ path('comments/', views.comment_list),
+ path('comments//', views.comment_detail)
]
urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html'])
diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md
index 4ff549f07..afc2cab56 100644
--- a/docs/api-guide/generic-views.md
+++ b/docs/api-guide/generic-views.md
@@ -45,7 +45,7 @@ For more complex cases you might also want to override various methods on the vi
For very simple cases you might want to pass through any class attributes using the `.as_view()` method. For example, your URLconf might include something like the following entry:
- url(r'^/users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')
+ path('users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')
---
diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md
index a3bc74a2b..e8f03de8b 100644
--- a/docs/api-guide/parsers.md
+++ b/docs/api-guide/parsers.md
@@ -125,7 +125,7 @@ If it is called without a `filename` URL keyword argument, then the client must
# urls.py
urlpatterns = [
# ...
- url(r'^upload/(?P[^/]+)$', FileUploadView.as_view())
+ re_path(r'^upload/(?P[^/]+)$', FileUploadView.as_view())
]
---
diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md
index 5f6802222..8d8594eee 100644
--- a/docs/api-guide/routers.md
+++ b/docs/api-guide/routers.md
@@ -63,7 +63,7 @@ For example, you can append `router.urls` to a list of existing views...
router.register(r'accounts', AccountViewSet)
urlpatterns = [
- url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
+ path('forgot-password/', ForgotPasswordFormView.as_view()),
]
urlpatterns += router.urls
@@ -71,22 +71,22 @@ For example, you can append `router.urls` to a list of existing views...
Alternatively you can use Django's `include` function, like so...
urlpatterns = [
- url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
- url(r'^', include(router.urls)),
+ path('forgot-password', ForgotPasswordFormView.as_view()),
+ path('', include(router.urls)),
]
You may use `include` with an application namespace:
urlpatterns = [
- url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
- url(r'^api/', include((router.urls, 'app_name'))),
+ path('forgot-password/', ForgotPasswordFormView.as_view()),
+ path('api/', include((router.urls, 'app_name'))),
]
Or both an application and instance namespace:
urlpatterns = [
- url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
- url(r'^api/', include((router.urls, 'app_name'), namespace='instance_name')),
+ path('forgot-password/', ForgotPasswordFormView.as_view()),
+ path('api/', include((router.urls, 'app_name'), namespace='instance_name')),
]
See Django's [URL namespaces docs][url-namespace-docs] and the [`include` API reference][include-api-reference] for more details.
diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md
index 402315ef9..e39cd21a8 100644
--- a/docs/api-guide/schemas.md
+++ b/docs/api-guide/schemas.md
@@ -114,7 +114,7 @@ The `get_schema_view()` helper takes the following keyword arguments:
only want the `myproject.api` urls to be exposed in the schema:
schema_url_patterns = [
- url(r'^api/', include('myproject.api.urls')),
+ path('api/', include('myproject.api.urls')),
]
schema_view = get_schema_view(
diff --git a/docs/community/3.5-announcement.md b/docs/community/3.5-announcement.md
index cce2dd050..91bfce428 100644
--- a/docs/community/3.5-announcement.md
+++ b/docs/community/3.5-announcement.md
@@ -69,7 +69,7 @@ schema_view = get_schema_view(
)
urlpatterns = [
- url(r'^swagger/$', schema_view),
+ path('swagger/', schema_view),
...
]
```
@@ -198,8 +198,8 @@ Make sure to include the view before your router urls. For example:
schema_view = get_schema_view(title='Example API')
urlpatterns = [
- url('^$', schema_view),
- url(r'^', include(router.urls)),
+ path('', schema_view),
+ path('', include(router.urls)),
]
### Schema path representations
diff --git a/docs/community/3.6-announcement.md b/docs/community/3.6-announcement.md
index c41ad8ecb..35704eb58 100644
--- a/docs/community/3.6-announcement.md
+++ b/docs/community/3.6-announcement.md
@@ -73,7 +73,7 @@ To install the API documentation, you'll need to include it in your projects URL
urlpatterns = [
...
- url(r'^docs/', include_docs_urls(title=API_TITLE, description=API_DESCRIPTION))
+ path('docs/', include_docs_urls(title=API_TITLE, description=API_DESCRIPTION))
]
Once installed you should see something a little like this:
diff --git a/docs/community/3.9-announcement.md b/docs/community/3.9-announcement.md
index 1cf4464d6..fee6e6909 100644
--- a/docs/community/3.9-announcement.md
+++ b/docs/community/3.9-announcement.md
@@ -62,6 +62,7 @@ Here's an example of adding an OpenAPI schema to the URL conf:
```python
from rest_framework.schemas import get_schema_view
from rest_framework.renderers import JSONOpenAPIRenderer
+from django.urls import path
schema_view = get_schema_view(
title='Server Monitoring API',
@@ -70,7 +71,7 @@ schema_view = get_schema_view(
)
urlpatterns = [
- url('^schema.json$', schema_view),
+ path('schema.json', schema_view),
...
]
```
diff --git a/docs/coreapi/from-documenting-your-api.md b/docs/coreapi/from-documenting-your-api.md
index 9ac3be686..604dfa668 100644
--- a/docs/coreapi/from-documenting-your-api.md
+++ b/docs/coreapi/from-documenting-your-api.md
@@ -19,7 +19,7 @@ To install the API documentation, you'll need to include it in your project's UR
urlpatterns = [
...
- url(r'^docs/', include_docs_urls(title='My API title'))
+ path('docs/', include_docs_urls(title='My API title'))
]
This will include two different views:
@@ -41,7 +41,7 @@ You may ensure views are given a `request` instance by calling `include_docs_url
urlpatterns = [
...
# Generate schema with valid `request` instance:
- url(r'^docs/', include_docs_urls(title='My API title', public=False))
+ path('docs/', include_docs_urls(title='My API title', public=False))
]
diff --git a/docs/coreapi/schemas.md b/docs/coreapi/schemas.md
index 69606f853..e7a418b80 100644
--- a/docs/coreapi/schemas.md
+++ b/docs/coreapi/schemas.md
@@ -43,11 +43,12 @@ To add a dynamically generated schema view to your API, use `get_schema_view`.
```python
from rest_framework.schemas import get_schema_view
+from django.urls import path
schema_view = get_schema_view(title="Example API")
urlpatterns = [
- url('^schema$', schema_view),
+ path('schema', schema_view),
...
]
```
@@ -292,7 +293,7 @@ The simplest way to include a schema in your project is to use the
schema_view = get_schema_view(title="Server Monitoring API")
urlpatterns = [
- url('^$', schema_view),
+ path('', schema_view),
...
]
@@ -358,7 +359,7 @@ List of url patterns to limit the schema introspection to. If you only want the
to be exposed in the schema:
schema_url_patterns = [
- url(r'^api/', include('myproject.api.urls')),
+ path('api/', include('myproject.api.urls')),
]
schema_view = get_schema_view(
@@ -411,7 +412,7 @@ return the schema.
**urls.py:**
urlpatterns = [
- url('/', schema_view),
+ path('', schema_view),
...
]
diff --git a/docs/index.md b/docs/index.md
index c7b78e9c3..54654c7c5 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -120,7 +120,7 @@ If you're intending to use the browsable API you'll probably also want to add RE
urlpatterns = [
...
- url(r'^api-auth/', include('rest_framework.urls'))
+ path('api-auth/', include('rest_framework.urls'))
]
Note that the URL path can be whatever you want.
diff --git a/docs/topics/api-clients.md b/docs/topics/api-clients.md
index b020c380a..9b61eaf42 100644
--- a/docs/topics/api-clients.md
+++ b/docs/topics/api-clients.md
@@ -384,7 +384,7 @@ First, install the API documentation views. These will include the schema resour
urlpatterns = [
...
- url(r'^docs/', include_docs_urls(title='My API service'), name='api-docs'),
+ path('docs/', include_docs_urls(title='My API service'), name='api-docs'),
]
Once the API documentation URLs are installed, you'll be able to include both the required JavaScript resources. Note that the ordering of these two lines is important, as the schema loading requires CoreAPI to already be installed.
diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md
index 6808780fa..79ce355c9 100644
--- a/docs/tutorial/4-authentication-and-permissions.md
+++ b/docs/tutorial/4-authentication-and-permissions.md
@@ -137,7 +137,7 @@ We can add a login view for use with the browsable API, by editing the URLconf i
Add the following import at the top of the file:
- from django.conf.urls import include
+ from django.urls import path, include
And, at the end of the file, add a pattern to include the login and logout views for the browsable API.
diff --git a/rest_framework/templates/rest_framework/docs/error.html b/rest_framework/templates/rest_framework/docs/error.html
index 6afc4a88b..694f88a15 100644
--- a/rest_framework/templates/rest_framework/docs/error.html
+++ b/rest_framework/templates/rest_framework/docs/error.html
@@ -48,7 +48,7 @@ being applied unexpectedly?
when including the docs urls:
- url(r'^docs/', include_docs_urls(title='Your API',
+ path('docs/', include_docs_urls(title='Your API',
authentication_classes=[],
permission_classes=[])),
diff --git a/rest_framework/urls.py b/rest_framework/urls.py
index d9b858ebc..0aa301332 100644
--- a/rest_framework/urls.py
+++ b/rest_framework/urls.py
@@ -6,7 +6,7 @@ your API requires authentication:
urlpatterns = [
...
- url(r'^auth/', include('rest_framework.urls'))
+ path('auth/', include('rest_framework.urls'))
]
You should make sure your authentication settings include `SessionAuthentication`.
diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py
index 8c35a1a58..78cfc9dc8 100644
--- a/rest_framework/versioning.py
+++ b/rest_framework/versioning.py
@@ -60,8 +60,8 @@ class URLPathVersioning(BaseVersioning):
An example URL conf for two views that accept two different versions.
urlpatterns = [
- url(r'^(?P[v1|v2]+)/users/$', users_list, name='users-list'),
- url(r'^(?P[v1|v2]+)/users/(?P[0-9]+)/$', users_detail, name='users-detail')
+ re_path(r'^(?P[v1|v2]+)/users/$', users_list, name='users-list'),
+ re_path(r'^(?P[v1|v2]+)/users/(?P[0-9]+)/$', users_detail, name='users-detail')
]
GET /1.0/something/ HTTP/1.1
@@ -99,14 +99,14 @@ class NamespaceVersioning(BaseVersioning):
# users/urls.py
urlpatterns = [
- url(r'^/users/$', users_list, name='users-list'),
- url(r'^/users/(?P[0-9]+)/$', users_detail, name='users-detail')
+ path('/users/', users_list, name='users-list'),
+ path('/users//', users_detail, name='users-detail')
]
# urls.py
urlpatterns = [
- url(r'^v1/', include('users.urls', namespace='v1')),
- url(r'^v2/', include('users.urls', namespace='v2'))
+ path('v1/', include('users.urls', namespace='v1')),
+ path('v2/', include('users.urls', namespace='v2'))
]
GET /1.0/something/ HTTP/1.1
diff --git a/tests/authentication/test_authentication.py b/tests/authentication/test_authentication.py
index e872fa744..a73e0d79c 100644
--- a/tests/authentication/test_authentication.py
+++ b/tests/authentication/test_authentication.py
@@ -2,10 +2,10 @@ import base64
import pytest
from django.conf import settings
-from django.conf.urls import include, url
from django.contrib.auth.models import User
from django.http import HttpResponse
from django.test import TestCase, override_settings
+from django.urls import include, path
from rest_framework import (
HTTP_HEADER_ENCODING, exceptions, permissions, renderers, status
@@ -47,34 +47,34 @@ class MockView(APIView):
urlpatterns = [
- url(
- r'^session/$',
+ path(
+ 'session/',
MockView.as_view(authentication_classes=[SessionAuthentication])
),
- url(
- r'^basic/$',
+ path(
+ 'basic/',
MockView.as_view(authentication_classes=[BasicAuthentication])
),
- url(
- r'^remote-user/$',
+ path(
+ 'remote-user/',
MockView.as_view(authentication_classes=[RemoteUserAuthentication])
),
- url(
- r'^token/$',
+ path(
+ 'token/',
MockView.as_view(authentication_classes=[TokenAuthentication])
),
- url(
- r'^customtoken/$',
+ path(
+ 'customtoken/',
MockView.as_view(authentication_classes=[CustomTokenAuthentication])
),
- url(
- r'^customkeywordtoken/$',
+ path(
+ 'customkeywordtoken/',
MockView.as_view(
authentication_classes=[CustomKeywordTokenAuthentication]
)
),
- url(r'^auth-token/$', obtain_auth_token),
- url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
+ path('auth-token/', obtain_auth_token),
+ path('auth/', include('rest_framework.urls', namespace='rest_framework')),
]
diff --git a/tests/browsable_api/auth_urls.py b/tests/browsable_api/auth_urls.py
index 7530c5e40..151278cbd 100644
--- a/tests/browsable_api/auth_urls.py
+++ b/tests/browsable_api/auth_urls.py
@@ -1,8 +1,8 @@
-from django.conf.urls import include, url
+from django.urls import include, path
from .views import MockView
urlpatterns = [
- url(r'^$', MockView.as_view()),
- url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
+ path('', MockView.as_view()),
+ path('auth/', include('rest_framework.urls', namespace='rest_framework')),
]
diff --git a/tests/browsable_api/no_auth_urls.py b/tests/browsable_api/no_auth_urls.py
index 348bfe1c0..65701c065 100644
--- a/tests/browsable_api/no_auth_urls.py
+++ b/tests/browsable_api/no_auth_urls.py
@@ -1,7 +1,7 @@
-from django.conf.urls import url
+from django.urls import path
from .views import MockView
urlpatterns = [
- url(r'^$', MockView.as_view()),
+ path('', MockView.as_view()),
]
diff --git a/tests/browsable_api/test_browsable_nested_api.py b/tests/browsable_api/test_browsable_nested_api.py
index f945d2a43..a6c2ee6bd 100644
--- a/tests/browsable_api/test_browsable_nested_api.py
+++ b/tests/browsable_api/test_browsable_nested_api.py
@@ -1,6 +1,6 @@
-from django.conf.urls import url
from django.test import TestCase
from django.test.utils import override_settings
+from django.urls import path
from rest_framework import serializers
from rest_framework.generics import ListCreateAPIView
@@ -23,7 +23,7 @@ class NestedSerializersView(ListCreateAPIView):
urlpatterns = [
- url(r'^api/$', NestedSerializersView.as_view(), name='api'),
+ path('api/', NestedSerializersView.as_view(), name='api'),
]
diff --git a/tests/schemas/test_coreapi.py b/tests/schemas/test_coreapi.py
index 5f646258b..7b1f15fef 100644
--- a/tests/schemas/test_coreapi.py
+++ b/tests/schemas/test_coreapi.py
@@ -1,11 +1,10 @@
import unittest
import pytest
-from django.conf.urls import include, url
from django.core.exceptions import PermissionDenied
from django.http import Http404
from django.test import TestCase, override_settings
-from django.urls import path
+from django.urls import include, path
from rest_framework import (
filters, generics, pagination, permissions, serializers
@@ -145,8 +144,8 @@ with override_settings(REST_FRAMEWORK={'DEFAULT_SCHEMA_CLASS': 'rest_framework.s
router = DefaultRouter()
router.register('example', ExampleViewSet, basename='example')
urlpatterns = [
- url(r'^$', schema_view),
- url(r'^', include(router.urls))
+ path('', schema_view),
+ path('', include(router.urls))
]
@@ -407,9 +406,9 @@ class ExampleDetailView(APIView):
class TestSchemaGenerator(TestCase):
def setUp(self):
self.patterns = [
- url(r'^example/?$', views.ExampleListView.as_view()),
- url(r'^example/(?P\d+)/?$', views.ExampleDetailView.as_view()),
- url(r'^example/(?P\d+)/sub/?$', views.ExampleDetailView.as_view()),
+ path('example/', views.ExampleListView.as_view()),
+ path('example//', views.ExampleDetailView.as_view()),
+ path('example//sub/', views.ExampleDetailView.as_view()),
]
def test_schema_for_regular_views(self):
@@ -513,9 +512,9 @@ class TestSchemaGeneratorDjango2(TestCase):
class TestSchemaGeneratorNotAtRoot(TestCase):
def setUp(self):
self.patterns = [
- url(r'^api/v1/example/?$', views.ExampleListView.as_view()),
- url(r'^api/v1/example/(?P\d+)/?$', views.ExampleDetailView.as_view()),
- url(r'^api/v1/example/(?P\d+)/sub/?$', views.ExampleDetailView.as_view()),
+ path('api/v1/example/', views.ExampleListView.as_view()),
+ path('api/v1/example//', views.ExampleDetailView.as_view()),
+ path('api/v1/example//sub/', views.ExampleDetailView.as_view()),
]
def test_schema_for_regular_views(self):
@@ -569,7 +568,7 @@ class TestSchemaGeneratorWithMethodLimitedViewSets(TestCase):
router = DefaultRouter()
router.register('example1', MethodLimitedViewSet, basename='example1')
self.patterns = [
- url(r'^', include(router.urls))
+ path('', include(router.urls))
]
def test_schema_for_regular_views(self):
@@ -635,8 +634,8 @@ class TestSchemaGeneratorWithRestrictedViewSets(TestCase):
router.register('example1', Http404ExampleViewSet, basename='example1')
router.register('example2', PermissionDeniedExampleViewSet, basename='example2')
self.patterns = [
- url('^example/?$', views.ExampleListView.as_view()),
- url(r'^', include(router.urls))
+ path('example/', views.ExampleListView.as_view()),
+ path('', include(router.urls))
]
def test_schema_for_regular_views(self):
@@ -679,7 +678,7 @@ class ForeignKeySourceView(generics.CreateAPIView):
class TestSchemaGeneratorWithForeignKey(TestCase):
def setUp(self):
self.patterns = [
- url(r'^example/?$', ForeignKeySourceView.as_view()),
+ path('example/', ForeignKeySourceView.as_view()),
]
def test_schema_for_regular_views(self):
@@ -725,7 +724,7 @@ class ManyToManySourceView(generics.CreateAPIView):
class TestSchemaGeneratorWithManyToMany(TestCase):
def setUp(self):
self.patterns = [
- url(r'^example/?$', ManyToManySourceView.as_view()),
+ path('example/', ManyToManySourceView.as_view()),
]
def test_schema_for_regular_views(self):
@@ -1041,9 +1040,9 @@ with override_settings(REST_FRAMEWORK={'DEFAULT_SCHEMA_CLASS': 'rest_framework.s
class SchemaGenerationExclusionTests(TestCase):
def setUp(self):
self.patterns = [
- url('^excluded-cbv/$', ExcludedAPIView.as_view()),
- url('^excluded-fbv/$', excluded_fbv),
- url('^included-fbv/$', included_fbv),
+ path('excluded-cbv/', ExcludedAPIView.as_view()),
+ path('excluded-fbv/', excluded_fbv),
+ path('included-fbv/', included_fbv),
]
def test_schema_generator_excludes_correctly(self):
@@ -1136,8 +1135,8 @@ class TestURLNamingCollisions(TestCase):
pass
patterns = [
- url(r'^test', simple_fbv),
- url(r'^test/list/', simple_fbv),
+ path('test', simple_fbv),
+ path('test/list/', simple_fbv),
]
generator = SchemaGenerator(title='Naming Colisions', patterns=patterns)
@@ -1173,14 +1172,14 @@ class TestURLNamingCollisions(TestCase):
def test_manually_routing_generic_view(self):
patterns = [
- url(r'^test', NamingCollisionView.as_view()),
- url(r'^test/retrieve/', NamingCollisionView.as_view()),
- url(r'^test/update/', NamingCollisionView.as_view()),
+ path('test', NamingCollisionView.as_view()),
+ path('test/retrieve/', NamingCollisionView.as_view()),
+ path('test/update/', NamingCollisionView.as_view()),
# Fails with method names:
- url(r'^test/get/', NamingCollisionView.as_view()),
- url(r'^test/put/', NamingCollisionView.as_view()),
- url(r'^test/delete/', NamingCollisionView.as_view()),
+ path('test/get/', NamingCollisionView.as_view()),
+ path('test/put/', NamingCollisionView.as_view()),
+ path('test/delete/', NamingCollisionView.as_view()),
]
generator = SchemaGenerator(title='Naming Colisions', patterns=patterns)
@@ -1196,7 +1195,7 @@ class TestURLNamingCollisions(TestCase):
def test_from_router(self):
patterns = [
- url(r'from-router', include(naming_collisions_router.urls)),
+ path('from-router', include(naming_collisions_router.urls)),
]
generator = SchemaGenerator(title='Naming Colisions', patterns=patterns)
@@ -1228,8 +1227,8 @@ class TestURLNamingCollisions(TestCase):
def test_url_under_same_key_not_replaced(self):
patterns = [
- url(r'example/(?P\d+)/$', BasicNamingCollisionView.as_view()),
- url(r'example/(?P\w+)/$', BasicNamingCollisionView.as_view()),
+ path('example//', BasicNamingCollisionView.as_view()),
+ path('example//', BasicNamingCollisionView.as_view()),
]
generator = SchemaGenerator(title='Naming Colisions', patterns=patterns)
@@ -1245,8 +1244,8 @@ class TestURLNamingCollisions(TestCase):
pass
patterns = [
- url(r'^test/list/', simple_fbv),
- url(r'^test/(?P\d+)/list/', simple_fbv),
+ path('test/list/', simple_fbv),
+ path('test//list/', simple_fbv),
]
generator = SchemaGenerator(title='Naming Colisions', patterns=patterns)
diff --git a/tests/schemas/test_managementcommand.py b/tests/schemas/test_managementcommand.py
index 115f871e5..645eaf91d 100644
--- a/tests/schemas/test_managementcommand.py
+++ b/tests/schemas/test_managementcommand.py
@@ -3,10 +3,10 @@ import os
import tempfile
import pytest
-from django.conf.urls import url
from django.core.management import call_command
from django.test import TestCase
from django.test.utils import override_settings
+from django.urls import path
from rest_framework.compat import uritemplate, yaml
from rest_framework.management.commands import generateschema
@@ -20,7 +20,7 @@ class FooView(APIView):
urlpatterns = [
- url(r'^$', FooView.as_view())
+ path('', FooView.as_view())
]
diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py
index d483f3d45..8ea910dc1 100644
--- a/tests/schemas/test_openapi.py
+++ b/tests/schemas/test_openapi.py
@@ -2,8 +2,8 @@ import uuid
import warnings
import pytest
-from django.conf.urls import url
from django.test import RequestFactory, TestCase, override_settings
+from django.urls import path
from django.utils.translation import gettext_lazy as _
from rest_framework import filters, generics, pagination, routers, serializers
@@ -719,8 +719,8 @@ class TestOperationIntrospection(TestCase):
def test_duplicate_operation_id(self):
patterns = [
- url(r'^duplicate1/?$', views.ExampleOperationIdDuplicate1.as_view()),
- url(r'^duplicate2/?$', views.ExampleOperationIdDuplicate2.as_view()),
+ path('duplicate1/', views.ExampleOperationIdDuplicate1.as_view()),
+ path('duplicate2/', views.ExampleOperationIdDuplicate2.as_view()),
]
generator = SchemaGenerator(patterns=patterns)
@@ -874,7 +874,7 @@ class TestOperationIntrospection(TestCase):
schema = AutoSchema(tags=['example1', 'example2'])
url_patterns = [
- url(r'^test/?$', ExampleStringTagsViewSet.as_view()),
+ path('test/', ExampleStringTagsViewSet.as_view()),
]
generator = SchemaGenerator(patterns=url_patterns)
schema = generator.get_schema(request=create_request('/'))
@@ -911,8 +911,8 @@ class TestOperationIntrospection(TestCase):
pass
url_patterns = [
- url(r'^any-dash_underscore/?$', RestaurantAPIView.as_view()),
- url(r'^restaurants/branches/?$', BranchAPIView.as_view())
+ path('any-dash_underscore/', RestaurantAPIView.as_view()),
+ path('restaurants/branches/', BranchAPIView.as_view())
]
generator = SchemaGenerator(patterns=url_patterns)
schema = generator.get_schema(request=create_request('/'))
@@ -930,7 +930,7 @@ class TestGenerator(TestCase):
def test_paths_construction(self):
"""Construction of the `paths` key."""
patterns = [
- url(r'^example/?$', views.ExampleListView.as_view()),
+ path('example/', views.ExampleListView.as_view()),
]
generator = SchemaGenerator(patterns=patterns)
generator._initialise_endpoints()
@@ -946,8 +946,8 @@ class TestGenerator(TestCase):
def test_prefixed_paths_construction(self):
"""Construction of the `paths` key maintains a common prefix."""
patterns = [
- url(r'^v1/example/?$', views.ExampleListView.as_view()),
- url(r'^v1/example/{pk}/?$', views.ExampleDetailView.as_view()),
+ path('v1/example/', views.ExampleListView.as_view()),
+ path('v1/example/{pk}/', views.ExampleDetailView.as_view()),
]
generator = SchemaGenerator(patterns=patterns)
generator._initialise_endpoints()
@@ -959,8 +959,8 @@ class TestGenerator(TestCase):
def test_mount_url_prefixed_to_paths(self):
patterns = [
- url(r'^example/?$', views.ExampleListView.as_view()),
- url(r'^example/{pk}/?$', views.ExampleDetailView.as_view()),
+ path('example/', views.ExampleListView.as_view()),
+ path('example/{pk}/', views.ExampleDetailView.as_view()),
]
generator = SchemaGenerator(patterns=patterns, url='/api')
generator._initialise_endpoints()
@@ -973,7 +973,7 @@ class TestGenerator(TestCase):
def test_schema_construction(self):
"""Construction of the top level dictionary."""
patterns = [
- url(r'^example/?$', views.ExampleListView.as_view()),
+ path('example/', views.ExampleListView.as_view()),
]
generator = SchemaGenerator(patterns=patterns)
@@ -995,7 +995,7 @@ class TestGenerator(TestCase):
def test_schema_information(self):
"""Construction of the top level dictionary."""
patterns = [
- url(r'^example/?$', views.ExampleListView.as_view()),
+ path('example/', views.ExampleListView.as_view()),
]
generator = SchemaGenerator(patterns=patterns, title='My title', version='1.2.3', description='My description')
@@ -1009,7 +1009,7 @@ class TestGenerator(TestCase):
def test_schema_information_empty(self):
"""Construction of the top level dictionary."""
patterns = [
- url(r'^example/?$', views.ExampleListView.as_view()),
+ path('example/', views.ExampleListView.as_view()),
]
generator = SchemaGenerator(patterns=patterns)
@@ -1022,7 +1022,7 @@ class TestGenerator(TestCase):
def test_serializer_model(self):
"""Construction of the top level dictionary."""
patterns = [
- url(r'^example/?$', views.ExampleGenericAPIViewModel.as_view()),
+ path('example/', views.ExampleGenericAPIViewModel.as_view()),
]
generator = SchemaGenerator(patterns=patterns)
@@ -1038,7 +1038,7 @@ class TestGenerator(TestCase):
def test_authtoken_serializer(self):
patterns = [
- url(r'^api-token-auth/', obtain_auth_token)
+ path('api-token-auth/', obtain_auth_token)
]
generator = SchemaGenerator(patterns=patterns)
@@ -1065,7 +1065,7 @@ class TestGenerator(TestCase):
def test_component_name(self):
patterns = [
- url(r'^example/?$', views.ExampleAutoSchemaComponentName.as_view()),
+ path('example/', views.ExampleAutoSchemaComponentName.as_view()),
]
generator = SchemaGenerator(patterns=patterns)
@@ -1080,8 +1080,8 @@ class TestGenerator(TestCase):
def test_duplicate_component_name(self):
patterns = [
- url(r'^duplicate1/?$', views.ExampleAutoSchemaDuplicate1.as_view()),
- url(r'^duplicate2/?$', views.ExampleAutoSchemaDuplicate2.as_view()),
+ path('duplicate1/', views.ExampleAutoSchemaDuplicate1.as_view()),
+ path('duplicate2/', views.ExampleAutoSchemaDuplicate2.as_view()),
]
generator = SchemaGenerator(patterns=patterns)
@@ -1104,7 +1104,7 @@ class TestGenerator(TestCase):
schema = AutoSchema(operation_id_base='example')
url_patterns = [
- url(r'^example/?$', ExampleView.as_view()),
+ path('example/', ExampleView.as_view()),
]
generator = SchemaGenerator(patterns=url_patterns)
schema = generator.get_schema(request=create_request('/'))
diff --git a/tests/test_api_client.py b/tests/test_api_client.py
index 74a3579e2..976f10ed1 100644
--- a/tests/test_api_client.py
+++ b/tests/test_api_client.py
@@ -2,9 +2,9 @@ import os
import tempfile
import unittest
-from django.conf.urls import url
from django.http import HttpResponse
from django.test import override_settings
+from django.urls import path, re_path
from rest_framework.compat import coreapi, coreschema
from rest_framework.parsers import FileUploadParser
@@ -178,13 +178,13 @@ class HeadersView(APIView):
urlpatterns = [
- url(r'^$', SchemaView.as_view()),
- url(r'^example/$', ListView.as_view()),
- url(r'^example/(?P[0-9]+)/$', DetailView.as_view()),
- url(r'^upload/$', UploadView.as_view()),
- url(r'^download/$', DownloadView.as_view()),
- url(r'^text/$', TextView.as_view()),
- url(r'^headers/$', HeadersView.as_view()),
+ path('', SchemaView.as_view()),
+ path('example/', ListView.as_view()),
+ re_path(r'^example/(?P[0-9]+)/$', DetailView.as_view()),
+ path('upload/', UploadView.as_view()),
+ path('download/', DownloadView.as_view()),
+ path('text/', TextView.as_view()),
+ path('headers/', HeadersView.as_view()),
]
diff --git a/tests/test_atomic_requests.py b/tests/test_atomic_requests.py
index de04d2c06..15b41e02f 100644
--- a/tests/test_atomic_requests.py
+++ b/tests/test_atomic_requests.py
@@ -1,9 +1,9 @@
import unittest
-from django.conf.urls import url
from django.db import connection, connections, transaction
from django.http import Http404
from django.test import TestCase, TransactionTestCase, override_settings
+from django.urls import path
from rest_framework import status
from rest_framework.exceptions import APIException
@@ -44,7 +44,7 @@ class NonAtomicAPIExceptionView(APIView):
urlpatterns = (
- url(r'^$', NonAtomicAPIExceptionView.as_view()),
+ path('', NonAtomicAPIExceptionView.as_view()),
)
diff --git a/tests/test_fields.py b/tests/test_fields.py
index 506e75905..fdd570d8a 100644
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -697,7 +697,7 @@ class TestNullBooleanField(TestBooleanField):
None: None,
'other': True
}
- field = serializers.NullBooleanField()
+ field = serializers.BooleanField(allow_null=True)
class TestNullableBooleanField(TestNullBooleanField):
diff --git a/tests/test_htmlrenderer.py b/tests/test_htmlrenderer.py
index e31a9ced5..fa0f4efc6 100644
--- a/tests/test_htmlrenderer.py
+++ b/tests/test_htmlrenderer.py
@@ -1,10 +1,10 @@
import django.template.loader
import pytest
-from django.conf.urls import url
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.http import Http404
from django.template import TemplateDoesNotExist, engines
from django.test import TestCase, override_settings
+from django.urls import path
from rest_framework import status
from rest_framework.decorators import api_view, renderer_classes
@@ -35,9 +35,9 @@ def not_found(request):
urlpatterns = [
- url(r'^$', example),
- url(r'^permission_denied$', permission_denied),
- url(r'^not_found$', not_found),
+ path('', example),
+ path('permission_denied', permission_denied),
+ path('not_found', not_found),
]
diff --git a/tests/test_lazy_hyperlinks.py b/tests/test_lazy_hyperlinks.py
index cf3ee735f..716d02d2a 100644
--- a/tests/test_lazy_hyperlinks.py
+++ b/tests/test_lazy_hyperlinks.py
@@ -1,6 +1,6 @@
-from django.conf.urls import url
from django.db import models
from django.test import TestCase, override_settings
+from django.urls import path
from rest_framework import serializers
from rest_framework.renderers import JSONRenderer
@@ -29,7 +29,7 @@ def dummy_view(request):
urlpatterns = [
- url(r'^example/(?P[0-9]+)/$', dummy_view, name='example-detail'),
+ path('example//', dummy_view, name='example-detail'),
]
diff --git a/tests/test_metadata.py b/tests/test_metadata.py
index e1a1fd352..4abc0fc07 100644
--- a/tests/test_metadata.py
+++ b/tests/test_metadata.py
@@ -311,7 +311,8 @@ class TestMetadata:
class TestSimpleMetadataFieldInfo(TestCase):
def test_null_boolean_field_info_type(self):
options = metadata.SimpleMetadata()
- field_info = options.get_field_info(serializers.NullBooleanField())
+ field_info = options.get_field_info(serializers.BooleanField(
+ allow_null=True))
assert field_info['type'] == 'boolean'
def test_related_field_choices(self):
diff --git a/tests/test_middleware.py b/tests/test_middleware.py
index 28a5e558a..6b2c91db7 100644
--- a/tests/test_middleware.py
+++ b/tests/test_middleware.py
@@ -1,7 +1,7 @@
-from django.conf.urls import url
from django.contrib.auth.models import User
from django.http import HttpRequest
from django.test import override_settings
+from django.urls import path
from rest_framework.authentication import TokenAuthentication
from rest_framework.authtoken.models import Token
@@ -17,8 +17,8 @@ class PostView(APIView):
urlpatterns = [
- url(r'^auth$', APIView.as_view(authentication_classes=(TokenAuthentication,))),
- url(r'^post$', PostView.as_view()),
+ path('auth', APIView.as_view(authentication_classes=(TokenAuthentication,))),
+ path('post', PostView.as_view()),
]
diff --git a/tests/test_relations.py b/tests/test_relations.py
index 9f05e3b31..92aeecf6c 100644
--- a/tests/test_relations.py
+++ b/tests/test_relations.py
@@ -2,9 +2,9 @@ import uuid
import pytest
from _pytest.monkeypatch import MonkeyPatch
-from django.conf.urls import url
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.test import override_settings
+from django.urls import re_path
from django.utils.datastructures import MultiValueDict
from rest_framework import relations, serializers
@@ -146,7 +146,7 @@ class TestProxiedPrimaryKeyRelatedField(APISimpleTestCase):
urlpatterns = [
- url(r'^example/(?P.+)/$', lambda: None, name='example'),
+ re_path(r'^example/(?P.+)/$', lambda: None, name='example'),
]
diff --git a/tests/test_relations_hyperlink.py b/tests/test_relations_hyperlink.py
index 5ad0e31ff..77e4cd95f 100644
--- a/tests/test_relations_hyperlink.py
+++ b/tests/test_relations_hyperlink.py
@@ -1,5 +1,5 @@
-from django.conf.urls import url
from django.test import TestCase, override_settings
+from django.urls import path
from rest_framework import serializers
from rest_framework.test import APIRequestFactory
@@ -17,14 +17,14 @@ def dummy_view(request, pk):
urlpatterns = [
- url(r'^dummyurl/(?P[0-9]+)/$', dummy_view, name='dummy-url'),
- url(r'^manytomanysource/(?P[0-9]+)/$', dummy_view, name='manytomanysource-detail'),
- url(r'^manytomanytarget/(?P[0-9]+)/$', dummy_view, name='manytomanytarget-detail'),
- url(r'^foreignkeysource/(?P[0-9]+)/$', dummy_view, name='foreignkeysource-detail'),
- url(r'^foreignkeytarget/(?P[0-9]+)/$', dummy_view, name='foreignkeytarget-detail'),
- url(r'^nullableforeignkeysource/(?P[0-9]+)/$', dummy_view, name='nullableforeignkeysource-detail'),
- url(r'^onetoonetarget/(?P[0-9]+)/$', dummy_view, name='onetoonetarget-detail'),
- url(r'^nullableonetoonesource/(?P[0-9]+)/$', dummy_view, name='nullableonetoonesource-detail'),
+ path('dummyurl//', dummy_view, name='dummy-url'),
+ path('manytomanysource//', dummy_view, name='manytomanysource-detail'),
+ path('manytomanytarget//', dummy_view, name='manytomanytarget-detail'),
+ path('foreignkeysource//', dummy_view, name='foreignkeysource-detail'),
+ path('foreignkeytarget//', dummy_view, name='foreignkeytarget-detail'),
+ path('nullableforeignkeysource//', dummy_view, name='nullableforeignkeysource-detail'),
+ path('onetoonetarget//', dummy_view, name='onetoonetarget-detail'),
+ path('nullableonetoonesource//', dummy_view, name='nullableonetoonesource-detail'),
]
diff --git a/tests/test_renderers.py b/tests/test_renderers.py
index 71c5fb3f6..8271608e1 100644
--- a/tests/test_renderers.py
+++ b/tests/test_renderers.py
@@ -3,12 +3,12 @@ from collections import OrderedDict
from collections.abc import MutableMapping
import pytest
-from django.conf.urls import include, url
from django.core.cache import cache
from django.db import models
from django.http.request import HttpRequest
from django.template import loader
from django.test import TestCase, override_settings
+from django.urls import include, path, re_path
from django.utils.safestring import SafeText
from django.utils.translation import gettext_lazy as _
@@ -111,14 +111,14 @@ class HTMLView1(APIView):
urlpatterns = [
- url(r'^.*\.(?P.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB])),
- url(r'^$', MockView.as_view(renderer_classes=[RendererA, RendererB])),
- url(r'^cache$', MockGETView.as_view()),
- url(r'^parseerror$', MockPOSTView.as_view(renderer_classes=[JSONRenderer, BrowsableAPIRenderer])),
- url(r'^html$', HTMLView.as_view()),
- url(r'^html1$', HTMLView1.as_view()),
- url(r'^empty$', EmptyGETView.as_view()),
- url(r'^api', include('rest_framework.urls', namespace='rest_framework'))
+ re_path(r'^.*\.(?P.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB])),
+ path('', MockView.as_view(renderer_classes=[RendererA, RendererB])),
+ path('cache', MockGETView.as_view()),
+ path('parseerror', MockPOSTView.as_view(renderer_classes=[JSONRenderer, BrowsableAPIRenderer])),
+ path('html', HTMLView.as_view()),
+ path('html1', HTMLView1.as_view()),
+ path('empty', EmptyGETView.as_view()),
+ path('api', include('rest_framework.urls', namespace='rest_framework'))
]
@@ -637,7 +637,7 @@ class BrowsableAPIRendererTests(URLPatternsTestCase):
router = SimpleRouter()
router.register('examples', ExampleViewSet, basename='example')
router.register('auth-examples', AuthExampleViewSet, basename='auth-example')
- urlpatterns = [url(r'^api/', include(router.urls))]
+ urlpatterns = [path('api/', include(router.urls))]
def setUp(self):
self.renderer = BrowsableAPIRenderer()
diff --git a/tests/test_request.py b/tests/test_request.py
index be84fe5f9..4425c020f 100644
--- a/tests/test_request.py
+++ b/tests/test_request.py
@@ -5,7 +5,6 @@ import os.path
import tempfile
import pytest
-from django.conf.urls import url
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.middleware import AuthenticationMiddleware
from django.contrib.auth.models import User
@@ -13,6 +12,7 @@ from django.contrib.sessions.middleware import SessionMiddleware
from django.core.files.uploadedfile import SimpleUploadedFile
from django.http.request import RawPostDataException
from django.test import TestCase, override_settings
+from django.urls import path
from rest_framework import status
from rest_framework.authentication import SessionAuthentication
@@ -153,9 +153,9 @@ class FileUploadView(APIView):
urlpatterns = [
- url(r'^$', MockView.as_view()),
- url(r'^echo/$', EchoView.as_view()),
- url(r'^upload/$', FileUploadView.as_view())
+ path('', MockView.as_view()),
+ path('echo/', EchoView.as_view()),
+ path('upload/', FileUploadView.as_view())
]
diff --git a/tests/test_requests_client.py b/tests/test_requests_client.py
index 59b388c5a..c8e7be6ee 100644
--- a/tests/test_requests_client.py
+++ b/tests/test_requests_client.py
@@ -1,10 +1,10 @@
import unittest
-from django.conf.urls import url
from django.contrib.auth import authenticate, login
from django.contrib.auth.models import User
from django.shortcuts import redirect
from django.test import override_settings
+from django.urls import path
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_protect, ensure_csrf_cookie
@@ -90,10 +90,10 @@ class AuthView(APIView):
urlpatterns = [
- url(r'^$', Root.as_view(), name='root'),
- url(r'^headers/$', HeadersView.as_view(), name='headers'),
- url(r'^session/$', SessionView.as_view(), name='session'),
- url(r'^auth/$', AuthView.as_view(), name='auth'),
+ path('', Root.as_view(), name='root'),
+ path('headers/', HeadersView.as_view(), name='headers'),
+ path('session/', SessionView.as_view(), name='session'),
+ path('auth/', AuthView.as_view(), name='auth'),
]
diff --git a/tests/test_response.py b/tests/test_response.py
index d3a56d01b..0d5528dc9 100644
--- a/tests/test_response.py
+++ b/tests/test_response.py
@@ -1,5 +1,5 @@
-from django.conf.urls import include, url
from django.test import TestCase, override_settings
+from django.urls import include, path, re_path
from rest_framework import generics, routers, serializers, status, viewsets
from rest_framework.parsers import JSONParser
@@ -117,15 +117,15 @@ new_model_viewset_router.register(r'', HTMLNewModelViewSet)
urlpatterns = [
- url(r'^setbyview$', MockViewSettingContentType.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
- url(r'^.*\.(?P.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
- url(r'^$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
- url(r'^html$', HTMLView.as_view()),
- url(r'^json$', JSONView.as_view()),
- url(r'^html1$', HTMLView1.as_view()),
- url(r'^html_new_model$', HTMLNewModelView.as_view()),
- url(r'^html_new_model_viewset', include(new_model_viewset_router.urls)),
- url(r'^restframework', include('rest_framework.urls', namespace='rest_framework'))
+ path('setbyview', MockViewSettingContentType.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
+ re_path(r'^.*\.(?P.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
+ path('', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
+ path('html', HTMLView.as_view()),
+ path('json', JSONView.as_view()),
+ path('html1', HTMLView1.as_view()),
+ path('html_new_model', HTMLNewModelView.as_view()),
+ path('html_new_model_viewset', include(new_model_viewset_router.urls)),
+ path('restframework', include('rest_framework.urls', namespace='rest_framework'))
]
diff --git a/tests/test_reverse.py b/tests/test_reverse.py
index 9ab1667c5..b26b448c9 100644
--- a/tests/test_reverse.py
+++ b/tests/test_reverse.py
@@ -1,6 +1,5 @@
-from django.conf.urls import url
from django.test import TestCase, override_settings
-from django.urls import NoReverseMatch
+from django.urls import NoReverseMatch, path
from rest_framework.reverse import reverse
from rest_framework.test import APIRequestFactory
@@ -13,7 +12,7 @@ def null_view(request):
urlpatterns = [
- url(r'^view$', null_view, name='view'),
+ path('view', null_view, name='view'),
]
diff --git a/tests/test_routers.py b/tests/test_routers.py
index 007cb4768..f767a843d 100644
--- a/tests/test_routers.py
+++ b/tests/test_routers.py
@@ -1,11 +1,10 @@
from collections import namedtuple
import pytest
-from django.conf.urls import include, url
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.test import TestCase, override_settings
-from django.urls import resolve, reverse
+from django.urls import include, path, resolve, reverse
from rest_framework import permissions, serializers, viewsets
from rest_framework.decorators import action
@@ -118,7 +117,7 @@ class TestSimpleRouter(URLPatternsTestCase, TestCase):
router.register('basics', BasicViewSet, basename='basic')
urlpatterns = [
- url(r'^api/', include(router.urls)),
+ path('api/', include(router.urls)),
]
def setUp(self):
@@ -163,8 +162,8 @@ class TestSimpleRouter(URLPatternsTestCase, TestCase):
class TestRootView(URLPatternsTestCase, TestCase):
urlpatterns = [
- url(r'^non-namespaced/', include(namespaced_router.urls)),
- url(r'^namespaced/', include((namespaced_router.urls, 'namespaced'), namespace='namespaced')),
+ path('non-namespaced/', include(namespaced_router.urls)),
+ path('namespaced/', include((namespaced_router.urls, 'namespaced'), namespace='namespaced')),
]
def test_retrieve_namespaced_root(self):
@@ -181,8 +180,8 @@ class TestCustomLookupFields(URLPatternsTestCase, TestCase):
Ensure that custom lookup fields are correctly routed.
"""
urlpatterns = [
- url(r'^example/', include(notes_router.urls)),
- url(r'^example2/', include(kwarged_notes_router.urls)),
+ path('example/', include(notes_router.urls)),
+ path('example2/', include(kwarged_notes_router.urls)),
]
def setUp(self):
@@ -238,8 +237,8 @@ class TestLookupUrlKwargs(URLPatternsTestCase, TestCase):
Setup a deep lookup_field, but map it to a simple URL kwarg.
"""
urlpatterns = [
- url(r'^example/', include(notes_router.urls)),
- url(r'^example2/', include(kwarged_notes_router.urls)),
+ path('example/', include(notes_router.urls)),
+ path('example2/', include(kwarged_notes_router.urls)),
]
def setUp(self):
@@ -426,7 +425,7 @@ class TestDynamicListAndDetailRouter(TestCase):
class TestEmptyPrefix(URLPatternsTestCase, TestCase):
urlpatterns = [
- url(r'^empty-prefix/', include(empty_prefix_router.urls)),
+ path('empty-prefix/', include(empty_prefix_router.urls)),
]
def test_empty_prefix_list(self):
@@ -443,7 +442,7 @@ class TestEmptyPrefix(URLPatternsTestCase, TestCase):
class TestRegexUrlPath(URLPatternsTestCase, TestCase):
urlpatterns = [
- url(r'^regex/', include(regex_url_path_router.urls)),
+ path('regex/', include(regex_url_path_router.urls)),
]
def test_regex_url_path_list(self):
@@ -462,7 +461,7 @@ class TestRegexUrlPath(URLPatternsTestCase, TestCase):
class TestViewInitkwargs(URLPatternsTestCase, TestCase):
urlpatterns = [
- url(r'^example/', include(notes_router.urls)),
+ path('example/', include(notes_router.urls)),
]
def test_suffix(self):
diff --git a/tests/test_testing.py b/tests/test_testing.py
index 8094bfd8d..cc60e4f00 100644
--- a/tests/test_testing.py
+++ b/tests/test_testing.py
@@ -1,9 +1,9 @@
from io import BytesIO
-from django.conf.urls import url
from django.contrib.auth.models import User
from django.shortcuts import redirect
from django.test import TestCase, override_settings
+from django.urls import path
from rest_framework import fields, serializers
from rest_framework.decorators import api_view
@@ -47,10 +47,10 @@ def post_view(request):
urlpatterns = [
- url(r'^view/$', view),
- url(r'^session-view/$', session_view),
- url(r'^redirect-view/$', redirect_view),
- url(r'^post-view/$', post_view)
+ path('view/', view),
+ path('session-view/', session_view),
+ path('redirect-view/', redirect_view),
+ path('post-view/', post_view)
]
@@ -284,7 +284,7 @@ class TestAPIRequestFactory(TestCase):
class TestUrlPatternTestCase(URLPatternsTestCase):
urlpatterns = [
- url(r'^$', view),
+ path('', view),
]
@classmethod
diff --git a/tests/test_urlpatterns.py b/tests/test_urlpatterns.py
index 90820b532..adcd0a742 100644
--- a/tests/test_urlpatterns.py
+++ b/tests/test_urlpatterns.py
@@ -1,8 +1,7 @@
from collections import namedtuple
-from django.conf.urls import include, url
from django.test import TestCase
-from django.urls import Resolver404, URLResolver, path, re_path
+from django.urls import Resolver404, URLResolver, include, path, re_path
from django.urls.resolvers import RegexPattern
from rest_framework.test import APIRequestFactory
@@ -61,7 +60,7 @@ class FormatSuffixTests(TestCase):
def test_trailing_slash(self):
urlpatterns = [
- url(r'^test/$', dummy_view),
+ path('test/', dummy_view),
]
self._test_trailing_slash(urlpatterns)
@@ -81,7 +80,7 @@ class FormatSuffixTests(TestCase):
def test_format_suffix(self):
urlpatterns = [
- url(r'^test$', dummy_view),
+ path('test', dummy_view),
]
self._test_format_suffix(urlpatterns)
@@ -116,7 +115,7 @@ class FormatSuffixTests(TestCase):
def test_default_args(self):
urlpatterns = [
- url(r'^test$', dummy_view, {'foo': 'bar'}),
+ path('test', dummy_view, {'foo': 'bar'}),
]
self._test_default_args(urlpatterns)
@@ -135,15 +134,6 @@ class FormatSuffixTests(TestCase):
self._resolve_urlpatterns(urlpatterns, test_paths)
def test_included_urls(self):
- nested_patterns = [
- url(r'^path$', dummy_view)
- ]
- urlpatterns = [
- url(r'^test/', include(nested_patterns), {'foo': 'bar'}),
- ]
- self._test_included_urls(urlpatterns)
-
- def test_included_urls_django2(self):
nested_patterns = [
path('path', dummy_view)
]
@@ -152,44 +142,35 @@ class FormatSuffixTests(TestCase):
]
self._test_included_urls(urlpatterns)
- def test_included_urls_django2_mixed(self):
- nested_patterns = [
- path('path', dummy_view)
- ]
- urlpatterns = [
- url('^test/', include(nested_patterns), {'foo': 'bar'}),
- ]
- self._test_included_urls(urlpatterns)
-
- def test_included_urls_django2_mixed_args(self):
+ def test_included_urls_mixed(self):
nested_patterns = [
path('path/', dummy_view),
- url('^url/(?P[0-9]+)$', dummy_view)
+ re_path(r'^re_path/(?P[0-9]+)$', dummy_view)
]
urlpatterns = [
- url('^purl/(?P[0-9]+)/', include(nested_patterns), {'foo': 'bar'}),
+ re_path(r'^pre_path/(?P[0-9]+)/', include(nested_patterns), {'foo': 'bar'}),
path('ppath//', include(nested_patterns), {'foo': 'bar'}),
]
test_paths = [
- # parent url() nesting child path()
- URLTestPath('/purl/87/path/42', (), {'parent': '87', 'child': 42, 'foo': 'bar', }),
- URLTestPath('/purl/87/path/42.api', (), {'parent': '87', 'child': 42, 'foo': 'bar', 'format': 'api'}),
- URLTestPath('/purl/87/path/42.asdf', (), {'parent': '87', 'child': 42, 'foo': 'bar', 'format': 'asdf'}),
+ # parent re_path() nesting child path()
+ URLTestPath('/pre_path/87/path/42', (), {'parent': '87', 'child': 42, 'foo': 'bar', }),
+ URLTestPath('/pre_path/87/path/42.api', (), {'parent': '87', 'child': 42, 'foo': 'bar', 'format': 'api'}),
+ URLTestPath('/pre_path/87/path/42.asdf', (), {'parent': '87', 'child': 42, 'foo': 'bar', 'format': 'asdf'}),
- # parent path() nesting child url()
- URLTestPath('/ppath/87/url/42', (), {'parent': 87, 'child': '42', 'foo': 'bar', }),
- URLTestPath('/ppath/87/url/42.api', (), {'parent': 87, 'child': '42', 'foo': 'bar', 'format': 'api'}),
- URLTestPath('/ppath/87/url/42.asdf', (), {'parent': 87, 'child': '42', 'foo': 'bar', 'format': 'asdf'}),
+ # parent path() nesting child re_path()
+ URLTestPath('/ppath/87/re_path/42', (), {'parent': 87, 'child': '42', 'foo': 'bar', }),
+ URLTestPath('/ppath/87/re_path/42.api', (), {'parent': 87, 'child': '42', 'foo': 'bar', 'format': 'api'}),
+ URLTestPath('/ppath/87/re_path/42.asdf', (), {'parent': 87, 'child': '42', 'foo': 'bar', 'format': 'asdf'}),
# parent path() nesting child path()
URLTestPath('/ppath/87/path/42', (), {'parent': 87, 'child': 42, 'foo': 'bar', }),
URLTestPath('/ppath/87/path/42.api', (), {'parent': 87, 'child': 42, 'foo': 'bar', 'format': 'api'}),
URLTestPath('/ppath/87/path/42.asdf', (), {'parent': 87, 'child': 42, 'foo': 'bar', 'format': 'asdf'}),
- # parent url() nesting child url()
- URLTestPath('/purl/87/url/42', (), {'parent': '87', 'child': '42', 'foo': 'bar', }),
- URLTestPath('/purl/87/url/42.api', (), {'parent': '87', 'child': '42', 'foo': 'bar', 'format': 'api'}),
- URLTestPath('/purl/87/url/42.asdf', (), {'parent': '87', 'child': '42', 'foo': 'bar', 'format': 'asdf'}),
+ # parent re_path() nesting child re_path()
+ URLTestPath('/pre_path/87/re_path/42', (), {'parent': '87', 'child': '42', 'foo': 'bar', }),
+ URLTestPath('/pre_path/87/re_path/42.api', (), {'parent': '87', 'child': '42', 'foo': 'bar', 'format': 'api'}),
+ URLTestPath('/pre_path/87/re_path/42.asdf', (), {'parent': '87', 'child': '42', 'foo': 'bar', 'format': 'asdf'}),
]
self._resolve_urlpatterns(urlpatterns, test_paths)
@@ -202,13 +183,13 @@ class FormatSuffixTests(TestCase):
]
self._resolve_urlpatterns(urlpatterns, test_paths, allowed=allowed_formats)
- def test_allowed_formats(self):
+ def test_allowed_formats_re_path(self):
urlpatterns = [
- url('^test$', dummy_view),
+ re_path(r'^test$', dummy_view),
]
self._test_allowed_formats(urlpatterns)
- def test_allowed_formats_django2(self):
+ def test_allowed_formats_path(self):
urlpatterns = [
path('test', dummy_view),
]
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 500c6a3fa..c72f680fe 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,7 +1,7 @@
from unittest import mock
-from django.conf.urls import url
from django.test import TestCase, override_settings
+from django.urls import path
from rest_framework.decorators import action
from rest_framework.routers import SimpleRouter
@@ -64,12 +64,12 @@ class ResourceViewSet(ModelViewSet):
router = SimpleRouter()
router.register(r'resources', ResourceViewSet)
urlpatterns = [
- url(r'^$', Root.as_view()),
- url(r'^resource/$', ResourceRoot.as_view()),
- url(r'^resource/customname$', CustomNameResourceInstance.as_view()),
- url(r'^resource/(?P[0-9]+)$', ResourceInstance.as_view()),
- url(r'^resource/(?P[0-9]+)/$', NestedResourceRoot.as_view()),
- url(r'^resource/(?P[0-9]+)/(?P[A-Za-z]+)$', NestedResourceInstance.as_view()),
+ path('', Root.as_view()),
+ path('resource/', ResourceRoot.as_view()),
+ path('resource/customname', CustomNameResourceInstance.as_view()),
+ path('resource/', ResourceInstance.as_view()),
+ path('resource//', NestedResourceRoot.as_view()),
+ path('resource//', NestedResourceInstance.as_view()),
]
urlpatterns += router.urls
diff --git a/tests/test_versioning.py b/tests/test_versioning.py
index d4e269df3..d40d54229 100644
--- a/tests/test_versioning.py
+++ b/tests/test_versioning.py
@@ -1,6 +1,6 @@
import pytest
-from django.conf.urls import include, url
from django.test import override_settings
+from django.urls import include, path, re_path
from rest_framework import serializers, status, versioning
from rest_framework.decorators import APIView
@@ -144,14 +144,14 @@ class TestRequestVersion:
class TestURLReversing(URLPatternsTestCase, APITestCase):
included = [
- url(r'^namespaced/$', dummy_view, name='another'),
- url(r'^example/(?P\d+)/$', dummy_pk_view, name='example-detail')
+ path('namespaced/', dummy_view, name='another'),
+ path('example//', dummy_pk_view, name='example-detail')
]
urlpatterns = [
- url(r'^v1/', include((included, 'v1'), namespace='v1')),
- url(r'^another/$', dummy_view, name='another'),
- url(r'^(?P[v1|v2]+)/another/$', dummy_view, name='another'),
+ path('v1/', include((included, 'v1'), namespace='v1')),
+ path('another/', dummy_view, name='another'),
+ re_path(r'^(?P[v1|v2]+)/another/$', dummy_view, name='another'),
]
def test_reverse_unversioned(self):
@@ -310,12 +310,12 @@ class TestAllowedAndDefaultVersion:
class TestHyperlinkedRelatedField(URLPatternsTestCase, APITestCase):
included = [
- url(r'^namespaced/(?P\d+)/$', dummy_pk_view, name='namespaced'),
+ path('namespaced//', dummy_pk_view, name='namespaced'),
]
urlpatterns = [
- url(r'^v1/', include((included, 'v1'), namespace='v1')),
- url(r'^v2/', include((included, 'v2'), namespace='v2'))
+ path('v1/', include((included, 'v1'), namespace='v1')),
+ path('v2/', include((included, 'v2'), namespace='v2'))
]
def setUp(self):
@@ -342,17 +342,17 @@ class TestHyperlinkedRelatedField(URLPatternsTestCase, APITestCase):
class TestNamespaceVersioningHyperlinkedRelatedFieldScheme(URLPatternsTestCase, APITestCase):
nested = [
- url(r'^namespaced/(?P\d+)/$', dummy_pk_view, name='nested'),
+ path('namespaced//', dummy_pk_view, name='nested'),
]
included = [
- url(r'^namespaced/(?P\d+)/$', dummy_pk_view, name='namespaced'),
- url(r'^nested/', include((nested, 'nested-namespace'), namespace='nested-namespace'))
+ path('namespaced//', dummy_pk_view, name='namespaced'),
+ path('nested/', include((nested, 'nested-namespace'), namespace='nested-namespace'))
]
urlpatterns = [
- url(r'^v1/', include((included, 'restframeworkv1'), namespace='v1')),
- url(r'^v2/', include((included, 'restframeworkv2'), namespace='v2')),
- url(r'^non-api/(?P\d+)/$', dummy_pk_view, name='non-api-view')
+ path('v1/', include((included, 'restframeworkv1'), namespace='v1')),
+ path('v2/', include((included, 'restframeworkv2'), namespace='v2')),
+ path('non-api//', dummy_pk_view, name='non-api-view')
]
def _create_field(self, view_name, version):
diff --git a/tests/test_viewsets.py b/tests/test_viewsets.py
index 2a2997a0b..8842b0b1c 100644
--- a/tests/test_viewsets.py
+++ b/tests/test_viewsets.py
@@ -2,9 +2,9 @@ from collections import OrderedDict
from functools import wraps
import pytest
-from django.conf.urls import include, url
from django.db import models
from django.test import TestCase, override_settings
+from django.urls import include, path
from rest_framework import status
from rest_framework.decorators import action
@@ -124,7 +124,7 @@ router.register(r'mapping', ActionViewSetWithMapping, basename='mapping')
urlpatterns = [
- url(r'^api/', include(router.urls)),
+ path('api/', include(router.urls)),
]
diff --git a/tests/urls.py b/tests/urls.py
index 76ada5e3d..d9147683f 100644
--- a/tests/urls.py
+++ b/tests/urls.py
@@ -3,14 +3,14 @@ URLConf for test suite.
We need only the docs urls for DocumentationRenderer tests.
"""
-from django.conf.urls import url
+from django.urls import path
from rest_framework.compat import coreapi
from rest_framework.documentation import include_docs_urls
if coreapi:
urlpatterns = [
- url(r'^docs/', include_docs_urls(title='Test Suite API')),
+ path('docs/', include_docs_urls(title='Test Suite API')),
]
else:
urlpatterns = []
From ddcd8b539be4a5ffb7e6c89830bec9a24914ba55 Mon Sep 17 00:00:00 2001
From: Michael K
Date: Tue, 8 Sep 2020 14:40:21 +0000
Subject: [PATCH 015/303] Run tests against Python 3.9 (#7517)
3.9.0 final is expected on Monday, 2020-10-05
See https://www.python.org/dev/peps/pep-0596/#schedule
Also mention Django 3.1 compat where missing.
---
.travis.yml | 3 +++
README.md | 4 ++--
docs/index.md | 4 ++--
setup.py | 1 +
tox.ini | 6 +++---
5 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 39efaf4fc..c204c5460 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,6 +21,9 @@ matrix:
- { python: "3.8", env: DJANGO=3.1 }
- { python: "3.8", env: DJANGO=master }
+ - { python: "3.9-dev", env: DJANGO=3.1 }
+ - { python: "3.9-dev", env: DJANGO=master }
+
- { python: "3.8", env: TOXENV=base }
- { python: "3.8", env: TOXENV=lint }
- { python: "3.8", env: TOXENV=docs }
diff --git a/README.md b/README.md
index 4dfff3119..8af1466f8 100644
--- a/README.md
+++ b/README.md
@@ -52,8 +52,8 @@ There is a live example API for testing purposes, [available here][sandbox].
# Requirements
-* Python (3.5, 3.6, 3.7, 3.8)
-* Django (2.2, 3.0)
+* Python (3.5, 3.6, 3.7, 3.8, 3.9)
+* Django (2.2, 3.0, 3.1)
We **highly recommend** and only officially support the latest patch release of
each Python and Django series.
diff --git a/docs/index.md b/docs/index.md
index 54654c7c5..0273da9f1 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -83,8 +83,8 @@ continued development by **[signing up for a paid plan][funding]**.
REST framework requires the following:
-* Python (3.5, 3.6, 3.7, 3.8)
-* Django (2.2, 3.0)
+* Python (3.5, 3.6, 3.7, 3.8, 3.9)
+* Django (2.2, 3.0, 3.1)
We **highly recommend** and only officially support the latest patch release of
each Python and Django series.
diff --git a/setup.py b/setup.py
index 38e680e10..e2a1c0222 100755
--- a/setup.py
+++ b/setup.py
@@ -101,6 +101,7 @@ setup(
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
+ 'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3 :: Only',
'Topic :: Internet :: WWW/HTTP',
],
diff --git a/tox.ini b/tox.ini
index 190865f23..d5e769764 100644
--- a/tox.ini
+++ b/tox.ini
@@ -2,8 +2,8 @@
envlist =
{py35,py36,py37}-django22,
{py36,py37,py38}-django30,
- {py36,py37,py38}-django31,
- {py36,py37,py38}-djangomaster,
+ {py36,py37,py38,py39}-django31,
+ {py36,py37,py38,py39}-djangomaster,
base,dist,lint,docs,
[travis:env]
@@ -22,7 +22,7 @@ setenv =
deps =
django22: Django>=2.2,<3.0
django30: Django>=3.0,<3.1
- django31: Django>=3.1a1,<3.2
+ django31: Django>=3.1,<3.2
djangomaster: https://github.com/django/django/archive/master.tar.gz
-rrequirements/requirements-testing.txt
-rrequirements/requirements-optionals.txt
From 6b632c15b1bccf53287f81df143428b59d710241 Mon Sep 17 00:00:00 2001
From: Francisco Couzo
Date: Tue, 8 Sep 2020 11:42:01 -0300
Subject: [PATCH 016/303] Remove unnecessary type conversions (#7526)
---
rest_framework/pagination.py | 2 +-
rest_framework/renderers.py | 2 +-
rest_framework/serializers.py | 12 ++++--------
rest_framework/utils/urls.py | 4 ++--
4 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py
index a73f2e526..60a57c8e4 100644
--- a/rest_framework/pagination.py
+++ b/rest_framework/pagination.py
@@ -80,7 +80,7 @@ def _get_displayed_page_numbers(current, final):
# Now sort the page numbers and drop anything outside the limits.
included = [
- idx for idx in sorted(list(included))
+ idx for idx in sorted(included)
if 0 < idx <= final
]
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index c790879b9..0fb74ca00 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -329,7 +329,7 @@ class HTMLFormRenderer(BaseRenderer):
if isinstance(field._field, serializers.HiddenField):
return ''
- style = dict(self.default_style[field])
+ style = self.default_style[field].copy()
style.update(field.style)
if 'template_pack' not in style:
style['template_pack'] = parent_style.get('template_pack', self.template_pack)
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 439220b34..3def1946c 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -194,10 +194,7 @@ class BaseSerializer(Field):
"inspect 'serializer.validated_data' instead. "
)
- validated_data = dict(
- list(self.validated_data.items()) +
- list(kwargs.items())
- )
+ validated_data = {**self.validated_data, **kwargs}
if self.instance is not None:
self.instance = self.update(self.instance, validated_data)
@@ -699,8 +696,7 @@ class ListSerializer(BaseSerializer):
)
validated_data = [
- dict(list(attrs.items()) + list(kwargs.items()))
- for attrs in self.validated_data
+ {**attrs, **kwargs} for attrs in self.validated_data
]
if self.instance is not None:
@@ -1410,7 +1406,7 @@ class ModelSerializer(Serializer):
# so long as all the field names are included on the serializer.
for parent_class in [model] + list(model._meta.parents):
for unique_together_list in parent_class._meta.unique_together:
- if set(field_names).issuperset(set(unique_together_list)):
+ if set(field_names).issuperset(unique_together_list):
unique_constraint_names |= set(unique_together_list)
# Now we have all the field names that have uniqueness constraints
@@ -1541,7 +1537,7 @@ class ModelSerializer(Serializer):
for parent_class in model_class_inheritance_tree:
for unique_together in parent_class._meta.unique_together:
# Skip if serializer does not map to all unique together sources
- if not set(source_map).issuperset(set(unique_together)):
+ if not set(source_map).issuperset(unique_together):
continue
for source in unique_together:
diff --git a/rest_framework/utils/urls.py b/rest_framework/utils/urls.py
index 3534e5f49..afb06994c 100644
--- a/rest_framework/utils/urls.py
+++ b/rest_framework/utils/urls.py
@@ -11,7 +11,7 @@ def replace_query_param(url, key, val):
(scheme, netloc, path, query, fragment) = parse.urlsplit(force_str(url))
query_dict = parse.parse_qs(query, keep_blank_values=True)
query_dict[force_str(key)] = [force_str(val)]
- query = parse.urlencode(sorted(list(query_dict.items())), doseq=True)
+ query = parse.urlencode(sorted(query_dict.items()), doseq=True)
return parse.urlunsplit((scheme, netloc, path, query, fragment))
@@ -23,5 +23,5 @@ def remove_query_param(url, key):
(scheme, netloc, path, query, fragment) = parse.urlsplit(force_str(url))
query_dict = parse.parse_qs(query, keep_blank_values=True)
query_dict.pop(key, None)
- query = parse.urlencode(sorted(list(query_dict.items())), doseq=True)
+ query = parse.urlencode(sorted(query_dict.items()), doseq=True)
return parse.urlunsplit((scheme, netloc, path, query, fragment))
From 327cbef29977bb999f292d3c8b7b3efc2491691d Mon Sep 17 00:00:00 2001
From: Rafael Henter
Date: Tue, 8 Sep 2020 11:44:03 -0300
Subject: [PATCH 017/303] Add Django API Client to the third party packages
(#7440)
---
docs/community/third-party-packages.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/community/third-party-packages.md b/docs/community/third-party-packages.md
index 79cb56e01..336380aee 100644
--- a/docs/community/third-party-packages.md
+++ b/docs/community/third-party-packages.md
@@ -274,6 +274,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
* [drf-viewset-profiler][drf-viewset-profiler] - Lib to profile all methods from a viewset line by line.
* [djangorestframework-features][djangorestframework-features] - Advanced schema generation and more based on named features.
* [django-elasticsearch-dsl-drf][django-elasticsearch-dsl-drf] - Integrate Elasticsearch DSL with Django REST framework. Package provides views, serializers, filter backends, pagination and other handy add-ons.
+* [django-api-client][django-api-client] - DRF client that groups the Endpoint response, for use in CBVs and FBV as if you were working with Django's Native Models..
[cite]: http://www.software-ecosystems.com/Software_Ecosystems/Ecosystems.html
[cookiecutter]: https://github.com/jpadilla/cookiecutter-django-rest-framework
@@ -358,3 +359,4 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
[drf-viewset-profiler]: https://github.com/fvlima/drf-viewset-profiler
[djangorestframework-features]: https://github.com/cloudcode-hungary/django-rest-framework-features/
[django-elasticsearch-dsl-drf]: https://github.com/barseghyanartur/django-elasticsearch-dsl-drf
+[django-api-client]: https://github.com/rhenter/django-api-client
From 04f39c42ee28bd462fcc9088909b18f2b321501d Mon Sep 17 00:00:00 2001
From: Mohammad Amin Haghpanah
Date: Wed, 9 Sep 2020 12:24:05 +0430
Subject: [PATCH 018/303] Add drf-psq package to docs (#7451)
* Add drf-psq to third party packages
* Add drf-psq to permissions.md
this package is an extension that gives support for having action-based **permission_classes**, **serializer_class**, and **queryset** dependent on permission-based rules.
Co-authored-by: Salar Nasiri
---
docs/api-guide/permissions.md | 6 ++++++
docs/community/third-party-packages.md | 2 ++
2 files changed, 8 insertions(+)
diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md
index f317c12e6..ade146257 100644
--- a/docs/api-guide/permissions.md
+++ b/docs/api-guide/permissions.md
@@ -312,6 +312,11 @@ The [Django REST Framework API Key][djangorestframework-api-key] package provide
The [Django Rest Framework Role Filters][django-rest-framework-role-filters] package provides simple filtering over multiple types of roles.
+## Django Rest Framework PSQ
+
+The [Django Rest Framework PSQ][drf-psq] package is an extension that gives support for having action-based **permission_classes**, **serializer_class**, and **queryset** dependent on permission-based rules.
+
+
[cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html
[authentication]: authentication.md
[throttling]: throttling.md
@@ -328,3 +333,4 @@ The [Django Rest Framework Role Filters][django-rest-framework-role-filters] pac
[django-rest-framework-role-filters]: https://github.com/allisson/django-rest-framework-role-filters
[django-rest-framework-guardian]: https://github.com/rpkilby/django-rest-framework-guardian
[drf-access-policy]: https://github.com/rsinger86/drf-access-policy
+[drf-psq]: https://github.com/drf-psq/drf-psq
diff --git a/docs/community/third-party-packages.md b/docs/community/third-party-packages.md
index 336380aee..d4359890d 100644
--- a/docs/community/third-party-packages.md
+++ b/docs/community/third-party-packages.md
@@ -198,6 +198,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
* [rest_condition][rest-condition] - Another extension for building complex permissions in a simple and convenient way.
* [dry-rest-permissions][dry-rest-permissions] - Provides a simple way to define permissions for individual api actions.
* [drf-access-policy][drf-access-policy] - Declarative and flexible permissions inspired by AWS' IAM policies.
+* [drf-psq][drf-psq] - An extension that gives support for having action-based **permission_classes**, **serializer_class**, and **queryset** dependent on permission-based rules.
### Serializers
@@ -360,3 +361,4 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
[djangorestframework-features]: https://github.com/cloudcode-hungary/django-rest-framework-features/
[django-elasticsearch-dsl-drf]: https://github.com/barseghyanartur/django-elasticsearch-dsl-drf
[django-api-client]: https://github.com/rhenter/django-api-client
+[drf-psq]: https://github.com/drf-psq/drf-psq
From e17779c47bccd218cfcdf1f3eaa61c354032ff71 Mon Sep 17 00:00:00 2001
From: johnthagen
Date: Mon, 14 Sep 2020 13:46:51 -0400
Subject: [PATCH 019/303] Make it more clear to add django_filters to
INSTALLED_APPS in docs. (#7535)
---
docs/api-guide/filtering.md | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md
index 41c1341dd..d305ede6b 100644
--- a/docs/api-guide/filtering.md
+++ b/docs/api-guide/filtering.md
@@ -145,10 +145,18 @@ Note that you can use both an overridden `.get_queryset()` and generic filtering
The [`django-filter`][django-filter-docs] library includes a `DjangoFilterBackend` class which
supports highly customizable field filtering for REST framework.
-To use `DjangoFilterBackend`, first install `django-filter`. Then add `django_filters` to Django's `INSTALLED_APPS`
+To use `DjangoFilterBackend`, first install `django-filter`.
pip install django-filter
+Then add `'django_filters'` to Django's `INSTALLED_APPS`:
+
+ INSTALLED_APPS = [
+ ...
+ 'django_filters',
+ ...
+ ]
+
You should now either add the filter backend to your settings:
REST_FRAMEWORK = {
@@ -365,4 +373,4 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter]
[django-url-filter]: https://github.com/miki725/django-url-filter
[drf-url-filter]: https://github.com/manjitkumar/drf-url-filters
[HStoreField]: https://docs.djangoproject.com/en/3.0/ref/contrib/postgres/fields/#hstorefield
-[JSONField]: https://docs.djangoproject.com/en/3.0/ref/contrib/postgres/fields/#jsonfield
\ No newline at end of file
+[JSONField]: https://docs.djangoproject.com/en/3.0/ref/contrib/postgres/fields/#jsonfield
From 98761e4ebb6c963aade674a568d010660302358e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=91=D0=BE=D1=80=D0=B8=D1=81=20=D0=92=D0=B5=D1=80=D1=85?=
=?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?=
Date: Wed, 16 Sep 2020 09:43:27 -0400
Subject: [PATCH 020/303] Correct indentation
---
docs/api-guide/views.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md
index 45226d57b..2224c1f3a 100644
--- a/docs/api-guide/views.md
+++ b/docs/api-guide/views.md
@@ -169,7 +169,7 @@ To override the default settings, REST framework provides a set of additional de
from rest_framework.throttling import UserRateThrottle
class OncePerDayUserThrottle(UserRateThrottle):
- rate = '1/day'
+ rate = '1/day'
@api_view(['GET'])
@throttle_classes([OncePerDayUserThrottle])
From c6e24521dab27a7af8e8637a32b868ffa03dec2f Mon Sep 17 00:00:00 2001
From: "T. Franzel"
Date: Mon, 21 Sep 2020 12:39:35 +0200
Subject: [PATCH 021/303] Add third-party schema library drf-spectacular to
docs (#7540)
Co-authored-by: T. Franzel <13507857+tfranzel@users.noreply.github.com>
---
docs/coreapi/schemas.md | 7 +++++++
docs/topics/documenting-your-api.md | 11 +++++++++++
2 files changed, 18 insertions(+)
diff --git a/docs/coreapi/schemas.md b/docs/coreapi/schemas.md
index e7a418b80..653105a7a 100644
--- a/docs/coreapi/schemas.md
+++ b/docs/coreapi/schemas.md
@@ -828,10 +828,17 @@ A short description of the meaning and intended usage of the input field.
[drf-yasg][drf-yasg] generates [OpenAPI][open-api] documents suitable for code generation - nested schemas,
named models, response bodies, enum/pattern/min/max validators, form parameters, etc.
+
+## drf-spectacular - Sane and flexible OpenAPI 3.0 schema generation for Django REST framework
+
+[drf-spectacular][drf-spectacular] is a [OpenAPI 3][open-api] schema generation tool with explicit focus on extensibility,
+customizability and client generation. It's usage patterns are very similar to [drf-yasg][drf-yasg].
+
[cite]: https://blog.heroku.com/archives/2014/1/8/json_schema_for_heroku_platform_api
[coreapi]: https://www.coreapi.org/
[corejson]: https://www.coreapi.org/specification/encoding/#core-json-encoding
[drf-yasg]: https://github.com/axnsan12/drf-yasg/
+[drf-spectacular]: https://github.com/tfranzel/drf-spectacular/
[open-api]: https://openapis.org/
[json-hyperschema]: https://json-schema.org/latest/json-schema-hypermedia.html
[api-blueprint]: https://apiblueprint.org/
diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md
index 5c5872650..7f39a9c33 100644
--- a/docs/topics/documenting-your-api.md
+++ b/docs/topics/documenting-your-api.md
@@ -142,6 +142,16 @@ This also translates into a very useful interactive documentation viewer in the
![Screenshot - drf-yasg][image-drf-yasg]
+#### drf-spectacular - Sane and flexible OpenAPI 3.0 schema generation for Django REST framework
+
+[drf-spectacular][drf-spectacular] is a [OpenAPI 3][open-api] schema generation tool with explicit focus on extensibility,
+customizability and client generation. Usage patterns are very similar to [drf-yasg][drf-yasg].
+
+It aims to extract as much schema information as possible, while providing decorators and extensions for easy
+customization. There is explicit support for [swagger-codegen][swagger], [SwaggerUI][swagger-ui] and [Redoc][redoc],
+i18n, versioning, authentication, polymorphism (dynamic requests and responses), query/path/header parameters,
+documentation and more. Several popular plugins for DRF are supported out-of-the-box as well.
+
---
## Self describing APIs
@@ -216,6 +226,7 @@ To implement a hypermedia API you'll need to decide on an appropriate media type
[image-self-describing-api]: ../img/self-describing.png
[drf-yasg]: https://github.com/axnsan12/drf-yasg/
+[drf-spectacular]: https://github.com/tfranzel/drf-spectacular/
[markdown]: https://daringfireball.net/projects/markdown/syntax
[open-api]: https://openapis.org/
[redoc]: https://github.com/Rebilly/ReDoc
From ae649336b110afe21b9429f2554052f31a9dfaf9 Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Wed, 23 Sep 2020 15:39:06 +0100
Subject: [PATCH 022/303] Drop urlize_quoted_links (#7548)
---
rest_framework/renderers.py | 2 +-
.../templates/rest_framework/base.html | 6 +-
rest_framework/templatetags/rest_framework.py | 83 +------------------
tests/test_templatetags.py | 26 +++---
4 files changed, 21 insertions(+), 96 deletions(-)
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index 0fb74ca00..3c4be8aeb 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -418,7 +418,7 @@ class BrowsableAPIRenderer(BaseRenderer):
if render_style == 'binary':
return '[%d bytes of binary content]' % len(content)
- return content
+ return content.decode('utf-8') if isinstance(content, bytes) else content
def show_form_for_method(self, view, method, request, obj):
"""
diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html
index 9207f049b..a88e1591c 100644
--- a/rest_framework/templates/rest_framework/base.html
+++ b/rest_framework/templates/rest_framework/base.html
@@ -77,7 +77,7 @@
{% block request_forms %}
-
+
{% if 'GET' in allowed_methods %}
diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py
index 79dd953ff..7bfa8f599 100644
--- a/rest_framework/templatetags/rest_framework.py
+++ b/rest_framework/templatetags/rest_framework.py
@@ -4,9 +4,9 @@ from collections import OrderedDict
from django import template
from django.template import loader
from django.urls import NoReverseMatch, reverse
-from django.utils.encoding import force_str, iri_to_uri
+from django.utils.encoding import iri_to_uri
from django.utils.html import escape, format_html, smart_urlquote
-from django.utils.safestring import SafeData, mark_safe
+from django.utils.safestring import mark_safe
from rest_framework.compat import apply_markdown, pygments_highlight
from rest_framework.renderers import HTMLFormRenderer
@@ -311,85 +311,6 @@ def smart_urlquote_wrapper(matched_url):
return None
-@register.filter(needs_autoescape=True)
-def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=True):
- """
- Converts any URLs in text into clickable links.
-
- Works on http://, https://, www. links, and also on links ending in one of
- the original seven gTLDs (.com, .edu, .gov, .int, .mil, .net, and .org).
- Links can have trailing punctuation (periods, commas, close-parens) and
- leading punctuation (opening parens) and it'll still do the right thing.
-
- If trim_url_limit is not None, the URLs in link text longer than this limit
- will truncated to trim_url_limit-3 characters and appended with an ellipsis.
-
- If nofollow is True, the URLs in link text will get a rel="nofollow"
- attribute.
-
- If autoescape is True, the link text and URLs will get autoescaped.
- """
- def trim_url(x, limit=trim_url_limit):
- return limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x
-
- safe_input = isinstance(text, SafeData)
-
- # Unfortunately, Django built-in cannot be used here, because escaping
- # is to be performed on words, which have been forcibly coerced to text
- def conditional_escape(text):
- return escape(text) if autoescape and not safe_input else text
-
- words = word_split_re.split(force_str(text))
- for i, word in enumerate(words):
- if '.' in word or '@' in word or ':' in word:
- # Deal with punctuation.
- lead, middle, trail = '', word, ''
- for punctuation in TRAILING_PUNCTUATION:
- if middle.endswith(punctuation):
- middle = middle[:-len(punctuation)]
- trail = punctuation + trail
- for opening, closing in WRAPPING_PUNCTUATION:
- if middle.startswith(opening):
- middle = middle[len(opening):]
- lead = lead + opening
- # Keep parentheses at the end only if they're balanced.
- if (
- middle.endswith(closing) and
- middle.count(closing) == middle.count(opening) + 1
- ):
- middle = middle[:-len(closing)]
- trail = closing + trail
-
- # Make URL we want to point to.
- url = None
- nofollow_attr = ' rel="nofollow"' if nofollow else ''
- if simple_url_re.match(middle):
- url = smart_urlquote_wrapper(middle)
- elif simple_url_2_re.match(middle):
- url = smart_urlquote_wrapper('http://%s' % middle)
- elif ':' not in middle and simple_email_re.match(middle):
- local, domain = middle.rsplit('@', 1)
- try:
- domain = domain.encode('idna').decode('ascii')
- except UnicodeError:
- continue
- url = 'mailto:%s@%s' % (local, domain)
- nofollow_attr = ''
-
- # Make link.
- if url:
- trimmed = trim_url(middle)
- lead, trail = conditional_escape(lead), conditional_escape(trail)
- url, trimmed = conditional_escape(url), conditional_escape(trimmed)
- middle = '%s' % (url, nofollow_attr, trimmed)
- words[i] = '%s%s%s' % (lead, middle, trail)
- else:
- words[i] = conditional_escape(word)
- else:
- words[i] = conditional_escape(word)
- return mark_safe(''.join(words))
-
-
@register.filter
def break_long_headers(header):
"""
diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py
index 28d6b4011..cdf10ab1e 100644
--- a/tests/test_templatetags.py
+++ b/tests/test_templatetags.py
@@ -2,13 +2,14 @@ import unittest
from django.template import Context, Template
from django.test import TestCase
+from django.utils.html import urlize
from rest_framework.compat import coreapi, coreschema
from rest_framework.relations import Hyperlink
from rest_framework.templatetags import rest_framework
from rest_framework.templatetags.rest_framework import (
add_nested_class, add_query_param, as_string, break_long_headers,
- format_value, get_pagination_html, schema_links, urlize_quoted_links
+ format_value, get_pagination_html, schema_links
)
from rest_framework.test import APIRequestFactory
@@ -246,7 +247,7 @@ class Issue1386Tests(TestCase):
def test_issue_1386(self):
"""
- Test function urlize_quoted_links with different args
+ Test function urlize with different args
"""
correct_urls = [
"asdf.com",
@@ -255,7 +256,7 @@ class Issue1386Tests(TestCase):
"as.d8f.ghj8.gov",
]
for i in correct_urls:
- res = urlize_quoted_links(i)
+ res = urlize(i)
self.assertNotEqual(res, i)
self.assertIn(i, res)
@@ -264,11 +265,11 @@ class Issue1386Tests(TestCase):
"asdf.netnet",
]
for i in incorrect_urls:
- res = urlize_quoted_links(i)
+ res = urlize(i)
self.assertEqual(i, res)
# example from issue #1386, this shouldn't raise an exception
- urlize_quoted_links("asdf:[/p]zxcv.com")
+ urlize("asdf:[/p]zxcv.com")
def test_smart_urlquote_wrapper_handles_value_error(self):
def mock_smart_urlquote(url):
@@ -289,7 +290,10 @@ class URLizerTests(TestCase):
For all items in dict test assert that the value is urlized key
"""
for original, urlized in data.items():
- assert urlize_quoted_links(original, nofollow=False) == urlized
+ print('====')
+ print(repr(urlize(original, nofollow=False)))
+ print(repr(urlized))
+ assert urlize(original, nofollow=False) == urlized
def test_json_with_url(self):
"""
@@ -297,26 +301,26 @@ class URLizerTests(TestCase):
"""
data = {}
data['"url": "http://api/users/1/", '] = \
- '"url": "http://api/users/1/", '
+ '"url": "http://api/users/1/", '
data['"foo_set": [\n "http://api/foos/1/"\n], '] = \
- '"foo_set": [\n "http://api/foos/1/"\n], '
+ '"foo_set": [\n "http://api/foos/1/"\n], '
self._urlize_dict_check(data)
def test_template_render_with_autoescape(self):
"""
Test that HTML is correctly escaped in Browsable API views.
"""
- template = Template("{% load rest_framework %}{{ content|urlize_quoted_links }}")
+ template = Template("{% load rest_framework %}{{ content|urlize }}")
rendered = template.render(Context({'content': ' http://example.com'}))
assert rendered == '<script>alert()</script>' \
' http://example.com'
def test_template_render_with_noautoescape(self):
"""
- Test if the autoescape value is getting passed to urlize_quoted_links filter.
+ Test if the autoescape value is getting passed to urlize filter.
"""
template = Template("{% load rest_framework %}"
- "{% autoescape off %}{{ content|urlize_quoted_links }}"
+ "{% autoescape off %}{{ content|urlize }}"
"{% endautoescape %}")
rendered = template.render(Context({'content': ' "http://example.com" '}))
assert rendered == ' "http://example.com" '
From 9ee67bbff77638316b6f1455d5dff38637a02306 Mon Sep 17 00:00:00 2001
From: Esieboma Jeremiah <57163971+esiebomaj@users.noreply.github.com>
Date: Mon, 28 Sep 2020 08:52:17 +0100
Subject: [PATCH 023/303] corrects typographical error in line 118 (#7553)
---
rest_framework/serializers.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 3def1946c..49eec8259 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -115,7 +115,7 @@ class BaseSerializer(Field):
super().__init__(**kwargs)
def __new__(cls, *args, **kwargs):
- # We override this method in order to automagically create
+ # We override this method in order to automatically create
# `ListSerializer` classes instead when `many=True` is set.
if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs)
From 6f7aad8ffa96226672eacb82ab003dcf01070ed3 Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Mon, 28 Sep 2020 10:47:50 +0100
Subject: [PATCH 024/303] Placeholder for 3.12 release (#7379)
* Placeholder for 3.12 release
* Updating release notes
* Updating release notes
* Updating release notes
* Update release notes
* Fix typo
* Basic structure for release announcement
* 3.12 release notes
* Version 3.12.0
---
docs/community/3.12-announcement.md | 169 ++++++++++++++++++++++++++++
docs/community/release-notes.md | 49 +++++++-
mkdocs.yml | 1 +
rest_framework/__init__.py | 2 +-
4 files changed, 219 insertions(+), 2 deletions(-)
create mode 100644 docs/community/3.12-announcement.md
diff --git a/docs/community/3.12-announcement.md b/docs/community/3.12-announcement.md
new file mode 100644
index 000000000..9d2220933
--- /dev/null
+++ b/docs/community/3.12-announcement.md
@@ -0,0 +1,169 @@
+
+
+# Django REST framework 3.12
+
+REST framework 3.12 brings a handful of refinements to the OpenAPI schema
+generation, plus support for Django's new database-agnostic `JSONField`,
+and some improvements to the `SearchFilter` class.
+
+## Grouping operations with tags.
+
+Open API schemas will now automatically include tags, based on the first element
+in the URL path.
+
+For example...
+
+Method | Path | Tags
+--------------------------------|-----------------|-------------
+`GET`, `PUT`, `PATCH`, `DELETE` | `/users/{id}/` | `['users']`
+`GET`, `POST` | `/users/` | `['users']`
+`GET`, `PUT`, `PATCH`, `DELETE` | `/orders/{id}/` | `['orders']`
+`GET`, `POST` | `/orders/` | `['orders']`
+
+The tags used for a particular view may also be overridden...
+
+```python
+class MyOrders(APIView):
+ schema = AutoSchema(tags=['users', 'orders'])
+ ...
+```
+
+See [the schema documentation](https://www.django-rest-framework.org/api-guide/schemas/#grouping-operations-with-tags) for more information.
+
+## Customizing the operation ID.
+
+REST framework automatically determines operation IDs to use in OpenAPI
+schemas. The latest version provides more control for overriding the behaviour
+used to generate the operation IDs.
+
+See [the schema documentation](https://www.django-rest-framework.org/api-guide/schemas/#operationid) for more information.
+
+## Support for OpenAPI components.
+
+In order to output more graceful OpenAPI schemes, REST framework 3.12 now
+defines components in the schema, and then references them inside request
+and response objects. This is in contrast with the previous approach, which
+fully expanded the request and response bodies for each operation.
+
+The names used for a component default to using the serializer class name, [but
+may be overridden if needed](https://www.django-rest-framework.org/api-guide/schemas/#components
+)...
+
+```python
+class MyOrders(APIView):
+ schema = AutoSchema(component_name="OrderDetails")
+```
+
+## More Public API
+
+Many methods on the `AutoSchema` class have now been promoted to public API,
+allowing you to more fully customize the schema generation. The following methods
+are now available for overriding...
+
+* `get_path_parameters`
+* `get_pagination_parameters`
+* `get_filter_parameters`
+* `get_request_body`
+* `get_responses`
+* `get_serializer`
+* `get_paginator`
+* `map_serializer`
+* `map_field`
+* `map_choice_field`
+* `map_field_validators`
+* `allows_filters`.
+
+See [the schema docs](https://www.django-rest-framework.org/api-guide/schemas/#per-view-customization)
+for details on using custom `AutoSchema` subclasses.
+
+## Support for JSONField.
+
+Django 3.1 deprecated the existing `django.contrib.postgres.fields.JSONField`
+in favour of a new database-agnositic `JSONField`.
+
+REST framework 3.12 now supports this new model field, and `ModelSerializer`
+classes will correctly map the model field.
+
+## SearchFilter improvements
+
+There are a couple of significant improvements to the `SearchFilter` class.
+
+### Nested searches against JSONField and HStoreField
+
+The class now supports nested search within `JSONField` and `HStoreField`, using
+the double underscore notation for traversing which element of the field the
+search should apply to.
+
+```python
+class SitesSearchView(generics.ListAPIView):
+ """
+ An API view to return a list of archaeological sites, optionally filtered
+ by a search against the site name or location. (Location searches are
+ matched against the region and country names.)
+ """
+ queryset = Sites.objects.all()
+ serializer_class = SitesSerializer
+ filter_backends = [filters.SearchFilter]
+ search_fields = ['site_name', 'location__region', 'location__country']
+```
+
+### Searches against annotate fields
+
+Django allows querysets to create additional virtual fields, using the `.annotate`
+method. We now support searching against annotate fields.
+
+```python
+class PublisherSearchView(generics.ListAPIView):
+ """
+ Search for publishers, optionally filtering the search against the average
+ rating of all their books.
+ """
+ queryset = Publisher.objects.annotate(avg_rating=Avg('book__rating'))
+ serializer_class = PublisherSerializer
+ filter_backends = [filters.SearchFilter]
+ search_fields = ['avg_rating']
+```
+
+---
+
+## Funding
+
+REST framework is a *collaboratively funded project*. If you use
+REST framework commercially we strongly encourage you to invest in its
+continued development by **[signing up for a paid plan][funding]**.
+
+*Every single sign-up helps us make REST framework long-term financially sustainable.*
+
+
+
+
+*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), and [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship).*
+
+[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors
+[funding]: funding.md
diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md
index d6f56317d..636e91d6c 100644
--- a/docs/community/release-notes.md
+++ b/docs/community/release-notes.md
@@ -36,11 +36,58 @@ You can determine your currently installed version using `pip show`:
## 3.11.x series
+### 3.12.0
+
+* Add `--file` option to `generateschema` command. [#7130]
+* Support `tags` for OpenAPI schema generation. See [the schema docs](https://www.django-rest-framework.org/api-guide/schemas/#grouping-operations-with-tags). [#7184]
+* Support customising the operation ID for schema generation. See [the schema docs](https://www.django-rest-framework.org/api-guide/schemas/#operationid). [#7190]
+* Support OpenAPI components for schema generation. See [the schema docs](https://www.django-rest-framework.org/api-guide/schemas/#components). [#7124]
+* The following methods on `AutoSchema` become public API: `get_path_parameters`, `get_pagination_parameters`, `get_filter_parameters`, `get_request_body`, `get_responses`, `get_serializer`, `get_paginator`, `map_serializer`, `map_field`, `map_choice_field`, `map_field_validators`, `allows_filters`. See [the schema docs](https://www.django-rest-framework.org/api-guide/schemas/#autoschema)
+* Add support for Django 3.1's database-agnositic `JSONField`. [#7467]
+* `SearchFilter` now supports nested search on `JSONField` and `HStoreField` model fields. [#7121]
+* `SearchFilter` now supports searching on `annotate()` fields. [#6240]
+* The authtoken model no longer exposes the `pk` in the admin URL. [#7341]
+* Add `__repr__` for Request instances. [#7239]
+* UTF-8 decoding with Latin-1 fallback for basic auth credentials. [#7193]
+* CharField treats surrogate characters as a validation failure. [#7026]
+* Don't include callables as default values in schemas. [#7105]
+* Improve `ListField` schema output to include all available child information. [#7137]
+* Allow `default=False` to be included for `BooleanField` schema outputs. [#7165]
+* Include `"type"` information in `ChoiceField` schema outputs. [#7161]
+* Include `"type": "object"` on schema objects. [#7169]
+* Don't include component in schema output for DELETE requests. [#7229]
+* Fix schema types for `DecimalField`. [#7254]
+* Fix schema generation for `ObtainAuthToken` view. [#7211]
+* Support passing `context=...` to view `.get_serializer()` methods. [#7298]
+* Pass custom code to `PermissionDenied` if permission class has one set. [#7306]
+* Include "example" in schema pagination output. [#7275]
+* Default status code of 201 on schema output for POST requests. [#7206]
+* Use camelCase for operation IDs in schema output. [#7208]
+* Warn if duplicate operation IDs exist in schema output. [#7207]
+* Improve handling of decimal type when mapping `ChoiceField` to a schema output. [#7264]
+* Disable YAML aliases for OpenAPI schema outputs. [#7131]
+* Fix action URL names for APIs included under a namespaced URL. [#7287]
+* Update jQuery version from 3.4 to 3.5. [#7313]
+* Fix `UniqueTogether` handling when serializer fields use `source=...`. [#7143]
+* HTTP `HEAD` requests now set `self.action` correctly on a ViewSet instance. [#7223]
+* Return a valid OpenAPI schema for the case where no API schema paths exist. [#7125]
+* Include tests in package distribution. [#7145]
+* Allow type checkers to support annotations like `ModelSerializer[Author]`. [#7385]
+* Don't include invalid `charset=None` portion in the request `Content-Type` header when using APIClient. [#7400]
+* Fix `\Z`/`\z` tokens in OpenAPI regexs. [#7389]
+* Fix `PrimaryKeyRelatedField` and `HyperlinkedRelatedField` when source field is actually a property. [#7142]
+* `Token.generate_key` is now a class method. [#7502]
+* `@action` warns if method is wrapped in a decorator that does not preserve information using `@functools.wraps`. [#7098]
+
+---
+
+## 3.11.x series
+
### 3.11.0
**Date**: 12th December 2019
-* Drop `.set_context` API [in favour of a `requires_context` marker](../3.11-announcement#validator-default-context).
+* Drop `.set_context` API [in favour of a `requires_context` marker](3.11-announcement.md#validator-default-context).
* Changed default widget for TextField with choices to select box. [#6892][gh6892]
* Supported nested writes on non-relational fields, such as JSONField. [#6916][gh6916]
* Include request/response media types in OpenAPI schemas, based on configured parsers/renderers. [#6865][gh6865]
diff --git a/mkdocs.yml b/mkdocs.yml
index 484971a71..573898bca 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -66,6 +66,7 @@ nav:
- 'Contributing to REST framework': 'community/contributing.md'
- 'Project management': 'community/project-management.md'
- 'Release Notes': 'community/release-notes.md'
+ - '3.12 Announcement': 'community/3.12-announcement.md'
- '3.11 Announcement': 'community/3.11-announcement.md'
- '3.10 Announcement': 'community/3.10-announcement.md'
- '3.9 Announcement': 'community/3.9-announcement.md'
diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py
index 8f2bc4466..afb2d3429 100644
--- a/rest_framework/__init__.py
+++ b/rest_framework/__init__.py
@@ -8,7 +8,7 @@ ______ _____ _____ _____ __
"""
__title__ = 'Django REST framework'
-__version__ = '3.11.0'
+__version__ = '3.12.0'
__author__ = 'Tom Christie'
__license__ = 'BSD 3-Clause'
__copyright__ = 'Copyright 2011-2019 Encode OSS Ltd'
From 68b23075a2d72935f1e6c428c664d49412a8af68 Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Mon, 28 Sep 2020 10:54:52 +0100
Subject: [PATCH 025/303] Add date to 3.12 release.
---
docs/community/release-notes.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md
index 636e91d6c..b9bccc3ac 100644
--- a/docs/community/release-notes.md
+++ b/docs/community/release-notes.md
@@ -38,6 +38,8 @@ You can determine your currently installed version using `pip show`:
### 3.12.0
+Date: 28th September 2020
+
* Add `--file` option to `generateschema` command. [#7130]
* Support `tags` for OpenAPI schema generation. See [the schema docs](https://www.django-rest-framework.org/api-guide/schemas/#grouping-operations-with-tags). [#7184]
* Support customising the operation ID for schema generation. See [the schema docs](https://www.django-rest-framework.org/api-guide/schemas/#operationid). [#7190]
From 995188f8c5bfdd0da29a7f9ca01898dfce5f0b37 Mon Sep 17 00:00:00 2001
From: Hugo Rodger-Brown
Date: Mon, 28 Sep 2020 16:16:18 +0100
Subject: [PATCH 026/303] Add missing TokenProxy migration (#7557)
Fixes #7554
---
.../authtoken/migrations/0003_tokenproxy.py | 25 +++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 rest_framework/authtoken/migrations/0003_tokenproxy.py
diff --git a/rest_framework/authtoken/migrations/0003_tokenproxy.py b/rest_framework/authtoken/migrations/0003_tokenproxy.py
new file mode 100644
index 000000000..79405a7c0
--- /dev/null
+++ b/rest_framework/authtoken/migrations/0003_tokenproxy.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.1.1 on 2020-09-28 09:34
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('authtoken', '0002_auto_20160226_1747'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='TokenProxy',
+ fields=[
+ ],
+ options={
+ 'verbose_name': 'token',
+ 'proxy': True,
+ 'indexes': [],
+ 'constraints': [],
+ },
+ bases=('authtoken.token',),
+ ),
+ ]
From 05b3865838bcc0e4298a2c6a75cca69aad4f51d7 Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Mon, 28 Sep 2020 16:18:23 +0100
Subject: [PATCH 027/303] Version 3.12.1
---
docs/community/release-notes.md | 6 ++++++
rest_framework/__init__.py | 2 +-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md
index b9bccc3ac..a300162aa 100644
--- a/docs/community/release-notes.md
+++ b/docs/community/release-notes.md
@@ -36,6 +36,12 @@ You can determine your currently installed version using `pip show`:
## 3.11.x series
+### 3.12.1
+
+Date: 28th September 2020
+
+* Add `TokenProxy` migration. [#7557]
+
### 3.12.0
Date: 28th September 2020
diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py
index afb2d3429..cd35f7a04 100644
--- a/rest_framework/__init__.py
+++ b/rest_framework/__init__.py
@@ -8,7 +8,7 @@ ______ _____ _____ _____ __
"""
__title__ = 'Django REST framework'
-__version__ = '3.12.0'
+__version__ = '3.12.1'
__author__ = 'Tom Christie'
__license__ = 'BSD 3-Clause'
__copyright__ = 'Copyright 2011-2019 Encode OSS Ltd'
From 9edd5dfe5d65b99289b7a6d0d74deab89749a512 Mon Sep 17 00:00:00 2001
From: Ali Mirlou
Date: Mon, 28 Sep 2020 23:15:30 +0330
Subject: [PATCH 028/303] Fix typo in release notes
---
docs/community/release-notes.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md
index a300162aa..26dca3e4f 100644
--- a/docs/community/release-notes.md
+++ b/docs/community/release-notes.md
@@ -34,7 +34,7 @@ You can determine your currently installed version using `pip show`:
---
-## 3.11.x series
+## 3.12.x series
### 3.12.1
From a03c85225ff79db42319fda4dfa6e65d316b6c03 Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Wed, 30 Sep 2020 09:10:36 +0100
Subject: [PATCH 029/303] Update test_templatetags.py
---
tests/test_templatetags.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py
index cdf10ab1e..4b84f6647 100644
--- a/tests/test_templatetags.py
+++ b/tests/test_templatetags.py
@@ -290,9 +290,6 @@ class URLizerTests(TestCase):
For all items in dict test assert that the value is urlized key
"""
for original, urlized in data.items():
- print('====')
- print(repr(urlize(original, nofollow=False)))
- print(repr(urlized))
assert urlize(original, nofollow=False) == urlized
def test_json_with_url(self):
From be87eb43b348f1f079de81329218b79814b97173 Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Wed, 30 Sep 2020 09:44:31 +0100
Subject: [PATCH 030/303] Update release notes with 3.11.1, 3.11.2
---
docs/community/release-notes.md | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md
index 26dca3e4f..a48888719 100644
--- a/docs/community/release-notes.md
+++ b/docs/community/release-notes.md
@@ -91,6 +91,18 @@ Date: 28th September 2020
## 3.11.x series
+### 3.11.2
+
+**Date**: 30th September 2020
+
+* **Security**: Drop `urlize_quoted_links` template tag in favour of Django's built-in `urlize`. Removes a XSS vulnerability for some kinds of content in the browsable API.
+
+### 3.11.1
+
+**Date**: 5th August 2020
+
+* Fix compat with Django 3.1
+
### 3.11.0
**Date**: 12th December 2019
From 91916a4db14cd6a06aca13fb9a46fc667f6c0682 Mon Sep 17 00:00:00 2001
From: Adam Johnson
Date: Wed, 30 Sep 2020 13:47:06 +0100
Subject: [PATCH 031/303] Fix NullBooleanField warning (#7565)
The argument to Field is 'allow_null'.
---
rest_framework/fields.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index f218713f1..fdfba13f2 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -749,7 +749,7 @@ class NullBooleanField(BooleanField):
warnings.warn(
"The `NullBooleanField` is deprecated and will be removed starting "
"with 3.14. Instead use the `BooleanField` field and set "
- "`null=True` which does the same thing.",
+ "`allow_null=True` which does the same thing.",
RemovedInDRF314Warning, stacklevel=2
)
From 86ec3da2daa7c585ac00c9900a6b482d7e819eac Mon Sep 17 00:00:00 2001
From: Emmanuel Meric de Bellefon
Date: Mon, 5 Oct 2020 20:56:42 +0200
Subject: [PATCH 032/303] Update 6-viewsets-and-routers.md
---
docs/tutorial/6-viewsets-and-routers.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md
index 11e24448f..18989a3ee 100644
--- a/docs/tutorial/6-viewsets-and-routers.md
+++ b/docs/tutorial/6-viewsets-and-routers.md
@@ -2,7 +2,7 @@
REST framework includes an abstraction for dealing with `ViewSets`, that allows the developer to concentrate on modeling the state and interactions of the API, and leave the URL construction to be handled automatically, based on common conventions.
-`ViewSet` classes are almost the same thing as `View` classes, except that they provide operations such as `read`, or `update`, and not method handlers such as `get` or `put`.
+`ViewSet` classes are almost the same thing as `View` classes, except that they provide operations such as `retrieve`, or `update`, and not method handlers such as `get` or `put`.
A `ViewSet` class is only bound to a set of method handlers at the last moment, when it is instantiated into a set of views, typically by using a `Router` class which handles the complexities of defining the URL conf for you.
From a849627f88c95cf3561a51055a0d8af901430e6a Mon Sep 17 00:00:00 2001
From: Xavier Ordoquy
Date: Mon, 5 Oct 2020 21:48:07 +0200
Subject: [PATCH 033/303] Fixes #7538 - use `retrieve` action name instead of
`detail`
---
docs/tutorial/6-viewsets-and-routers.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md
index 11e24448f..3ce3ae710 100644
--- a/docs/tutorial/6-viewsets-and-routers.md
+++ b/docs/tutorial/6-viewsets-and-routers.md
@@ -16,7 +16,7 @@ First of all let's refactor our `UserList` and `UserDetail` views into a single
class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""
- This viewset automatically provides `list` and `detail` actions.
+ This viewset automatically provides `list` and `retrieve` actions.
"""
queryset = User.objects.all()
serializer_class = UserSerializer
From 6a45a6a36fedf5b2a9bfbb23cf12b8a1cc8acb3e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E0=A4=B8=E0=A5=8D=E0=A4=AA=E0=A4=B0=E0=A5=8D=E0=A4=B6?=
Date: Tue, 6 Oct 2020 17:59:11 +0545
Subject: [PATCH 034/303] Fix broken machinalis links (#7580)
#7572 #7423 #7573 all are related to this
---
docs/community/tutorials-and-resources.md | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/docs/community/tutorials-and-resources.md b/docs/community/tutorials-and-resources.md
index 6fdac6004..cfd3ba852 100644
--- a/docs/community/tutorials-and-resources.md
+++ b/docs/community/tutorials-and-resources.md
@@ -101,12 +101,12 @@ Want your Django REST Framework talk/tutorial/article to be added to our website
[django-rest-framework-course]: https://teamtreehouse.com/library/django-rest-framework
[pycon-uk-2016]: https://www.youtube.com/watch?v=FjmiGh7OqVg
[django-under-hood-2014]: https://www.youtube.com/watch?v=3cSsbe-tA0E
-[integrating-pandas-drf-and-bokeh]: https://machinalis.com/blog/pandas-django-rest-framework-bokeh/
-[controlling-uncertainty-on-web-apps-and-apis]: https://machinalis.com/blog/controlling-uncertainty-on-web-applications-and-apis/
-[full-text-search-in-drf]: https://machinalis.com/blog/full-text-search-on-django-rest-framework/
-[oauth2-authentication-with-drf]: https://machinalis.com/blog/oauth2-authentication/
-[nested-resources-with-drf]: https://machinalis.com/blog/nested-resources-with-django/
-[image-fields-with-drf]: https://machinalis.com/blog/image-fields-with-django-rest-framework/
+[integrating-pandas-drf-and-bokeh]: https://web.archive.org/web/20180104205117/http://machinalis.com/blog/pandas-django-rest-framework-bokeh/
+[controlling-uncertainty-on-web-apps-and-apis]: https://web.archive.org/web/20180104205043/https://machinalis.com/blog/controlling-uncertainty-on-web-applications-and-apis/
+[full-text-search-in-drf]: https://web.archive.org/web/20180104205059/http://machinalis.com/blog/full-text-search-on-django-rest-framework/
+[oauth2-authentication-with-drf]: https://web.archive.org/web/20180104205054/http://machinalis.com/blog/oauth2-authentication/
+[nested-resources-with-drf]: https://web.archive.org/web/20180104205109/http://machinalis.com/blog/nested-resources-with-django/
+[image-fields-with-drf]: https://web.archive.org/web/20180104205048/http://machinalis.com/blog/image-fields-with-django-rest-framework/
[chatbot-using-drf-part1]: https://chatbotslife.com/chatbot-using-django-rest-framework-api-ai-slack-part-1-3-69c7e38b7b1e#.g2aceuncf
[new-django-admin-with-drf-and-emberjs]: https://blog.levit.be/new-django-admin-with-emberjs-what-are-the-news/
[drf-schema]: https://drf-schema-adapter.readthedocs.io/en/latest/
From 5e23b559f83f2ce2a4683111590909dc475d41a6 Mon Sep 17 00:00:00 2001
From: Mopsan
Date: Fri, 9 Oct 2020 11:38:11 +0300
Subject: [PATCH 035/303] Fix example override of SchemaGenerator.get_schema()
---
docs/api-guide/schemas.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md
index e39cd21a8..b4832b369 100644
--- a/docs/api-guide/schemas.md
+++ b/docs/api-guide/schemas.md
@@ -181,8 +181,8 @@ dictionary For example you might wish to add terms of service to the [top-level
```
class TOSSchemaGenerator(SchemaGenerator):
- def get_schema(self):
- schema = super().get_schema()
+ def get_schema(self, *args, **kwargs):
+ schema = super().get_schema(*args, **kwargs)
schema["info"]["termsOfService"] = "https://example.com/tos.html"
return schema
```
From 7921e9af434f2ccfde6962cf8a1b76331cc77722 Mon Sep 17 00:00:00 2001
From: Adam Johnson
Date: Fri, 9 Oct 2020 10:48:03 +0100
Subject: [PATCH 036/303] Fix RemovedInDjango40Warning for middleware
get_resopnse() (#7513)
Fixes #7417.
Fixes all these issues seen with `tox -e py38-django31`:
```
/Users/chainz/Documents/Projects/django-rest-framework/tests/test_request.py:208: RemovedInDjango40Warning: Passing None for the middleware get_response argument is deprecated.
SessionMiddleware().process_request(self.wrapped_request)
tests/test_requests_client.py: 1 test with warning
tests/test_testing.py: 4 tests with warnings
tests/test_throttling.py: 1 test with warning
tests/authentication/test_authentication.py: 4 tests with warnings
tests/browsable_api/test_browsable_api.py: 4 tests with warnings
/Users/chainz/Documents/Projects/django-rest-framework/rest_framework/authentication.py:139: RemovedInDjango40Warning: Passing None for the middleware get_response argument is deprecated.
check = CSRFCheck()
```
---
rest_framework/authentication.py | 5 ++++-
tests/test_request.py | 8 ++++++--
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py
index a2ba53480..9111007c0 100644
--- a/rest_framework/authentication.py
+++ b/rest_framework/authentication.py
@@ -136,7 +136,10 @@ class SessionAuthentication(BaseAuthentication):
"""
Enforce CSRF validation for session based authentication.
"""
- check = CSRFCheck()
+ def dummy_get_response(request): # pragma: no cover
+ return None
+
+ check = CSRFCheck(dummy_get_response)
# populates request.META['CSRF_COOKIE'], which is used in process_view()
check.process_request(request)
reason = check.process_view(request, None, (), {})
diff --git a/tests/test_request.py b/tests/test_request.py
index 4425c020f..8f55d00ed 100644
--- a/tests/test_request.py
+++ b/tests/test_request.py
@@ -205,8 +205,12 @@ class TestUserSetter(TestCase):
# available to login and logout functions
self.wrapped_request = factory.get('/')
self.request = Request(self.wrapped_request)
- SessionMiddleware().process_request(self.wrapped_request)
- AuthenticationMiddleware().process_request(self.wrapped_request)
+
+ def dummy_get_response(request): # pragma: no cover
+ return None
+
+ SessionMiddleware(dummy_get_response).process_request(self.wrapped_request)
+ AuthenticationMiddleware(dummy_get_response).process_request(self.wrapped_request)
User.objects.create_user('ringo', 'starr@thebeatles.com', 'yellow')
self.user = authenticate(username='ringo', password='yellow')
From 931b34e7deb3b54e9b8205aad3a2448ff319e2b8 Mon Sep 17 00:00:00 2001
From: Adam Johnson
Date: Fri, 9 Oct 2020 11:09:34 +0100
Subject: [PATCH 037/303] Remove tox-venv from Travis setup (#7585)
This was added in #6139. However it seems [tox-venv is no longer maintained](https://github.com/tox-dev/tox-venv), the related [virtualenv issue has been closed](https://github.com/pypa/virtualenv/issues/355), and I suspect with the virtualenv rewrite fixed the problem with site.py and the warnings referred to for the DRF tests.
---
.travis.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index c204c5460..2c2724bf6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -40,7 +40,7 @@ matrix:
- env: DJANGO=master
install:
- - pip install tox tox-venv tox-travis
+ - pip install tox tox-travis
script:
- tox
From 79daf315c4504d55bfd6025567be8ec9f35d361c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kry=C5=A1tof=20Piln=C3=A1=C4=8Dek?=
Date: Fri, 9 Oct 2020 13:16:15 +0200
Subject: [PATCH 038/303] Fix: authtoken.TokenProxy cannot be proxy when not
installed (#7571)
closes https://github.com/encode/django-rest-framework/issues/7442
---
rest_framework/authtoken/models.py | 3 ++-
tests/test_authtoken.py | 11 ++++++++++-
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/rest_framework/authtoken/models.py b/rest_framework/authtoken/models.py
index fd8a50e0e..540049295 100644
--- a/rest_framework/authtoken/models.py
+++ b/rest_framework/authtoken/models.py
@@ -49,5 +49,6 @@ class TokenProxy(Token):
return self.user.pk
class Meta:
- proxy = True
+ proxy = 'rest_framework.authtoken' in settings.INSTALLED_APPS
+ abstract = 'rest_framework.authtoken' not in settings.INSTALLED_APPS
verbose_name = "token"
diff --git a/tests/test_authtoken.py b/tests/test_authtoken.py
index 036e317ef..30e416d65 100644
--- a/tests/test_authtoken.py
+++ b/tests/test_authtoken.py
@@ -1,10 +1,11 @@
+import importlib
from io import StringIO
import pytest
from django.contrib.admin import site
from django.contrib.auth.models import User
from django.core.management import CommandError, call_command
-from django.test import TestCase
+from django.test import TestCase, modify_settings
from rest_framework.authtoken.admin import TokenAdmin
from rest_framework.authtoken.management.commands.drf_create_token import \
@@ -21,6 +22,14 @@ class AuthTokenTests(TestCase):
self.user = User.objects.create_user(username='test_user')
self.token = Token.objects.create(key='test token', user=self.user)
+ def test_authtoken_can_be_imported_when_not_included_in_installed_apps(self):
+ import rest_framework.authtoken.models
+ with modify_settings(INSTALLED_APPS={'remove': 'rest_framework.authtoken'}):
+ importlib.reload(rest_framework.authtoken.models)
+ # Set the proxy and abstract properties back to the version,
+ # where authtoken is among INSTALLED_APPS.
+ importlib.reload(rest_framework.authtoken.models)
+
def test_model_admin_displayed_fields(self):
mock_request = object()
token_admin = TokenAdmin(self.token, self.site)
From 79c37d0dc3740aa1c439f8a447e5e8b64fd567be Mon Sep 17 00:00:00 2001
From: David Smith <39445562+smithdc1@users.noreply.github.com>
Date: Fri, 9 Oct 2020 12:21:22 +0100
Subject: [PATCH 039/303] Return NotImplemented sooner for ErrorDetail equality
test (#7531)
The test suite raises warnings when tested against Python 3.9
`DeprecationWarning: NotImplemented should not be used in a boolean context`
Where `r` returns `NotImplemented` then this change returns `NotImplemented` first to avoid the comparison test.
---
rest_framework/exceptions.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py
index 345a40524..943dcc88c 100644
--- a/rest_framework/exceptions.py
+++ b/rest_framework/exceptions.py
@@ -73,6 +73,8 @@ class ErrorDetail(str):
def __eq__(self, other):
r = super().__eq__(other)
+ if r is NotImplemented:
+ return NotImplemented
try:
return r and self.code == other.code
except AttributeError:
From 563b04376828a9c3d4d0c3232c1dad342627168b Mon Sep 17 00:00:00 2001
From: Adam Johnson
Date: Sat, 10 Oct 2020 17:34:00 +0100
Subject: [PATCH 040/303] Error on deprecation and pending deprecation warnings
(#7586)
Erroring on deprecation or pending deprecation warnings means they are caught early. This will avoid the cycle of releasing with 'support for Django X', then chasing all the deprecation warnings one-by-one. Instead, when a new Django version is added to the test matrix, it will fail until all the relevant warnings are fixed. This avoids passing these warnings on to users, some of whom don't upgrade until they are all fixed.
---
tox.ini | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tox.ini b/tox.ini
index d5e769764..df6387d5e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -14,7 +14,7 @@ DJANGO =
master: djangomaster
[testenv]
-commands = ./runtests.py --fast --coverage {posargs}
+commands = python -W error::DeprecationWarning -W error::PendingDeprecationWarning runtests.py --fast --coverage {posargs}
envdir = {toxworkdir}/venvs/{envname}
setenv =
PYTHONDONTWRITEBYTECODE=1
From 1ef192811cee3e6953aeb3d8f724d45c3057b6e7 Mon Sep 17 00:00:00 2001
From: Thomas Riccardi
Date: Sat, 10 Oct 2020 18:34:39 +0200
Subject: [PATCH 041/303] Add missing quoting for `data` keyword argument in
serializer doc (#7587)
---
docs/api-guide/serializers.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md
index efa325620..5e2705834 100644
--- a/docs/api-guide/serializers.md
+++ b/docs/api-guide/serializers.md
@@ -251,7 +251,7 @@ For more information see the [validators documentation](validators.md).
When passing an initial object or queryset to a serializer instance, the object will be made available as `.instance`. If no initial object is passed then the `.instance` attribute will be `None`.
-When passing data to a serializer instance, the unmodified data will be made available as `.initial_data`. If the data keyword argument is not passed then the `.initial_data` attribute will not exist.
+When passing data to a serializer instance, the unmodified data will be made available as `.initial_data`. If the `data` keyword argument is not passed then the `.initial_data` attribute will not exist.
## Partial updates
From ffde1691025761c97927f9f4bbd5a9f3ec9ea96e Mon Sep 17 00:00:00 2001
From: zach valenta
Date: Sat, 10 Oct 2020 12:36:15 -0400
Subject: [PATCH 042/303] Add docs link to to_internal_value() (#7476)
---
docs/api-guide/relations.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md
index 88b462e1a..d3d8b30b8 100644
--- a/docs/api-guide/relations.md
+++ b/docs/api-guide/relations.md
@@ -337,7 +337,7 @@ output representation should be generated from the model instance.
To implement a custom relational field, you should override `RelatedField`, and implement the `.to_representation(self, value)` method. This method takes the target of the field as the `value` argument, and should return the representation that should be used to serialize the target. The `value` argument will typically be a model instance.
-If you want to implement a read-write relational field, you must also implement the `.to_internal_value(self, data)` method.
+If you want to implement a read-write relational field, you must also implement the [`.to_internal_value(self, data)` method][to_internal_value].
To provide a dynamic queryset based on the `context`, you can also override `.get_queryset(self)` instead of specifying `.queryset` on the class or when initializing the field.
@@ -605,3 +605,4 @@ The [rest-framework-generic-relations][drf-nested-relations] library provides re
[drf-nested-relations]: https://github.com/Ian-Foote/rest-framework-generic-relations
[django-intermediary-manytomany]: https://docs.djangoproject.com/en/2.2/topics/db/models/#intermediary-manytomany
[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
From eff97efa283529c6503b9ebde6506691cecb17a2 Mon Sep 17 00:00:00 2001
From: Thomas Leese
Date: Sat, 10 Oct 2020 18:02:21 +0100
Subject: [PATCH 043/303] Don't catch exceptions in get_queryset (#7480)
In the `to_internal_value` method of the primary key and slug related fields, `TypeError`s and `ValueError`s are caught from `self.get_queryset().get(...)` and presented to the user. This works fine for most cases, but can cause problems if the exception is coming from `self.get_queryset()` rather than from the `.get(...)`.
It means errors in the `get_queryset` method can be hidden and presented back to the user as though, for example, the input provided to the `to_internal_value` was the wrong type, whereas in reality there's a bug in the `get_queryset` method and therefore it should bubble up and be exposed as a server error.
I've decided to fix this because twice now I've had to debug why I'm seeing `invalid_type` errors from my serializer (errors like `wrong pk type - int` when the `pk` type on my model is `int`) when the real problem was a bug in my custom `get_queryset` method.
---
rest_framework/relations.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/rest_framework/relations.py b/rest_framework/relations.py
index 3cd46379d..eaf27e1d9 100644
--- a/rest_framework/relations.py
+++ b/rest_framework/relations.py
@@ -257,8 +257,9 @@ class PrimaryKeyRelatedField(RelatedField):
def to_internal_value(self, data):
if self.pk_field is not None:
data = self.pk_field.to_internal_value(data)
+ queryset = self.get_queryset()
try:
- return self.get_queryset().get(pk=data)
+ return queryset.get(pk=data)
except ObjectDoesNotExist:
self.fail('does_not_exist', pk_value=data)
except (TypeError, ValueError):
@@ -454,8 +455,9 @@ class SlugRelatedField(RelatedField):
super().__init__(**kwargs)
def to_internal_value(self, data):
+ queryset = self.get_queryset()
try:
- return self.get_queryset().get(**{self.slug_field: data})
+ return queryset.get(**{self.slug_field: data})
except ObjectDoesNotExist:
self.fail('does_not_exist', slug_name=self.slug_field, value=smart_str(data))
except (TypeError, ValueError):
From 0bdd537cc454477886fefc9873353b2364d94158 Mon Sep 17 00:00:00 2001
From: Aarni Koskela
Date: Mon, 12 Oct 2020 17:40:26 +0300
Subject: [PATCH 044/303] Ignore derivations of BrowsableAPIRenderer in OpenAPI
schema (#7497)
* Ignore derivations of BrowsableAPIRenderer in OpenAPI schema
* Improve test_renderer_mapping test
Co-authored-by: Carlton Gibson
---
rest_framework/schemas/openapi.py | 2 +-
tests/schemas/test_openapi.py | 17 +++++++++++++----
2 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py
index 8a8e267e0..4ecb7a65f 100644
--- a/rest_framework/schemas/openapi.py
+++ b/rest_framework/schemas/openapi.py
@@ -595,7 +595,7 @@ class AutoSchema(ViewInspector):
media_types = []
for renderer in self.view.renderer_classes:
# BrowsableAPIRenderer not relevant to OpenAPI spec
- if renderer == renderers.BrowsableAPIRenderer:
+ if issubclass(renderer, renderers.BrowsableAPIRenderer):
continue
media_types.append(renderer.media_type)
return media_types
diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py
index 8ea910dc1..542c377b1 100644
--- a/tests/schemas/test_openapi.py
+++ b/tests/schemas/test_openapi.py
@@ -10,7 +10,9 @@ from rest_framework import filters, generics, pagination, routers, serializers
from rest_framework.authtoken.views import obtain_auth_token
from rest_framework.compat import uritemplate
from rest_framework.parsers import JSONParser, MultiPartParser
-from rest_framework.renderers import JSONRenderer, OpenAPIRenderer
+from rest_framework.renderers import (
+ BaseRenderer, BrowsableAPIRenderer, JSONRenderer, OpenAPIRenderer
+)
from rest_framework.request import Request
from rest_framework.schemas.openapi import AutoSchema, SchemaGenerator
@@ -507,9 +509,16 @@ class TestOperationIntrospection(TestCase):
path = '/{id}/'
method = 'GET'
+ class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):
+ media_type = 'image/jpeg' # that's a wild API renderer
+
+ class TextRenderer(BaseRenderer):
+ media_type = 'text/plain'
+ format = 'text'
+
class View(generics.CreateAPIView):
serializer_class = views.ExampleSerializer
- renderer_classes = [JSONRenderer]
+ renderer_classes = [JSONRenderer, TextRenderer, BrowsableAPIRenderer, CustomBrowsableAPIRenderer]
view = create_view(
View,
@@ -524,8 +533,8 @@ class TestOperationIntrospection(TestCase):
# schema support is there
success_response = responses['200']
- assert len(success_response['content'].keys()) == 1
- assert 'application/json' in success_response['content']
+ # Check that the API renderers aren't included, but custom renderers are
+ assert set(success_response['content']) == {'application/json', 'text/plain'}
def test_openapi_yaml_rendering_without_aliases(self):
renderer = OpenAPIRenderer()
From 95f0b0867a7542f7304401bfb6ae74c6e804934b Mon Sep 17 00:00:00 2001
From: Pawel
Date: Tue, 13 Oct 2020 08:32:38 +0200
Subject: [PATCH 045/303] Documentation: improve the action decorator
documentation (#7316) (#7380)
* Documentation: improve the action decorator documentation (#7316)
---
docs/api-guide/viewsets.md | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md
index d7aa7ad3b..22cc3d8aa 100644
--- a/docs/api-guide/viewsets.md
+++ b/docs/api-guide/viewsets.md
@@ -171,11 +171,6 @@ A more complete example of extra actions:
serializer = self.get_serializer(recent_users, many=True)
return Response(serializer.data)
-The decorator can additionally take extra arguments that will be set for the routed view only. For example:
-
- @action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])
- def set_password(self, request, pk=None):
- ...
The `action` decorator will route `GET` requests by default, but may also accept other HTTP methods by setting the `methods` argument. For example:
@@ -183,7 +178,14 @@ The `action` decorator will route `GET` requests by default, but may also accept
def unset_password(self, request, pk=None):
...
-The two new actions will then be available at the urls `^users/{pk}/set_password/$` and `^users/{pk}/unset_password/$`
+
+The decorator allows you to override any viewset-level configuration such as `permission_classes`, `serializer_class`, `filter_backends`...:
+
+ @action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])
+ def set_password(self, request, pk=None):
+ ...
+
+The two new actions will then be available at the urls `^users/{pk}/set_password/$` and `^users/{pk}/unset_password/$`. Use the `url_path` and `url_name` parameters to change the URL segement and the reverse URL name of the action.
To view all extra actions, call the `.get_extra_actions()` method.
From 3799633cdeb679eec681451bd318867dbd292114 Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Tue, 13 Oct 2020 14:17:53 +0100
Subject: [PATCH 046/303] Version 3.12.2
---
docs/community/release-notes.md | 8 ++++++++
rest_framework/__init__.py | 2 +-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md
index a48888719..c981b9ac9 100644
--- a/docs/community/release-notes.md
+++ b/docs/community/release-notes.md
@@ -36,6 +36,14 @@ You can determine your currently installed version using `pip show`:
## 3.12.x series
+### 3.12.2
+
+Date: 13th October 2020
+
+* Fix issue if `rest_framework.authtoken.models` is imported, but `rest_framework.authtoken` is not in INSTALLED_APPS. [#7571]
+* Ignore subclasses of BrowsableAPIRenderer in OpenAPI schema. [#7497]
+* Narrower exception catching in serilizer fields, to ensure that any errors in broken `get_queryset()` methods are not masked. [#7480]
+
### 3.12.1
Date: 28th September 2020
diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py
index 05d5adc46..7ff188a5a 100644
--- a/rest_framework/__init__.py
+++ b/rest_framework/__init__.py
@@ -10,7 +10,7 @@ ______ _____ _____ _____ __
import django
__title__ = 'Django REST framework'
-__version__ = '3.12.1'
+__version__ = '3.12.2'
__author__ = 'Tom Christie'
__license__ = 'BSD 3-Clause'
__copyright__ = 'Copyright 2011-2019 Encode OSS Ltd'
From 04e0c2b9ab6616a28148ce32e2d19858ccfe6c69 Mon Sep 17 00:00:00 2001
From: David Smith <39445562+smithdc1@users.noreply.github.com>
Date: Tue, 13 Oct 2020 17:27:08 +0100
Subject: [PATCH 047/303] Bumped Markdown version to 3.3 (#7590)
---
requirements/requirements-optionals.txt | 3 +-
tests/test_description.py | 372 ++++++++++++------------
2 files changed, 182 insertions(+), 193 deletions(-)
diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt
index 2b7a18a13..739555667 100644
--- a/requirements/requirements-optionals.txt
+++ b/requirements/requirements-optionals.txt
@@ -1,6 +1,7 @@
# Optional packages which may be used with REST framework.
psycopg2-binary>=2.8.5, <2.9
-markdown==3.1.1
+markdown==3.3;python_version>="3.6"
+markdown==3.2.2;python_version=="3.5"
pygments==2.4.2
django-guardian==2.2.0
django-filter>=2.2.0, <2.3
diff --git a/tests/test_description.py b/tests/test_description.py
index ae00fe4a9..9e7e4dc32 100644
--- a/tests/test_description.py
+++ b/tests/test_description.py
@@ -1,192 +1,180 @@
-from django.test import TestCase
-
-from rest_framework.compat import apply_markdown
-from rest_framework.utils.formatting import dedent
-from rest_framework.views import APIView
-
-# We check that docstrings get nicely un-indented.
-DESCRIPTION = """an example docstring
-====================
-
-* list
-* list
-
-another header
---------------
-
- code block
-
-indented
-
-# hash style header #
-
-``` json
-[{
- "alpha": 1,
- "beta: "this is a string"
-}]
-```"""
-
-
-# If markdown is installed we also test it's working
-# (and that our wrapped forces '=' to h2 and '-' to h3)
-MARKED_DOWN_HILITE = """
-[{
"alpha": 1,
\
- "beta: "this\
- is a \
-string"
}]
-
-
"""
-
-MARKED_DOWN_NOT_HILITE = """
-json
-[{
- "alpha": 1,
- "beta: "this is a string"
-}]
"""
-
-# We support markdown < 2.1 and markdown >= 2.1
-MARKED_DOWN_lt_21 = """an example docstring
-
-another header
-code block
-
-indented
-%s"""
-
-MARKED_DOWN_gte_21 = """an example docstring
-
-
-code block
-
-indented
-%s"""
-
-
-class TestViewNamesAndDescriptions(TestCase):
- def test_view_name_uses_class_name(self):
- """
- Ensure view names are based on the class name.
- """
- class MockView(APIView):
- pass
- assert MockView().get_view_name() == 'Mock'
-
- def test_view_name_uses_name_attribute(self):
- class MockView(APIView):
- name = 'Foo'
- assert MockView().get_view_name() == 'Foo'
-
- def test_view_name_uses_suffix_attribute(self):
- class MockView(APIView):
- suffix = 'List'
- assert MockView().get_view_name() == 'Mock List'
-
- def test_view_name_preferences_name_over_suffix(self):
- class MockView(APIView):
- name = 'Foo'
- suffix = 'List'
- assert MockView().get_view_name() == 'Foo'
-
- def test_view_description_uses_docstring(self):
- """Ensure view descriptions are based on the docstring."""
- class MockView(APIView):
- """an example docstring
- ====================
-
- * list
- * list
-
- another header
- --------------
-
- code block
-
- indented
-
- # hash style header #
-
- ``` json
- [{
- "alpha": 1,
- "beta: "this is a string"
- }]
- ```"""
-
- assert MockView().get_view_description() == DESCRIPTION
-
- def test_view_description_uses_description_attribute(self):
- class MockView(APIView):
- description = 'Foo'
- assert MockView().get_view_description() == 'Foo'
-
- def test_view_description_allows_empty_description(self):
- class MockView(APIView):
- """Description."""
- description = ''
- assert MockView().get_view_description() == ''
-
- def test_view_description_can_be_empty(self):
- """
- Ensure that if a view has no docstring,
- then it's description is the empty string.
- """
- class MockView(APIView):
- pass
- assert MockView().get_view_description() == ''
-
- def test_view_description_can_be_promise(self):
- """
- Ensure a view may have a docstring that is actually a lazily evaluated
- class that can be converted to a string.
-
- See: https://github.com/encode/django-rest-framework/issues/1708
- """
- # use a mock object instead of gettext_lazy to ensure that we can't end
- # up with a test case string in our l10n catalog
-
- class MockLazyStr:
- def __init__(self, string):
- self.s = string
-
- def __str__(self):
- return self.s
-
- class MockView(APIView):
- __doc__ = MockLazyStr("a gettext string")
-
- assert MockView().get_view_description() == 'a gettext string'
-
- def test_markdown(self):
- """
- Ensure markdown to HTML works as expected.
- """
- if apply_markdown:
- md_applied = apply_markdown(DESCRIPTION)
- gte_21_match = (
- md_applied == (
- MARKED_DOWN_gte_21 % MARKED_DOWN_HILITE) or
- md_applied == (
- MARKED_DOWN_gte_21 % MARKED_DOWN_NOT_HILITE))
- lt_21_match = (
- md_applied == (
- MARKED_DOWN_lt_21 % MARKED_DOWN_HILITE) or
- md_applied == (
- MARKED_DOWN_lt_21 % MARKED_DOWN_NOT_HILITE))
- assert gte_21_match or lt_21_match
-
-
-def test_dedent_tabs():
- result = 'first string\n\nsecond string'
- assert dedent(" first string\n\n second string") == result
- assert dedent("first string\n\n second string") == result
- assert dedent("\tfirst string\n\n\tsecond string") == result
- assert dedent("first string\n\n\tsecond string") == result
+import sys
+
+import pytest
+from django.test import TestCase
+
+from rest_framework.compat import apply_markdown
+from rest_framework.utils.formatting import dedent
+from rest_framework.views import APIView
+
+# We check that docstrings get nicely un-indented.
+DESCRIPTION = """an example docstring
+====================
+
+* list
+* list
+
+another header
+--------------
+
+ code block
+
+indented
+
+# hash style header #
+
+``` json
+[{
+ "alpha": 1,
+ "beta: "this is a string"
+}]
+```"""
+
+
+# If markdown is installed we also test it's working
+# (and that our wrapped forces '=' to h2 and '-' to h3)
+MARKDOWN_BASE = """an example docstring
+
+
+code block
+
+indented
+%s"""
+
+MARKDOWN_gte_33 = """
+[{
\
+ "alpha":\
+ 1,
\
+ "beta: "this\
+ is a \
+string"
}]\
+
+
"""
+
+MARKDOWN_lt_33 = """
+[{
\
+ "alpha":\
+ 1,
\
+ "beta: "this\
+ is a\
+ string"
}]\
+
+
+
"""
+
+
+class TestViewNamesAndDescriptions(TestCase):
+ def test_view_name_uses_class_name(self):
+ """
+ Ensure view names are based on the class name.
+ """
+ class MockView(APIView):
+ pass
+ assert MockView().get_view_name() == 'Mock'
+
+ def test_view_name_uses_name_attribute(self):
+ class MockView(APIView):
+ name = 'Foo'
+ assert MockView().get_view_name() == 'Foo'
+
+ def test_view_name_uses_suffix_attribute(self):
+ class MockView(APIView):
+ suffix = 'List'
+ assert MockView().get_view_name() == 'Mock List'
+
+ def test_view_name_preferences_name_over_suffix(self):
+ class MockView(APIView):
+ name = 'Foo'
+ suffix = 'List'
+ assert MockView().get_view_name() == 'Foo'
+
+ def test_view_description_uses_docstring(self):
+ """Ensure view descriptions are based on the docstring."""
+ class MockView(APIView):
+ """an example docstring
+ ====================
+
+ * list
+ * list
+
+ another header
+ --------------
+
+ code block
+
+ indented
+
+ # hash style header #
+
+ ``` json
+ [{
+ "alpha": 1,
+ "beta: "this is a string"
+ }]
+ ```"""
+
+ assert MockView().get_view_description() == DESCRIPTION
+
+ def test_view_description_uses_description_attribute(self):
+ class MockView(APIView):
+ description = 'Foo'
+ assert MockView().get_view_description() == 'Foo'
+
+ def test_view_description_allows_empty_description(self):
+ class MockView(APIView):
+ """Description."""
+ description = ''
+ assert MockView().get_view_description() == ''
+
+ def test_view_description_can_be_empty(self):
+ """
+ Ensure that if a view has no docstring,
+ then it's description is the empty string.
+ """
+ class MockView(APIView):
+ pass
+ assert MockView().get_view_description() == ''
+
+ def test_view_description_can_be_promise(self):
+ """
+ Ensure a view may have a docstring that is actually a lazily evaluated
+ class that can be converted to a string.
+
+ See: https://github.com/encode/django-rest-framework/issues/1708
+ """
+ # use a mock object instead of gettext_lazy to ensure that we can't end
+ # up with a test case string in our l10n catalog
+
+ class MockLazyStr:
+ def __init__(self, string):
+ self.s = string
+
+ def __str__(self):
+ return self.s
+
+ class MockView(APIView):
+ __doc__ = MockLazyStr("a gettext string")
+
+ assert MockView().get_view_description() == 'a gettext string'
+
+ @pytest.mark.skipif(not apply_markdown, reason="Markdown is not installed")
+ def test_markdown(self):
+ """
+ Ensure markdown to HTML works as expected.
+ """
+ # Markdown 3.3 is only supported on Python 3.6 and higher
+ if sys.version_info >= (3, 6):
+ assert apply_markdown(DESCRIPTION) == MARKDOWN_BASE % MARKDOWN_gte_33
+ else:
+ assert apply_markdown(DESCRIPTION) == MARKDOWN_BASE % MARKDOWN_lt_33
+
+
+def test_dedent_tabs():
+ result = 'first string\n\nsecond string'
+ assert dedent(" first string\n\n second string") == result
+ assert dedent("first string\n\n second string") == result
+ assert dedent("\tfirst string\n\n\tsecond string") == result
+ assert dedent("first string\n\n\tsecond string") == result
From a0115e66d4583905613346f65ff6cbb98092e3c4 Mon Sep 17 00:00:00 2001
From: Xavier Ordoquy
Date: Tue, 13 Oct 2020 21:30:05 +0200
Subject: [PATCH 048/303] restore the transifex configuration
This reverts commit 6957aaae8594b42cbf368d361031ce5fb9eb7246.
---
.tx/config | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 .tx/config
diff --git a/.tx/config b/.tx/config
new file mode 100644
index 000000000..e151a7e6f
--- /dev/null
+++ b/.tx/config
@@ -0,0 +1,9 @@
+[main]
+host = https://www.transifex.com
+lang_map = sr@latin:sr_Latn, zh-Hans:zh_Hans, zh-Hant:zh_Hant
+
+[django-rest-framework.djangopo]
+file_filter = rest_framework/locale//LC_MESSAGES/django.po
+source_file = rest_framework/locale/en_US/LC_MESSAGES/django.po
+source_lang = en_US
+type = PO
From 47cfbdac97c8bafd88a1eea796301e61f9bb9f36 Mon Sep 17 00:00:00 2001
From: Xavier Ordoquy
Date: Tue, 13 Oct 2020 21:40:33 +0200
Subject: [PATCH 049/303] Translations updated from transifex and compiled
---
.../locale/ar/LC_MESSAGES/django.mo | Bin 6525 -> 12150 bytes
.../locale/ar/LC_MESSAGES/django.po | 536 ++++++++++------
.../locale/az/LC_MESSAGES/django.mo | Bin 0 -> 10428 bytes
.../locale/az/LC_MESSAGES/django.po | 573 +++++++++++++++++
.../locale/bg/LC_MESSAGES/django.mo | Bin 0 -> 13083 bytes
.../locale/bg/LC_MESSAGES/django.po | 572 +++++++++++++++++
.../locale/ca/LC_MESSAGES/django.mo | Bin 9605 -> 9300 bytes
.../locale/ca/LC_MESSAGES/django.po | 379 ++++++++----
.../locale/cs/LC_MESSAGES/django.mo | Bin 8990 -> 10519 bytes
.../locale/cs/LC_MESSAGES/django.po | 427 ++++++++-----
.../locale/da/LC_MESSAGES/django.mo | Bin 10360 -> 9955 bytes
.../locale/da/LC_MESSAGES/django.po | 387 ++++++++----
.../locale/de/LC_MESSAGES/django.mo | Bin 10885 -> 10490 bytes
.../locale/de/LC_MESSAGES/django.po | 389 ++++++++----
.../locale/el/LC_MESSAGES/django.mo | Bin 13466 -> 12933 bytes
.../locale/el/LC_MESSAGES/django.po | 395 ++++++++----
.../locale/en/LC_MESSAGES/django.mo | Bin 10185 -> 12285 bytes
.../locale/en/LC_MESSAGES/django.po | 387 ++++++++----
.../locale/en_US/LC_MESSAGES/django.po | 371 +++++++----
.../locale/es/LC_MESSAGES/django.mo | Bin 11023 -> 10627 bytes
.../locale/es/LC_MESSAGES/django.po | 392 ++++++++----
.../locale/et/LC_MESSAGES/django.mo | Bin 8691 -> 10096 bytes
.../locale/et/LC_MESSAGES/django.po | 428 ++++++++-----
.../locale/fa/LC_MESSAGES/django.mo | Bin 466 -> 11976 bytes
.../locale/fa/LC_MESSAGES/django.po | 549 ++++++++++-------
.../locale/fa_IR/LC_MESSAGES/django.mo | Bin 479 -> 11989 bytes
.../locale/fa_IR/LC_MESSAGES/django.po | 549 ++++++++++-------
.../locale/fi/LC_MESSAGES/django.mo | Bin 10508 -> 10197 bytes
.../locale/fi/LC_MESSAGES/django.po | 414 ++++++++-----
.../locale/fr/LC_MESSAGES/django.mo | Bin 10973 -> 10662 bytes
.../locale/fr/LC_MESSAGES/django.po | 447 +++++++++-----
.../locale/hu/LC_MESSAGES/django.mo | Bin 9082 -> 10844 bytes
.../locale/hu/LC_MESSAGES/django.po | 437 ++++++++-----
.../locale/hy/LC_MESSAGES/django.mo | Bin 0 -> 12885 bytes
.../locale/hy/LC_MESSAGES/django.po | 573 +++++++++++++++++
.../locale/id/LC_MESSAGES/django.mo | Bin 469 -> 5188 bytes
.../locale/id/LC_MESSAGES/django.po | 461 +++++++++-----
.../locale/it/LC_MESSAGES/django.mo | Bin 10318 -> 10480 bytes
.../locale/it/LC_MESSAGES/django.po | 417 ++++++++-----
.../locale/ja/LC_MESSAGES/django.mo | Bin 12254 -> 11759 bytes
.../locale/ja/LC_MESSAGES/django.po | 387 ++++++++----
.../locale/ko_KR/LC_MESSAGES/django.mo | Bin 11774 -> 11698 bytes
.../locale/ko_KR/LC_MESSAGES/django.po | 400 ++++++++----
.../locale/lt/LC_MESSAGES/django.mo | Bin 0 -> 5056 bytes
.../locale/lt/LC_MESSAGES/django.po | 573 +++++++++++++++++
.../locale/lv/LC_MESSAGES/django.mo | Bin 10800 -> 10423 bytes
.../locale/lv/LC_MESSAGES/django.po | 387 ++++++++----
.../locale/mk/LC_MESSAGES/django.mo | Bin 12619 -> 12121 bytes
.../locale/mk/LC_MESSAGES/django.po | 387 ++++++++----
.../locale/nb/LC_MESSAGES/django.mo | Bin 10317 -> 9928 bytes
.../locale/nb/LC_MESSAGES/django.po | 387 ++++++++----
.../locale/ne_NP/LC_MESSAGES/django.mo | Bin 0 -> 15636 bytes
.../locale/ne_NP/LC_MESSAGES/django.po | 574 ++++++++++++++++++
.../locale/nl/LC_MESSAGES/django.mo | Bin 10567 -> 10163 bytes
.../locale/nl/LC_MESSAGES/django.po | 390 ++++++++----
.../locale/pl/LC_MESSAGES/django.mo | Bin 11080 -> 10673 bytes
.../locale/pl/LC_MESSAGES/django.po | 389 ++++++++----
.../locale/pt/LC_MESSAGES/django.mo | Bin 476 -> 10382 bytes
.../locale/pt/LC_MESSAGES/django.po | 546 ++++++++++-------
.../locale/pt_BR/LC_MESSAGES/django.mo | Bin 10804 -> 10397 bytes
.../locale/pt_BR/LC_MESSAGES/django.po | 390 ++++++++----
.../locale/ro/LC_MESSAGES/django.mo | Bin 10854 -> 10701 bytes
.../locale/ro/LC_MESSAGES/django.po | 398 ++++++++----
.../locale/ru/LC_MESSAGES/django.mo | Bin 13400 -> 13160 bytes
.../locale/ru/LC_MESSAGES/django.po | 393 ++++++++----
.../locale/ru_RU/LC_MESSAGES/django.mo | Bin 0 -> 5208 bytes
.../locale/ru_RU/LC_MESSAGES/django.po | 573 +++++++++++++++++
.../locale/sk/LC_MESSAGES/django.mo | Bin 9370 -> 9164 bytes
.../locale/sk/LC_MESSAGES/django.po | 381 ++++++++----
.../locale/sl/LC_MESSAGES/django.mo | Bin 10349 -> 9985 bytes
.../locale/sl/LC_MESSAGES/django.po | 387 ++++++++----
.../locale/sv/LC_MESSAGES/django.mo | Bin 10584 -> 10204 bytes
.../locale/sv/LC_MESSAGES/django.po | 387 ++++++++----
.../locale/th/LC_MESSAGES/django.mo | Bin 0 -> 8880 bytes
.../locale/th/LC_MESSAGES/django.po | 573 +++++++++++++++++
.../locale/tr/LC_MESSAGES/django.mo | Bin 10509 -> 10073 bytes
.../locale/tr/LC_MESSAGES/django.po | 389 ++++++++----
.../locale/tr_TR/LC_MESSAGES/django.mo | Bin 10481 -> 10292 bytes
.../locale/tr_TR/LC_MESSAGES/django.po | 396 ++++++++----
.../locale/uk/LC_MESSAGES/django.mo | Bin 13437 -> 13245 bytes
.../locale/uk/LC_MESSAGES/django.po | 411 ++++++++-----
.../locale/vi/LC_MESSAGES/django.mo | Bin 469 -> 2179 bytes
.../locale/vi/LC_MESSAGES/django.po | 413 ++++++++-----
.../locale/zh_CN/LC_MESSAGES/django.mo | Bin 10301 -> 9915 bytes
.../locale/zh_CN/LC_MESSAGES/django.po | 387 ++++++++----
.../locale/zh_Hans/LC_MESSAGES/django.mo | Bin 10321 -> 9938 bytes
.../locale/zh_Hans/LC_MESSAGES/django.po | 389 ++++++++----
.../locale/zh_Hant/LC_MESSAGES/django.mo | Bin 488 -> 4809 bytes
.../locale/zh_Hant/LC_MESSAGES/django.po | 470 +++++++++-----
89 files changed, 14471 insertions(+), 5369 deletions(-)
create mode 100644 rest_framework/locale/az/LC_MESSAGES/django.mo
create mode 100644 rest_framework/locale/az/LC_MESSAGES/django.po
create mode 100644 rest_framework/locale/bg/LC_MESSAGES/django.mo
create mode 100644 rest_framework/locale/bg/LC_MESSAGES/django.po
create mode 100644 rest_framework/locale/hy/LC_MESSAGES/django.mo
create mode 100644 rest_framework/locale/hy/LC_MESSAGES/django.po
create mode 100644 rest_framework/locale/lt/LC_MESSAGES/django.mo
create mode 100644 rest_framework/locale/lt/LC_MESSAGES/django.po
create mode 100644 rest_framework/locale/ne_NP/LC_MESSAGES/django.mo
create mode 100644 rest_framework/locale/ne_NP/LC_MESSAGES/django.po
create mode 100644 rest_framework/locale/ru_RU/LC_MESSAGES/django.mo
create mode 100644 rest_framework/locale/ru_RU/LC_MESSAGES/django.po
create mode 100644 rest_framework/locale/th/LC_MESSAGES/django.mo
create mode 100644 rest_framework/locale/th/LC_MESSAGES/django.po
diff --git a/rest_framework/locale/ar/LC_MESSAGES/django.mo b/rest_framework/locale/ar/LC_MESSAGES/django.mo
index 19a41dfd6e6ccc0c4ebc3b5cce8325eecd65fb65..f793d7c73b74b37eee04252018de72e7505a305d 100644
GIT binary patch
literal 12150
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
B_B#Lo
delta 1987
zcmY+^TWnNC7{KvaC{QZ5mP;>`GA+GOx@>o=m9`WE5-jTS_w3&t!#`G(j4RqYj#`eAX-e(HU#4@SbJfgc8i(*A$|vez&q;>sPy*Y6R8zf;h_X1_IAlVpLN|x3hcz6ULo%1FrtE#F8M+Ei;OUdVQmCTl=iGe<9nY#(W|4OPQ2Cw3s68fwr^
zQpe&c9o7fK15QNu#p46fa4cX}mNu9JrKj_)vwc33D67xkDeLl?_?%bgZg!#r5xvhD
zNJSG#6Pp_{BlEYI(FOkLTcbzJ>GI?4v8dX{ocrSGSR|mHNJOFuC-$7$9!@3?#S;;=
zJ(?JFl1V2X(~)S*G4E8Yn7K3EH|V6y+J&dgxkZ!9lF@|P#YuSseOY_x;I?t1;Wx>-ye^~
zqW!6GB$+R%eBM{7-51@L-BEj9yQA()_EYKF^8vG`YLoe<>U!Q*`+YmyF>S9hp40Y{
z&2o`-$Nbt(*qr^)X6(D#p0#;2i#$NCy%M#@&cf@Se
zYt3gXj+)P#2HJQD8Jr-BjLj+%8L<;nQ8PoHo2P7+pxrU^eapDHwsN^CZ*4Ty?X|rD
zMJ`$Ufs#, 2017
# Bashar Al-Abdulhadi, 2016-2017
# Eyad Toma , 2015,2017
+# zak zak , 2020
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-07-12 16:13+0100\n"
-"PO-Revision-Date: 2017-10-18 09:51+0000\n"
-"Last-Translator: Andrew Ayoub \n"
+"POT-Creation-Date: 2020-10-13 21:45+0200\n"
+"PO-Revision-Date: 2020-10-13 19:45+0000\n"
+"Last-Translator: Xavier Ordoquy \n"
"Language-Team: Arabic (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ar/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -21,40 +22,40 @@ msgstr ""
"Language: ar\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
-#: authentication.py:73
+#: authentication.py:70
msgid "Invalid basic header. No credentials provided."
-msgstr ""
+msgstr "رأس أساسي غير صالح, لم تقدم اي بيانات."
-#: authentication.py:76
+#: authentication.py:73
msgid "Invalid basic header. Credentials string should not contain spaces."
-msgstr ""
+msgstr "رأس أساسي غير صالح, سلسلة البيانات لا يجب أن تحتوي على أي أحرف مسافات"
-#: authentication.py:82
+#: authentication.py:83
msgid "Invalid basic header. Credentials not correctly base64 encoded."
-msgstr ""
+msgstr "رأس أساسي غير صالح, البيانات ليست مرمّزة بصحة على أساس64."
-#: authentication.py:99
+#: authentication.py:101
msgid "Invalid username/password."
msgstr "اسم المستخدم/كلمة السر غير صحيحين."
-#: authentication.py:102 authentication.py:198
+#: authentication.py:104 authentication.py:206
msgid "User inactive or deleted."
msgstr "المستخدم غير مفعل او تم حذفه."
-#: authentication.py:176
+#: authentication.py:184
msgid "Invalid token header. No credentials provided."
-msgstr ""
+msgstr "رمز الراْس المميّز غير صالح, لم تقدم أي بيانات."
-#: authentication.py:179
+#: authentication.py:187
msgid "Invalid token header. Token string should not contain spaces."
-msgstr ""
+msgstr "رمز الراْس المميّز غير صالح, سلسلة الرمز المميّز لا يجب أن تحتوي على أي أحرف مسافات."
-#: authentication.py:185
+#: authentication.py:193
msgid ""
"Invalid token header. Token string should not contain invalid characters."
-msgstr ""
+msgstr "رمز الراْس المميّز غير صالح, سلسلة الرمز المميّز لا يجب أن تحتوي على أي أحرف غير صالحة."
-#: authentication.py:195
+#: authentication.py:203
msgid "Invalid token."
msgstr "رمز غير صحيح."
@@ -62,382 +63,515 @@ msgstr "رمز غير صحيح."
msgid "Auth Token"
msgstr "رمز التفويض"
-#: authtoken/models.py:15
+#: authtoken/models.py:13
msgid "Key"
msgstr "المفتاح"
-#: authtoken/models.py:18
+#: authtoken/models.py:16
msgid "User"
msgstr "المستخدم"
-#: authtoken/models.py:20
+#: authtoken/models.py:18
msgid "Created"
msgstr "أنشئ"
-#: authtoken/models.py:29
+#: authtoken/models.py:27 authtoken/serializers.py:19
msgid "Token"
msgstr "الرمز"
-#: authtoken/models.py:30
+#: authtoken/models.py:28
msgid "Tokens"
msgstr "الرموز"
-#: authtoken/serializers.py:8
+#: authtoken/serializers.py:9
msgid "Username"
msgstr "اسم المستخدم"
-#: authtoken/serializers.py:9
+#: authtoken/serializers.py:13
msgid "Password"
msgstr "كلمة المرور"
-#: authtoken/serializers.py:20
-msgid "User account is disabled."
-msgstr "حساب المستخدم غير مفعل."
-
-#: authtoken/serializers.py:23
+#: authtoken/serializers.py:35
msgid "Unable to log in with provided credentials."
msgstr "تعذر تسجيل الدخول بالبيانات التي ادخلتها."
-#: authtoken/serializers.py:26
+#: authtoken/serializers.py:38
msgid "Must include \"username\" and \"password\"."
msgstr "يجب أن تتضمن \"اسم المستخدم\" و \"كلمة المرور\"."
-#: exceptions.py:49
+#: exceptions.py:102
msgid "A server error occurred."
msgstr "حدث خطأ في المخدم."
-#: exceptions.py:84
-msgid "Malformed request."
+#: exceptions.py:142
+msgid "Invalid input."
msgstr ""
-#: exceptions.py:89
+#: exceptions.py:161
+msgid "Malformed request."
+msgstr "الطلب صيغ بشكل سيء."
+
+#: exceptions.py:167
msgid "Incorrect authentication credentials."
msgstr "بيانات الدخول غير صحيحة."
-#: exceptions.py:94
+#: exceptions.py:173
msgid "Authentication credentials were not provided."
msgstr "لم يتم تزويد بيانات الدخول."
-#: exceptions.py:99
+#: exceptions.py:179
msgid "You do not have permission to perform this action."
msgstr "ليس لديك صلاحية للقيام بهذا الإجراء."
-#: exceptions.py:104 views.py:81
+#: exceptions.py:185
msgid "Not found."
msgstr "غير موجود."
-#: exceptions.py:109
+#: exceptions.py:191
+#, python-brace-format
msgid "Method \"{method}\" not allowed."
-msgstr "طلب غير مسموح به"
+msgstr "الطريقة \"{method}\" غير مسموح بها."
-#: exceptions.py:120
+#: exceptions.py:202
msgid "Could not satisfy the request Accept header."
-msgstr ""
+msgstr "لم نتمكن من تلبية الرٱس Accept في الطلب."
-#: exceptions.py:132
+#: exceptions.py:212
+#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request."
-msgstr ""
+msgstr "الوسيط \"{media_type}\" الموجود في الطلب غير معتمد."
-#: exceptions.py:145
+#: exceptions.py:223
msgid "Request was throttled."
+msgstr "تم تقييد الطلب."
+
+#: exceptions.py:224
+#, python-brace-format
+msgid "Expected available in {wait} second."
msgstr ""
-#: fields.py:269 relations.py:206 relations.py:239 validators.py:98
-#: validators.py:181
+#: exceptions.py:225
+#, python-brace-format
+msgid "Expected available in {wait} seconds."
+msgstr ""
+
+#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
+#: validators.py:183
msgid "This field is required."
msgstr "هذا الحقل مطلوب."
-#: fields.py:270
+#: fields.py:317
msgid "This field may not be null."
msgstr "لا يمكن لهذا الحقل ان يكون فارغاً null."
-#: fields.py:608 fields.py:639
-msgid "\"{input}\" is not a valid boolean."
-msgstr "\"{input}\" ليس قيمة منطقية."
+#: fields.py:701
+msgid "Must be a valid boolean."
+msgstr ""
-#: fields.py:674
+#: fields.py:766
+msgid "Not a valid string."
+msgstr ""
+
+#: fields.py:767
msgid "This field may not be blank."
msgstr "لا يمكن لهذا الحقل ان يكون فارغاً."
-#: fields.py:675 fields.py:1675
+#: fields.py:768 fields.py:1881
+#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters."
msgstr "تأكد ان الحقل لا يزيد عن {max_length} محرف."
-#: fields.py:676
+#: fields.py:769
+#, python-brace-format
msgid "Ensure this field has at least {min_length} characters."
msgstr "تأكد ان الحقل {min_length} محرف على الاقل."
-#: fields.py:713
+#: fields.py:816
msgid "Enter a valid email address."
msgstr "عليك ان تدخل بريد إلكتروني صالح."
-#: fields.py:724
+#: fields.py:827
msgid "This value does not match the required pattern."
msgstr "هذه القيمة لا تطابق النمط المطلوب."
-#: fields.py:735
+#: fields.py:838
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
+msgstr "أدخل \"slug\" صالح يحتوي على حروف، أرقام، شُرط سفلية أو واصلات."
+
+#: fields.py:839
+msgid ""
+"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
+"or hyphens."
msgstr ""
-#: fields.py:747
+#: fields.py:854
msgid "Enter a valid URL."
msgstr "الرجاء إدخال رابط إلكتروني صالح."
-#: fields.py:760
-msgid "\"{value}\" is not a valid UUID."
+#: fields.py:867
+msgid "Must be a valid UUID."
msgstr ""
-#: fields.py:796
+#: fields.py:903
msgid "Enter a valid IPv4 or IPv6 address."
-msgstr "برجاء إدخال عنوان IPV4 أو IPV6 صحيح"
+msgstr "أدخِل عنوان IPV4 أو IPV6 صحيح."
-#: fields.py:821
+#: fields.py:931
msgid "A valid integer is required."
msgstr "الرجاء إدخال رقم صحيح صالح."
-#: fields.py:822 fields.py:857 fields.py:891
+#: fields.py:932 fields.py:969 fields.py:1005 fields.py:1366
+#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "تأكد ان القيمة أقل أو تساوي {max_value}."
-#: fields.py:823 fields.py:858 fields.py:892
+#: fields.py:933 fields.py:970 fields.py:1006 fields.py:1367
+#, python-brace-format
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "تأكد ان القيمة أكبر أو تساوي {min_value}."
-#: fields.py:824 fields.py:859 fields.py:896
+#: fields.py:934 fields.py:971 fields.py:1010
msgid "String value too large."
-msgstr "القيمه اكبر من المسموح"
+msgstr "السلسلة اطول من القيمة المسموح بها."
-#: fields.py:856 fields.py:890
+#: fields.py:968 fields.py:1004
msgid "A valid number is required."
msgstr "الرجاء إدخال رقم صالح."
-#: fields.py:893
+#: fields.py:1007
+#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "تأكد ان القيمة لا تحوي أكثر من {max_digits} رقم."
-#: fields.py:894
+#: fields.py:1008
+#, python-brace-format
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
-msgstr ""
+msgstr "تأكد انه لا يوجد اكثر من {max_decimal_places} منازل عشرية."
-#: fields.py:895
+#: fields.py:1009
+#, python-brace-format
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
-msgstr ""
+msgstr "تأكد انه لا يوجد اكثر من {max_whole_digits} أرقام قبل النقطة العشرية."
-#: fields.py:1025
+#: fields.py:1148
+#, python-brace-format
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "صيغة التاريخ و الوقت غير صحيحة. عليك أن تستخدم واحدة من هذه الصيغ التالية: {format}."
-#: fields.py:1026
+#: fields.py:1149
msgid "Expected a datetime but got a date."
msgstr "متوقع تاريخ و وقت و وجد تاريخ فقط"
-#: fields.py:1103
+#: fields.py:1150
+#, python-brace-format
+msgid "Invalid datetime for the timezone \"{timezone}\"."
+msgstr ""
+
+#: fields.py:1151
+msgid "Datetime value out of range."
+msgstr ""
+
+#: fields.py:1236
+#, python-brace-format
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "صيغة التاريخ غير صحيحة. عليك أن تستخدم واحدة من هذه الصيغ التالية: {format}."
-#: fields.py:1104
+#: fields.py:1237
msgid "Expected a date but got a datetime."
msgstr "متوقع تاريخ فقط و وجد تاريخ ووقت"
-#: fields.py:1170
+#: fields.py:1303
+#, python-brace-format
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "صيغة الوقت غير صحيحة. عليك أن تستخدم واحدة من هذه الصيغ التالية: {format}."
-#: fields.py:1232
+#: fields.py:1365
+#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}."
-msgstr "صيغة المده غير صحيحه, برجاء إستخدام أحد هذه الصيغ {format}"
+msgstr "صيغة المدة غير صحيحه, يرجى إستخدام إحدى هذه الصيغ: {format}."
-#: fields.py:1251 fields.py:1300
+#: fields.py:1399 fields.py:1456
+#, python-brace-format
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" ليست واحدة من الخيارات الصالحة."
-#: fields.py:1254 relations.py:71 relations.py:441
+#: fields.py:1402
+#, python-brace-format
msgid "More than {count} items..."
msgstr "أكثر من {count} عنصر..."
-#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524
+#: fields.py:1457 fields.py:1603 relations.py:485 serializers.py:570
+#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"."
-msgstr ""
+msgstr "المتوقع وجود قائمة عناصر لكن وجد النوع \"{input_type}\"."
-#: fields.py:1302
+#: fields.py:1458
msgid "This selection may not be empty."
-msgstr ""
+msgstr "هذا التحديد لا يجب أن يكون فارغا."
-#: fields.py:1339
+#: fields.py:1495
+#, python-brace-format
msgid "\"{input}\" is not a valid path choice."
-msgstr ""
+msgstr "{input} كإختيار مسار غير صالح."
-#: fields.py:1358
+#: fields.py:1514
msgid "No file was submitted."
msgstr "لم يتم إرسال أي ملف."
-#: fields.py:1359
+#: fields.py:1515
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
-msgstr ""
+msgstr "المعطيات المرسولة ليست ملف. إفحص نوع الترميز في النموذج."
-#: fields.py:1360
+#: fields.py:1516
msgid "No filename could be determined."
-msgstr ""
+msgstr "ما من إسم ملف أمكن تحديده."
-#: fields.py:1361
+#: fields.py:1517
msgid "The submitted file is empty."
msgstr "الملف الذي تم إرساله فارغ."
-#: fields.py:1362
+#: fields.py:1518
+#, python-brace-format
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "تأكد ان اسم الملف لا يحوي أكثر من {max_length} محرف (الإسم المرسل يحوي {length} محرف)."
-#: fields.py:1410
+#: fields.py:1566
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
-msgstr ""
+msgstr "الرجاء تحميل صورة صالحة. الملف الذي تم تحميله إما لم يكن صورة او انه كان صورة تالفة."
-#: fields.py:1449 relations.py:438 serializers.py:525
+#: fields.py:1604 relations.py:486 serializers.py:571
msgid "This list may not be empty."
+msgstr "القائمة يجب أن لا تكون فارغة."
+
+#: fields.py:1605
+#, python-brace-format
+msgid "Ensure this field has at least {min_length} elements."
msgstr ""
-#: fields.py:1502
+#: fields.py:1606
+#, python-brace-format
+msgid "Ensure this field has no more than {max_length} elements."
+msgstr ""
+
+#: fields.py:1682
+#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"."
+msgstr "المتوقع كان قاموس عناصر و لكن النوع المتحصل عليه هو \"{input_type}\"."
+
+#: fields.py:1683
+msgid "This dictionary may not be empty."
msgstr ""
-#: fields.py:1549
+#: fields.py:1755
msgid "Value must be valid JSON."
-msgstr ""
+msgstr "القيمة يجب أن تكون JSON صالح."
-#: filters.py:36 templates/rest_framework/filters/django_filter.html:5
-msgid "Submit"
-msgstr "أرسل"
-
-#: filters.py:336
-msgid "ascending"
-msgstr "تصاعدي"
-
-#: filters.py:337
-msgid "descending"
-msgstr "تنازلي"
-
-#: pagination.py:193
-msgid "Invalid page."
-msgstr "صفحة غير صحيحة."
-
-#: pagination.py:427
-msgid "Invalid cursor"
-msgstr ""
-
-#: relations.py:207
-msgid "Invalid pk \"{pk_value}\" - object does not exist."
-msgstr "معرف العنصر \"{pk_value}\" غير صالح - العنصر غير موجود."
-
-#: relations.py:208
-msgid "Incorrect type. Expected pk value, received {data_type}."
-msgstr ""
-
-#: relations.py:240
-msgid "Invalid hyperlink - No URL match."
-msgstr ""
-
-#: relations.py:241
-msgid "Invalid hyperlink - Incorrect URL match."
-msgstr ""
-
-#: relations.py:242
-msgid "Invalid hyperlink - Object does not exist."
-msgstr ""
-
-#: relations.py:243
-msgid "Incorrect type. Expected URL string, received {data_type}."
-msgstr ""
-
-#: relations.py:401
-msgid "Object with {slug_name}={value} does not exist."
-msgstr ""
-
-#: relations.py:402
-msgid "Invalid value."
-msgstr "قيمة غير صالحة."
-
-#: serializers.py:326
-msgid "Invalid data. Expected a dictionary, but got {datatype}."
-msgstr ""
-
-#: templates/rest_framework/admin.html:116
-#: templates/rest_framework/base.html:128
-msgid "Filters"
-msgstr "مرشحات"
-
-#: templates/rest_framework/filters/django_filter.html:2
-#: templates/rest_framework/filters/django_filter_crispyforms.html:4
-msgid "Field filters"
-msgstr "مرشحات الحقول"
-
-#: templates/rest_framework/filters/ordering.html:3
-msgid "Ordering"
-msgstr "الترتيب"
-
-#: templates/rest_framework/filters/search.html:2
+#: filters.py:49 templates/rest_framework/filters/search.html:2
msgid "Search"
msgstr "بحث"
-#: templates/rest_framework/horizontal/radio.html:2
-#: templates/rest_framework/inline/radio.html:2
-#: templates/rest_framework/vertical/radio.html:2
+#: filters.py:50
+msgid "A search term."
+msgstr ""
+
+#: filters.py:180 templates/rest_framework/filters/ordering.html:3
+msgid "Ordering"
+msgstr "الترتيب"
+
+#: filters.py:181
+msgid "Which field to use when ordering the results."
+msgstr ""
+
+#: filters.py:287
+msgid "ascending"
+msgstr "تصاعدي"
+
+#: filters.py:288
+msgid "descending"
+msgstr "تنازلي"
+
+#: pagination.py:174
+msgid "A page number within the paginated result set."
+msgstr ""
+
+#: pagination.py:179 pagination.py:372 pagination.py:590
+msgid "Number of results to return per page."
+msgstr ""
+
+#: pagination.py:189
+msgid "Invalid page."
+msgstr "صفحة غير صحيحة."
+
+#: pagination.py:374
+msgid "The initial index from which to return the results."
+msgstr ""
+
+#: pagination.py:581
+msgid "The pagination cursor value."
+msgstr ""
+
+#: pagination.py:583
+msgid "Invalid cursor"
+msgstr "مؤشر غير صالح"
+
+#: relations.py:246
+#, python-brace-format
+msgid "Invalid pk \"{pk_value}\" - object does not exist."
+msgstr "معرف العنصر \"{pk_value}\" غير صالح - العنصر غير موجود."
+
+#: relations.py:247
+#, python-brace-format
+msgid "Incorrect type. Expected pk value, received {data_type}."
+msgstr "نوع خاطئ. المتوقع قيمة من pk، لكن المتحصل عليه {data_type}."
+
+#: relations.py:280
+msgid "Invalid hyperlink - No URL match."
+msgstr "إرتباط تشعبي غير صالح - لا مطابقة لURL."
+
+#: relations.py:281
+msgid "Invalid hyperlink - Incorrect URL match."
+msgstr "إرتباط تشعبي غير صالح - مطابقة خاطئة لURL."
+
+#: relations.py:282
+msgid "Invalid hyperlink - Object does not exist."
+msgstr "إرتباط تشعبي غير صالح - عنصر غير موجود."
+
+#: relations.py:283
+#, python-brace-format
+msgid "Incorrect type. Expected URL string, received {data_type}."
+msgstr "نوع خاطئ. المتوقع سلسلة URL، لكن المتحصل عليه {data_type}."
+
+#: relations.py:448
+#, python-brace-format
+msgid "Object with {slug_name}={value} does not exist."
+msgstr "عنصر ب {slug_name}={value} غير موجود."
+
+#: relations.py:449
+msgid "Invalid value."
+msgstr "قيمة غير صالحة."
+
+#: schemas/utils.py:32
+msgid "unique integer value"
+msgstr ""
+
+#: schemas/utils.py:34
+msgid "UUID string"
+msgstr ""
+
+#: schemas/utils.py:36
+msgid "unique value"
+msgstr ""
+
+#: schemas/utils.py:38
+#, python-brace-format
+msgid "A {value_type} identifying this {name}."
+msgstr ""
+
+#: serializers.py:337
+#, python-brace-format
+msgid "Invalid data. Expected a dictionary, but got {datatype}."
+msgstr "معطيات غير صالحة. المتوقع هو قاموس، لكن المتحصل عليه {datatype}."
+
+#: templates/rest_framework/admin.html:116
+#: templates/rest_framework/base.html:136
+msgid "Extra Actions"
+msgstr ""
+
+#: templates/rest_framework/admin.html:130
+#: templates/rest_framework/base.html:150
+msgid "Filters"
+msgstr "مرشحات"
+
+#: templates/rest_framework/base.html:37
+msgid "navbar"
+msgstr ""
+
+#: templates/rest_framework/base.html:75
+msgid "content"
+msgstr ""
+
+#: templates/rest_framework/base.html:78
+msgid "request form"
+msgstr ""
+
+#: templates/rest_framework/base.html:157
+msgid "main content"
+msgstr ""
+
+#: templates/rest_framework/base.html:173
+msgid "request info"
+msgstr ""
+
+#: templates/rest_framework/base.html:177
+msgid "response info"
+msgstr ""
+
+#: templates/rest_framework/horizontal/radio.html:4
+#: templates/rest_framework/inline/radio.html:3
+#: templates/rest_framework/vertical/radio.html:3
msgid "None"
msgstr "لا شيء"
-#: templates/rest_framework/horizontal/select_multiple.html:2
-#: templates/rest_framework/inline/select_multiple.html:2
-#: templates/rest_framework/vertical/select_multiple.html:2
+#: templates/rest_framework/horizontal/select_multiple.html:4
+#: templates/rest_framework/inline/select_multiple.html:3
+#: templates/rest_framework/vertical/select_multiple.html:3
msgid "No items to select."
-msgstr ""
+msgstr "ما من عناصر للتحديد."
-#: validators.py:43
+#: validators.py:39
msgid "This field must be unique."
msgstr "هذا الحقل يجب أن يكون فريد"
-#: validators.py:97
+#: validators.py:89
+#, python-brace-format
msgid "The fields {field_names} must make a unique set."
+msgstr "الحقول {field_names} يجب أن تشكل مجموعة فريدة."
+
+#: validators.py:171
+#, python-brace-format
+msgid "Surrogate characters are not allowed: U+{code_point:X}."
msgstr ""
-#: validators.py:245
+#: validators.py:243
+#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date."
-msgstr ""
+msgstr "الحقل يجب ان يكون فريد للتاريخ {date_field}."
-#: validators.py:260
+#: validators.py:258
+#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month."
-msgstr ""
+msgstr "الحقل يجب ان يكون فريد للشهر {date_field}."
-#: validators.py:273
+#: validators.py:271
+#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year."
-msgstr ""
+msgstr "الحقل يجب ان يكون فريد للعام {date_field}."
-#: versioning.py:42
+#: versioning.py:40
msgid "Invalid version in \"Accept\" header."
-msgstr ""
+msgstr "إصدار غير صالح في الرٱس \"Accept\"."
-#: versioning.py:73
+#: versioning.py:71
msgid "Invalid version in URL path."
-msgstr ""
+msgstr "إصدار غير صالح في المسار URL."
-#: versioning.py:115
+#: versioning.py:116
msgid "Invalid version in URL path. Does not match any version namespace."
-msgstr ""
+msgstr " إصدار غير صالح في المسار URL. لا يطابق أي إصدار من مساحة الإسم."
-#: versioning.py:147
+#: versioning.py:148
msgid "Invalid version in hostname."
-msgstr ""
+msgstr "إصدار غير صالح في اسم المضيف."
-#: versioning.py:169
+#: versioning.py:170
msgid "Invalid version in query parameter."
-msgstr ""
-
-#: views.py:88
-msgid "Permission denied."
-msgstr "ليس لديك صلاحية."
+msgstr "إصدار غير صالح في معلمة الإستعلام."
diff --git a/rest_framework/locale/az/LC_MESSAGES/django.mo b/rest_framework/locale/az/LC_MESSAGES/django.mo
new file mode 100644
index 0000000000000000000000000000000000000000..08648351e8ed72361f7148ed5f2e38e412dea206
GIT binary patch
literal 10428
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
OM2QKMStIZS>i+>T@S}zR
literal 0
HcmV?d00001
diff --git a/rest_framework/locale/az/LC_MESSAGES/django.po b/rest_framework/locale/az/LC_MESSAGES/django.po
new file mode 100644
index 000000000..43a224259
--- /dev/null
+++ b/rest_framework/locale/az/LC_MESSAGES/django.po
@@ -0,0 +1,573 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Emin Mastizada , 2020
+msgid ""
+msgstr ""
+"Project-Id-Version: Django REST framework\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-10-13 21:45+0200\n"
+"PO-Revision-Date: 2020-10-13 19:45+0000\n"
+"Last-Translator: Xavier Ordoquy \n"
+"Language-Team: Azerbaijani (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/az/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: az\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: authentication.py:70
+msgid "Invalid basic header. No credentials provided."
+msgstr "Xətalı sadə başlıq. İstifadəçi məlumatları təchiz edilməyib."
+
+#: authentication.py:73
+msgid "Invalid basic header. Credentials string should not contain spaces."
+msgstr "Xətalı sadə başlıq. İstifadəçi məlumatlarında boşluq olmamalıdır."
+
+#: authentication.py:83
+msgid "Invalid basic header. Credentials not correctly base64 encoded."
+msgstr "Xətalı sadə başlıq. İstifadəçi məlumatları base64 ilə düzgün şifrələnməyib."
+
+#: authentication.py:101
+msgid "Invalid username/password."
+msgstr "Xətalı istifadəçi adı/parol."
+
+#: authentication.py:104 authentication.py:206
+msgid "User inactive or deleted."
+msgstr "İstifadəçi aktiv deyil və ya silinib."
+
+#: authentication.py:184
+msgid "Invalid token header. No credentials provided."
+msgstr "Xətalı token başlığı. İstifadəçi məlumatları təchiz edilməyib."
+
+#: authentication.py:187
+msgid "Invalid token header. Token string should not contain spaces."
+msgstr "Xətalı token başlığı. Token mətnində boşluqlar olmamalıdır."
+
+#: authentication.py:193
+msgid ""
+"Invalid token header. Token string should not contain invalid characters."
+msgstr "Xətalı token başlığı. Token mətnində xətalı simvollar olmamalıdır."
+
+#: authentication.py:203
+msgid "Invalid token."
+msgstr "Xətalı token."
+
+#: authtoken/apps.py:7
+msgid "Auth Token"
+msgstr "Təsdiqləmə Tokeni"
+
+#: authtoken/models.py:13
+msgid "Key"
+msgstr "Açar"
+
+#: authtoken/models.py:16
+msgid "User"
+msgstr "İstifadəçi"
+
+#: authtoken/models.py:18
+msgid "Created"
+msgstr "Yaradılıb"
+
+#: authtoken/models.py:27 authtoken/serializers.py:19
+msgid "Token"
+msgstr "Token"
+
+#: authtoken/models.py:28
+msgid "Tokens"
+msgstr "Tokenlər"
+
+#: authtoken/serializers.py:9
+msgid "Username"
+msgstr "İstifadəçi adı"
+
+#: authtoken/serializers.py:13
+msgid "Password"
+msgstr "Parol"
+
+#: authtoken/serializers.py:35
+msgid "Unable to log in with provided credentials."
+msgstr "Təchiz edilən istifadəçi məlumatları ilə daxil olmaq mümkün olmadı."
+
+#: authtoken/serializers.py:38
+msgid "Must include \"username\" and \"password\"."
+msgstr "Mütləq \"username\" və \"password\" olmalıdır."
+
+#: exceptions.py:102
+msgid "A server error occurred."
+msgstr "Server xətası yaşandı."
+
+#: exceptions.py:142
+msgid "Invalid input."
+msgstr ""
+
+#: exceptions.py:161
+msgid "Malformed request."
+msgstr "Qüsurlu istək."
+
+#: exceptions.py:167
+msgid "Incorrect authentication credentials."
+msgstr "Səhv təsdiqləmə məlumatları."
+
+#: exceptions.py:173
+msgid "Authentication credentials were not provided."
+msgstr "Təsdiqləmə məlumatları təchiz edilməyib."
+
+#: exceptions.py:179
+msgid "You do not have permission to perform this action."
+msgstr "Bu əməliyyat üçün icazəniz yoxdur."
+
+#: exceptions.py:185
+msgid "Not found."
+msgstr "Tapılmadı."
+
+#: exceptions.py:191
+#, python-brace-format
+msgid "Method \"{method}\" not allowed."
+msgstr "\"{method}\" yöntəminə icazə verilmir."
+
+#: exceptions.py:202
+msgid "Could not satisfy the request Accept header."
+msgstr "İstəyin Accept başlığını qane etmək mümkün olmadı."
+
+#: exceptions.py:212
+#, python-brace-format
+msgid "Unsupported media type \"{media_type}\" in request."
+msgstr "İstəkdə dəstəklənməyən \"{media_type}\" mediya növü."
+
+#: exceptions.py:223
+msgid "Request was throttled."
+msgstr "İstək nəzərə alınmadı."
+
+#: exceptions.py:224
+#, python-brace-format
+msgid "Expected available in {wait} second."
+msgstr ""
+
+#: exceptions.py:225
+#, python-brace-format
+msgid "Expected available in {wait} seconds."
+msgstr ""
+
+#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
+#: validators.py:183
+msgid "This field is required."
+msgstr "Bu sahə tələb edilir."
+
+#: fields.py:317
+msgid "This field may not be null."
+msgstr "Bu sahə null ola bilməz."
+
+#: fields.py:701
+msgid "Must be a valid boolean."
+msgstr ""
+
+#: fields.py:766
+msgid "Not a valid string."
+msgstr ""
+
+#: fields.py:767
+msgid "This field may not be blank."
+msgstr "Bu sahə boş ola bilməz."
+
+#: fields.py:768 fields.py:1881
+#, python-brace-format
+msgid "Ensure this field has no more than {max_length} characters."
+msgstr "Bu sahənin ən çox {max_length} simvolu olduğuna əmin olun."
+
+#: fields.py:769
+#, python-brace-format
+msgid "Ensure this field has at least {min_length} characters."
+msgstr "Bu sahənin ən az {min_length} simvolu olduğuna əmin olun."
+
+#: fields.py:816
+msgid "Enter a valid email address."
+msgstr "Keçərli e-poçt ünvanı daxil edin."
+
+#: fields.py:827
+msgid "This value does not match the required pattern."
+msgstr "Bu dəyər tələb edilən formaya uyğun gəlmir."
+
+#: fields.py:838
+msgid ""
+"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
+"hyphens."
+msgstr "Hərf, rəqəm, alt xətt və defislərdən ibarət keçərli \"slug\" daxil edin."
+
+#: fields.py:839
+msgid ""
+"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
+"or hyphens."
+msgstr ""
+
+#: fields.py:854
+msgid "Enter a valid URL."
+msgstr "Keçərli URL daxil edin."
+
+#: fields.py:867
+msgid "Must be a valid UUID."
+msgstr ""
+
+#: fields.py:903
+msgid "Enter a valid IPv4 or IPv6 address."
+msgstr "Keçərli IPv4 və ya IPv6 ünvanı daxil edin."
+
+#: fields.py:931
+msgid "A valid integer is required."
+msgstr "Keçərli tam ədəd tələb edilir."
+
+#: fields.py:932 fields.py:969 fields.py:1005 fields.py:1366
+#, python-brace-format
+msgid "Ensure this value is less than or equal to {max_value}."
+msgstr "Bu dəyərin uzunluğunun ən çox {max_value} olduğuna əmin olun."
+
+#: fields.py:933 fields.py:970 fields.py:1006 fields.py:1367
+#, python-brace-format
+msgid "Ensure this value is greater than or equal to {min_value}."
+msgstr "Bu dəyərin uzunluğunun ən az {min_value} olduğuna əmin olun."
+
+#: fields.py:934 fields.py:971 fields.py:1010
+msgid "String value too large."
+msgstr "Yazı dəyəri çox uzundur."
+
+#: fields.py:968 fields.py:1004
+msgid "A valid number is required."
+msgstr "Keçərli rəqəm tələb edilir."
+
+#: fields.py:1007
+#, python-brace-format
+msgid "Ensure that there are no more than {max_digits} digits in total."
+msgstr "Ən çox {max_digits} rəqəm olduğuna əmin olun."
+
+#: fields.py:1008
+#, python-brace-format
+msgid ""
+"Ensure that there are no more than {max_decimal_places} decimal places."
+msgstr "Ən çox {max_decimal_places} onluq kəsr hissəsi olduğuna əmin olun."
+
+#: fields.py:1009
+#, python-brace-format
+msgid ""
+"Ensure that there are no more than {max_whole_digits} digits before the "
+"decimal point."
+msgstr "Onluq kərsdən əvvəl ən çox {max_whole_digits} rəqəm olduğuna əmin olun."
+
+#: fields.py:1148
+#, python-brace-format
+msgid "Datetime has wrong format. Use one of these formats instead: {format}."
+msgstr "Datetime dəyəri səhvdir. Əvəzinə bu formatlardan birini işlədin: {format}."
+
+#: fields.py:1149
+msgid "Expected a datetime but got a date."
+msgstr "Datetime gözlənirdi amma date gəldi."
+
+#: fields.py:1150
+#, python-brace-format
+msgid "Invalid datetime for the timezone \"{timezone}\"."
+msgstr ""
+
+#: fields.py:1151
+msgid "Datetime value out of range."
+msgstr ""
+
+#: fields.py:1236
+#, python-brace-format
+msgid "Date has wrong format. Use one of these formats instead: {format}."
+msgstr "Date dəyəri səhvdir. Əvəzinə bu formatlardan birini işlədin: {format}."
+
+#: fields.py:1237
+msgid "Expected a date but got a datetime."
+msgstr "Date gözlənirdi amma datetime gəldi."
+
+#: fields.py:1303
+#, python-brace-format
+msgid "Time has wrong format. Use one of these formats instead: {format}."
+msgstr "Vaxt formatı səhvdir. Əvəzinə bu formatlardan birini işlədin: {format}."
+
+#: fields.py:1365
+#, python-brace-format
+msgid "Duration has wrong format. Use one of these formats instead: {format}."
+msgstr "Müddət formatı səhvdir. Əvəzinə bu formatlardan birini işlədin: {format}."
+
+#: fields.py:1399 fields.py:1456
+#, python-brace-format
+msgid "\"{input}\" is not a valid choice."
+msgstr "\"{input}\" keçərli seçim deyil."
+
+#: fields.py:1402
+#, python-brace-format
+msgid "More than {count} items..."
+msgstr "{count} elementdən daha çoxdur..."
+
+#: fields.py:1457 fields.py:1603 relations.py:485 serializers.py:570
+#, python-brace-format
+msgid "Expected a list of items but got type \"{input_type}\"."
+msgstr "Elementlər siyahısı gözlənirdi, amma \"{input_type}\" növü gəldi."
+
+#: fields.py:1458
+msgid "This selection may not be empty."
+msgstr "Bu seçim boş ola bilməz."
+
+#: fields.py:1495
+#, python-brace-format
+msgid "\"{input}\" is not a valid path choice."
+msgstr "\"{input}\" keçərli yol seçimi deyil."
+
+#: fields.py:1514
+msgid "No file was submitted."
+msgstr "Heç bir fayl göndərilmədi."
+
+#: fields.py:1515
+msgid ""
+"The submitted data was not a file. Check the encoding type on the form."
+msgstr "Göndərilən məlumat fayl deyildi. Anketin şifrələmə (encoding) növünü yoxlayın."
+
+#: fields.py:1516
+msgid "No filename could be determined."
+msgstr "Faylın adı təyin edilə bilmədi."
+
+#: fields.py:1517
+msgid "The submitted file is empty."
+msgstr "Göndərilən fayl boşdur."
+
+#: fields.py:1518
+#, python-brace-format
+msgid ""
+"Ensure this filename has at most {max_length} characters (it has {length})."
+msgstr "Fayl adının ən çox {max_length} simvoldan ibarət olduğuna əmin olun (hazırki: {length})."
+
+#: fields.py:1566
+msgid ""
+"Upload a valid image. The file you uploaded was either not an image or a "
+"corrupted image."
+msgstr "Keçərli şəkil yükləyin. Yüklədiyiniz fayl ya şəkil deyil, ya da ola bilsin zədələnib."
+
+#: fields.py:1604 relations.py:486 serializers.py:571
+msgid "This list may not be empty."
+msgstr "Bu siyahı boş ola bilməz."
+
+#: fields.py:1605
+#, python-brace-format
+msgid "Ensure this field has at least {min_length} elements."
+msgstr ""
+
+#: fields.py:1606
+#, python-brace-format
+msgid "Ensure this field has no more than {max_length} elements."
+msgstr ""
+
+#: fields.py:1682
+#, python-brace-format
+msgid "Expected a dictionary of items but got type \"{input_type}\"."
+msgstr "Elementlərin kitabçası (dictionary) gözlənirdi amma \"{input_type}\" növü gəldi."
+
+#: fields.py:1683
+msgid "This dictionary may not be empty."
+msgstr ""
+
+#: fields.py:1755
+msgid "Value must be valid JSON."
+msgstr "Dəyər keçərli JSON olmalıdır."
+
+#: filters.py:49 templates/rest_framework/filters/search.html:2
+msgid "Search"
+msgstr "Axtarış"
+
+#: filters.py:50
+msgid "A search term."
+msgstr ""
+
+#: filters.py:180 templates/rest_framework/filters/ordering.html:3
+msgid "Ordering"
+msgstr "Sıralama"
+
+#: filters.py:181
+msgid "Which field to use when ordering the results."
+msgstr ""
+
+#: filters.py:287
+msgid "ascending"
+msgstr "artan"
+
+#: filters.py:288
+msgid "descending"
+msgstr "azalan"
+
+#: pagination.py:174
+msgid "A page number within the paginated result set."
+msgstr ""
+
+#: pagination.py:179 pagination.py:372 pagination.py:590
+msgid "Number of results to return per page."
+msgstr ""
+
+#: pagination.py:189
+msgid "Invalid page."
+msgstr "Xətalı səhifə."
+
+#: pagination.py:374
+msgid "The initial index from which to return the results."
+msgstr ""
+
+#: pagination.py:581
+msgid "The pagination cursor value."
+msgstr ""
+
+#: pagination.py:583
+msgid "Invalid cursor"
+msgstr "Xətalı kursor"
+
+#: relations.py:246
+#, python-brace-format
+msgid "Invalid pk \"{pk_value}\" - object does not exist."
+msgstr "Xətalı pk \"{pk_value}\" - obyekt mövcud deyil."
+
+#: relations.py:247
+#, python-brace-format
+msgid "Incorrect type. Expected pk value, received {data_type}."
+msgstr "Xətalı növ. PK dəyəri gözlənirdi, {data_type} alındı."
+
+#: relations.py:280
+msgid "Invalid hyperlink - No URL match."
+msgstr "Xətalı hiperkeçid - Heç bir URL uyğun gəlmir."
+
+#: relations.py:281
+msgid "Invalid hyperlink - Incorrect URL match."
+msgstr "Xətalı hiperkeçid - Xətalı URL uyğunluğu."
+
+#: relations.py:282
+msgid "Invalid hyperlink - Object does not exist."
+msgstr "Xətalı hiperkeçid - Obyekt mövcud deyil."
+
+#: relations.py:283
+#, python-brace-format
+msgid "Incorrect type. Expected URL string, received {data_type}."
+msgstr "Xətalı növ. URL yazısı gözlənirdi, {data_type} gəldi."
+
+#: relations.py:448
+#, python-brace-format
+msgid "Object with {slug_name}={value} does not exist."
+msgstr "{slug_name}={value} üçün uyğun obyekt mövcud deyil."
+
+#: relations.py:449
+msgid "Invalid value."
+msgstr "Xətalı dəyər."
+
+#: schemas/utils.py:32
+msgid "unique integer value"
+msgstr ""
+
+#: schemas/utils.py:34
+msgid "UUID string"
+msgstr ""
+
+#: schemas/utils.py:36
+msgid "unique value"
+msgstr ""
+
+#: schemas/utils.py:38
+#, python-brace-format
+msgid "A {value_type} identifying this {name}."
+msgstr ""
+
+#: serializers.py:337
+#, python-brace-format
+msgid "Invalid data. Expected a dictionary, but got {datatype}."
+msgstr "Xətalı məlumat. Kitabça (dictionary) gözlənirdi, {datatype} gəldi."
+
+#: templates/rest_framework/admin.html:116
+#: templates/rest_framework/base.html:136
+msgid "Extra Actions"
+msgstr ""
+
+#: templates/rest_framework/admin.html:130
+#: templates/rest_framework/base.html:150
+msgid "Filters"
+msgstr "Filterlər"
+
+#: templates/rest_framework/base.html:37
+msgid "navbar"
+msgstr ""
+
+#: templates/rest_framework/base.html:75
+msgid "content"
+msgstr ""
+
+#: templates/rest_framework/base.html:78
+msgid "request form"
+msgstr ""
+
+#: templates/rest_framework/base.html:157
+msgid "main content"
+msgstr ""
+
+#: templates/rest_framework/base.html:173
+msgid "request info"
+msgstr ""
+
+#: templates/rest_framework/base.html:177
+msgid "response info"
+msgstr ""
+
+#: templates/rest_framework/horizontal/radio.html:4
+#: templates/rest_framework/inline/radio.html:3
+#: templates/rest_framework/vertical/radio.html:3
+msgid "None"
+msgstr "Heç nə"
+
+#: templates/rest_framework/horizontal/select_multiple.html:4
+#: templates/rest_framework/inline/select_multiple.html:3
+#: templates/rest_framework/vertical/select_multiple.html:3
+msgid "No items to select."
+msgstr "Seçiləcək element yoxdur."
+
+#: validators.py:39
+msgid "This field must be unique."
+msgstr "Bu sahə unikal olmalıdır."
+
+#: validators.py:89
+#, python-brace-format
+msgid "The fields {field_names} must make a unique set."
+msgstr "{field_names} sahələri birlikdə unikal dəst olmalıdırlar."
+
+#: validators.py:171
+#, python-brace-format
+msgid "Surrogate characters are not allowed: U+{code_point:X}."
+msgstr ""
+
+#: validators.py:243
+#, python-brace-format
+msgid "This field must be unique for the \"{date_field}\" date."
+msgstr "Bu sahə \"{date_field}\" günü üçün unikal olmalıdır."
+
+#: validators.py:258
+#, python-brace-format
+msgid "This field must be unique for the \"{date_field}\" month."
+msgstr "Bu sahə \"{date_field}\" ayı üçün unikal olmalıdır."
+
+#: validators.py:271
+#, python-brace-format
+msgid "This field must be unique for the \"{date_field}\" year."
+msgstr "Bu sahə \"{date_field}\" ili üçün unikal olmalıdır."
+
+#: versioning.py:40
+msgid "Invalid version in \"Accept\" header."
+msgstr "\"Accept\" başlığında xətalı versiya."
+
+#: versioning.py:71
+msgid "Invalid version in URL path."
+msgstr "URL yolunda xətalı versiya."
+
+#: versioning.py:116
+msgid "Invalid version in URL path. Does not match any version namespace."
+msgstr "URL yolunda xətalı versiya. Heç bir versiya namespace-inə uyğun gəlmir."
+
+#: versioning.py:148
+msgid "Invalid version in hostname."
+msgstr "Hostname-də xətalı versiya."
+
+#: versioning.py:170
+msgid "Invalid version in query parameter."
+msgstr "Sorğu parametrində xətalı versiya."
diff --git a/rest_framework/locale/bg/LC_MESSAGES/django.mo b/rest_framework/locale/bg/LC_MESSAGES/django.mo
new file mode 100644
index 0000000000000000000000000000000000000000..71a0d8a74863adbce4792dd85f71dd318fa09d8c
GIT binary patch
literal 13083
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
Z)7DY7BZU1%)5`JoOQlb%aFFP}{9nBZ#RUKW
literal 0
HcmV?d00001
diff --git a/rest_framework/locale/bg/LC_MESSAGES/django.po b/rest_framework/locale/bg/LC_MESSAGES/django.po
new file mode 100644
index 000000000..56e1c9ef8
--- /dev/null
+++ b/rest_framework/locale/bg/LC_MESSAGES/django.po
@@ -0,0 +1,572 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Django REST framework\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-10-13 21:45+0200\n"
+"PO-Revision-Date: 2020-10-13 19:45+0000\n"
+"Last-Translator: Xavier Ordoquy \n"
+"Language-Team: Bulgarian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/bg/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: bg\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: authentication.py:70
+msgid "Invalid basic header. No credentials provided."
+msgstr "Невалиден header за базово удостоверение (basic authentication). Не се предоставени удостоверения."
+
+#: authentication.py:73
+msgid "Invalid basic header. Credentials string should not contain spaces."
+msgstr "Невалиден header за базово удостоверение (basic authentication). Носителите на удостоверение не трябва да съдържат интервали."
+
+#: authentication.py:83
+msgid "Invalid basic header. Credentials not correctly base64 encoded."
+msgstr "Невалиден header за базово удостоверение (basic authentication). Носителите на удостоверение не са кодирани в base64."
+
+#: authentication.py:101
+msgid "Invalid username/password."
+msgstr "Невалидни потребителско име/парола."
+
+#: authentication.py:104 authentication.py:206
+msgid "User inactive or deleted."
+msgstr "Потребителят е неактивен или изтрит."
+
+#: authentication.py:184
+msgid "Invalid token header. No credentials provided."
+msgstr "Невалиден token header. Не са предоставени носители на удостоверение."
+
+#: authentication.py:187
+msgid "Invalid token header. Token string should not contain spaces."
+msgstr "Невалиден token header. Жетона (token-a) не трябва да съдържа интервали."
+
+#: authentication.py:193
+msgid ""
+"Invalid token header. Token string should not contain invalid characters."
+msgstr "Невалиден token header. Жетона (token-a) не трябва да съдържа невалидни символи."
+
+#: authentication.py:203
+msgid "Invalid token."
+msgstr "Невалиден жетон."
+
+#: authtoken/apps.py:7
+msgid "Auth Token"
+msgstr "Жетон за удостоверение"
+
+#: authtoken/models.py:13
+msgid "Key"
+msgstr "Ключ"
+
+#: authtoken/models.py:16
+msgid "User"
+msgstr "Потребител"
+
+#: authtoken/models.py:18
+msgid "Created"
+msgstr "Създаден"
+
+#: authtoken/models.py:27 authtoken/serializers.py:19
+msgid "Token"
+msgstr "Жетон"
+
+#: authtoken/models.py:28
+msgid "Tokens"
+msgstr "Жетони"
+
+#: authtoken/serializers.py:9
+msgid "Username"
+msgstr "Потребителско име"
+
+#: authtoken/serializers.py:13
+msgid "Password"
+msgstr "Парола"
+
+#: authtoken/serializers.py:35
+msgid "Unable to log in with provided credentials."
+msgstr "Не е възможен вход с предоставените удостоверения."
+
+#: authtoken/serializers.py:38
+msgid "Must include \"username\" and \"password\"."
+msgstr "Трябва да съдържа \"username\" (потребителско име) и \"password\" (парола)."
+
+#: exceptions.py:102
+msgid "A server error occurred."
+msgstr "Сървърна грешка."
+
+#: exceptions.py:142
+msgid "Invalid input."
+msgstr ""
+
+#: exceptions.py:161
+msgid "Malformed request."
+msgstr "Неправилно оформена заявка."
+
+#: exceptions.py:167
+msgid "Incorrect authentication credentials."
+msgstr "Невалидни носители на удостоверение."
+
+#: exceptions.py:173
+msgid "Authentication credentials were not provided."
+msgstr "Носители на удостоверение не са предоставени."
+
+#: exceptions.py:179
+msgid "You do not have permission to perform this action."
+msgstr "Нямате права да се направи това действие."
+
+#: exceptions.py:185
+msgid "Not found."
+msgstr "Обектът не е намерен."
+
+#: exceptions.py:191
+#, python-brace-format
+msgid "Method \"{method}\" not allowed."
+msgstr "Метод \"{method}\" не е позволен."
+
+#: exceptions.py:202
+msgid "Could not satisfy the request Accept header."
+msgstr "Не може да бъде удовлетворен Accept header."
+
+#: exceptions.py:212
+#, python-brace-format
+msgid "Unsupported media type \"{media_type}\" in request."
+msgstr "Неподдържан тип на документа \"{media_type}\" в заявката."
+
+#: exceptions.py:223
+msgid "Request was throttled."
+msgstr "Заявката е ограничена."
+
+#: exceptions.py:224
+#, python-brace-format
+msgid "Expected available in {wait} second."
+msgstr ""
+
+#: exceptions.py:225
+#, python-brace-format
+msgid "Expected available in {wait} seconds."
+msgstr ""
+
+#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
+#: validators.py:183
+msgid "This field is required."
+msgstr "Това поле е задължително."
+
+#: fields.py:317
+msgid "This field may not be null."
+msgstr "Това поле не може да има празна стойност."
+
+#: fields.py:701
+msgid "Must be a valid boolean."
+msgstr ""
+
+#: fields.py:766
+msgid "Not a valid string."
+msgstr ""
+
+#: fields.py:767
+msgid "This field may not be blank."
+msgstr "Това поле не може да е празно."
+
+#: fields.py:768 fields.py:1881
+#, python-brace-format
+msgid "Ensure this field has no more than {max_length} characters."
+msgstr "Това поле не трябва да съдържа повече от {max_length} символа."
+
+#: fields.py:769
+#, python-brace-format
+msgid "Ensure this field has at least {min_length} characters."
+msgstr "Това поле трябва да съдържа поде {min_length} символа."
+
+#: fields.py:816
+msgid "Enter a valid email address."
+msgstr "Въведете валиден имейл адрес."
+
+#: fields.py:827
+msgid "This value does not match the required pattern."
+msgstr "Тази стойност не съответства на изисквания шаблон."
+
+#: fields.py:838
+msgid ""
+"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
+"hyphens."
+msgstr "Въведете валиден \"slug\" съдържащ латински букви, цифри, долни черти или тирета."
+
+#: fields.py:839
+msgid ""
+"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
+"or hyphens."
+msgstr ""
+
+#: fields.py:854
+msgid "Enter a valid URL."
+msgstr "Въведете валиден URL."
+
+#: fields.py:867
+msgid "Must be a valid UUID."
+msgstr ""
+
+#: fields.py:903
+msgid "Enter a valid IPv4 or IPv6 address."
+msgstr "Въведете валиден IPv4 или IPv6 адрес."
+
+#: fields.py:931
+msgid "A valid integer is required."
+msgstr "Необходимо е валидно цяло число."
+
+#: fields.py:932 fields.py:969 fields.py:1005 fields.py:1366
+#, python-brace-format
+msgid "Ensure this value is less than or equal to {max_value}."
+msgstr "Тази стойност трябва да е не повече от {max_value}."
+
+#: fields.py:933 fields.py:970 fields.py:1006 fields.py:1367
+#, python-brace-format
+msgid "Ensure this value is greater than or equal to {min_value}."
+msgstr "Тази стойност трябва да е поне {min_value}."
+
+#: fields.py:934 fields.py:971 fields.py:1010
+msgid "String value too large."
+msgstr "Низът е прекалено голям."
+
+#: fields.py:968 fields.py:1004
+msgid "A valid number is required."
+msgstr "Необходимо е валидно число."
+
+#: fields.py:1007
+#, python-brace-format
+msgid "Ensure that there are no more than {max_digits} digits in total."
+msgstr "Допустими са не повече от {max_digits} цифри."
+
+#: fields.py:1008
+#, python-brace-format
+msgid ""
+"Ensure that there are no more than {max_decimal_places} decimal places."
+msgstr "Допустими са не повече от {max_decimal_places} цифри след десетичната запетая."
+
+#: fields.py:1009
+#, python-brace-format
+msgid ""
+"Ensure that there are no more than {max_whole_digits} digits before the "
+"decimal point."
+msgstr "Допустими са не повече от {max_whole_digits} цифри преди десетичната запетая."
+
+#: fields.py:1148
+#, python-brace-format
+msgid "Datetime has wrong format. Use one of these formats instead: {format}."
+msgstr "Датата и часът са в невалиден формат. Валидни са следните формати: {format}."
+
+#: fields.py:1149
+msgid "Expected a datetime but got a date."
+msgstr "Очаква се дата и час, но е намерена само дата."
+
+#: fields.py:1150
+#, python-brace-format
+msgid "Invalid datetime for the timezone \"{timezone}\"."
+msgstr ""
+
+#: fields.py:1151
+msgid "Datetime value out of range."
+msgstr ""
+
+#: fields.py:1236
+#, python-brace-format
+msgid "Date has wrong format. Use one of these formats instead: {format}."
+msgstr "Дата е в невалиден формат. Валидни са следните формати: {format}."
+
+#: fields.py:1237
+msgid "Expected a date but got a datetime."
+msgstr "Очаква се дата, но са подадени дата и час."
+
+#: fields.py:1303
+#, python-brace-format
+msgid "Time has wrong format. Use one of these formats instead: {format}."
+msgstr "Времето е в невалиден формат. Валидни са следните формати: {format}."
+
+#: fields.py:1365
+#, python-brace-format
+msgid "Duration has wrong format. Use one of these formats instead: {format}."
+msgstr "Отрязъкът от време е в невалиден формат. Валидни са следните формати: {format}."
+
+#: fields.py:1399 fields.py:1456
+#, python-brace-format
+msgid "\"{input}\" is not a valid choice."
+msgstr "\"{input}\" не е валиден избор."
+
+#: fields.py:1402
+#, python-brace-format
+msgid "More than {count} items..."
+msgstr "Повече от {count} неща..."
+
+#: fields.py:1457 fields.py:1603 relations.py:485 serializers.py:570
+#, python-brace-format
+msgid "Expected a list of items but got type \"{input_type}\"."
+msgstr "Очаква се списък от неща, но е получен тип \"{input_type}\"."
+
+#: fields.py:1458
+msgid "This selection may not be empty."
+msgstr "Този избор не може да е празен."
+
+#: fields.py:1495
+#, python-brace-format
+msgid "\"{input}\" is not a valid path choice."
+msgstr "\"{input}\" не е валиден избор за път."
+
+#: fields.py:1514
+msgid "No file was submitted."
+msgstr "Не е подаден файл."
+
+#: fields.py:1515
+msgid ""
+"The submitted data was not a file. Check the encoding type on the form."
+msgstr "Подадените данни не са файл. Проверете кодировката на формата."
+
+#: fields.py:1516
+msgid "No filename could be determined."
+msgstr "Не може да бъде определено името на файла."
+
+#: fields.py:1517
+msgid "The submitted file is empty."
+msgstr "Подадения файл е празен."
+
+#: fields.py:1518
+#, python-brace-format
+msgid ""
+"Ensure this filename has at most {max_length} characters (it has {length})."
+msgstr "Името на файла може да е до {max_length} символа (то е {length})."
+
+#: fields.py:1566
+msgid ""
+"Upload a valid image. The file you uploaded was either not an image or a "
+"corrupted image."
+msgstr "Невалидно изображение. Подадения файл не е изображение или е повредено изображение."
+
+#: fields.py:1604 relations.py:486 serializers.py:571
+msgid "This list may not be empty."
+msgstr "Този списък не може да е празен."
+
+#: fields.py:1605
+#, python-brace-format
+msgid "Ensure this field has at least {min_length} elements."
+msgstr ""
+
+#: fields.py:1606
+#, python-brace-format
+msgid "Ensure this field has no more than {max_length} elements."
+msgstr ""
+
+#: fields.py:1682
+#, python-brace-format
+msgid "Expected a dictionary of items but got type \"{input_type}\"."
+msgstr "Очаква се асоциативен масив, но е получен \"{input_type}\"."
+
+#: fields.py:1683
+msgid "This dictionary may not be empty."
+msgstr ""
+
+#: fields.py:1755
+msgid "Value must be valid JSON."
+msgstr "Стойността трябва да е валиден JSON."
+
+#: filters.py:49 templates/rest_framework/filters/search.html:2
+msgid "Search"
+msgstr "Търсене"
+
+#: filters.py:50
+msgid "A search term."
+msgstr ""
+
+#: filters.py:180 templates/rest_framework/filters/ordering.html:3
+msgid "Ordering"
+msgstr "Подредба"
+
+#: filters.py:181
+msgid "Which field to use when ordering the results."
+msgstr ""
+
+#: filters.py:287
+msgid "ascending"
+msgstr "в нарастващ ред"
+
+#: filters.py:288
+msgid "descending"
+msgstr "в намаляващ ред"
+
+#: pagination.py:174
+msgid "A page number within the paginated result set."
+msgstr ""
+
+#: pagination.py:179 pagination.py:372 pagination.py:590
+msgid "Number of results to return per page."
+msgstr ""
+
+#: pagination.py:189
+msgid "Invalid page."
+msgstr "Невалидна страница."
+
+#: pagination.py:374
+msgid "The initial index from which to return the results."
+msgstr ""
+
+#: pagination.py:581
+msgid "The pagination cursor value."
+msgstr ""
+
+#: pagination.py:583
+msgid "Invalid cursor"
+msgstr "Невалиден курсор"
+
+#: relations.py:246
+#, python-brace-format
+msgid "Invalid pk \"{pk_value}\" - object does not exist."
+msgstr "Невалиден идентификатор \"{pk_value}\" - обектът не съществува."
+
+#: relations.py:247
+#, python-brace-format
+msgid "Incorrect type. Expected pk value, received {data_type}."
+msgstr "Неправилен тип. Очакван е тип за основен ключ, получен е {data_type}."
+
+#: relations.py:280
+msgid "Invalid hyperlink - No URL match."
+msgstr "Невалидна връзка (hyperlink) - няма намерен URL."
+
+#: relations.py:281
+msgid "Invalid hyperlink - Incorrect URL match."
+msgstr "Невалидна връзка (hyperlink) - неправилно съвпадение на URL."
+
+#: relations.py:282
+msgid "Invalid hyperlink - Object does not exist."
+msgstr "Невалидна връзка (hyperlink) - обектът не съществува."
+
+#: relations.py:283
+#, python-brace-format
+msgid "Incorrect type. Expected URL string, received {data_type}."
+msgstr "Невалиден тип. Очаква се URL низ, получен е {data_type}."
+
+#: relations.py:448
+#, python-brace-format
+msgid "Object with {slug_name}={value} does not exist."
+msgstr "Обект с {slug_name}={value} не съществува."
+
+#: relations.py:449
+msgid "Invalid value."
+msgstr "Невалидна стойност."
+
+#: schemas/utils.py:32
+msgid "unique integer value"
+msgstr ""
+
+#: schemas/utils.py:34
+msgid "UUID string"
+msgstr ""
+
+#: schemas/utils.py:36
+msgid "unique value"
+msgstr ""
+
+#: schemas/utils.py:38
+#, python-brace-format
+msgid "A {value_type} identifying this {name}."
+msgstr ""
+
+#: serializers.py:337
+#, python-brace-format
+msgid "Invalid data. Expected a dictionary, but got {datatype}."
+msgstr "Невалидни данни. Очаква се асоциативен масив, но е получен {datatype}."
+
+#: templates/rest_framework/admin.html:116
+#: templates/rest_framework/base.html:136
+msgid "Extra Actions"
+msgstr ""
+
+#: templates/rest_framework/admin.html:130
+#: templates/rest_framework/base.html:150
+msgid "Filters"
+msgstr "Филтри"
+
+#: templates/rest_framework/base.html:37
+msgid "navbar"
+msgstr ""
+
+#: templates/rest_framework/base.html:75
+msgid "content"
+msgstr ""
+
+#: templates/rest_framework/base.html:78
+msgid "request form"
+msgstr ""
+
+#: templates/rest_framework/base.html:157
+msgid "main content"
+msgstr ""
+
+#: templates/rest_framework/base.html:173
+msgid "request info"
+msgstr ""
+
+#: templates/rest_framework/base.html:177
+msgid "response info"
+msgstr ""
+
+#: templates/rest_framework/horizontal/radio.html:4
+#: templates/rest_framework/inline/radio.html:3
+#: templates/rest_framework/vertical/radio.html:3
+msgid "None"
+msgstr "Нищо"
+
+#: templates/rest_framework/horizontal/select_multiple.html:4
+#: templates/rest_framework/inline/select_multiple.html:3
+#: templates/rest_framework/vertical/select_multiple.html:3
+msgid "No items to select."
+msgstr "Няма неща за избиране."
+
+#: validators.py:39
+msgid "This field must be unique."
+msgstr "Това поле трябва да е уникално."
+
+#: validators.py:89
+#, python-brace-format
+msgid "The fields {field_names} must make a unique set."
+msgstr "Полетата {field_names} трябва да образуват уникална комбинация."
+
+#: validators.py:171
+#, python-brace-format
+msgid "Surrogate characters are not allowed: U+{code_point:X}."
+msgstr ""
+
+#: validators.py:243
+#, python-brace-format
+msgid "This field must be unique for the \"{date_field}\" date."
+msgstr "Това поле трябва да е уникално за \"{date_field}\" дата."
+
+#: validators.py:258
+#, python-brace-format
+msgid "This field must be unique for the \"{date_field}\" month."
+msgstr "Това поле трябва да е уникално за \"{date_field}\" месец."
+
+#: validators.py:271
+#, python-brace-format
+msgid "This field must be unique for the \"{date_field}\" year."
+msgstr "Това поле трябва да е уникално за \"{date_field}\" година."
+
+#: versioning.py:40
+msgid "Invalid version in \"Accept\" header."
+msgstr "Невалидна версия в \"Accept\" header."
+
+#: versioning.py:71
+msgid "Invalid version in URL path."
+msgstr "Невалидна версия в URL пътя."
+
+#: versioning.py:116
+msgid "Invalid version in URL path. Does not match any version namespace."
+msgstr "Невалидна версия в URL пътя. Няма съвпадение с пространството от имена на версии."
+
+#: versioning.py:148
+msgid "Invalid version in hostname."
+msgstr "Невалидна версия в името на сървъра (hostname)."
+
+#: versioning.py:170
+msgid "Invalid version in query parameter."
+msgstr "Невалидна версия в GET параметър."
diff --git a/rest_framework/locale/ca/LC_MESSAGES/django.mo b/rest_framework/locale/ca/LC_MESSAGES/django.mo
index 0f72ec03947a137debd0497f8e67a4d84ca95ac7..7da9971a8ec201a5e8907883c09508c10db7c4d9 100644
GIT binary patch
delta 1850
zcmYk+-A`0U7{~F4H&5*8
z`|uCnT`SGrrT+y+%@Vdq;|LD|*=D=&Lu|nd*p0tpGnV9d{~y2@{jYHgE@2*~2fYau
zqMq0J9zp%yBsSm;R3Oi>nfF^>u2~ZgdaxDG;9gwBYRp+>wi?5zah+I!r?C{T;|{!!
zHTXB`dlh`C3;S_5euMnkZ+yIoFVyG#mi>k|S%?aA+`2~@2026UkM$54AbiHwN(%N
zeipM+L=n`uespjg_2P4=T)2ys7+{vQSf9`O570QlgJQgmb@&voq>+RLW^d8|zR>%@
zN4S;#pQz`>grgOC2ld0pP!pWS{rD3ez#!qq@F?p4SACb(vHluZNJ?vuKEg0w#>4mo
z6=^lItH+PA4`;9nU!qoMBjGCt!dQ*ReUliae+P96GFTqv&VFpc@dS+y8uNGvUt<)z
z_|*uWMRvu0L5+KYdU44nFN?cTk&mKQZWf#HA!>X!%M-?4Bu#7@H{&J0pSVRs$Kf9G
zZhMLRS&&hB(GFC<-**BP!FePoTR=_VDY96Wm6jMa`h<7%HmeqT|^@RfHt7doh7qn713LI=R%{YWPLTsIrHu)l_b@Qnf;y
zLia}13DW!Bs2fnrq@v}~60301+>*(sR!N1Vx9wEr!D?!hsvJ3l{qQr`6+Nl37;
delta 2103
zcmaLXTWl0n9LMof3cYaYg>5OV)D9~a#kRDR(%OrxU8uztini4Vi8$>}>`1#?y1Sqd
z-9*zE600T~V+au{5)(r-sY@a+Xtd%BF&Hfm8pP;>vGGBLMB|I``ZPw4+90uxIKRZ0L)gEJf3%W(W7cCSTG)(t;t<}BM{zBlK;8d7
z-iBAO9)HFVgr7RyYN@+!uDdG!~4zCbgJ-0oxn4wnarVPaMkyQ
zf4;aRJHuvF3VLuOI=BXB(89AAz)QFkzeX0zT*uA0h$$^qWoh=tI;`M0jE%S-m714O
z16lBY|II%x!@o7ArLkds>n_}7<@D;T1yzf=4<@k61_iARR73)U5qwox6zs5#Dn-L
z?!jV8x*s1${eI5(2h??yj7m9-;JtVThw&P=;ufaej8EYZp29l3i5jrQa;QeSQgj;V
zjQPHfA&x&oZIgViR&njY4m^T8F@uBnCx&o$wK1LeGLlwv8Fk$?)Qf{mTSY#Ant2kn
zlBqZ8w9#2W-LR@AJEK9Q-pma0XWsCSFQOjwIWk6b6S>tCTiF*ippN(Z9z{*y1TuN^
z32Fe}B8!sHhDQ_~C7^a0Vs71hr
zb@O5LmRxnD#)6vr32kwuV5jo0rPZ?OgP=61>1+1yMxl5`SdJ95*Vy{E+THZJiGE@Q
z!FKmjM<&bwp*jf@1%!5u()KXnt&@sFg{Ia^1iW7MlWBs-+~{O7@odm?<5o15ux#tF
zJ?=)Vkyvcpv7-&?3wgcO!DF0EI{&?DczD;2%wKuW7nbgIrY78Y+>J%8h!b_4Nanec
zp4@;vIvPtx6MA~YjoTyRP9%LXaJHzawZ5^fzOmVA+S=aImX4KGSVLp62|I3u$EMtP
z!gZ|9L~J5NjQS@sZ\n"
+"POT-Creation-Date: 2020-10-13 21:45+0200\n"
+"PO-Revision-Date: 2020-10-13 19:45+0000\n"
+"Last-Translator: Xavier Ordoquy \n"
"Language-Team: Catalan (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ca/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -17,40 +17,40 @@ msgstr ""
"Language: ca\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: authentication.py:73
+#: authentication.py:70
msgid "Invalid basic header. No credentials provided."
msgstr "Header Basic invàlid. No hi ha disponibles les credencials."
-#: authentication.py:76
+#: authentication.py:73
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "Header Basic invàlid. Les credencials no poden contenir espais."
-#: authentication.py:82
+#: authentication.py:83
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "Header Basic invàlid. Les credencials no estan codificades correctament en base64."
-#: authentication.py:99
+#: authentication.py:101
msgid "Invalid username/password."
msgstr "Usuari/Contrasenya incorrectes."
-#: authentication.py:102 authentication.py:198
+#: authentication.py:104 authentication.py:206
msgid "User inactive or deleted."
msgstr "Usuari inactiu o esborrat."
-#: authentication.py:176
+#: authentication.py:184
msgid "Invalid token header. No credentials provided."
msgstr "Token header invàlid. No s'han indicat les credencials."
-#: authentication.py:179
+#: authentication.py:187
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Token header invàlid. El token no ha de contenir espais."
-#: authentication.py:185
+#: authentication.py:193
msgid ""
"Invalid token header. Token string should not contain invalid characters."
msgstr "Token header invàlid. El token no pot contenir caràcters invàlids."
-#: authentication.py:195
+#: authentication.py:203
msgid "Invalid token."
msgstr "Token invàlid."
@@ -58,382 +58,515 @@ msgstr "Token invàlid."
msgid "Auth Token"
msgstr ""
-#: authtoken/models.py:15
+#: authtoken/models.py:13
msgid "Key"
msgstr ""
-#: authtoken/models.py:18
+#: authtoken/models.py:16
msgid "User"
msgstr ""
-#: authtoken/models.py:20
+#: authtoken/models.py:18
msgid "Created"
msgstr ""
-#: authtoken/models.py:29
+#: authtoken/models.py:27 authtoken/serializers.py:19
msgid "Token"
msgstr ""
-#: authtoken/models.py:30
+#: authtoken/models.py:28
msgid "Tokens"
msgstr ""
-#: authtoken/serializers.py:8
+#: authtoken/serializers.py:9
msgid "Username"
msgstr ""
-#: authtoken/serializers.py:9
+#: authtoken/serializers.py:13
msgid "Password"
msgstr ""
-#: authtoken/serializers.py:20
-msgid "User account is disabled."
-msgstr "Compte d'usuari desactivat."
-
-#: authtoken/serializers.py:23
+#: authtoken/serializers.py:35
msgid "Unable to log in with provided credentials."
msgstr "No es possible loguejar-se amb les credencials introduïdes."
-#: authtoken/serializers.py:26
+#: authtoken/serializers.py:38
msgid "Must include \"username\" and \"password\"."
msgstr "S'ha d'incloure \"username\" i \"password\"."
-#: exceptions.py:49
+#: exceptions.py:102
msgid "A server error occurred."
msgstr "S'ha produït un error en el servidor."
-#: exceptions.py:84
+#: exceptions.py:142
+msgid "Invalid input."
+msgstr ""
+
+#: exceptions.py:161
msgid "Malformed request."
msgstr "Request amb format incorrecte."
-#: exceptions.py:89
+#: exceptions.py:167
msgid "Incorrect authentication credentials."
msgstr "Credencials d'autenticació incorrectes."
-#: exceptions.py:94
+#: exceptions.py:173
msgid "Authentication credentials were not provided."
msgstr "Credencials d'autenticació no disponibles."
-#: exceptions.py:99
+#: exceptions.py:179
msgid "You do not have permission to perform this action."
msgstr "No té permisos per realitzar aquesta acció."
-#: exceptions.py:104 views.py:81
+#: exceptions.py:185
msgid "Not found."
msgstr "No trobat."
-#: exceptions.py:109
+#: exceptions.py:191
+#, python-brace-format
msgid "Method \"{method}\" not allowed."
msgstr "Mètode \"{method}\" no permès."
-#: exceptions.py:120
+#: exceptions.py:202
msgid "Could not satisfy the request Accept header."
msgstr "No s'ha pogut satisfer l'Accept header de la petició."
-#: exceptions.py:132
+#: exceptions.py:212
+#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "Media type \"{media_type}\" no suportat."
-#: exceptions.py:145
+#: exceptions.py:223
msgid "Request was throttled."
msgstr "La petició ha estat limitada pel número màxim de peticions definit."
-#: fields.py:269 relations.py:206 relations.py:239 validators.py:98
-#: validators.py:181
+#: exceptions.py:224
+#, python-brace-format
+msgid "Expected available in {wait} second."
+msgstr ""
+
+#: exceptions.py:225
+#, python-brace-format
+msgid "Expected available in {wait} seconds."
+msgstr ""
+
+#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
+#: validators.py:183
msgid "This field is required."
msgstr "Aquest camp és obligatori."
-#: fields.py:270
+#: fields.py:317
msgid "This field may not be null."
msgstr "Aquest camp no pot ser nul."
-#: fields.py:608 fields.py:639
-msgid "\"{input}\" is not a valid boolean."
-msgstr "\"{input}\" no és un booleà."
+#: fields.py:701
+msgid "Must be a valid boolean."
+msgstr ""
-#: fields.py:674
+#: fields.py:766
+msgid "Not a valid string."
+msgstr ""
+
+#: fields.py:767
msgid "This field may not be blank."
msgstr "Aquest camp no pot estar en blanc."
-#: fields.py:675 fields.py:1675
+#: fields.py:768 fields.py:1881
+#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Aquest camp no pot tenir més de {max_length} caràcters."
-#: fields.py:676
+#: fields.py:769
+#, python-brace-format
msgid "Ensure this field has at least {min_length} characters."
msgstr "Aquest camp ha de tenir un mínim de {min_length} caràcters."
-#: fields.py:713
+#: fields.py:816
msgid "Enter a valid email address."
msgstr "Introdueixi una adreça de correu vàlida."
-#: fields.py:724
+#: fields.py:827
msgid "This value does not match the required pattern."
msgstr "Aquest valor no compleix el patró requerit."
-#: fields.py:735
+#: fields.py:838
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Introdueix un \"slug\" vàlid consistent en lletres, números, guions o guions baixos."
-#: fields.py:747
+#: fields.py:839
+msgid ""
+"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
+"or hyphens."
+msgstr ""
+
+#: fields.py:854
msgid "Enter a valid URL."
msgstr "Introdueixi una URL vàlida."
-#: fields.py:760
-msgid "\"{value}\" is not a valid UUID."
-msgstr "\"{value}\" no és un UUID vàlid."
+#: fields.py:867
+msgid "Must be a valid UUID."
+msgstr ""
-#: fields.py:796
+#: fields.py:903
msgid "Enter a valid IPv4 or IPv6 address."
msgstr "Introdueixi una adreça IPv4 o IPv6 vàlida."
-#: fields.py:821
+#: fields.py:931
msgid "A valid integer is required."
msgstr "Es requereix un nombre enter vàlid."
-#: fields.py:822 fields.py:857 fields.py:891
+#: fields.py:932 fields.py:969 fields.py:1005 fields.py:1366
+#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Aquest valor ha de ser menor o igual a {max_value}."
-#: fields.py:823 fields.py:858 fields.py:892
+#: fields.py:933 fields.py:970 fields.py:1006 fields.py:1367
+#, python-brace-format
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Aquest valor ha de ser més gran o igual a {min_value}."
-#: fields.py:824 fields.py:859 fields.py:896
+#: fields.py:934 fields.py:971 fields.py:1010
msgid "String value too large."
msgstr "Valor del text massa gran."
-#: fields.py:856 fields.py:890
+#: fields.py:968 fields.py:1004
msgid "A valid number is required."
msgstr "Es requereix un nombre vàlid."
-#: fields.py:893
+#: fields.py:1007
+#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "No pot haver-hi més de {max_digits} dígits en total."
-#: fields.py:894
+#: fields.py:1008
+#, python-brace-format
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "No pot haver-hi més de {max_decimal_places} decimals."
-#: fields.py:895
+#: fields.py:1009
+#, python-brace-format
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "No pot haver-hi més de {max_whole_digits} dígits abans del punt decimal."
-#: fields.py:1025
+#: fields.py:1148
+#, python-brace-format
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "El Datetime té un format incorrecte. Utilitzi un d'aquests formats: {format}."
-#: fields.py:1026
+#: fields.py:1149
msgid "Expected a datetime but got a date."
msgstr "S'espera un Datetime però s'ha rebut un Date."
-#: fields.py:1103
+#: fields.py:1150
+#, python-brace-format
+msgid "Invalid datetime for the timezone \"{timezone}\"."
+msgstr ""
+
+#: fields.py:1151
+msgid "Datetime value out of range."
+msgstr ""
+
+#: fields.py:1236
+#, python-brace-format
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "El Date té un format incorrecte. Utilitzi un d'aquests formats: {format}."
-#: fields.py:1104
+#: fields.py:1237
msgid "Expected a date but got a datetime."
msgstr "S'espera un Date però s'ha rebut un Datetime."
-#: fields.py:1170
+#: fields.py:1303
+#, python-brace-format
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "El Time té un format incorrecte. Utilitzi un d'aquests formats: {format}."
-#: fields.py:1232
+#: fields.py:1365
+#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}."
msgstr "La durada té un format incorrecte. Utilitzi un d'aquests formats: {format}."
-#: fields.py:1251 fields.py:1300
+#: fields.py:1399 fields.py:1456
+#, python-brace-format
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" no és una opció vàlida."
-#: fields.py:1254 relations.py:71 relations.py:441
+#: fields.py:1402
+#, python-brace-format
msgid "More than {count} items..."
msgstr ""
-#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524
+#: fields.py:1457 fields.py:1603 relations.py:485 serializers.py:570
+#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "S'espera una llista d'ítems però s'ha rebut el tipus \"{input_type}\"."
-#: fields.py:1302
+#: fields.py:1458
msgid "This selection may not be empty."
msgstr "Aquesta selecció no pot estar buida."
-#: fields.py:1339
+#: fields.py:1495
+#, python-brace-format
msgid "\"{input}\" is not a valid path choice."
msgstr "\"{input}\" no és un path vàlid."
-#: fields.py:1358
+#: fields.py:1514
msgid "No file was submitted."
msgstr "No s'ha enviat cap fitxer."
-#: fields.py:1359
+#: fields.py:1515
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "Les dades enviades no són un fitxer. Comproveu l'encoding type del formulari."
-#: fields.py:1360
+#: fields.py:1516
msgid "No filename could be determined."
msgstr "No s'ha pogut determinar el nom del fitxer."
-#: fields.py:1361
+#: fields.py:1517
msgid "The submitted file is empty."
msgstr "El fitxer enviat està buit."
-#: fields.py:1362
+#: fields.py:1518
+#, python-brace-format
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "El nom del fitxer ha de tenir com a màxim {max_length} caràcters (en té {length})."
-#: fields.py:1410
+#: fields.py:1566
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "Envieu una imatge vàlida. El fitxer enviat no és una imatge o és una imatge corrompuda."
-#: fields.py:1449 relations.py:438 serializers.py:525
+#: fields.py:1604 relations.py:486 serializers.py:571
msgid "This list may not be empty."
msgstr "Aquesta llista no pot estar buida."
-#: fields.py:1502
+#: fields.py:1605
+#, python-brace-format
+msgid "Ensure this field has at least {min_length} elements."
+msgstr ""
+
+#: fields.py:1606
+#, python-brace-format
+msgid "Ensure this field has no more than {max_length} elements."
+msgstr ""
+
+#: fields.py:1682
+#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "S'espera un diccionari però s'ha rebut el tipus \"{input_type}\"."
-#: fields.py:1549
+#: fields.py:1683
+msgid "This dictionary may not be empty."
+msgstr ""
+
+#: fields.py:1755
msgid "Value must be valid JSON."
msgstr ""
-#: filters.py:36 templates/rest_framework/filters/django_filter.html:5
-msgid "Submit"
+#: filters.py:49 templates/rest_framework/filters/search.html:2
+msgid "Search"
msgstr ""
-#: filters.py:336
+#: filters.py:50
+msgid "A search term."
+msgstr ""
+
+#: filters.py:180 templates/rest_framework/filters/ordering.html:3
+msgid "Ordering"
+msgstr ""
+
+#: filters.py:181
+msgid "Which field to use when ordering the results."
+msgstr ""
+
+#: filters.py:287
msgid "ascending"
msgstr ""
-#: filters.py:337
+#: filters.py:288
msgid "descending"
msgstr ""
-#: pagination.py:193
+#: pagination.py:174
+msgid "A page number within the paginated result set."
+msgstr ""
+
+#: pagination.py:179 pagination.py:372 pagination.py:590
+msgid "Number of results to return per page."
+msgstr ""
+
+#: pagination.py:189
msgid "Invalid page."
msgstr ""
-#: pagination.py:427
+#: pagination.py:374
+msgid "The initial index from which to return the results."
+msgstr ""
+
+#: pagination.py:581
+msgid "The pagination cursor value."
+msgstr ""
+
+#: pagination.py:583
msgid "Invalid cursor"
msgstr "Cursor invàlid."
-#: relations.py:207
+#: relations.py:246
+#, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "PK invàlida \"{pk_value}\" - l'objecte no existeix."
-#: relations.py:208
+#: relations.py:247
+#, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "Tipus incorrecte. S'espera el valor d'una PK, s'ha rebut {data_type}."
-#: relations.py:240
+#: relations.py:280
msgid "Invalid hyperlink - No URL match."
msgstr "Hyperlink invàlid - Cap match d'URL."
-#: relations.py:241
+#: relations.py:281
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "Hyperlink invàlid - Match d'URL incorrecta."
-#: relations.py:242
+#: relations.py:282
msgid "Invalid hyperlink - Object does not exist."
msgstr "Hyperlink invàlid - L'objecte no existeix."
-#: relations.py:243
+#: relations.py:283
+#, python-brace-format
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "Tipus incorrecte. S'espera una URL, s'ha rebut {data_type}."
-#: relations.py:401
+#: relations.py:448
+#, python-brace-format
msgid "Object with {slug_name}={value} does not exist."
msgstr "L'objecte amb {slug_name}={value} no existeix."
-#: relations.py:402
+#: relations.py:449
msgid "Invalid value."
msgstr "Valor invàlid."
-#: serializers.py:326
+#: schemas/utils.py:32
+msgid "unique integer value"
+msgstr ""
+
+#: schemas/utils.py:34
+msgid "UUID string"
+msgstr ""
+
+#: schemas/utils.py:36
+msgid "unique value"
+msgstr ""
+
+#: schemas/utils.py:38
+#, python-brace-format
+msgid "A {value_type} identifying this {name}."
+msgstr ""
+
+#: serializers.py:337
+#, python-brace-format
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "Dades invàlides. S'espera un diccionari però s'ha rebut {datatype}."
#: templates/rest_framework/admin.html:116
-#: templates/rest_framework/base.html:128
+#: templates/rest_framework/base.html:136
+msgid "Extra Actions"
+msgstr ""
+
+#: templates/rest_framework/admin.html:130
+#: templates/rest_framework/base.html:150
msgid "Filters"
msgstr ""
-#: templates/rest_framework/filters/django_filter.html:2
-#: templates/rest_framework/filters/django_filter_crispyforms.html:4
-msgid "Field filters"
+#: templates/rest_framework/base.html:37
+msgid "navbar"
msgstr ""
-#: templates/rest_framework/filters/ordering.html:3
-msgid "Ordering"
+#: templates/rest_framework/base.html:75
+msgid "content"
msgstr ""
-#: templates/rest_framework/filters/search.html:2
-msgid "Search"
+#: templates/rest_framework/base.html:78
+msgid "request form"
msgstr ""
-#: templates/rest_framework/horizontal/radio.html:2
-#: templates/rest_framework/inline/radio.html:2
-#: templates/rest_framework/vertical/radio.html:2
+#: templates/rest_framework/base.html:157
+msgid "main content"
+msgstr ""
+
+#: templates/rest_framework/base.html:173
+msgid "request info"
+msgstr ""
+
+#: templates/rest_framework/base.html:177
+msgid "response info"
+msgstr ""
+
+#: templates/rest_framework/horizontal/radio.html:4
+#: templates/rest_framework/inline/radio.html:3
+#: templates/rest_framework/vertical/radio.html:3
msgid "None"
msgstr "Cap"
-#: templates/rest_framework/horizontal/select_multiple.html:2
-#: templates/rest_framework/inline/select_multiple.html:2
-#: templates/rest_framework/vertical/select_multiple.html:2
+#: templates/rest_framework/horizontal/select_multiple.html:4
+#: templates/rest_framework/inline/select_multiple.html:3
+#: templates/rest_framework/vertical/select_multiple.html:3
msgid "No items to select."
msgstr "Cap opció seleccionada."
-#: validators.py:43
+#: validators.py:39
msgid "This field must be unique."
msgstr "Aquest camp ha de ser únic."
-#: validators.py:97
+#: validators.py:89
+#, python-brace-format
msgid "The fields {field_names} must make a unique set."
msgstr "Aquests camps {field_names} han de constituir un conjunt únic."
-#: validators.py:245
+#: validators.py:171
+#, python-brace-format
+msgid "Surrogate characters are not allowed: U+{code_point:X}."
+msgstr ""
+
+#: validators.py:243
+#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "Aquest camp ha de ser únic per a la data \"{date_field}\"."
-#: validators.py:260
+#: validators.py:258
+#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "Aquest camp ha de ser únic per al mes \"{date_field}\"."
-#: validators.py:273
+#: validators.py:271
+#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "Aquest camp ha de ser únic per a l'any \"{date_field}\"."
-#: versioning.py:42
+#: versioning.py:40
msgid "Invalid version in \"Accept\" header."
msgstr "Versió invàlida al header \"Accept\"."
-#: versioning.py:73
+#: versioning.py:71
msgid "Invalid version in URL path."
msgstr "Versió invàlida a la URL."
-#: versioning.py:115
+#: versioning.py:116
msgid "Invalid version in URL path. Does not match any version namespace."
msgstr ""
-#: versioning.py:147
+#: versioning.py:148
msgid "Invalid version in hostname."
msgstr "Versió invàlida al hostname."
-#: versioning.py:169
+#: versioning.py:170
msgid "Invalid version in query parameter."
msgstr "Versió invàlida al paràmetre de consulta."
-
-#: views.py:88
-msgid "Permission denied."
-msgstr "Permís denegat."
diff --git a/rest_framework/locale/cs/LC_MESSAGES/django.mo b/rest_framework/locale/cs/LC_MESSAGES/django.mo
index 1561cd98c570a634e4abd2fcbda46ab764e56ca7..ebf7db5aa7b4cb3f8bc3c7e406e4246ea92501ce 100644
GIT binary patch
delta 3414
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\n"
+"POT-Creation-Date: 2020-10-13 21:45+0200\n"
+"PO-Revision-Date: 2020-10-13 19:45+0000\n"
+"Last-Translator: Xavier Ordoquy \n"
"Language-Team: Czech (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/cs/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: cs\n"
-"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
-#: authentication.py:73
+#: authentication.py:70
msgid "Invalid basic header. No credentials provided."
msgstr "Chybná hlavička. Nebyly poskytnuty přihlašovací údaje."
-#: authentication.py:76
+#: authentication.py:73
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "Chybná hlavička. Přihlašovací údaje by neměly obsahovat mezery."
-#: authentication.py:82
+#: authentication.py:83
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "Chybná hlavička. Přihlašovací údaje nebyly správně zakódovány pomocí base64."
-#: authentication.py:99
+#: authentication.py:101
msgid "Invalid username/password."
msgstr "Chybné uživatelské jméno nebo heslo."
-#: authentication.py:102 authentication.py:198
+#: authentication.py:104 authentication.py:206
msgid "User inactive or deleted."
msgstr "Uživatelský účet je neaktivní nebo byl smazán."
-#: authentication.py:176
+#: authentication.py:184
msgid "Invalid token header. No credentials provided."
msgstr "Chybná hlavička tokenu. Nebyly zadány přihlašovací údaje."
-#: authentication.py:179
+#: authentication.py:187
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Chybná hlavička tokenu. Přihlašovací údaje by neměly obsahovat mezery."
-#: authentication.py:185
+#: authentication.py:193
msgid ""
"Invalid token header. Token string should not contain invalid characters."
-msgstr ""
+msgstr "Chybná hlavička s tokenem. Token nesmí obsahovat nevalidní znaky."
-#: authentication.py:195
+#: authentication.py:203
msgid "Invalid token."
msgstr "Chybný token."
#: authtoken/apps.py:7
msgid "Auth Token"
-msgstr ""
+msgstr "Autentizační token"
-#: authtoken/models.py:15
+#: authtoken/models.py:13
msgid "Key"
-msgstr ""
+msgstr "Klíč"
+
+#: authtoken/models.py:16
+msgid "User"
+msgstr "Uživatel"
#: authtoken/models.py:18
-msgid "User"
-msgstr ""
-
-#: authtoken/models.py:20
msgid "Created"
-msgstr ""
+msgstr "Vytvořeno"
-#: authtoken/models.py:29
+#: authtoken/models.py:27 authtoken/serializers.py:19
msgid "Token"
-msgstr ""
+msgstr "Token"
-#: authtoken/models.py:30
+#: authtoken/models.py:28
msgid "Tokens"
-msgstr ""
-
-#: authtoken/serializers.py:8
-msgid "Username"
-msgstr ""
+msgstr "Tokeny"
#: authtoken/serializers.py:9
+msgid "Username"
+msgstr "Uživatelské jméno"
+
+#: authtoken/serializers.py:13
msgid "Password"
-msgstr ""
+msgstr "Heslo"
-#: authtoken/serializers.py:20
-msgid "User account is disabled."
-msgstr "Uživatelský účet je uzamčen."
-
-#: authtoken/serializers.py:23
+#: authtoken/serializers.py:35
msgid "Unable to log in with provided credentials."
msgstr "Zadanými údaji se nebylo možné přihlásit."
-#: authtoken/serializers.py:26
+#: authtoken/serializers.py:38
msgid "Must include \"username\" and \"password\"."
msgstr "Musí obsahovat \"uživatelské jméno\" a \"heslo\"."
-#: exceptions.py:49
+#: exceptions.py:102
msgid "A server error occurred."
msgstr "Chyba na straně serveru."
-#: exceptions.py:84
+#: exceptions.py:142
+msgid "Invalid input."
+msgstr ""
+
+#: exceptions.py:161
msgid "Malformed request."
msgstr "Neplatný formát požadavku."
-#: exceptions.py:89
+#: exceptions.py:167
msgid "Incorrect authentication credentials."
msgstr "Chybné přihlašovací údaje."
-#: exceptions.py:94
+#: exceptions.py:173
msgid "Authentication credentials were not provided."
msgstr "Nebyly zadány přihlašovací údaje."
-#: exceptions.py:99
+#: exceptions.py:179
msgid "You do not have permission to perform this action."
msgstr "K této akci nemáte oprávnění."
-#: exceptions.py:104 views.py:81
+#: exceptions.py:185
msgid "Not found."
msgstr "Nenalezeno."
-#: exceptions.py:109
+#: exceptions.py:191
+#, python-brace-format
msgid "Method \"{method}\" not allowed."
msgstr "Metoda \"{method}\" není povolena."
-#: exceptions.py:120
+#: exceptions.py:202
msgid "Could not satisfy the request Accept header."
msgstr "Nelze vyhovět požadavku v hlavičce Accept."
-#: exceptions.py:132
+#: exceptions.py:212
+#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "Nepodporovaný media type \"{media_type}\" v požadavku."
-#: exceptions.py:145
+#: exceptions.py:223
msgid "Request was throttled."
msgstr "Požadavek byl limitován kvůli omezení počtu požadavků za časovou periodu."
-#: fields.py:269 relations.py:206 relations.py:239 validators.py:98
-#: validators.py:181
+#: exceptions.py:224
+#, python-brace-format
+msgid "Expected available in {wait} second."
+msgstr ""
+
+#: exceptions.py:225
+#, python-brace-format
+msgid "Expected available in {wait} seconds."
+msgstr ""
+
+#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
+#: validators.py:183
msgid "This field is required."
msgstr "Toto pole je vyžadováno."
-#: fields.py:270
+#: fields.py:317
msgid "This field may not be null."
msgstr "Toto pole nesmí být prázdné (null)."
-#: fields.py:608 fields.py:639
-msgid "\"{input}\" is not a valid boolean."
-msgstr "\"{input}\" nelze použít jako typ boolean."
+#: fields.py:701
+msgid "Must be a valid boolean."
+msgstr ""
-#: fields.py:674
+#: fields.py:766
+msgid "Not a valid string."
+msgstr ""
+
+#: fields.py:767
msgid "This field may not be blank."
msgstr "Toto pole nesmí být prázdné."
-#: fields.py:675 fields.py:1675
+#: fields.py:768 fields.py:1881
+#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Zkontrolujte, že toto pole není delší než {max_length} znaků."
-#: fields.py:676
+#: fields.py:769
+#, python-brace-format
msgid "Ensure this field has at least {min_length} characters."
msgstr "Zkontrolujte, že toto pole obsahuje alespoň {min_length} znaků."
-#: fields.py:713
+#: fields.py:816
msgid "Enter a valid email address."
msgstr "Vložte platnou e-mailovou adresu."
-#: fields.py:724
+#: fields.py:827
msgid "This value does not match the required pattern."
msgstr "Hodnota v tomto poli neodpovídá požadovanému formátu."
-#: fields.py:735
+#: fields.py:838
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Vložte platnou \"zkrácenou formu\" obsahující pouze malá písmena, čísla, spojovník nebo podtržítko."
-#: fields.py:747
+#: fields.py:839
+msgid ""
+"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
+"or hyphens."
+msgstr ""
+
+#: fields.py:854
msgid "Enter a valid URL."
msgstr "Vložte platný odkaz."
-#: fields.py:760
-msgid "\"{value}\" is not a valid UUID."
-msgstr "\"{value}\" není platné UUID."
-
-#: fields.py:796
-msgid "Enter a valid IPv4 or IPv6 address."
+#: fields.py:867
+msgid "Must be a valid UUID."
msgstr ""
-#: fields.py:821
+#: fields.py:903
+msgid "Enter a valid IPv4 or IPv6 address."
+msgstr "Vložte platnou IPv4 nebo IPv6 adresu."
+
+#: fields.py:931
msgid "A valid integer is required."
msgstr "Je vyžadováno celé číslo."
-#: fields.py:822 fields.py:857 fields.py:891
+#: fields.py:932 fields.py:969 fields.py:1005 fields.py:1366
+#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Zkontrolujte, že hodnota je menší nebo rovna {max_value}."
-#: fields.py:823 fields.py:858 fields.py:892
+#: fields.py:933 fields.py:970 fields.py:1006 fields.py:1367
+#, python-brace-format
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Zkontrolujte, že hodnota je větší nebo rovna {min_value}."
-#: fields.py:824 fields.py:859 fields.py:896
+#: fields.py:934 fields.py:971 fields.py:1010
msgid "String value too large."
msgstr "Řetězec je příliš dlouhý."
-#: fields.py:856 fields.py:890
+#: fields.py:968 fields.py:1004
msgid "A valid number is required."
msgstr "Je vyžadováno číslo."
-#: fields.py:893
+#: fields.py:1007
+#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Zkontrolujte, že číslo neobsahuje více než {max_digits} čislic."
-#: fields.py:894
+#: fields.py:1008
+#, python-brace-format
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Zkontrolujte, že číslo nemá více než {max_decimal_places} desetinných míst."
-#: fields.py:895
+#: fields.py:1009
+#, python-brace-format
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Zkontrolujte, že číslo neobsahuje více než {max_whole_digits} čislic před desetinnou čárkou."
-#: fields.py:1025
+#: fields.py:1148
+#, python-brace-format
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "Chybný formát data a času. Použijte jeden z těchto formátů: {format}."
-#: fields.py:1026
+#: fields.py:1149
msgid "Expected a datetime but got a date."
msgstr "Bylo zadáno pouze datum bez času."
-#: fields.py:1103
+#: fields.py:1150
+#, python-brace-format
+msgid "Invalid datetime for the timezone \"{timezone}\"."
+msgstr ""
+
+#: fields.py:1151
+msgid "Datetime value out of range."
+msgstr ""
+
+#: fields.py:1236
+#, python-brace-format
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "Chybný formát data. Použijte jeden z těchto formátů: {format}."
-#: fields.py:1104
+#: fields.py:1237
msgid "Expected a date but got a datetime."
msgstr "Bylo zadáno datum a čas, místo samotného data."
-#: fields.py:1170
+#: fields.py:1303
+#, python-brace-format
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "Chybný formát času. Použijte jeden z těchto formátů: {format}."
-#: fields.py:1232
+#: fields.py:1365
+#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}."
-msgstr ""
+msgstr "Trvání má nesprávný formát. Použijte jeden z následujících: {format}."
-#: fields.py:1251 fields.py:1300
+#: fields.py:1399 fields.py:1456
+#, python-brace-format
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" není platnou možností."
-#: fields.py:1254 relations.py:71 relations.py:441
+#: fields.py:1402
+#, python-brace-format
msgid "More than {count} items..."
-msgstr ""
+msgstr "Více než {count} položek..."
-#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524
+#: fields.py:1457 fields.py:1603 relations.py:485 serializers.py:570
+#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "Byl očekáván seznam položek ale nalezen \"{input_type}\"."
-#: fields.py:1302
+#: fields.py:1458
msgid "This selection may not be empty."
-msgstr ""
+msgstr "Tento výběr by neměl být prázdný."
-#: fields.py:1339
+#: fields.py:1495
+#, python-brace-format
msgid "\"{input}\" is not a valid path choice."
-msgstr ""
+msgstr "\"{input}\" není validní cesta k souboru."
-#: fields.py:1358
+#: fields.py:1514
msgid "No file was submitted."
msgstr "Nebyl zaslán žádný soubor."
-#: fields.py:1359
+#: fields.py:1515
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "Zaslaná data neobsahují soubor. Zkontrolujte typ kódování ve formuláři."
-#: fields.py:1360
+#: fields.py:1516
msgid "No filename could be determined."
msgstr "Nebylo možné zjistit jméno souboru."
-#: fields.py:1361
+#: fields.py:1517
msgid "The submitted file is empty."
msgstr "Zaslaný soubor je prázdný."
-#: fields.py:1362
+#: fields.py:1518
+#, python-brace-format
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "Zajistěte, aby jméno souboru obsahovalo maximálně {max_length} znaků (teď má {length} znaků)."
-#: fields.py:1410
+#: fields.py:1566
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "Nahrajte platný obrázek. Nahraný soubor buď není obrázkem nebo je poškozen."
-#: fields.py:1449 relations.py:438 serializers.py:525
+#: fields.py:1604 relations.py:486 serializers.py:571
msgid "This list may not be empty."
+msgstr "Tento list by neměl být prázdný."
+
+#: fields.py:1605
+#, python-brace-format
+msgid "Ensure this field has at least {min_length} elements."
msgstr ""
-#: fields.py:1502
+#: fields.py:1606
+#, python-brace-format
+msgid "Ensure this field has no more than {max_length} elements."
+msgstr ""
+
+#: fields.py:1682
+#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "Byl očekáván slovník položek ale nalezen \"{input_type}\"."
-#: fields.py:1549
+#: fields.py:1683
+msgid "This dictionary may not be empty."
+msgstr ""
+
+#: fields.py:1755
msgid "Value must be valid JSON."
+msgstr "Hodnota musí být platná hodnota JSON."
+
+#: filters.py:49 templates/rest_framework/filters/search.html:2
+msgid "Search"
+msgstr "Hledat"
+
+#: filters.py:50
+msgid "A search term."
msgstr ""
-#: filters.py:36 templates/rest_framework/filters/django_filter.html:5
-msgid "Submit"
+#: filters.py:180 templates/rest_framework/filters/ordering.html:3
+msgid "Ordering"
+msgstr "Řazení"
+
+#: filters.py:181
+msgid "Which field to use when ordering the results."
msgstr ""
-#: filters.py:336
+#: filters.py:287
msgid "ascending"
-msgstr ""
+msgstr "vzestupně"
-#: filters.py:337
+#: filters.py:288
msgid "descending"
+msgstr "sestupně"
+
+#: pagination.py:174
+msgid "A page number within the paginated result set."
msgstr ""
-#: pagination.py:193
+#: pagination.py:179 pagination.py:372 pagination.py:590
+msgid "Number of results to return per page."
+msgstr ""
+
+#: pagination.py:189
msgid "Invalid page."
+msgstr "Nevalidní strana."
+
+#: pagination.py:374
+msgid "The initial index from which to return the results."
msgstr ""
-#: pagination.py:427
-msgid "Invalid cursor"
-msgstr "Chybný kurzor."
+#: pagination.py:581
+msgid "The pagination cursor value."
+msgstr ""
-#: relations.py:207
+#: pagination.py:583
+msgid "Invalid cursor"
+msgstr "Chybný kurzor"
+
+#: relations.py:246
+#, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "Chybný primární klíč \"{pk_value}\" - objekt neexistuje."
-#: relations.py:208
+#: relations.py:247
+#, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "Chybný typ. Byl přijat typ {data_type} místo hodnoty primárního klíče."
-#: relations.py:240
+#: relations.py:280
msgid "Invalid hyperlink - No URL match."
msgstr "Chybný odkaz - nebyla nalezena žádní shoda."
-#: relations.py:241
+#: relations.py:281
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "Chybný odkaz - byla nalezena neplatná shoda."
-#: relations.py:242
+#: relations.py:282
msgid "Invalid hyperlink - Object does not exist."
msgstr "Chybný odkaz - objekt neexistuje."
-#: relations.py:243
+#: relations.py:283
+#, python-brace-format
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "Chybný typ. Byl přijat typ {data_type} místo očekávaného odkazu."
-#: relations.py:401
+#: relations.py:448
+#, python-brace-format
msgid "Object with {slug_name}={value} does not exist."
msgstr "Objekt s {slug_name}={value} neexistuje."
-#: relations.py:402
+#: relations.py:449
msgid "Invalid value."
msgstr "Chybná hodnota."
-#: serializers.py:326
+#: schemas/utils.py:32
+msgid "unique integer value"
+msgstr ""
+
+#: schemas/utils.py:34
+msgid "UUID string"
+msgstr ""
+
+#: schemas/utils.py:36
+msgid "unique value"
+msgstr ""
+
+#: schemas/utils.py:38
+#, python-brace-format
+msgid "A {value_type} identifying this {name}."
+msgstr ""
+
+#: serializers.py:337
+#, python-brace-format
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "Chybná data. Byl přijat typ {datatype} místo očekávaného slovníku."
#: templates/rest_framework/admin.html:116
-#: templates/rest_framework/base.html:128
+#: templates/rest_framework/base.html:136
+msgid "Extra Actions"
+msgstr ""
+
+#: templates/rest_framework/admin.html:130
+#: templates/rest_framework/base.html:150
msgid "Filters"
+msgstr "Filtry"
+
+#: templates/rest_framework/base.html:37
+msgid "navbar"
msgstr ""
-#: templates/rest_framework/filters/django_filter.html:2
-#: templates/rest_framework/filters/django_filter_crispyforms.html:4
-msgid "Field filters"
+#: templates/rest_framework/base.html:75
+msgid "content"
msgstr ""
-#: templates/rest_framework/filters/ordering.html:3
-msgid "Ordering"
+#: templates/rest_framework/base.html:78
+msgid "request form"
msgstr ""
-#: templates/rest_framework/filters/search.html:2
-msgid "Search"
+#: templates/rest_framework/base.html:157
+msgid "main content"
msgstr ""
-#: templates/rest_framework/horizontal/radio.html:2
-#: templates/rest_framework/inline/radio.html:2
-#: templates/rest_framework/vertical/radio.html:2
+#: templates/rest_framework/base.html:173
+msgid "request info"
+msgstr ""
+
+#: templates/rest_framework/base.html:177
+msgid "response info"
+msgstr ""
+
+#: templates/rest_framework/horizontal/radio.html:4
+#: templates/rest_framework/inline/radio.html:3
+#: templates/rest_framework/vertical/radio.html:3
msgid "None"
-msgstr ""
+msgstr "Neuvedeno"
-#: templates/rest_framework/horizontal/select_multiple.html:2
-#: templates/rest_framework/inline/select_multiple.html:2
-#: templates/rest_framework/vertical/select_multiple.html:2
+#: templates/rest_framework/horizontal/select_multiple.html:4
+#: templates/rest_framework/inline/select_multiple.html:3
+#: templates/rest_framework/vertical/select_multiple.html:3
msgid "No items to select."
-msgstr ""
+msgstr "Žádné položky k výběru."
-#: validators.py:43
+#: validators.py:39
msgid "This field must be unique."
msgstr "Tato položka musí být unikátní."
-#: validators.py:97
+#: validators.py:89
+#, python-brace-format
msgid "The fields {field_names} must make a unique set."
msgstr "Položka {field_names} musí tvořit unikátní množinu."
-#: validators.py:245
+#: validators.py:171
+#, python-brace-format
+msgid "Surrogate characters are not allowed: U+{code_point:X}."
+msgstr ""
+
+#: validators.py:243
+#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "Tato položka musí být pro datum \"{date_field}\" unikátní."
-#: validators.py:260
+#: validators.py:258
+#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "Tato položka musí být pro měsíc \"{date_field}\" unikátní."
-#: validators.py:273
+#: validators.py:271
+#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "Tato položka musí být pro rok \"{date_field}\" unikátní."
-#: versioning.py:42
+#: versioning.py:40
msgid "Invalid version in \"Accept\" header."
msgstr "Chybné číslo verze v hlavičce Accept."
-#: versioning.py:73
+#: versioning.py:71
msgid "Invalid version in URL path."
msgstr "Chybné číslo verze v odkazu."
-#: versioning.py:115
+#: versioning.py:116
msgid "Invalid version in URL path. Does not match any version namespace."
-msgstr ""
+msgstr "Nevalidní verze v URL cestě. Neodpovídá žádnému z jmenných prostorů pro verze."
-#: versioning.py:147
+#: versioning.py:148
msgid "Invalid version in hostname."
msgstr "Chybné číslo verze v hostname."
-#: versioning.py:169
+#: versioning.py:170
msgid "Invalid version in query parameter."
msgstr "Chybné čislo verze v URL parametru."
-
-#: views.py:88
-msgid "Permission denied."
-msgstr ""
diff --git a/rest_framework/locale/da/LC_MESSAGES/django.mo b/rest_framework/locale/da/LC_MESSAGES/django.mo
index 77fd7c2abbe3eda1b7b4e4862469a3970ef24175..d70bc13a51ec471f0bb2093aac13daead653e7e2 100644
GIT binary patch
delta 2193
zcmYk-e@xVM9LMqZb({hkBEQZ{!7qgz5*(mFh^U7ll7OJ30#Yc-3R7;eN%BX#TpN=%
z=GOJQRvR15wOy;-@{hLG)@sg{Te@tP(^+k8%?(?Bm|MB^e0}fB)@QtVd_LdfzTeO1
z^L~H6-0S_VJAN}S<8`AA6KjY9zgZlYFX0z0Gt}4y*8c
zT#EN`I|j1M+Ofm)Ebi2!exwuSgOY5s{Wy$|;d$JQ3)qR3%gmZ_67~6~*oA-KBiOjy
zEDr}!6FY&r{)*?vsQX>P2F!U-6J)7vbhNTA)B`5436r=Df5*qMJjX18$B-X;n_v37
zb6A1DU@2y=V6s?+eANzMEskOY-@qsE8{DF;UXh!=VK+X+c*q;SfLg&cYDFKRw(^2E
z&R&`RJdA#>ccONn2TO1Sb-&XX#`mxYzeWeIV?3YEzjQJ%mv6J%R){s|AnCJS&jHj8
zMo{H3g+Y7?Yw;~4r*;uF;g#H6pGQ#Rc2un#!A9ia@pP`e3k#?juPaFZVH6$4L#PS8
zh}wZ92JlbRMDF6FcpvLwmBdcQA`?d=<4*x8ihE%@y3W
z4G*El^QhvxfjTzD>{~M)#ZCAD?#7=`#a6-oGFR(E<^C9!;|wbIpI{WP;bsgmYwcWo
zAD#7dhOh~zu@%2W&Dh7?I&e2?Yu`i)-7cbTbPe@@g0<;W)PkDO2y)VF7B%sAkWIF$
z*on81=a7Gg6TpQjWO6p+jX%UP#^+JjucN+r4_UOem8M@bhfph-MRwU7#Ae|={2dFyPMN}&PYI_M4jq;~|8cItOp(8d-1PHYjVw>Ab|5Do2
zdI{A?m{65JK&W<_i4KB)8E&zH^$`aM9eW)~x2YgBSWDC-W;2Tu-(^Npp{%`#1KbTwx~ZFL<#8=bmM&Gik5WH4CPe|U0ibi(PGI2wEYJx+ZUqZ`2`98QbvjfYNTo{p1BumTt*OzniWFKvTaYRjx1C|L>~@yyPHTji
z4aOIEs%a1vqKSzRBTsCiNW_p3O*GNOXu<+Jl~w5)4&(kk$`3&O%JnWBO!64%ksI9&{kNj%|Ii=YfD^cSn
zR1pneBOXE3z;rU=TGvfSUMmwD4?-
zP6eGSSdTYwJxAe1a?R2C7DKxY&-Xuoa`o2{h+XsZHY(co|jwx3B{%Dzg(m
zh+7$7K&Adqq?l7?rIr1`M%056sAKmQYGPNBqipV=R&XELK2yIm`yS{)J?Ip2!pwDK
zac0UN=aKhij7w41n{hkt!E&Adcj>I*!l$T}+(EX_{Ej+iMSPG1vlgqd4=G?XhAh&Y
zL{6%CA60CZQ4_w3s)esmwelAhU@o)KM2oPJ=bLgmdDx0rq8t$Z3uidn+GQa)xgrZRs(N5wFX`FIm&;%(Ff?qVla
zvOi%QLVf=wDl^~s{)##c#iU7@S%|gRgj&!c-#E@-yhr=5YSha^fB2wricCMD^rP%L6wL-Bab3Awcsg@s!y0>`*G>iqw%-Mf3b
z(>n_9=M_0I+d349c}^mk?kcX#FWKcJMk2{%B<@IC9u&kGS^ea5PyPPlVIomzB;~;96cJ77N*4W;MyEHFoV>!j4Q#
zFQ_7dOCK)FNm!9+)Uh1T3cnPyBVo_VEUji?b$(I*wM$_~YfCukfr`QWIURO5o0u6R7=
diff --git a/rest_framework/locale/da/LC_MESSAGES/django.po b/rest_framework/locale/da/LC_MESSAGES/django.po
index 900695649..574066f2a 100644
--- a/rest_framework/locale/da/LC_MESSAGES/django.po
+++ b/rest_framework/locale/da/LC_MESSAGES/django.po
@@ -9,9 +9,9 @@ msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-07-12 16:13+0100\n"
-"PO-Revision-Date: 2017-08-03 14:58+0000\n"
-"Last-Translator: Mads Jensen \n"
+"POT-Creation-Date: 2020-10-13 21:45+0200\n"
+"PO-Revision-Date: 2020-10-13 19:45+0000\n"
+"Last-Translator: Xavier Ordoquy \n"
"Language-Team: Danish (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/da/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -19,40 +19,40 @@ msgstr ""
"Language: da\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: authentication.py:73
+#: authentication.py:70
msgid "Invalid basic header. No credentials provided."
msgstr "Ugyldig basic header. Ingen legitimation angivet."
-#: authentication.py:76
+#: authentication.py:73
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "Ugyldig basic header. Legitimationsstrenge må ikke indeholde mellemrum."
-#: authentication.py:82
+#: authentication.py:83
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "Ugyldig basic header. Legitimationen er ikke base64 encoded på korrekt vis."
-#: authentication.py:99
+#: authentication.py:101
msgid "Invalid username/password."
msgstr "Ugyldigt brugernavn/kodeord."
-#: authentication.py:102 authentication.py:198
+#: authentication.py:104 authentication.py:206
msgid "User inactive or deleted."
msgstr "Inaktiv eller slettet bruger."
-#: authentication.py:176
+#: authentication.py:184
msgid "Invalid token header. No credentials provided."
msgstr "Ugyldig token header."
-#: authentication.py:179
+#: authentication.py:187
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Ugyldig token header. Token-strenge må ikke indeholde mellemrum."
-#: authentication.py:185
+#: authentication.py:193
msgid ""
"Invalid token header. Token string should not contain invalid characters."
msgstr "Ugyldig token header. Token streng bør ikke indeholde ugyldige karakterer."
-#: authentication.py:195
+#: authentication.py:203
msgid "Invalid token."
msgstr "Ugyldigt token."
@@ -60,382 +60,515 @@ msgstr "Ugyldigt token."
msgid "Auth Token"
msgstr ""
-#: authtoken/models.py:15
+#: authtoken/models.py:13
msgid "Key"
msgstr "Nøgle"
-#: authtoken/models.py:18
+#: authtoken/models.py:16
msgid "User"
msgstr "Bruger"
-#: authtoken/models.py:20
+#: authtoken/models.py:18
msgid "Created"
msgstr "Oprettet"
-#: authtoken/models.py:29
+#: authtoken/models.py:27 authtoken/serializers.py:19
msgid "Token"
msgstr "Token"
-#: authtoken/models.py:30
+#: authtoken/models.py:28
msgid "Tokens"
msgstr "Tokens"
-#: authtoken/serializers.py:8
+#: authtoken/serializers.py:9
msgid "Username"
msgstr "Brugernavn"
-#: authtoken/serializers.py:9
+#: authtoken/serializers.py:13
msgid "Password"
msgstr "Kodeord"
-#: authtoken/serializers.py:20
-msgid "User account is disabled."
-msgstr "Brugerkontoen er deaktiveret."
-
-#: authtoken/serializers.py:23
+#: authtoken/serializers.py:35
msgid "Unable to log in with provided credentials."
msgstr "Kunne ikke logge ind med den angivne legitimation."
-#: authtoken/serializers.py:26
+#: authtoken/serializers.py:38
msgid "Must include \"username\" and \"password\"."
msgstr "Skal indeholde \"username\" og \"password\"."
-#: exceptions.py:49
+#: exceptions.py:102
msgid "A server error occurred."
msgstr "Der er sket en serverfejl."
-#: exceptions.py:84
+#: exceptions.py:142
+msgid "Invalid input."
+msgstr ""
+
+#: exceptions.py:161
msgid "Malformed request."
msgstr "Misdannet forespørgsel."
-#: exceptions.py:89
+#: exceptions.py:167
msgid "Incorrect authentication credentials."
msgstr "Ugyldig legitimation til autentificering."
-#: exceptions.py:94
+#: exceptions.py:173
msgid "Authentication credentials were not provided."
msgstr "Legitimation til autentificering blev ikke angivet."
-#: exceptions.py:99
+#: exceptions.py:179
msgid "You do not have permission to perform this action."
msgstr "Du har ikke lov til at udføre denne handling."
-#: exceptions.py:104 views.py:81
+#: exceptions.py:185
msgid "Not found."
msgstr "Ikke fundet."
-#: exceptions.py:109
+#: exceptions.py:191
+#, python-brace-format
msgid "Method \"{method}\" not allowed."
msgstr "Metoden \"{method}\" er ikke tilladt."
-#: exceptions.py:120
+#: exceptions.py:202
msgid "Could not satisfy the request Accept header."
msgstr "Kunne ikke efterkomme forespørgslens Accept header."
-#: exceptions.py:132
+#: exceptions.py:212
+#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "Forespørgslens media type, \"{media_type}\", er ikke understøttet."
-#: exceptions.py:145
+#: exceptions.py:223
msgid "Request was throttled."
msgstr "Forespørgslen blev neddroslet."
-#: fields.py:269 relations.py:206 relations.py:239 validators.py:98
-#: validators.py:181
+#: exceptions.py:224
+#, python-brace-format
+msgid "Expected available in {wait} second."
+msgstr ""
+
+#: exceptions.py:225
+#, python-brace-format
+msgid "Expected available in {wait} seconds."
+msgstr ""
+
+#: fields.py:316 relations.py:245 relations.py:279 validators.py:90
+#: validators.py:183
msgid "This field is required."
msgstr "Dette felt er påkrævet."
-#: fields.py:270
+#: fields.py:317
msgid "This field may not be null."
msgstr "Dette felt må ikke være null."
-#: fields.py:608 fields.py:639
-msgid "\"{input}\" is not a valid boolean."
-msgstr "\"{input}\" er ikke en tilladt boolsk værdi."
+#: fields.py:701
+msgid "Must be a valid boolean."
+msgstr ""
-#: fields.py:674
+#: fields.py:766
+msgid "Not a valid string."
+msgstr ""
+
+#: fields.py:767
msgid "This field may not be blank."
msgstr "Dette felt må ikke være tomt."
-#: fields.py:675 fields.py:1675
+#: fields.py:768 fields.py:1881
+#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Tjek at dette felt ikke indeholder flere end {max_length} tegn."
-#: fields.py:676
+#: fields.py:769
+#, python-brace-format
msgid "Ensure this field has at least {min_length} characters."
msgstr "Tjek at dette felt indeholder mindst {min_length} tegn."
-#: fields.py:713
+#: fields.py:816
msgid "Enter a valid email address."
msgstr "Angiv en gyldig e-mailadresse."
-#: fields.py:724
+#: fields.py:827
msgid "This value does not match the required pattern."
msgstr "Denne værdi passer ikke med det påkrævede mønster."
-#: fields.py:735
+#: fields.py:838
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Indtast en gyldig \"slug\", bestående af bogstaver, tal, bund- og bindestreger."
-#: fields.py:747
+#: fields.py:839
+msgid ""
+"Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, "
+"or hyphens."
+msgstr ""
+
+#: fields.py:854
msgid "Enter a valid URL."
msgstr "Indtast en gyldig URL."
-#: fields.py:760
-msgid "\"{value}\" is not a valid UUID."
-msgstr "\"{value}\" er ikke et gyldigt UUID."
+#: fields.py:867
+msgid "Must be a valid UUID."
+msgstr ""
-#: fields.py:796
+#: fields.py:903
msgid "Enter a valid IPv4 or IPv6 address."
msgstr "Indtast en gyldig IPv4 eller IPv6 adresse."
-#: fields.py:821
+#: fields.py:931
msgid "A valid integer is required."
msgstr "Et gyldigt heltal er påkrævet."
-#: fields.py:822 fields.py:857 fields.py:891
+#: fields.py:932 fields.py:969 fields.py:1005 fields.py:1366
+#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Tjek at værdien er mindre end eller lig med {max_value}."
-#: fields.py:823 fields.py:858 fields.py:892
+#: fields.py:933 fields.py:970 fields.py:1006 fields.py:1367
+#, python-brace-format
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Tjek at værdien er større end eller lig med {min_value}."
-#: fields.py:824 fields.py:859 fields.py:896
+#: fields.py:934 fields.py:971 fields.py:1010
msgid "String value too large."
msgstr "Strengværdien er for stor."
-#: fields.py:856 fields.py:890
+#: fields.py:968 fields.py:1004
msgid "A valid number is required."
msgstr "Et gyldigt tal er påkrævet."
-#: fields.py:893
+#: fields.py:1007
+#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Tjek at der ikke er flere end {max_digits} cifre i alt."
-#: fields.py:894
+#: fields.py:1008
+#, python-brace-format
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Tjek at der ikke er flere end {max_decimal_places} cifre efter kommaet."
-#: fields.py:895
+#: fields.py:1009
+#, python-brace-format
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Tjek at der ikke er flere end {max_whole_digits} cifre før kommaet."
-#: fields.py:1025
+#: fields.py:1148
+#, python-brace-format
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "Datotid har et forkert format. Brug i stedet et af disse formater: {format}."
-#: fields.py:1026
+#: fields.py:1149
msgid "Expected a datetime but got a date."
msgstr "Forventede en datotid, men fik en dato."
-#: fields.py:1103
+#: fields.py:1150
+#, python-brace-format
+msgid "Invalid datetime for the timezone \"{timezone}\"."
+msgstr ""
+
+#: fields.py:1151
+msgid "Datetime value out of range."
+msgstr ""
+
+#: fields.py:1236
+#, python-brace-format
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "Dato har et forkert format. Brug i stedet et af disse formater: {format}."
-#: fields.py:1104
+#: fields.py:1237
msgid "Expected a date but got a datetime."
msgstr "Forventede en dato men fik en datotid."
-#: fields.py:1170
+#: fields.py:1303
+#, python-brace-format
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "Klokkeslæt har forkert format. Brug i stedet et af disse formater: {format}. "
-#: fields.py:1232
+#: fields.py:1365
+#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}."
msgstr "Varighed har forkert format. Brug istedet et af følgende formater: {format}."
-#: fields.py:1251 fields.py:1300
+#: fields.py:1399 fields.py:1456
+#, python-brace-format
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" er ikke et gyldigt valg."
-#: fields.py:1254 relations.py:71 relations.py:441
+#: fields.py:1402
+#, python-brace-format
msgid "More than {count} items..."
msgstr "Flere end {count} objekter..."
-#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524
+#: fields.py:1457 fields.py:1603 relations.py:485 serializers.py:570
+#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "Forventede en liste, men fik noget af typen \"{input_type}\"."
-#: fields.py:1302
+#: fields.py:1458
msgid "This selection may not be empty."
msgstr "Dette valg kan være tomt."
-#: fields.py:1339
+#: fields.py:1495
+#, python-brace-format
msgid "\"{input}\" is not a valid path choice."
msgstr "\"{input}\" er ikke et gyldigt valg af adresse."
-#: fields.py:1358
+#: fields.py:1514
msgid "No file was submitted."
msgstr "Ingen medsendt fil."
-#: fields.py:1359
+#: fields.py:1515
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "Det medsendte data var ikke en fil. Tjek typen af indkodning på formularen."
-#: fields.py:1360
+#: fields.py:1516
msgid "No filename could be determined."
msgstr "Filnavnet kunne ikke afgøres."
-#: fields.py:1361
+#: fields.py:1517
msgid "The submitted file is empty."
msgstr "Den medsendte fil er tom."
-#: fields.py:1362
+#: fields.py:1518
+#, python-brace-format
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "Sørg for at filnavnet er højst {max_length} langt (det er {length})."
-#: fields.py:1410
+#: fields.py:1566
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "Medsend et gyldigt billede. Den medsendte fil var enten ikke et billede eller billedfilen var ødelagt."
-#: fields.py:1449 relations.py:438 serializers.py:525
+#: fields.py:1604 relations.py:486 serializers.py:571
msgid "This list may not be empty."
msgstr "Denne liste er muligvis ikke tom."
-#: fields.py:1502
+#: fields.py:1605
+#, python-brace-format
+msgid "Ensure this field has at least {min_length} elements."
+msgstr ""
+
+#: fields.py:1606
+#, python-brace-format
+msgid "Ensure this field has no more than {max_length} elements."
+msgstr ""
+
+#: fields.py:1682
+#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "Forventede en dictionary, men fik noget af typen \"{input_type}\"."
-#: fields.py:1549
+#: fields.py:1683
+msgid "This dictionary may not be empty."
+msgstr ""
+
+#: fields.py:1755
msgid "Value must be valid JSON."
msgstr "Værdi skal være gyldig JSON."
-#: filters.py:36 templates/rest_framework/filters/django_filter.html:5
-msgid "Submit"
-msgstr "Indsend."
+#: filters.py:49 templates/rest_framework/filters/search.html:2
+msgid "Search"
+msgstr "Søg"
-#: filters.py:336
+#: filters.py:50
+msgid "A search term."
+msgstr ""
+
+#: filters.py:180 templates/rest_framework/filters/ordering.html:3
+msgid "Ordering"
+msgstr "Sortering"
+
+#: filters.py:181
+msgid "Which field to use when ordering the results."
+msgstr ""
+
+#: filters.py:287
msgid "ascending"
msgstr "stigende"
-#: filters.py:337
+#: filters.py:288
msgid "descending"
msgstr "faldende"
-#: pagination.py:193
+#: pagination.py:174
+msgid "A page number within the paginated result set."
+msgstr ""
+
+#: pagination.py:179 pagination.py:372 pagination.py:590
+msgid "Number of results to return per page."
+msgstr ""
+
+#: pagination.py:189
msgid "Invalid page."
msgstr "Ugyldig side"
-#: pagination.py:427
+#: pagination.py:374
+msgid "The initial index from which to return the results."
+msgstr ""
+
+#: pagination.py:581
+msgid "The pagination cursor value."
+msgstr ""
+
+#: pagination.py:583
msgid "Invalid cursor"
msgstr "Ugyldig cursor"
-#: relations.py:207
+#: relations.py:246
+#, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "Ugyldig primærnøgle \"{pk_value}\" - objektet findes ikke."
-#: relations.py:208
+#: relations.py:247
+#, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "Ugyldig type. Forventet værdi er primærnøgle, fik {data_type}."
-#: relations.py:240
+#: relations.py:280
msgid "Invalid hyperlink - No URL match."
msgstr "Ugyldigt hyperlink - intet URL match."
-#: relations.py:241
+#: relations.py:281
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "Ugyldigt hyperlink - forkert URL match."
-#: relations.py:242
+#: relations.py:282
msgid "Invalid hyperlink - Object does not exist."
msgstr "Ugyldigt hyperlink - objektet findes ikke."
-#: relations.py:243
+#: relations.py:283
+#, python-brace-format
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "Forkert type. Forventede en URL-streng, fik {data_type}."
-#: relations.py:401
+#: relations.py:448
+#, python-brace-format
msgid "Object with {slug_name}={value} does not exist."
msgstr "Object med {slug_name}={value} findes ikke."
-#: relations.py:402
+#: relations.py:449
msgid "Invalid value."
msgstr "Ugyldig værdi."
-#: serializers.py:326
+#: schemas/utils.py:32
+msgid "unique integer value"
+msgstr ""
+
+#: schemas/utils.py:34
+msgid "UUID string"
+msgstr ""
+
+#: schemas/utils.py:36
+msgid "unique value"
+msgstr ""
+
+#: schemas/utils.py:38
+#, python-brace-format
+msgid "A {value_type} identifying this {name}."
+msgstr ""
+
+#: serializers.py:337
+#, python-brace-format
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "Ugyldig data. Forventede en dictionary, men fik {datatype}."
#: templates/rest_framework/admin.html:116
-#: templates/rest_framework/base.html:128
+#: templates/rest_framework/base.html:136
+msgid "Extra Actions"
+msgstr ""
+
+#: templates/rest_framework/admin.html:130
+#: templates/rest_framework/base.html:150
msgid "Filters"
msgstr "Filtre"
-#: templates/rest_framework/filters/django_filter.html:2
-#: templates/rest_framework/filters/django_filter_crispyforms.html:4
-msgid "Field filters"
-msgstr "Søgefiltre"
+#: templates/rest_framework/base.html:37
+msgid "navbar"
+msgstr ""
-#: templates/rest_framework/filters/ordering.html:3
-msgid "Ordering"
-msgstr "Sortering"
+#: templates/rest_framework/base.html:75
+msgid "content"
+msgstr ""
-#: templates/rest_framework/filters/search.html:2
-msgid "Search"
-msgstr "Søg"
+#: templates/rest_framework/base.html:78
+msgid "request form"
+msgstr ""
-#: templates/rest_framework/horizontal/radio.html:2
-#: templates/rest_framework/inline/radio.html:2
-#: templates/rest_framework/vertical/radio.html:2
+#: templates/rest_framework/base.html:157
+msgid "main content"
+msgstr ""
+
+#: templates/rest_framework/base.html:173
+msgid "request info"
+msgstr ""
+
+#: templates/rest_framework/base.html:177
+msgid "response info"
+msgstr ""
+
+#: templates/rest_framework/horizontal/radio.html:4
+#: templates/rest_framework/inline/radio.html:3
+#: templates/rest_framework/vertical/radio.html:3
msgid "None"
msgstr "Ingen"
-#: templates/rest_framework/horizontal/select_multiple.html:2
-#: templates/rest_framework/inline/select_multiple.html:2
-#: templates/rest_framework/vertical/select_multiple.html:2
+#: templates/rest_framework/horizontal/select_multiple.html:4
+#: templates/rest_framework/inline/select_multiple.html:3
+#: templates/rest_framework/vertical/select_multiple.html:3
msgid "No items to select."
msgstr "Intet at vælge."
-#: validators.py:43
+#: validators.py:39
msgid "This field must be unique."
msgstr "Dette felt skal være unikt."
-#: validators.py:97
+#: validators.py:89
+#, python-brace-format
msgid "The fields {field_names} must make a unique set."
msgstr "Felterne {field_names} skal udgøre et unikt sæt."
-#: validators.py:245
+#: validators.py:171
+#, python-brace-format
+msgid "Surrogate characters are not allowed: U+{code_point:X}."
+msgstr ""
+
+#: validators.py:243
+#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "Dette felt skal være unikt for \"{date_field}\"-datoen."
-#: validators.py:260
+#: validators.py:258
+#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "Dette felt skal være unikt for \"{date_field}\"-måneden."
-#: validators.py:273
+#: validators.py:271
+#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "Dette felt skal være unikt for \"{date_field}\"-året."
-#: versioning.py:42
+#: versioning.py:40
msgid "Invalid version in \"Accept\" header."
msgstr "Ugyldig version i \"Accept\" headeren."
-#: versioning.py:73
+#: versioning.py:71
msgid "Invalid version in URL path."
msgstr "Ugyldig version i URL-stien."
-#: versioning.py:115
+#: versioning.py:116
msgid "Invalid version in URL path. Does not match any version namespace."
msgstr "Ugyldig version in URLen. Den stemmer ikke overens med nogen versionsnumre."
-#: versioning.py:147
+#: versioning.py:148
msgid "Invalid version in hostname."
msgstr "Ugyldig version i hostname."
-#: versioning.py:169
+#: versioning.py:170
msgid "Invalid version in query parameter."
msgstr "Ugyldig version i forespørgselsparameteren."
-
-#: views.py:88
-msgid "Permission denied."
-msgstr "Adgang nægtet."
diff --git a/rest_framework/locale/de/LC_MESSAGES/django.mo b/rest_framework/locale/de/LC_MESSAGES/django.mo
index 0042572ef9e2c518221cf6536bbbb57b11626fb4..98688039789f0300dd6aed554c84050365f9991c 100644
GIT binary patch
delta 2243
zcmYk-drXye9LMo5aq)npDFU5PIZ((&I33i3aDY7CLr8=GB`Y{fmy~(r5?SU>3(?g^
zYo0M{6StakU0j!)+FDzGgl+Xlx>j?e&6(L)ZLZl||D-E>f1c;D^&8*w`u(2soagyn
zzP}&i-8VWCmlkGB8l|6DtVlN-lUaOFDtu-Eti&?hf_LM7)cvnu5uU~zoW>eFht+r!
z=U_#)**Xm2CLDH6*ymI>b0LjiN3jKa@Ks!mKVu{M=bE)*H%9O+)b|&$4HxB_-HS0?
zh~uaZzTsW})bmHw^Zv%LCCnP`bbA;_jqo|t3#PChXK)QJm}j;gJ1~fo$e(@1hwl3U
zD=>GySqX-*3b!MVu$M7}uVWCu#1`Ife^6=0$X)IO58yqtk9zI5Q6u{pHKGfssl4vB
z!wcN+J29Q>qo^5p0hi(g>Un3d9KXf_yoQMYl`QVg!*a~PMr774iuYqHvW#}n^DycG
z6R0#fg@yP&hVU$EN&iH3xQU1B`(9Lg5S25p4XLw6jTlEkzKu#+|4&A3=RTh5Fqz(li6%WMt%#m=Cw=Qj^10t*8f|K;8H&@@Ic}?V?imV5vdgVLMSBKj^imQQ3V3wFEh=la?SK^_(hH
zF12Ab?!i*+|4Hw{XUHIJ7Bv$YEa-fkiyA=@>cOq3