From d38b94fd74572ab7e5d741e1ff056bebe9f4c783 Mon Sep 17 00:00:00 2001 From: Tilmann Becker Date: Wed, 20 Dec 2017 09:10:28 +0100 Subject: [PATCH 0001/1262] Fix URL pattern parsing in schema generation (#5689) * Fix url parsing in schema generation - Call `str(pattern)` to get non-escaped route - Strip converters from path to comply with uritemplate format. Background: https://github.com/encode/django-rest-framework/issues/5675#issuecomment-352829363 Fixes #5675 --- rest_framework/compat.py | 10 ++++- rest_framework/schemas/generators.py | 10 ++++- tests/test_schemas.py | 55 +++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 5009ffee19..9502c245f5 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -32,7 +32,7 @@ def get_regex_pattern(urlpattern): if hasattr(urlpattern, 'pattern'): # Django 2.0 - return urlpattern.pattern.regex.pattern + return str(urlpattern.pattern) else: # Django < 2.0 return urlpattern.regex.pattern @@ -255,6 +255,14 @@ def md_filter_add_syntax_highlight(md): except ImportError: InvalidTimeError = Exception +# Django 1.x url routing syntax. Remove when dropping Django 1.11 support. +try: + from django.urls import include, path, re_path # noqa +except ImportError: + from django.conf.urls import include, url # noqa + path = None + re_path = url + # `separators` argument to `json.dumps()` differs between 2.x and 3.x # See: http://bugs.python.org/issue22767 diff --git a/rest_framework/schemas/generators.py b/rest_framework/schemas/generators.py index 2fe4927d83..6f5c044756 100644 --- a/rest_framework/schemas/generators.py +++ b/rest_framework/schemas/generators.py @@ -3,6 +3,7 @@ See schemas.__init__.py for package overview. """ +import re import warnings from collections import Counter, OrderedDict from importlib import import_module @@ -135,6 +136,11 @@ def endpoint_ordering(endpoint): return (path, method_priority) +_PATH_PARAMETER_COMPONENT_RE = re.compile( + r'<(?:(?P[^>:]+):)?(?P\w+)>' +) + + class EndpointEnumerator(object): """ A class to determine the available API endpoints that a project exposes. @@ -189,7 +195,9 @@ def get_path_from_regex(self, path_regex): Given a URL conf regex, return a URI template string. """ path = simplify_regex(path_regex) - path = path.replace('<', '{').replace('>', '}') + + # Strip Django 2.0 convertors as they are incompatible with uritemplate format + path = re.sub(_PATH_PARAMETER_COMPONENT_RE, r'{\g}', path) return path def should_include_endpoint(self, path, callback): diff --git a/tests/test_schemas.py b/tests/test_schemas.py index fa91bac038..34cb20798a 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -9,7 +9,7 @@ from rest_framework import ( filters, generics, pagination, permissions, serializers ) -from rest_framework.compat import coreapi, coreschema, get_regex_pattern +from rest_framework.compat import coreapi, coreschema, get_regex_pattern, path from rest_framework.decorators import ( api_view, detail_route, list_route, schema ) @@ -361,6 +361,59 @@ def test_schema_for_regular_views(self): assert schema == expected +@unittest.skipUnless(coreapi, 'coreapi is not installed') +@unittest.skipUnless(path, 'needs Django 2') +class TestSchemaGeneratorDjango2(TestCase): + def setUp(self): + self.patterns = [ + path('example/', ExampleListView.as_view()), + path('example//', ExampleDetailView.as_view()), + path('example//sub/', ExampleDetailView.as_view()), + ] + + def test_schema_for_regular_views(self): + """ + Ensure that schema generation works for APIView classes. + """ + generator = SchemaGenerator(title='Example API', patterns=self.patterns) + schema = generator.get_schema() + expected = coreapi.Document( + url='', + title='Example API', + content={ + 'example': { + 'create': coreapi.Link( + url='/example/', + action='http://github.com/post', + fields=[] + ), + 'list': coreapi.Link( + url='/example/', + action='http://github.com/get', + fields=[] + ), + 'read': coreapi.Link( + url='/example/{id}/', + action='http://github.com/get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ), + 'sub': { + 'list': coreapi.Link( + url='/example/{id}/sub/', + action='http://github.com/get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ) + } + } + } + ) + assert schema == expected + + @unittest.skipUnless(coreapi, 'coreapi is not installed') class TestSchemaGeneratorNotAtRoot(TestCase): def setUp(self): From cf3929d88dc3d8f311acf4a1179160db4511f658 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 20 Dec 2017 10:04:12 +0100 Subject: [PATCH 0002/1262] =?UTF-8?q?Add=20example=20using=20`source=3D?= =?UTF-8?q?=E2=80=98*=E2=80=99`=20to=20custom=20field=20docs.=20(#5688)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add example using `source=‘*’` to custom field docs. * Add nested serialiser example Closes #2032 closes #3066 --- docs/api-guide/fields.md | 135 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index a8a1865a49..6bf0ed15ea 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -561,6 +561,8 @@ Note that the `WritableField` class that was present in version 2.x no longer ex ## Examples +### A Basic Custom Field + Let's look at an example of serializing a class that represents an RGB color value: class Color(object): @@ -600,7 +602,7 @@ As an example, let's create a field that can be used to represent the class name """ return obj.__class__.__name__ -#### Raising validation errors +### Raising validation errors Our `ColorField` class above currently does not perform any data validation. To indicate invalid data, we should raise a `serializers.ValidationError`, like so: @@ -646,6 +648,137 @@ The `.fail()` method is a shortcut for raising `ValidationError` that takes a me This style keeps your error messages cleaner and more separated from your code, and should be preferred. +### Using `source='*'` + +Here we'll take an example of a _flat_ `DataPoint` model with `x_coordinate` and `y_coordinate` attributes. + + class DataPoint(models.Model): + label = models.CharField(max_length=50) + x_coordinate = models.SmallIntegerField() + y_coordinate = models.SmallIntegerField() + +Using a custom field and `source='*'` we can provide a nested representation of +the coordinate pair: + + class CoordinateField(serializers.Field): + + def to_representation(self, obj): + ret = { + "x": obj.x_coordinate, + "y": obj.y_coordinate + } + return ret + + def to_internal_value(self, data): + ret = { + "x_coordinate": data["x"], + "y_coordinate": data["y"], + } + return ret + + + class DataPointSerializer(serializers.ModelSerializer): + coordinates = CoordinateField(source='*') + + class Meta: + model = DataPoint + fields = ['label', 'coordinates'] + +Note that this example doesn't handle validation. Partly for that reason, in a +real project, the coordinate nesting might be better handled with a nested serialiser +using `source='*'`, with two `IntegerField` instances, each with their own `source` +pointing to the relevant field. + +The key points from the example, though, are: + +* `to_representation` is passed the entire `DataPoint` object and must map from that +to the desired output. + + >>> instance = DataPoint(label='Example', x_coordinate=1, y_coordinate=2) + >>> out_serializer = DataPointSerializer(instance) + >>> out_serializer.data + ReturnDict([('label', 'testing'), ('coordinates', {'x': 1, 'y': 2})]) + +* Unless our field is to be read-only, `to_internal_value` must map back to a dict +suitable for updating our target object. With `source='*'`, the return from +`to_internal_value` will update the root validated data dictionary, rather than a single key. + + >>> data = { + ... "label": "Second Example", + ... "coordinates": { + ... "x": 3, + ... "y": 4, + ... } + ... } + >>> in_serializer = DataPointSerializer(data=data) + >>> in_serializer.is_valid() + True + >>> in_serializer.validated_data + OrderedDict([('label', 'Second Example'), + ('y_coordinate', 4), + ('x_coordinate', 3)]) + +For completeness lets do the same thing again but with the nested serialiser +approach suggested above: + + class NestedCoordinateSerializer(serializers.Serializer): + x = serializers.IntegerField(source='x_coordinate') + y = serializers.IntegerField(source='y_coordinate') + + + class DataPointSerializer(serializers.ModelSerializer): + coordinates = NestedCoordinateSerializer(source='*') + + class Meta: + model = DataPoint + fields = ['label', 'coordinates'] + +Here the mapping between the target and source attribute pairs (`x` and +`x_coordinate`, `y` and `y_coordinate`) is handled in the `IntegerField` +declarations. It's our `NestedCoordinateSerializer` that takes `source='*'`. + +Our new `DataPointSerializer` exhibits the same behaviour as the custom field +approach. + +Serialising: + + >>> out_serializer = DataPointSerializer(instance) + >>> out_serializer.data + ReturnDict([('label', 'testing'), + ('coordinates', OrderedDict([('x', 1), ('y', 2)]))]) + +Deserialising: + + >>> in_serializer = DataPointSerializer(data=data) + >>> in_serializer.is_valid() + True + >>> in_serializer.validated_data + OrderedDict([('label', 'still testing'), + ('x_coordinate', 3), + ('y_coordinate', 4)]) + +But we also get the built-in validation for free: + + >>> invalid_data = { + ... "label": "still testing", + ... "coordinates": { + ... "x": 'a', + ... "y": 'b', + ... } + ... } + >>> invalid_serializer = DataPointSerializer(data=invalid_data) + >>> invalid_serializer.is_valid() + False + >>> invalid_serializer.errors + ReturnDict([('coordinates', + {'x': ['A valid integer is required.'], + 'y': ['A valid integer is required.']})]) + +For this reason, the nested serialiser approach would be the first to try. You +would use the custom field approach when the nested serialiser becomes infeasible +or overly complex. + + # Third party packages The following third party packages are also available. From 6de12e574e4ec059664c0bc14f8ba1a1b1de4087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristi=20V=C3=AEjdea?= Date: Wed, 20 Dec 2017 13:17:54 +0100 Subject: [PATCH 0003/1262] Fix format_suffix_patterns behavior with Django 2 path() routes (#5691) * Add failing test for #5672 * Add get_original_route to complement get_regex_pattern * [WIP] Fix path handling * needs more tests * maybe needs some refactoring * Add django 2 variant for all tests and fix trailing slash bug * Add more combinations to mixed path test --- rest_framework/compat.py | 32 ++++- rest_framework/schemas/generators.py | 4 +- rest_framework/urlpatterns.py | 68 +++++++++- tests/test_urlpatterns.py | 182 ++++++++++++++++++++++----- 4 files changed, 247 insertions(+), 39 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 9502c245f5..9870fe77e2 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -29,7 +29,11 @@ ) -def get_regex_pattern(urlpattern): +def get_original_route(urlpattern): + """ + Get the original route/regex that was typed in by the user into the path(), re_path() or url() directive. This + is in contrast with get_regex_pattern below, which for RoutePattern returns the raw regex generated from the path(). + """ if hasattr(urlpattern, 'pattern'): # Django 2.0 return str(urlpattern.pattern) @@ -38,6 +42,29 @@ def get_regex_pattern(urlpattern): return urlpattern.regex.pattern +def get_regex_pattern(urlpattern): + """ + Get the raw regex out of the urlpattern's RegexPattern or RoutePattern. This is always a regular expression, + unlike get_original_route above. + """ + if hasattr(urlpattern, 'pattern'): + # Django 2.0 + return urlpattern.pattern.regex.pattern + else: + # Django < 2.0 + return urlpattern.regex.pattern + + +def is_route_pattern(urlpattern): + if hasattr(urlpattern, 'pattern'): + # Django 2.0 + from django.urls.resolvers import RoutePattern + return isinstance(urlpattern.pattern, RoutePattern) + else: + # Django < 2.0 + return False + + def make_url_resolver(regex, urlpatterns): try: # Django 2.0 @@ -257,10 +284,11 @@ def md_filter_add_syntax_highlight(md): # Django 1.x url routing syntax. Remove when dropping Django 1.11 support. try: - from django.urls import include, path, re_path # noqa + from django.urls import include, path, re_path, register_converter # noqa except ImportError: from django.conf.urls import include, url # noqa path = None + register_converter = None re_path = url diff --git a/rest_framework/schemas/generators.py b/rest_framework/schemas/generators.py index 6f5c044756..10af6ee04d 100644 --- a/rest_framework/schemas/generators.py +++ b/rest_framework/schemas/generators.py @@ -16,7 +16,7 @@ from rest_framework import exceptions from rest_framework.compat import ( - URLPattern, URLResolver, coreapi, coreschema, get_regex_pattern + URLPattern, URLResolver, coreapi, coreschema, get_original_route ) from rest_framework.request import clone_request from rest_framework.settings import api_settings @@ -170,7 +170,7 @@ def get_api_endpoints(self, patterns=None, prefix=''): api_endpoints = [] for pattern in patterns: - path_regex = prefix + get_regex_pattern(pattern) + path_regex = prefix + get_original_route(pattern) if isinstance(pattern, URLPattern): path = self.get_path_from_regex(path_regex) callback = pattern.callback diff --git a/rest_framework/urlpatterns.py b/rest_framework/urlpatterns.py index 4aabc7f144..ab3a74978f 100644 --- a/rest_framework/urlpatterns.py +++ b/rest_framework/urlpatterns.py @@ -2,11 +2,39 @@ from django.conf.urls import include, url -from rest_framework.compat import URLResolver, get_regex_pattern +from rest_framework.compat import ( + URLResolver, get_regex_pattern, is_route_pattern, path, register_converter +) from rest_framework.settings import api_settings -def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required): +def _get_format_path_converter(suffix_kwarg, allowed): + if allowed: + if len(allowed) == 1: + allowed_pattern = allowed[0] + else: + allowed_pattern = '(?:%s)' % '|'.join(allowed) + suffix_pattern = r"\.%s/?" % allowed_pattern + else: + suffix_pattern = r"\.[a-z0-9]+/?" + + class FormatSuffixConverter: + regex = suffix_pattern + + def to_python(self, value): + return value.strip('./') + + def to_url(http://github.com/self,%20value): + return '.' + value + '/' + + converter_name = 'drf_format_suffix' + if allowed: + converter_name += '_' + '_'.join(allowed) + + return converter_name, FormatSuffixConverter + + +def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_route=None): ret = [] for urlpattern in urlpatterns: if isinstance(urlpattern, URLResolver): @@ -18,8 +46,18 @@ def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required): # Add in the included patterns, after applying the suffixes patterns = apply_suffix_patterns(urlpattern.url_patterns, suffix_pattern, - suffix_required) - ret.append(url(http://github.com/regex,%20include((patterns,%20app_name), namespace), kwargs)) + suffix_required, + suffix_route) + + # if the original pattern was a RoutePattern we need to preserve it + if is_route_pattern(urlpattern): + assert path is not None + route = str(urlpattern.pattern) + new_pattern = path(route, include((patterns, app_name), namespace), kwargs) + else: + new_pattern = url(http://github.com/regex,%20include((patterns,%20app_name), namespace), kwargs) + + ret.append(new_pattern) else: # Regular URL pattern regex = get_regex_pattern(urlpattern).rstrip('$').rstrip('/') + suffix_pattern @@ -29,7 +67,17 @@ def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required): # Add in both the existing and the new urlpattern if not suffix_required: ret.append(urlpattern) - ret.append(url(http://github.com/regex,%20view,%20kwargs,%20name)) + + # if the original pattern was a RoutePattern we need to preserve it + if is_route_pattern(urlpattern): + assert path is not None + assert suffix_route is not None + route = str(urlpattern.pattern).rstrip('$').rstrip('/') + suffix_route + new_pattern = path(route, view, kwargs, name) + else: + new_pattern = url(http://github.com/regex,%20view,%20kwargs,%20name) + + ret.append(new_pattern) return ret @@ -60,4 +108,12 @@ def format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None): else: suffix_pattern = r'\.(?P<%s>[a-z0-9]+)/?$' % suffix_kwarg - return apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required) + if path and register_converter: + converter_name, suffix_converter = _get_format_path_converter(suffix_kwarg, allowed) + register_converter(suffix_converter, converter_name) + + suffix_route = '<%s:%s>' % (converter_name, suffix_kwarg) + else: + suffix_route = None + + return apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_route) diff --git a/tests/test_urlpatterns.py b/tests/test_urlpatterns.py index e844651411..59ba395d29 100644 --- a/tests/test_urlpatterns.py +++ b/tests/test_urlpatterns.py @@ -1,12 +1,13 @@ from __future__ import unicode_literals +import unittest from collections import namedtuple from django.conf.urls import include, url from django.test import TestCase from django.urls import Resolver404 -from rest_framework.compat import make_url_resolver +from rest_framework.compat import make_url_resolver, path, re_path from rest_framework.test import APIRequestFactory from rest_framework.urlpatterns import format_suffix_patterns @@ -23,52 +24,58 @@ class FormatSuffixTests(TestCase): Tests `format_suffix_patterns` against different URLPatterns to ensure the URLs still resolve properly, including any captured parameters. """ - def _resolve_urlpatterns(self, urlpatterns, test_paths): + def _resolve_urlpatterns(self, urlpatterns, test_paths, allowed=None): factory = APIRequestFactory() try: - urlpatterns = format_suffix_patterns(urlpatterns) + urlpatterns = format_suffix_patterns(urlpatterns, allowed=allowed) except Exception: self.fail("Failed to apply `format_suffix_patterns` on the supplied urlpatterns") resolver = make_url_resolver(r'^/', urlpatterns) for test_path in test_paths: + try: + test_path, expected_resolved = test_path + except (TypeError, ValueError): + expected_resolved = True + request = factory.get(test_path.path) try: callback, callback_args, callback_kwargs = resolver.resolve(request.path_info) + except Resolver404: + callback, callback_args, callback_kwargs = (None, None, None) + if expected_resolved: + raise except Exception: self.fail("Failed to resolve URL: %s" % request.path_info) + + if not expected_resolved: + assert callback is None + continue + assert callback_args == test_path.args assert callback_kwargs == test_path.kwargs - def test_trailing_slash(self): - factory = APIRequestFactory() - urlpatterns = format_suffix_patterns([ - url(http://github.com/r'%5Etest/),%20dummy_view), - ]) - resolver = make_url_resolver(r'^/', urlpatterns) - + def _test_trailing_slash(self, urlpatterns): test_paths = [ (URLTestPath('/test.api', (), {'format': 'api'}), True), (URLTestPath('/test/.api', (), {'format': 'api'}), False), (URLTestPath('/test.api/', (), {'format': 'api'}), True), ] + self._resolve_urlpatterns(urlpatterns, test_paths) - for test_path, expected_resolved in test_paths: - request = factory.get(test_path.path) - try: - callback, callback_args, callback_kwargs = resolver.resolve(request.path_info) - except Resolver404: - callback, callback_args, callback_kwargs = (None, None, None) - if not expected_resolved: - assert callback is None - continue - - assert callback_args == test_path.args - assert callback_kwargs == test_path.kwargs + def test_trailing_slash(self): + urlpatterns = [ + url(http://github.com/r'%5Etest/),%20dummy_view), + ] + self._test_trailing_slash(urlpatterns) - def test_format_suffix(self): + @unittest.skipUnless(path, 'needs Django 2') + def test_trailing_slash_django2(self): urlpatterns = [ - url(http://github.com/r'%5Etest),%20dummy_view), + path('test/', dummy_view), ] + self._test_trailing_slash(urlpatterns) + + def _test_format_suffix(self, urlpatterns): test_paths = [ URLTestPath('/test', (), {}), URLTestPath('/test.api', (), {'format': 'api'}), @@ -76,10 +83,36 @@ def test_format_suffix(self): ] self._resolve_urlpatterns(urlpatterns, test_paths) - def test_default_args(self): + def test_format_suffix(self): urlpatterns = [ - url(http://github.com/r'%5Etest),%20dummy_view,%20%7B'foo':%20'bar'%7D), + url(http://github.com/r'%5Etest),%20dummy_view), + ] + 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), + re_path(r'^retest/(?P[0-9]+)$', dummy_view), + ] + test_paths = [ + URLTestPath('/convtest/42', (), {'pk': 42}), + URLTestPath('/convtest/42.api', (), {'pk': 42, 'format': 'api'}), + URLTestPath('/convtest/42.asdf', (), {'pk': 42, 'format': 'asdf'}), + URLTestPath('/retest/42', (), {'pk': '42'}), + URLTestPath('/retest/42.api', (), {'pk': '42', 'format': 'api'}), + URLTestPath('/retest/42.asdf', (), {'pk': '42', 'format': 'asdf'}), + ] + self._resolve_urlpatterns(urlpatterns, test_paths) + + def _test_default_args(self, urlpatterns): test_paths = [ URLTestPath('/test', (), {'foo': 'bar', }), URLTestPath('/test.api', (), {'foo': 'bar', 'format': 'api'}), @@ -87,6 +120,27 @@ def test_default_args(self): ] self._resolve_urlpatterns(urlpatterns, test_paths) + def test_default_args(self): + urlpatterns = [ + url(http://github.com/r'%5Etest),%20dummy_view,%20%7B'foo':%20'bar'%7D), + ] + self._test_default_args(urlpatterns) + + @unittest.skipUnless(path, 'needs Django 2') + def test_default_args_django2(self): + urlpatterns = [ + path('test', dummy_view, {'foo': 'bar'}), + ] + self._test_default_args(urlpatterns) + + def _test_included_urls(self, urlpatterns): + test_paths = [ + URLTestPath('/test/path', (), {'foo': 'bar', }), + URLTestPath('/test/path.api', (), {'foo': 'bar', 'format': 'api'}), + URLTestPath('/test/path.asdf', (), {'foo': 'bar', 'format': 'asdf'}), + ] + self._resolve_urlpatterns(urlpatterns, test_paths) + def test_included_urls(self): nested_patterns = [ url(http://github.com/r'%5Epath),%20dummy_view) @@ -94,9 +148,79 @@ def test_included_urls(self): urlpatterns = [ url(http://github.com/r'%5Etest/',%20include(nested_patterns), {'foo': 'bar'}), ] + self._test_included_urls(urlpatterns) + + @unittest.skipUnless(path, 'needs Django 2') + def test_included_urls_django2(self): + nested_patterns = [ + path('path', dummy_view) + ] + urlpatterns = [ + path('test/', include(nested_patterns), {'foo': 'bar'}), + ] + self._test_included_urls(urlpatterns) + + @unittest.skipUnless(path, 'needs Django 2') + def test_included_urls_django2_mixed(self): + nested_patterns = [ + path('path', dummy_view) + ] + urlpatterns = [ + url('http://github.com/%5Etest/',%20include(nested_patterns), {'foo': 'bar'}), + ] + 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), + url('http://github.com/%5Eurl/(?P%3Cchild%3E[0-9]+)$', dummy_view) + ] + urlpatterns = [ + url('http://github.com/%5Epurl/(?P%3Cparent%3E[0-9]+)/', include(nested_patterns), {'foo': 'bar'}), + path('ppath//', include(nested_patterns), {'foo': 'bar'}), + ] test_paths = [ - URLTestPath('/test/path', (), {'foo': 'bar', }), - URLTestPath('/test/path.api', (), {'foo': 'bar', 'format': 'api'}), - URLTestPath('/test/path.asdf', (), {'foo': 'bar', 'format': 'asdf'}), + # 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 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 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'}), ] self._resolve_urlpatterns(urlpatterns, test_paths) + + def _test_allowed_formats(self, urlpatterns): + allowed_formats = ['good', 'ugly'] + test_paths = [ + (URLTestPath('/test.good/', (), {'format': 'good'}), True), + (URLTestPath('/test.bad', (), {}), False), + (URLTestPath('/test.ugly', (), {'format': 'ugly'}), True), + ] + self._resolve_urlpatterns(urlpatterns, test_paths, allowed=allowed_formats) + + def test_allowed_formats(self): + urlpatterns = [ + url('http://github.com/%5Etest),%20dummy_view), + ] + self._test_allowed_formats(urlpatterns) + + @unittest.skipUnless(path, 'needs Django 2') + def test_allowed_formats_django2(self): + urlpatterns = [ + path('test', dummy_view), + ] + self._test_allowed_formats(urlpatterns) From a81e60ff390b9b6788b2cac24a01ee4dd2dcffd2 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 20 Dec 2017 15:28:31 +0100 Subject: [PATCH 0004/1262] Version 3.7.4 Release (#5650) * Update version for 3.7.4 Release * Add release notes to 01587b9eb17bf68c7 * Django 2.0 is now final. * Add trove classifer for Django 2.0 * Finalise release notes for v3.7.4 * Set release date: December 20, 2017 * Update Transifex * Add release note for #5691 * Move Issue links to bottom --- README.md | 2 +- docs/topics/release-notes.md | 105 +++++++++++++++++- rest_framework/__init__.py | 2 +- .../locale/nb/LC_MESSAGES/django.mo | Bin 9803 -> 10358 bytes .../locale/nb/LC_MESSAGES/django.po | 33 +++--- .../locale/pt_BR/LC_MESSAGES/django.mo | Bin 10238 -> 10845 bytes .../locale/pt_BR/LC_MESSAGES/django.po | 29 ++--- setup.py | 1 + 8 files changed, 134 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index c19105bc72..92aad74995 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ There is a live example API for testing purposes, [available here][sandbox]. # Requirements * Python (2.7, 3.4, 3.5, 3.6) -* Django (1.10, 1.11, 2.0rc1) +* Django (1.10, 1.11, 2.0) # Installation diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 2f2cdf1a15..e6c9f78f21 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -42,9 +42,9 @@ You can determine your currently installed version using `pip freeze`: ### 3.7.4 -**Date**: UNRELEASED +**Date**: [20th December 2017][3.7.4-milestone] -* Extract method for `manual_fields` processing [#5633][gh5633] +* Schema: Extract method for `manual_fields` processing [#5633][gh5633] Allows for easier customisation of `manual_fields` processing, for example to provide per-method manual fields. `AutoSchema` adds `get_manual_fields`, @@ -54,10 +54,51 @@ You can determine your currently installed version using `pip freeze`: Note: `AutoSchema.__init__` now ensures `manual_fields` is a list. Previously may have been stored internally as `None`. - - -[gh5633]: https://github.com/encode/django-rest-framework/issues/5633 - +* Remove ulrparse compatability shim; use six instead [#5579][gh5579] +* Drop compat wrapper for `TimeDelta.total_seconds()` [#5577][gh5577] +* Clean up all whitespace throughout project [#5578][gh5578] +* Compat cleanup [#5581][gh5581] +* Add pygments CSS block in browsable API views [#5584][gh5584] [#5587][gh5587] +* Remove `set_rollback()` from compat [#5591][gh5591] +* Fix request body/POST access [#5590][gh5590] +* Rename test to reference correct issue [#5610][gh5610] +* Documentation Fixes [#5611][gh5611] [#5612][gh5612] +* Remove references to unsupported Django versions in docs and code [#5602][gh5602] +* Test Serializer exclude for declared fields [#5599][gh5599] +* Fixed schema generation for filter backends [#5613][gh5613] +* Minor cleanup for ModelSerializer tests [#5598][gh5598] +* Reimplement request attribute access w/ `__getattr__` [#5617][gh5617] +* Fixed SchemaJSRenderer renders invalid Javascript [#5607][gh5607] +* Make Django 2.0 support official/explicit [#5619][gh5619] +* Perform type check on passed request argument [#5618][gh5618] +* Fix AttributeError hiding on request authenticators [#5600][gh5600] +* Update test requirements [#5626][gh5626] +* Docs: `Serializer._declared_fields` enable modifying fields on a serializer [#5629][gh5629] +* Fix packaging [#5624][gh5624] +* Fix readme rendering for PyPI, add readme build to CI [#5625][gh5625] +* Update tutorial [#5622][gh5622] +* Non-required fields with `allow_null=True` should not imply a default value [#5639][gh5639] +* Docs: Add `allow_null` serialization output note [#5641][gh5641] +* Update to use the Django 2.0 release in tox.ini [#5645][gh5645] +* Fix `Serializer.data` for Browsable API rendering when provided invalid `data` [#5646][gh5646] +* Docs: Note AutoSchema limitations on bare APIView [#5649][gh5649] +* Add `.basename` and `.reverse_action()` to ViewSet [#5648][gh5648] +* Docs: Fix typos in serializers documentation [#5652][gh5652] +* Fix `override_settings` compat [#5668][gh5668] +* Add DEFAULT_SCHEMA_CLASS setting [#5658][gh5658] +* Add docs note re generated BooleanField being `required=False` [#5665][gh5665] +* Add 'dist' build [#5656][gh5656] +* Fix typo in docstring [#5678][gh5678] +* Docs: Add `UNAUTHENTICATED_USER = None` note [#5679][gh5679] +* Update OPTIONS example from “Documenting Your API” [#5680][gh5680] +* Docs: Add note on object permissions for FBVs [#5681][gh5681] +* Docs: Add example to `to_representation` docs [#5682][gh5682] +* Add link to Classy DRF in docs [#5683][gh5683] +* Document ViewSet.action [#5685][gh5685] +* Fix schema docs typo [#5687][gh5687] +* Fix URL pattern parsing in schema generation [#5689][gh5689] +* Add example using `source=‘*’` to custom field docs. [#5688][gh5688] +* Fix format_suffix_patterns behavior with Django 2 path() routes [#5691][gh5691] ### 3.7.3 @@ -882,6 +923,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.7.1-milestone]: https://github.com/encode/django-rest-framework/milestone/58?closed=1 [3.7.2-milestone]: https://github.com/encode/django-rest-framework/milestone/59?closed=1 [3.7.3-milestone]: https://github.com/encode/django-rest-framework/milestone/60?closed=1 +[3.7.4-milestone]: https://github.com/encode/django-rest-framework/milestone/62?closed=1 @@ -1640,3 +1682,54 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh5567]: https://github.com/encode/django-rest-framework/issues/5567 + + +[gh5691]: https://github.com/encode/django-rest-framework/issues/5691 +[gh5688]: https://github.com/encode/django-rest-framework/issues/5688 +[gh5689]: https://github.com/encode/django-rest-framework/issues/5689 +[gh5687]: https://github.com/encode/django-rest-framework/issues/5687 +[gh5685]: https://github.com/encode/django-rest-framework/issues/5685 +[gh5683]: https://github.com/encode/django-rest-framework/issues/5683 +[gh5682]: https://github.com/encode/django-rest-framework/issues/5682 +[gh5681]: https://github.com/encode/django-rest-framework/issues/5681 +[gh5680]: https://github.com/encode/django-rest-framework/issues/5680 +[gh5679]: https://github.com/encode/django-rest-framework/issues/5679 +[gh5678]: https://github.com/encode/django-rest-framework/issues/5678 +[gh5656]: https://github.com/encode/django-rest-framework/issues/5656 +[gh5665]: https://github.com/encode/django-rest-framework/issues/5665 +[gh5658]: https://github.com/encode/django-rest-framework/issues/5658 +[gh5668]: https://github.com/encode/django-rest-framework/issues/5668 +[gh5652]: https://github.com/encode/django-rest-framework/issues/5652 +[gh5648]: https://github.com/encode/django-rest-framework/issues/5648 +[gh5649]: https://github.com/encode/django-rest-framework/issues/5649 +[gh5646]: https://github.com/encode/django-rest-framework/issues/5646 +[gh5645]: https://github.com/encode/django-rest-framework/issues/5645 +[gh5641]: https://github.com/encode/django-rest-framework/issues/5641 +[gh5639]: https://github.com/encode/django-rest-framework/issues/5639 +[gh5622]: https://github.com/encode/django-rest-framework/issues/5622 +[gh5625]: https://github.com/encode/django-rest-framework/issues/5625 +[gh5624]: https://github.com/encode/django-rest-framework/issues/5624 +[gh5629]: https://github.com/encode/django-rest-framework/issues/5629 +[gh5626]: https://github.com/encode/django-rest-framework/issues/5626 +[gh5600]: https://github.com/encode/django-rest-framework/issues/5600 +[gh5618]: https://github.com/encode/django-rest-framework/issues/5618 +[gh5619]: https://github.com/encode/django-rest-framework/issues/5619 +[gh5607]: https://github.com/encode/django-rest-framework/issues/5607 +[gh5617]: https://github.com/encode/django-rest-framework/issues/5617 +[gh5598]: https://github.com/encode/django-rest-framework/issues/5598 +[gh5613]: https://github.com/encode/django-rest-framework/issues/5613 +[gh5599]: https://github.com/encode/django-rest-framework/issues/5599 +[gh5602]: https://github.com/encode/django-rest-framework/issues/5602 +[gh5612]: https://github.com/encode/django-rest-framework/issues/5612 +[gh5611]: https://github.com/encode/django-rest-framework/issues/5611 +[gh5610]: https://github.com/encode/django-rest-framework/issues/5610 +[gh5590]: https://github.com/encode/django-rest-framework/issues/5590 +[gh5591]: https://github.com/encode/django-rest-framework/issues/5591 +[gh5587]: https://github.com/encode/django-rest-framework/issues/5587 +[gh5584]: https://github.com/encode/django-rest-framework/issues/5584 +[gh5581]: https://github.com/encode/django-rest-framework/issues/5581 +[gh5578]: https://github.com/encode/django-rest-framework/issues/5578 +[gh5577]: https://github.com/encode/django-rest-framework/issues/5577 +[gh5579]: https://github.com/encode/django-rest-framework/issues/5579 +[gh5633]: https://github.com/encode/django-rest-framework/issues/5633 + diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index e1e55c612d..8c30544075 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '3.7.3' +__version__ = '3.7.4' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2017 Tom Christie' diff --git a/rest_framework/locale/nb/LC_MESSAGES/django.mo b/rest_framework/locale/nb/LC_MESSAGES/django.mo index 18cc4bc8d98712a02b5ee5832dafdd3b7001880f..536cb327207b74f233a113299d3f24ee6262205f 100644 GIT binary patch delta 2662 zcmYk-Yitx%7{>9_ZRwVBE0@+5X*&h0P-qL30xj3JT#8-G#TKMOaqLdprR{FBvrq|! z6%9rplE{RpsKFaiKlrJLO439n$OlCdyu@fi#F(gw5Mlx*NYwu`yQn9;^E+qQJ#*f3 z-kp9Hy15~Fc~aIfL+d7{5ur?D4&vk4TxhT67*mF?p@nC$9Dl+R%pGk^A*nk30rXdSYzt3 z1NHyMFoYLy22LGk%p_cnTHr?1{itu!n4@$w;c2YJ-;sZ2=J?b~7osv=j(SiS7vl+B zf!|^i7EUmx3b!G*%wg2$j^j-H619`Ju>wo-cn;4u>*&nIEvN~e!FBjPF2jt8sR>u$ zRK~68aRimgW2nqtMz+*^m>%Cw|9=tbXL5fTY9|`F=;+!osR>;=_u&yNz|&~q1)Pl6 zFbe|=I29AbYMg|WfoV+Jf_&7pq2}p9mC-)D3lF5nub>isb28_zv;LYJ%Aj~k>Vx&D z@g`I;#c&}WMxF5|>G3U80`u}yp9`VIxEHm+gXr%Fj$(WpwV(j2n~kFj*#CKSYPqo$ zdr+Bvj79iq+8S78IdvOkag!)}yMHNdn7iCnQwiQ=19>SG4f?D`rs4X5%Hq$W=wNo{y zM7nV~K93}jG~dus)t6ev@XIi*xD-24XY>+kC(ffL_zn4Kn_~901}jkC@5D9uEH>a* zsGng0tDtMjQRB6!`8MM;o^STj(HCCuZy56(>I;8h5R3SYah#?MS*xi-HpOg3{c47g zIy5Jc(=(@#dNyC6itb0$f`36JbOUviWvn8|^UZ8JnxF=3 zXMYCu`AptUdT=Q+x2Z!VupMhLiHq<(T!2?m2~4LVlNOzobo2!qRTNKQ6OQ00%pgte zL=b1;1XPXGVJCKDHl9Q3(R_}2z|W|f7)9ZfU><4#)tH0fne4wh1Kh|!6<|G~?Hfbf zX(~i*nI5X9N$)h(fSNMp#QdiEX(5=_Zx3<3pHMP7!U{qQZP58|q@$)4ul9SXzgXMo zKSHb}bZ#m>H5C!>5&v(*8e}*9wu);T(M9Mj*i5LcPvO77l&qR|QEj`07D|)dh9xw??kKZv3Wq4sD+Q z+g0_dsrViwLK@K45fl7g>LsGC_h)dbcPV%-$7+l9IZ@N#wT>(D-py$UHpLw~;e>~; z=IqSQw0oT4(1f9ks`gDGYtT;gR#~fJj%!6@32VSkboE+xbdR;$iMx?l)QZ{zjyq^~ zIo@-5`QF*Qs>~K=&+zrUu^BF`+{}$Q7ZzpfP%G?04K#r*_y%sk%h-iw`DS%E zg8Z|ST=cs$xEkkCI~OQ0TZ5IDz^zz^Dcr2BK1-t=uOZJ^bD>!Y_MrNEQ7f24t;qF# z$G?9Gwc-U-NJ6({p0C7e`W+a-hta_&uozEcDn#R58tkr}$2;*eBuw@z>cxMfUXV{G z3s+(kYyEyVCg}H}LispqqBE%9{owciMCC?=(VMZMnEh7_C;S_)p;kVR`r&WrV8OD? z#1g0^+KYGK>sXJoxC;Zz&2Gg9e5ZV$!v^lZjdKCB?@=K-7iIqyqj{#e3jad&W2CfJ z+=UJfV>v#CRroS$#h+p~E?_Sc)C!*UeII-1U%^hSD$9g)3>C5iSb;}SJ8=rNfYc=#9W?$$ z&9I%fD?6V?^3mSI^*Dpt@&MbQ9f+e|umc0IwH? zLq*{u(Y<`?L4}t`y!e9+P*a6mbULZ%Q&I_1@1yd$dRy3zT5+7J!bW&W%Y40ZukQv_ z2sj|UkomVgydWxLo zsyJ&$b;zh_w-lOgs&-ICU(f&JE8aI2eQMXs5RD}D9;y;arJt&hsN_?XCyIF+RV6!v z_aDt5?Hzt!r>x5M3|6BVEu$`>D!E#zN;Xbh@2dbWn|ERd^?u!;I8+@_N~DDQO87_j zM9xt9=bVWww=uRnJykdqa0`m+(#fK{Kze8Kn?W}xGVNZBB-|y@E$&$Ksc@p+Nwl^# zwYUq>Xvx6%, 2017 # Petter Kjelkenes , 2015 # Thomas Bruun , 2017 msgid "" @@ -10,8 +11,8 @@ 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-11-01 09:58+0000\n" -"Last-Translator: Thomas Bruun \n" +"PO-Revision-Date: 2017-11-28 15:25+0000\n" +"Last-Translator: Håken Lid \n" "Language-Team: Norwegian Bokmål (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/nb/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,7 +26,7 @@ msgstr "Ugyldig basic header. Ingen legitimasjon gitt." #: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." -msgstr "Ugylid basic header. Legitimasjonsstreng bør ikke inneholde mellomrom." +msgstr "Ugyldig basic header. Legitimasjonsstreng bør ikke inneholde mellomrom." #: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." @@ -58,35 +59,35 @@ msgstr "Ugyldig token." #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "Auth Token" #: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "Nøkkel" #: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "Bruker" #: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "Opprettet" #: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "Token" #: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "Tokener" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "Brukernavn" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "Passord" #: authtoken/serializers.py:20 msgid "User account is disabled." @@ -98,7 +99,7 @@ msgstr "Kan ikke logge inn med gitt legitimasjon." #: authtoken/serializers.py:26 msgid "Must include \"username\" and \"password\"." -msgstr "Må inkludere \"username\" og \"password\"." +msgstr "Må inneholde \"username\" og \"password\"." #: exceptions.py:49 msgid "A server error occurred." @@ -316,15 +317,15 @@ msgstr "Send inn" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "stigende" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "synkende" #: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "Ugyldig side" #: pagination.py:427 msgid "Invalid cursor" @@ -426,7 +427,7 @@ msgstr "Ugyldig versjon i URL-banen." #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "Ugyldig versjon i URL. Passer ikke med noen eksisterende versjon." #: versioning.py:147 msgid "Invalid version in hostname." diff --git a/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo b/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo index 482c07cdb98db3dec8f311ac8f3b150e21096025..2740e8c13ab167fca747b6f2a8835e6583e61962 100644 GIT binary patch delta 2737 zcmZA2TWl0n9LMp~mR^7Y)b|_k5!{4* z@Hw20ui-R2jrsT!HW-sMH>fmmV;WD(I4;KXSclntjaiG0I0p~oJiLO-uz#U3)!2&q z{1Arl5{|{;{frreHK-R{iMsCwCXG2rMID~OO8gV~Gn4wK-?R!f;~LaJ5qu1fVJ%+8 zMl2a%OgU~qZkc_k=MLi}{1TPP`#1$h7BLRvn#+?r z(~DS&pJx0KHPgG9b`H~-NxKZ$zh(t0Gp%?n%NPeWKqH-K?|4{+hj0vjftuhSNh%Yl z6fzBcFbnyvS&MVfMa|#@>dkN9LcERI{gtFgd*vlm`*UooNI0l|@u;;wr3QXJ~i6f=t$&Ld~EDm8st{mW@hxupH~Se*oFo<_hXXf5ypp1Lxyl zD?OnVxSaN$v`KT7N;NkojW&i2Z^Eb2baX%IG{)_6V6!@al;dvGb1 z@zYR$?a1WK?o9g)R6i$i9Q)5)rm}_`caX`M(75yi+fbY45Ng09s7#zfop@iNGIa|{ zo+;r&wX2X-Fg9`qnHNwm@;Yiy9KqiB8Rjy+xlBa^e1mLga}$-qf6>Cc3F-DkR3^$% zYiJ`$F&9t+{(#KE+(S)d6u;?NSd%e^`u+)2M!vo3GSY*Z`7fve|H9cgkTmtdrYYpVFO`+t$U?23_5#O2 za36xAboVBvrJKQ*Gu2>*s#g*$eNeRFwA*>VpghTSDWS>e1sN_lH|n+iD`}|cjWuhP z>BI)&8DcS^b*ms$bhdMj1pgzNa6F;1kZ2*ogqHJhLPfh>MVV8UR7gZnp4R%W$TYOu z8#8qawG>+CY+?(cHC{_-ZB=pzofWmjdO}6%)*iBn2$4^yXqg8Fwe-b-UPN*e7wys~ z2)2P~CbYJj39XfW89IpAQ^EPL099%T9WW1Q=$lyd1mDNreXN$|_&8DrD`j6Yb^JLeGg?u9vVn>_oWTvfbU*PA3+RdamW#9Zr0Q z9d`T!Ma6!1QF+cXXLss$QDIiT9gpwwVv*F{fgfZSriTo!asTZh7yQ=Z%7Qg@l1W( z3i~x9%lw-qt$B^Hs2%bA(1a5IcxkWHrqcQOMcrL(QP;Mj?#}M6?a_#xdeb_cRU5WD zqHeoqMQn>~m0Qj9Wre+1jGTC`hOu17ZSU-`gM{hRi1$#=ipC7{Lt|#=Hn!V4om6tn zoUGi{Ja4By9((Qjv#y*VZn>FF1sP7Nr20V}D zSbB$91Upd!8bb9?I^ROw?+dKgRBuozpre`(CS}d28Fr!`G>(mU1lw^2JF#TGSq+XM zfA$6+`rRR1ffrFL7brAaiRGBYjaY+e?A20#LZub|Lawof1!haJ8`T~~&0qpGBhUG% z>;Dcl z!b*%`!nM0FNxKiVmHSWw{RZ{BpIrNIRBjaU^aflVW&O2>_(Q<#GV1yhG1gyueU)KqM0uog8MdQl zupO6U+Vy{g8t@cqAOW7%g;7ce{helSgCgv$Hafj6-m z+j#l_zJyAiY19@3OR@uAf|_}|^J&!ehcSiMkaV);B!srC0jsbTTW}|8BIzSkHc`3k zOq6D`ejk!gb`mwyZ&44-k7t*%6?Ma>P&soDoADfO#n3Xd2k;TpeU2h?u+Lq48o54g zKU1mTiwNt^7;G)-#zRQH*nZR&dC02UY1E3GM%~q5bzc_$Q, 2015 # Filipe Rinaldi , 2015 # Hugo Leonardo Chalhoub Mendonça , 2015 +# Jonatas Baldin , 2017 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: Thomas Christie \n" +"PO-Revision-Date: 2017-12-06 09:53+0000\n" +"Last-Translator: Jonatas Baldin \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -60,35 +61,35 @@ msgstr "Token inválido." #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "Token de autenticação" #: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "Chave" #: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "Usuário" #: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "Criado" #: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "Token" #: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "Tokens" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "Nome do usuário" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "Senha" #: authtoken/serializers.py:20 msgid "User account is disabled." @@ -318,15 +319,15 @@ msgstr "Enviar" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "ascendente" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "descendente" #: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "Página inválida." #: pagination.py:427 msgid "Invalid cursor" @@ -428,7 +429,7 @@ msgstr "Versão inválida no caminho de URL." #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "Versão inválida no caminho da URL. Não corresponde a nenhuma versão do namespace." #: versioning.py:147 msgid "Invalid version in hostname." diff --git a/setup.py b/setup.py index 54acad01ba..9223facc95 100755 --- a/setup.py +++ b/setup.py @@ -69,6 +69,7 @@ def get_version(package): 'Framework :: Django', 'Framework :: Django :: 1.10', 'Framework :: Django :: 1.11', + 'Framework :: Django :: 2.0', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', From 5e21bf852b3b00e62d6c8d8b1ef86d06de7f78a4 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Wed, 20 Dec 2017 21:27:22 +0100 Subject: [PATCH 0005/1262] Bumped Django 2.0 version in requirements list. --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 0e747463bc..0b544e5bfa 100644 --- a/docs/index.md +++ b/docs/index.md @@ -87,7 +87,7 @@ continued development by **[signing up for a paid plan][funding]**. REST framework requires the following: * Python (2.7, 3.2, 3.3, 3.4, 3.5, 3.6) -* Django (1.10, 1.11, 2.0 alpha) +* Django (1.10, 1.11, 2.0) The following packages are optional: From 37cfe903385bc1c0b9de2674ad3b2d8682f93e53 Mon Sep 17 00:00:00 2001 From: Matthias Runge Date: Thu, 21 Dec 2017 11:00:43 +0100 Subject: [PATCH 0006/1262] Add locale files back to installed files (#5696) Resolves https://github.com/encode/django-rest-framework/issues/5695 --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 48ec57edf7..3c6c45e523 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,5 +2,6 @@ include README.md include LICENSE.md recursive-include rest_framework/static *.js *.css *.png *.eot *.svg *.ttf *.woff recursive-include rest_framework/templates *.html schema.js +recursice-include rest_framework/locale *.mo global-exclude __pycache__ global-exclude *.py[co] From df469260e148e569843541f8de7dca121fff6357 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 21 Dec 2017 10:03:03 +0000 Subject: [PATCH 0007/1262] Include woff2 in packaging. Closes #5692 --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 3c6c45e523..789f2bb167 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,6 @@ include README.md include LICENSE.md -recursive-include rest_framework/static *.js *.css *.png *.eot *.svg *.ttf *.woff +recursive-include rest_framework/static *.js *.css *.png *.eot *.svg *.ttf *.woff *.woff2 recursive-include rest_framework/templates *.html schema.js recursice-include rest_framework/locale *.mo global-exclude __pycache__ From 3a22b1d1f0c8df2924dc35bb45a1f5b01a973c11 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 21 Dec 2017 10:03:41 +0000 Subject: [PATCH 0008/1262] Version 3.7.5 --- rest_framework/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 8c30544075..492ecad159 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '3.7.4' +__version__ = '3.7.5' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2017 Tom Christie' From 0d96be9266488d0f7de0dcffad1092316d98e4bf Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 21 Dec 2017 10:13:18 +0000 Subject: [PATCH 0009/1262] Add 3.7.5 release notes --- docs/topics/release-notes.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index e6c9f78f21..52a7e6fbd0 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -42,6 +42,13 @@ You can determine your currently installed version using `pip freeze`: ### 3.7.4 +**Date**: [21st December 2017][3.7.5-milestone] + +* Add missing *.woff2 font files to packaging. [#5692][gh5692] +* Add missing *.mo locale files to packaging. [#5695][gh5695], [#5696][gh5696] + +### 3.7.4 + **Date**: [20th December 2017][3.7.4-milestone] * Schema: Extract method for `manual_fields` processing [#5633][gh5633] @@ -924,7 +931,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.7.2-milestone]: https://github.com/encode/django-rest-framework/milestone/59?closed=1 [3.7.3-milestone]: https://github.com/encode/django-rest-framework/milestone/60?closed=1 [3.7.4-milestone]: https://github.com/encode/django-rest-framework/milestone/62?closed=1 - +[3.7.5-milestone]: https://github.com/encode/django-rest-framework/milestone/63?closed=1 [gh2013]: https://github.com/encode/django-rest-framework/issues/2013 @@ -1733,3 +1740,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh5579]: https://github.com/encode/django-rest-framework/issues/5579 [gh5633]: https://github.com/encode/django-rest-framework/issues/5633 + +[gh5692]: https://github.com/encode/django-rest-framework/issues/5692 +[gh5695]: https://github.com/encode/django-rest-framework/issues/5695 +[gh5696]: https://github.com/encode/django-rest-framework/issues/5696 From 65791d8c79bcf53ea578dad0a1dc78d17a39346c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 21 Dec 2017 10:17:59 +0000 Subject: [PATCH 0010/1262] Version 3.7.5. Add missing .ico to packaging. --- MANIFEST.in | 2 +- docs/topics/release-notes.md | 9 ++++++++- rest_framework/__init__.py | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 789f2bb167..78f9928979 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,6 @@ include README.md include LICENSE.md -recursive-include rest_framework/static *.js *.css *.png *.eot *.svg *.ttf *.woff *.woff2 +recursive-include rest_framework/static *.js *.css *.png *.ico *.eot *.svg *.ttf *.woff *.woff2 recursive-include rest_framework/templates *.html schema.js recursice-include rest_framework/locale *.mo global-exclude __pycache__ diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 52a7e6fbd0..7b6cad95e9 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,7 +40,13 @@ You can determine your currently installed version using `pip freeze`: ## 3.7.x series -### 3.7.4 +### 3.7.6 + +**Date**: [21st December 2017][3.7.6-milestone] + +* Add missing *.ico icon files to packaging. + +### 3.7.5 **Date**: [21st December 2017][3.7.5-milestone] @@ -932,6 +938,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.7.3-milestone]: https://github.com/encode/django-rest-framework/milestone/60?closed=1 [3.7.4-milestone]: https://github.com/encode/django-rest-framework/milestone/62?closed=1 [3.7.5-milestone]: https://github.com/encode/django-rest-framework/milestone/63?closed=1 +[3.7.6-milestone]: https://github.com/encode/django-rest-framework/milestone/64?closed=1 [gh2013]: https://github.com/encode/django-rest-framework/issues/2013 diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 492ecad159..665d9ea41b 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '3.7.5' +__version__ = '3.7.6' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2017 Tom Christie' From 67964a1bf4af3a9f37df03b419e8e7ff06b750ac Mon Sep 17 00:00:00 2001 From: Laurent Paoletti Date: Thu, 21 Dec 2017 12:12:08 +0100 Subject: [PATCH 0011/1262] Fix typo to bring locale back to installed files --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 78f9928979..6f7cb8f13e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,6 +2,6 @@ include README.md include LICENSE.md recursive-include rest_framework/static *.js *.css *.png *.ico *.eot *.svg *.ttf *.woff *.woff2 recursive-include rest_framework/templates *.html schema.js -recursice-include rest_framework/locale *.mo +recursive-include rest_framework/locale *.mo global-exclude __pycache__ global-exclude *.py[co] From 955a6b902b0838cd4f4471dc5fab6b334f36615b Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Thu, 21 Dec 2017 12:35:37 +0100 Subject: [PATCH 0012/1262] Add 3.7.7 release notes --- docs/topics/release-notes.md | 8 ++++++++ rest_framework/__init__.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 7b6cad95e9..4d668254c9 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,12 @@ You can determine your currently installed version using `pip freeze`: ## 3.7.x series +### 3.7.7 + +**Date**: [21st December 2017][3.7.7-milestone] + +* Fix typo to include *.mo locale files to packaging. [#5697][gh5697], [#5695][gh5695] + ### 3.7.6 **Date**: [21st December 2017][3.7.6-milestone] @@ -939,6 +945,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.7.4-milestone]: https://github.com/encode/django-rest-framework/milestone/62?closed=1 [3.7.5-milestone]: https://github.com/encode/django-rest-framework/milestone/63?closed=1 [3.7.6-milestone]: https://github.com/encode/django-rest-framework/milestone/64?closed=1 +[3.7.7-milestone]: https://github.com/encode/django-rest-framework/milestone/65?closed=1 [gh2013]: https://github.com/encode/django-rest-framework/issues/2013 @@ -1751,3 +1758,4 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh5692]: https://github.com/encode/django-rest-framework/issues/5692 [gh5695]: https://github.com/encode/django-rest-framework/issues/5695 [gh5696]: https://github.com/encode/django-rest-framework/issues/5696 +[gh5697]: https://github.com/encode/django-rest-framework/issues/5697 diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 665d9ea41b..5d16490e85 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '3.7.6' +__version__ = '3.7.7' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2017 Tom Christie' From 832267e945d65b97802b84fe70ef1e156ecb8ab1 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Fri, 22 Dec 2017 07:28:35 +0000 Subject: [PATCH 0013/1262] Fix formatting of the 3.7.4 release note (#5704) --- docs/topics/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 4d668254c9..244adef0b3 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -73,6 +73,7 @@ You can determine your currently installed version using `pip freeze`: Note: `AutoSchema.__init__` now ensures `manual_fields` is a list. Previously may have been stored internally as `None`. + * Remove ulrparse compatability shim; use six instead [#5579][gh5579] * Drop compat wrapper for `TimeDelta.total_seconds()` [#5577][gh5577] * Clean up all whitespace throughout project [#5578][gh5578] From 5fc35eb7eb1ce8ca91fa6364978b5e423b80a332 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Fri, 22 Dec 2017 11:52:51 -0500 Subject: [PATCH 0014/1262] Add missing word in 'Field.allow_null' docs --- 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 6bf0ed15ea..a2f10ed19e 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -45,7 +45,7 @@ Defaults to `True`. Normally an error will be raised if `None` is passed to a serializer field. Set this keyword argument to `True` if `None` should be considered a valid value. -Note that setting this argument to `True` will imply a default value of `null` for serialization output, but does imply a default for input deserialization. +Note that setting this argument to `True` will imply a default value of `null` for serialization output, but does not imply a default for input deserialization. Defaults to `False` From 6ce60cd594ce3ddb14f08fb69f16b1901f1624ea Mon Sep 17 00:00:00 2001 From: Mariano Baragiola Date: Wed, 27 Dec 2017 10:17:10 -0300 Subject: [PATCH 0015/1262] Update writable nested serializers docs --- 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 85682838b4..059e873914 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -352,7 +352,7 @@ Here's an example for an `.update()` method on our previous `UserSerializer` cla Because the behavior of nested creates and updates can be ambiguous, and may require complex dependencies between related models, REST framework 3 requires you to always write these methods explicitly. The default `ModelSerializer` `.create()` and `.update()` methods do not include support for writable nested representations. -It is possible that a third party package, providing automatic support some kinds of automatic writable nested representations may be released alongside the 3.1 release. +There are however, third-party packages available such as [DRF Writable Nested][route-decorators] that support automatic writable nested representations. #### Handling saving related instances in model manager classes From be0a57b0f12ad09d773cd9a898ddccd60324a608 Mon Sep 17 00:00:00 2001 From: Mariano Baragiola Date: Wed, 27 Dec 2017 10:28:59 -0300 Subject: [PATCH 0016/1262] FIX link --- docs/api-guide/serializers.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 059e873914..140de0fe2e 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -352,7 +352,7 @@ Here's an example for an `.update()` method on our previous `UserSerializer` cla Because the behavior of nested creates and updates can be ambiguous, and may require complex dependencies between related models, REST framework 3 requires you to always write these methods explicitly. The default `ModelSerializer` `.create()` and `.update()` methods do not include support for writable nested representations. -There are however, third-party packages available such as [DRF Writable Nested][route-decorators] that support automatic writable nested representations. +There are however, third-party packages available such as [DRF Writable Nested][thirdparty-writable-nested] that support automatic writable nested representations. #### Handling saving related instances in model manager classes @@ -1173,6 +1173,7 @@ The [drf-writable-nested][drf-writable-nested] package provides writable nested [relations]: relations.md [model-managers]: https://docs.djangoproject.com/en/stable/topics/db/managers/ [encapsulation-blogpost]: http://www.dabapps.com/blog/django-models-and-encapsulation/ +[thirdparty-writable-nested]: serializers.md#drf-writable-nested [django-rest-marshmallow]: http://tomchristie.github.io/django-rest-marshmallow/ [marshmallow]: https://marshmallow.readthedocs.io/en/latest/ [serpy]: https://github.com/clarkduvall/serpy From 88c75ba808c5b0ac830efdf36117297e350f0925 Mon Sep 17 00:00:00 2001 From: guntanis Date: Thu, 28 Dec 2017 02:01:01 -0800 Subject: [PATCH 0017/1262] Fixed "typo" in example. Fixing code "typo" in example. In the original file, line 145 reads: url(http://github.com/r'%5Eapi-auth/',%20include('rest_framework.urls'), It's missing the closing parenthesis. --- docs/tutorial/4-authentication-and-permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md index 6e5b077ce5..af9a7c0ffd 100644 --- a/docs/tutorial/4-authentication-and-permissions.md +++ b/docs/tutorial/4-authentication-and-permissions.md @@ -142,7 +142,7 @@ Add the following import at the top of the file: And, at the end of the file, add a pattern to include the login and logout views for the browsable API. urlpatterns += [ - url(http://github.com/r'%5Eapi-auth/',%20include('rest_framework.urls'), + url(http://github.com/r'%5Eapi-auth/',%20include('rest_framework.urls')), ] The `r'^api-auth/'` part of pattern can actually be whatever URL you want to use. From 6bd773e7f8bf3535ee51e67708f4396c00f78a1d Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 2 Jan 2018 04:45:59 -0500 Subject: [PATCH 0018/1262] Improve composite field child errors (#5655) * Fixup DictField test descriptions * Nest ListField/DictField errors under the idx/key * Add nested ListField/DictField tests --- rest_framework/fields.py | 37 +++++++++++++++++++++++++++----- tests/test_fields.py | 46 +++++++++++++++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index a710df7b48..9b88784c83 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1626,7 +1626,7 @@ def to_internal_value(self, data): self.fail('not_a_list', input_type=type(data).__name__) if not self.allow_empty and len(data) == 0: self.fail('empty') - return [self.child.run_validation(item) for item in data] + return self.run_child_validation(data) def to_representation(self, data): """ @@ -1634,6 +1634,20 @@ def to_representation(self, data): """ return [self.child.to_representation(item) if item is not None else None for item in data] + def run_child_validation(self, data): + result = [] + errors = OrderedDict() + + for idx, item in enumerate(data): + try: + result.append(self.child.run_validation(item)) + except ValidationError as e: + errors[idx] = e.detail + + if not errors: + return result + raise ValidationError(errors) + class DictField(Field): child = _UnvalidatedField() @@ -1669,10 +1683,7 @@ def to_internal_value(self, data): data = html.parse_html_dict(data) if not isinstance(data, dict): self.fail('not_a_dict', input_type=type(data).__name__) - return { - six.text_type(key): self.child.run_validation(value) - for key, value in data.items() - } + return self.run_child_validation(data) def to_representation(self, value): """ @@ -1683,6 +1694,22 @@ def to_representation(self, value): for key, val in value.items() } + def run_child_validation(self, data): + result = {} + errors = OrderedDict() + + for key, value in data.items(): + key = six.text_type(key) + + try: + result[key] = self.child.run_validation(value) + except ValidationError as e: + errors[key] = e.detail + + if not errors: + return result + raise ValidationError(errors) + class JSONField(Field): default_error_messages = { diff --git a/tests/test_fields.py b/tests/test_fields.py index fc9ce192ad..bc11cd133a 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1767,7 +1767,7 @@ class TestListField(FieldValues): ] invalid_inputs = [ ('not a list', ['Expected a list of items but got type "str".']), - ([1, 2, 'error'], ['A valid integer is required.']), + ([1, 2, 'error', 'error'], {2: ['A valid integer is required.'], 3: ['A valid integer is required.']}), ({'one': 'two'}, ['Expected a list of items but got type "dict".']) ] outputs = [ @@ -1794,6 +1794,25 @@ def test_collection_types_are_invalid_input(self): assert exc_info.value.detail == ['Expected a list of items but got type "dict".'] +class TestNestedListField(FieldValues): + """ + Values for nested `ListField` with IntegerField as child. + """ + valid_inputs = [ + ([[1, 2], [3]], [[1, 2], [3]]), + ([[]], [[]]) + ] + invalid_inputs = [ + (['not a list'], {0: ['Expected a list of items but got type "str".']}), + ([[1, 2, 'error'], ['error']], {0: {2: ['A valid integer is required.']}, 1: {0: ['A valid integer is required.']}}), + ([{'one': 'two'}], {0: ['Expected a list of items but got type "dict".']}) + ] + outputs = [ + ([[1, 2], [3]], [[1, 2], [3]]), + ] + field = serializers.ListField(child=serializers.ListField(child=serializers.IntegerField())) + + class TestEmptyListField(FieldValues): """ Values for `ListField` with allow_empty=False flag. @@ -1834,13 +1853,13 @@ class TestUnvalidatedListField(FieldValues): class TestDictField(FieldValues): """ - Values for `ListField` with CharField as child. + Values for `DictField` with CharField as child. """ valid_inputs = [ ({'a': 1, 'b': '2', 3: 3}, {'a': '1', 'b': '2', '3': '3'}), ] invalid_inputs = [ - ({'a': 1, 'b': None}, ['This field may not be null.']), + ({'a': 1, 'b': None, 'c': None}, {'b': ['This field may not be null.'], 'c': ['This field may not be null.']}), ('not a dict', ['Expected a dictionary of items but got type "str".']), ] outputs = [ @@ -1866,9 +1885,26 @@ def test_allow_null(self): assert output is None +class TestNestedDictField(FieldValues): + """ + Values for nested `DictField` with CharField as child. + """ + valid_inputs = [ + ({0: {'a': 1, 'b': '2'}, 1: {3: 3}}, {'0': {'a': '1', 'b': '2'}, '1': {'3': '3'}}), + ] + invalid_inputs = [ + ({0: {'a': 1, 'b': None}, 1: {'c': None}}, {'0': {'b': ['This field may not be null.']}, '1': {'c': ['This field may not be null.']}}), + ({0: 'not a dict'}, {'0': ['Expected a dictionary of items but got type "str".']}), + ] + outputs = [ + ({0: {'a': 1, 'b': '2'}, 1: {3: 3}}, {'0': {'a': '1', 'b': '2'}, '1': {'3': '3'}}), + ] + field = serializers.DictField(child=serializers.DictField(child=serializers.CharField())) + + class TestDictFieldWithNullChild(FieldValues): """ - Values for `ListField` with allow_null CharField as child. + Values for `DictField` with allow_null CharField as child. """ valid_inputs = [ ({'a': None, 'b': '2', 3: 3}, {'a': None, 'b': '2', '3': '3'}), @@ -1883,7 +1919,7 @@ class TestDictFieldWithNullChild(FieldValues): class TestUnvalidatedDictField(FieldValues): """ - Values for `ListField` with no `child` argument. + Values for `DictField` with no `child` argument. """ valid_inputs = [ ({'a': 1, 'b': [4, 5, 6], 1: 123}, {'a': 1, 'b': [4, 5, 6], '1': 123}), From b7ed6459276f6416a155f1733abee7d1ce4ec7d2 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 2 Jan 2018 04:50:49 -0500 Subject: [PATCH 0019/1262] Disable HTML inputs for dict/list fields (#5702) --- rest_framework/renderers.py | 6 ++++++ .../rest_framework/horizontal/dict_field.html | 11 +++++++++++ .../rest_framework/horizontal/list_field.html | 11 +++++++++++ .../templates/rest_framework/inline/dict_field.html | 9 +++++++++ .../templates/rest_framework/inline/list_field.html | 9 +++++++++ .../templates/rest_framework/vertical/dict_field.html | 7 +++++++ .../templates/rest_framework/vertical/list_field.html | 7 +++++++ 7 files changed, 60 insertions(+) create mode 100644 rest_framework/templates/rest_framework/horizontal/dict_field.html create mode 100644 rest_framework/templates/rest_framework/horizontal/list_field.html create mode 100644 rest_framework/templates/rest_framework/inline/dict_field.html create mode 100644 rest_framework/templates/rest_framework/inline/list_field.html create mode 100644 rest_framework/templates/rest_framework/vertical/dict_field.html create mode 100644 rest_framework/templates/rest_framework/vertical/list_field.html diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 80a22dee59..f071e7b4d3 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -320,6 +320,12 @@ class HTMLFormRenderer(BaseRenderer): serializers.ListSerializer: { 'base_template': 'list_fieldset.html' }, + serializers.ListField: { + 'base_template': 'list_field.html' + }, + serializers.DictField: { + 'base_template': 'dict_field.html' + }, serializers.FilePathField: { 'base_template': 'select.html', }, diff --git a/rest_framework/templates/rest_framework/horizontal/dict_field.html b/rest_framework/templates/rest_framework/horizontal/dict_field.html new file mode 100644 index 0000000000..7c7414bc4b --- /dev/null +++ b/rest_framework/templates/rest_framework/horizontal/dict_field.html @@ -0,0 +1,11 @@ +
+ {% if field.label %} + + {% endif %} + +
+

Dictionaries are not currently supported in HTML input.

+
+
diff --git a/rest_framework/templates/rest_framework/horizontal/list_field.html b/rest_framework/templates/rest_framework/horizontal/list_field.html new file mode 100644 index 0000000000..46a9b7ecdf --- /dev/null +++ b/rest_framework/templates/rest_framework/horizontal/list_field.html @@ -0,0 +1,11 @@ +
+ {% if field.label %} + + {% endif %} + +
+

Lists are not currently supported in HTML input.

+
+
diff --git a/rest_framework/templates/rest_framework/inline/dict_field.html b/rest_framework/templates/rest_framework/inline/dict_field.html new file mode 100644 index 0000000000..1301452b90 --- /dev/null +++ b/rest_framework/templates/rest_framework/inline/dict_field.html @@ -0,0 +1,9 @@ +
+ {% if field.label %} + + {% endif %} + +

Dictionaries are not currently supported in HTML input.

+
diff --git a/rest_framework/templates/rest_framework/inline/list_field.html b/rest_framework/templates/rest_framework/inline/list_field.html new file mode 100644 index 0000000000..321d01bd1d --- /dev/null +++ b/rest_framework/templates/rest_framework/inline/list_field.html @@ -0,0 +1,9 @@ +
+ {% if field.label %} + + {% endif %} + +

Lists are not currently supported in HTML input.

+
diff --git a/rest_framework/templates/rest_framework/vertical/dict_field.html b/rest_framework/templates/rest_framework/vertical/dict_field.html new file mode 100644 index 0000000000..dde803b492 --- /dev/null +++ b/rest_framework/templates/rest_framework/vertical/dict_field.html @@ -0,0 +1,7 @@ +
+ {% if field.label %} + + {% endif %} + +

Dictionaries are not currently supported in HTML input.

+
diff --git a/rest_framework/templates/rest_framework/vertical/list_field.html b/rest_framework/templates/rest_framework/vertical/list_field.html new file mode 100644 index 0000000000..47a60c5d91 --- /dev/null +++ b/rest_framework/templates/rest_framework/vertical/list_field.html @@ -0,0 +1,7 @@ +
+ {% if field.label %} + + {% endif %} + +

Lists are not currently supported in HTML input.

+
From 7e2cca285dacf8bf2d32a7db6efd90303aff6d7b Mon Sep 17 00:00:00 2001 From: Floyd Hightower Date: Tue, 2 Jan 2018 05:51:42 -0400 Subject: [PATCH 0020/1262] Adding missing parenthesis (#5707) From 0712094ea2e846d41ef3ce2a4d12b9159911abfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristi=20V=C3=AEjdea?= Date: Tue, 2 Jan 2018 10:52:52 +0100 Subject: [PATCH 0021/1262] Fix typo in HostNameVersioning doc (#5709) --- docs/api-guide/versioning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/versioning.md b/docs/api-guide/versioning.md index 29672c96ea..4a875b87e9 100644 --- a/docs/api-guide/versioning.md +++ b/docs/api-guide/versioning.md @@ -183,7 +183,7 @@ By default this implementation expects the hostname to match this simple regular Note that the first group is enclosed in brackets, indicating that this is the matched portion of the hostname. -The `HostNameVersioning` scheme can be awkward to use in debug mode as you will typically be accessing a raw IP address such as `127.0.0.1`. There are various online services which you to [access localhost with a custom subdomain][lvh] which you may find helpful in this case. +The `HostNameVersioning` scheme can be awkward to use in debug mode as you will typically be accessing a raw IP address such as `127.0.0.1`. There are various online tutorials on how to [access localhost with a custom subdomain][lvh] which you may find helpful in this case. Hostname based versioning can be particularly useful if you have requirements to route incoming requests to different servers based on the version, as you can configure different DNS records for different API versions. From 6b0bf72bb8ba3e6ff1e7a334e6a23d95e8c2944a Mon Sep 17 00:00:00 2001 From: David De Sousa Date: Tue, 2 Jan 2018 10:59:08 +0100 Subject: [PATCH 0022/1262] using rsplit to get module and classname for imports (#5712) --- rest_framework/settings.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rest_framework/settings.py b/rest_framework/settings.py index db92b7a7b6..6c581f8e85 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -179,8 +179,7 @@ def import_from_string(val, setting_name): """ try: # Nod to tastypie's use of importlib. - parts = val.split('.') - module_path, class_name = '.'.join(parts[:-1]), parts[-1] + module_path, class_name = val.rsplit('.', 1) module = import_module(module_path) return getattr(module, class_name) except (ImportError, AttributeError) as e: From b65967711c911de05a43cf494de076a13ff74448 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 2 Jan 2018 05:14:25 -0500 Subject: [PATCH 0023/1262] Formalize URLPatternsTestCase (#5703) * Add formalized URLPatternsTestCase * Update versioning tests w/ new URLPatternsTestCase * Cleanup router tests urlpatterns * Add docs for URLPatternsTestCase --- docs/api-guide/testing.md | 28 ++++++++++++++++++- rest_framework/test.py | 44 ++++++++++++++++++++++++++++- tests/test_routers.py | 59 ++++++++++++++++++++++----------------- tests/test_testing.py | 29 ++++++++++++++++++- tests/test_versioning.py | 33 ++++------------------ 5 files changed, 138 insertions(+), 55 deletions(-) diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md index caba5cea22..d2ff6e7cb5 100644 --- a/docs/api-guide/testing.md +++ b/docs/api-guide/testing.md @@ -292,7 +292,7 @@ similar way as with `RequestsClient`. --- -# Test cases +# API Test cases REST framework includes the following test case classes, that mirror the existing Django test case classes, but use `APIClient` instead of Django's default `Client`. @@ -324,6 +324,32 @@ You can use any of REST framework's test case classes as you would for the regul --- +# URLPatternsTestCase + +REST framework also provides a test case class for isolating `urlpatterns` on a per-class basis. Note that this inherits from Django's `SimpleTestCase`, and will most likely need to be mixed with another test case class. + +## Example + + from django.urls import include, path, reverse + from rest_framework.test import APITestCase, URLPatternsTestCase + + + class AccountTests(APITestCase, URLPatternsTestCase): + urlpatterns = [ + path('api/', include('api.urls')), + ] + + def test_create_account(self): + """ + Ensure we can create a new account object. + """ + url = reverse('account-list') + response = self.client.get(url, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 1) + +--- + # Testing responses ## Checking the response data diff --git a/rest_framework/test.py b/rest_framework/test.py index ebad19a4e4..3b745bd622 100644 --- a/rest_framework/test.py +++ b/rest_framework/test.py @@ -5,11 +5,12 @@ from __future__ import unicode_literals import io +from importlib import import_module from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.core.handlers.wsgi import WSGIHandler -from django.test import testcases +from django.test import override_settings, testcases from django.test.client import Client as DjangoClient from django.test.client import RequestFactory as DjangoRequestFactory from django.test.client import ClientHandler @@ -358,3 +359,44 @@ class APISimpleTestCase(testcases.SimpleTestCase): class APILiveServerTestCase(testcases.LiveServerTestCase): client_class = APIClient + + +class URLPatternsTestCase(testcases.SimpleTestCase): + """ + Isolate URL patterns on a per-TestCase basis. For example, + + class ATestCase(URLPatternsTestCase): + urlpatterns = [...] + + def test_something(self): + ... + + class AnotherTestCase(URLPatternsTestCase): + urlpatterns = [...] + + def test_something_else(self): + ... + """ + @classmethod + def setUpClass(cls): + # Get the module of the TestCase subclass + cls._module = import_module(cls.__module__) + cls._override = override_settings(ROOT_URLCONF=cls.__module__) + + if hasattr(cls._module, 'urlpatterns'): + cls._module_urlpatterns = cls._module.urlpatterns + + cls._module.urlpatterns = cls.urlpatterns + + cls._override.enable() + super(URLPatternsTestCase, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(URLPatternsTestCase, cls).tearDownClass() + cls._override.disable() + + if hasattr(cls, '_module_urlpatterns'): + cls._module.urlpatterns = cls._module_urlpatterns + else: + del cls._module.urlpatterns diff --git a/tests/test_routers.py b/tests/test_routers.py index 5a1cfe8f40..55ccc647b3 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -14,7 +14,7 @@ from rest_framework.decorators import detail_route, list_route from rest_framework.response import Response from rest_framework.routers import DefaultRouter, SimpleRouter -from rest_framework.test import APIRequestFactory +from rest_framework.test import APIRequestFactory, URLPatternsTestCase from rest_framework.utils import json factory = APIRequestFactory() @@ -90,23 +90,10 @@ def regex_url_path_detail(self, request, *args, **kwargs): empty_prefix_router = SimpleRouter() empty_prefix_router.register(r'', EmptyPrefixViewSet, base_name='empty_prefix') -empty_prefix_urls = [ - url(http://github.com/r'%5E',%20include(empty_prefix_router.urls)), -] regex_url_path_router = SimpleRouter() regex_url_path_router.register(r'', RegexUrlPathViewSet, base_name='regex') -urlpatterns = [ - url(http://github.com/r'%5Enon-namespaced/',%20include(namespaced_router.urls)), - url(http://github.com/r'%5Enamespaced/',%20include((namespaced_router.urls,%20'example'), namespace='example')), - url(http://github.com/r'%5Eexample/',%20include(notes_router.urls)), - url(http://github.com/r'%5Eexample2/',%20include(kwarged_notes_router.urls)), - - url(http://github.com/r'%5Eempty-prefix/',%20include(empty_prefix_urls)), - url(http://github.com/r'%5Eregex/',%20include(regex_url_path_router.urls)) -] - class BasicViewSet(viewsets.ViewSet): def list(self, request, *args, **kwargs): @@ -156,8 +143,12 @@ def test_link_and_action_decorator(self): assert route.mapping[method] == endpoint -@override_settings(ROOT_URLCONF='tests.test_routers') -class TestRootView(TestCase): +class TestRootView(URLPatternsTestCase, TestCase): + urlpatterns = [ + url(http://github.com/r'%5Enon-namespaced/',%20include(namespaced_router.urls)), + url(http://github.com/r'%5Enamespaced/',%20include((namespaced_router.urls,%20'namespaced'), namespace='namespaced')), + ] + def test_retrieve_namespaced_root(self): response = self.client.get('/namespaced/') assert response.data == {"example": "http://testserver/namespaced/example/"} @@ -167,11 +158,15 @@ def test_retrieve_non_namespaced_root(self): assert response.data == {"example": "http://testserver/non-namespaced/example/"} -@override_settings(ROOT_URLCONF='tests.test_routers') -class TestCustomLookupFields(TestCase): +class TestCustomLookupFields(URLPatternsTestCase, TestCase): """ Ensure that custom lookup fields are correctly routed. """ + urlpatterns = [ + url(http://github.com/r'%5Eexample/',%20include(notes_router.urls)), + url(http://github.com/r'%5Eexample2/',%20include(kwarged_notes_router.urls)), + ] + def setUp(self): RouterTestModel.objects.create(uuid='123', text='foo bar') RouterTestModel.objects.create(uuid='a b', text='baz qux') @@ -219,12 +214,17 @@ def test_urls_limited_by_lookup_value_regex(self): @override_settings(ROOT_URLCONF='tests.test_routers') -class TestLookupUrlKwargs(TestCase): +class TestLookupUrlKwargs(URLPatternsTestCase, TestCase): """ Ensure the router honors lookup_url_kwarg. Setup a deep lookup_field, but map it to a simple URL kwarg. """ + urlpatterns = [ + url(http://github.com/r'%5Eexample/',%20include(notes_router.urls)), + url(http://github.com/r'%5Eexample2/',%20include(kwarged_notes_router.urls)), + ] + def setUp(self): RouterTestModel.objects.create(uuid='123', text='foo bar') @@ -408,8 +408,11 @@ def test_inherited_list_and_detail_route_decorators(self): self._test_list_and_detail_route_decorators(SubDynamicListAndDetailViewSet) -@override_settings(ROOT_URLCONF='tests.test_routers') -class TestEmptyPrefix(TestCase): +class TestEmptyPrefix(URLPatternsTestCase, TestCase): + urlpatterns = [ + url(http://github.com/r'%5Eempty-prefix/',%20include(empty_prefix_router.urls)), + ] + def test_empty_prefix_list(self): response = self.client.get('/empty-prefix/') assert response.status_code == 200 @@ -422,8 +425,11 @@ def test_empty_prefix_detail(self): assert json.loads(response.content.decode('utf-8')) == {'uuid': '111', 'text': 'First'} -@override_settings(ROOT_URLCONF='tests.test_routers') -class TestRegexUrlPath(TestCase): +class TestRegexUrlPath(URLPatternsTestCase, TestCase): + urlpatterns = [ + url(http://github.com/r'%5Eregex/',%20include(regex_url_path_router.urls)), + ] + def test_regex_url_path_list(self): kwarg = '1234' response = self.client.get('/regex/list/{}/'.format(kwarg)) @@ -438,8 +444,11 @@ def test_regex_url_path_detail(self): assert json.loads(response.content.decode('utf-8')) == {'pk': pk, 'kwarg': kwarg} -@override_settings(ROOT_URLCONF='tests.test_routers') -class TestViewInitkwargs(TestCase): +class TestViewInitkwargs(URLPatternsTestCase, TestCase): + urlpatterns = [ + url(http://github.com/r'%5Eexample/',%20include(notes_router.urls)), + ] + def test_suffix(self): match = resolve('/example/notes/') initkwargs = match.func.initkwargs diff --git a/tests/test_testing.py b/tests/test_testing.py index 1af6ef02e5..7868f724c1 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -12,7 +12,7 @@ from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework.test import ( - APIClient, APIRequestFactory, force_authenticate + APIClient, APIRequestFactory, URLPatternsTestCase, force_authenticate ) @@ -283,3 +283,30 @@ def test_empty_request_content_type(self): content_type='application/json', ) assert request.META['CONTENT_TYPE'] == 'application/json' + + +class TestUrlPatternTestCase(URLPatternsTestCase): + urlpatterns = [ + url(http://github.com/r'%5E),%20view), + ] + + @classmethod + def setUpClass(cls): + assert urlpatterns is not cls.urlpatterns + super(TestUrlPatternTestCase, cls).setUpClass() + assert urlpatterns is cls.urlpatterns + + @classmethod + def tearDownClass(cls): + assert urlpatterns is cls.urlpatterns + super(TestUrlPatternTestCase, cls).tearDownClass() + assert urlpatterns is not cls.urlpatterns + + def test_urlpatterns(self): + assert self.client.get('/').status_code == 200 + + +class TestExistingPatterns(TestCase): + def test_urlpatterns(self): + # sanity test to ensure that this test module does not have a '/' route + assert self.client.get('/').status_code == 404 diff --git a/tests/test_versioning.py b/tests/test_versioning.py index e73059c7d7..7e650e2752 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -7,33 +7,12 @@ from rest_framework.relations import PKOnlyObject from rest_framework.response import Response from rest_framework.reverse import reverse -from rest_framework.test import APIRequestFactory, APITestCase +from rest_framework.test import ( + APIRequestFactory, APITestCase, URLPatternsTestCase +) from rest_framework.versioning import NamespaceVersioning -@override_settings(ROOT_URLCONF='tests.test_versioning') -class URLPatternsTestCase(APITestCase): - """ - Isolates URL patterns used during testing on the test class itself. - For example: - - class MyTestCase(URLPatternsTestCase): - urlpatterns = [ - ... - ] - - def test_something(self): - ... - """ - def setUp(self): - global urlpatterns - urlpatterns = self.urlpatterns - - def tearDown(self): - global urlpatterns - urlpatterns = [] - - class RequestVersionView(APIView): def get(self, request, *args, **kwargs): return Response({'version': request.version}) @@ -163,7 +142,7 @@ class FakeResolverMatch: assert response.data == {'version': None} -class TestURLReversing(URLPatternsTestCase): +class TestURLReversing(URLPatternsTestCase, APITestCase): included = [ url(http://github.com/r'%5Enamespaced/'),%20dummy_view,%20name='another'), url(http://github.com/r'%5Eexample/(?P%3Cpk%3E\d+)/$', dummy_pk_view, name='example-detail') @@ -329,7 +308,7 @@ def test_missing_with_default_and_none_allowed(self): assert response.data == {'version': 'v2'} -class TestHyperlinkedRelatedField(URLPatternsTestCase): +class TestHyperlinkedRelatedField(URLPatternsTestCase, APITestCase): included = [ url(http://github.com/r'%5Enamespaced/(?P%3Cpk%3E\d+)/$', dummy_pk_view, name='namespaced'), ] @@ -361,7 +340,7 @@ def test_bug_2489(self): self.field.to_internal_value('/v2/namespaced/3/') -class TestNamespaceVersioningHyperlinkedRelatedFieldScheme(URLPatternsTestCase): +class TestNamespaceVersioningHyperlinkedRelatedFieldScheme(URLPatternsTestCase, APITestCase): nested = [ url(http://github.com/r'%5Enamespaced/(?P%3Cpk%3E\d+)/$', dummy_pk_view, name='nested'), ] From 351503907ceaf969f07cb61d9bbca320a14f4623 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 2 Jan 2018 05:28:45 -0500 Subject: [PATCH 0024/1262] Add exception translation test (#5700) --- tests/test_exceptions.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 8b5628ef2c..176aeb1746 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -1,11 +1,11 @@ from __future__ import unicode_literals from django.test import TestCase -from django.utils import six +from django.utils import six, translation from django.utils.translation import ugettext_lazy as _ from rest_framework.exceptions import ( - ErrorDetail, Throttled, _get_error_details + APIException, ErrorDetail, Throttled, _get_error_details ) @@ -51,3 +51,12 @@ def test_get_full_details_with_throttling(self): assert exception.get_full_details() == { 'message': 'Slow down! Expected available in {} seconds.'.format(2 if six.PY3 else 2.), 'code': 'throttled'} + + +class TranslationTests(TestCase): + + @translation.override('fr') + def test_message(self): + # this test largely acts as a sanity test to ensure the translation files are present. + self.assertEqual(_('A server error occurred.'), 'Une erreur du serveur est survenue.') + self.assertEqual(six.text_type(APIException()), 'Une erreur du serveur est survenue.') From 68519c092f62693f161a455621ad89ec15b47a31 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 2 Jan 2018 05:35:56 -0500 Subject: [PATCH 0025/1262] Test staticfiles (#5701) * Remove 'MIDDLEWARE_CLASSES' compat setting * Remove 'django.setup()' compat import * Move '--no-pkgroot' handling to conftest * Add staticfiles handling to dist build --- runtests.py | 15 ------------- tests/conftest.py | 57 ++++++++++++++++++++++++++++++++++------------- tox.ini | 2 +- 3 files changed, 43 insertions(+), 31 deletions(-) diff --git a/runtests.py b/runtests.py index a8f818ad73..d0e22a9677 100755 --- a/runtests.py +++ b/runtests.py @@ -1,7 +1,6 @@ #! /usr/bin/env python from __future__ import print_function -import os import subprocess import sys @@ -82,20 +81,6 @@ def is_class(string): run_flake8 = False run_isort = False - try: - # Remove the package root directory from `sys.path`, ensuring that rest_framework - # is imported from the installed site packages. Used for testing the distribution - sys.argv.remove('--no-pkgroot') - except ValueError: - pass - else: - sys.path.pop(0) - - # import rest_framework before pytest re-adds the package root directory. - import rest_framework - package_dir = os.path.join(os.getcwd(), 'rest_framework') - assert not rest_framework.__file__.startswith(package_dir) - if len(sys.argv) > 1: pytest_args = sys.argv[1:] first_arg = pytest_args[0] diff --git a/tests/conftest.py b/tests/conftest.py index 9906935d7c..927ddee62e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,12 +1,22 @@ -def pytest_configure(): - from django.conf import settings +import os +import sys - MIDDLEWARE = ( - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - ) +import django +from django.core import management + + +def pytest_addoption(parser): + parser.addoption('--no-pkgroot', action='http://github.com/store_true', default=False, + help='Remove package root directory from sys.path, ensuring that ' + 'rest_framework is imported from the installed site-packages. ' + 'Used for testing the distribution.') + parser.addoption('--staticfiles', action='http://github.com/store_true', default=False, + help='Run tests with static files collection, using manifest ' + 'staticfiles storage. Used for testing the distribution.') + + +def pytest_configure(config): + from django.conf import settings settings.configure( DEBUG_PROPAGATE_EXCEPTIONS=True, @@ -31,8 +41,12 @@ def pytest_configure(): } }, ], - MIDDLEWARE=MIDDLEWARE, - MIDDLEWARE_CLASSES=MIDDLEWARE, + MIDDLEWARE=( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + ), INSTALLED_APPS=( 'django.contrib.auth', 'django.contrib.contenttypes', @@ -64,8 +78,21 @@ def pytest_configure(): 'guardian', ) - try: - import django - django.setup() - except AttributeError: - pass + if config.getoption('--no-pkgroot'): + sys.path.pop(0) + + # import rest_framework before pytest re-adds the package root directory. + import rest_framework + package_dir = os.path.join(os.getcwd(), 'rest_framework') + assert not rest_framework.__file__.startswith(package_dir) + + # Manifest storage will raise an exception if static files are not present (ie, a packaging failure). + if config.getoption('--staticfiles'): + import rest_framework + settings.STATIC_ROOT = os.path.join(os.path.dirname(rest_framework.__file__), 'static-root') + settings.STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage' + + django.setup() + + if config.getoption('--staticfiles'): + management.call_command('collectstatic', verbosity=0, interactive=False) diff --git a/tox.ini b/tox.ini index f04e6ba939..d48d79be61 100644 --- a/tox.ini +++ b/tox.ini @@ -31,7 +31,7 @@ deps = -rrequirements/requirements-optionals.txt [testenv:dist] -commands = ./runtests.py --fast {posargs} --no-pkgroot -rw +commands = ./runtests.py --fast {posargs} --no-pkgroot --staticfiles -rw deps = django -rrequirements/requirements-testing.txt From 522d45354633f080288f82ebe1535ef4abbf0b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristi=20V=C3=AEjdea?= Date: Tue, 2 Jan 2018 14:51:54 +0100 Subject: [PATCH 0026/1262] Add drf-yasg to documentation and schema 3rd party packages (#5720) Also fixed broken `swagger` link. --- docs/api-guide/schemas.md | 7 +++++++ docs/img/drf-yasg.png | Bin 0 -> 59949 bytes docs/topics/documenting-your-api.md | 20 +++++++++++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 docs/img/drf-yasg.png diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 9234212ea8..3284c9e1b0 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -806,6 +806,12 @@ A short description of the meaning and intended usage of the input field. # Third party packages +## drf-yasg - Yet Another Swagger Generator + +[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 OpenAPI [DRF OpenAPI][drf-openapi] renders the schema generated by Django Rest Framework @@ -815,6 +821,7 @@ in [OpenAPI][open-api] format. [cite]: https://blog.heroku.com/archives/2014/1/8/json_schema_for_heroku_platform_api [coreapi]: http://www.coreapi.org/ [corejson]: http://www.coreapi.org/specification/encoding/#core-json-encoding +[drf-yasg]: https://github.com/axnsan12/drf-yasg/ [open-api]: https://openapis.org/ [drf-openapi]: https://github.com/limdauto/drf_openapi [json-hyperschema]: http://json-schema.org/latest/json-schema-hypermedia.html diff --git a/docs/img/drf-yasg.png b/docs/img/drf-yasg.png new file mode 100644 index 0000000000000000000000000000000000000000..ed770a386825da2040d9958cc7f60cb3b8fd6c5e GIT binary patch literal 59949 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
l>MqR8Yn04&lC0m{Jf;51eYV=QNzWgoMN7_` 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 Date: Mon, 8 Jan 2018 01:19:08 -0800 Subject: [PATCH 0027/1262] Remove unused compat._resolve_model() (#5733) Last use removed in c674687782ef96a3bb8466b412e99debd3d04f00. --- rest_framework/compat.py | 29 ------------------- tests/test_utils.py | 62 ---------------------------------------- 2 files changed, 91 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 9870fe77e2..88a76000e0 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -5,14 +5,9 @@ from __future__ import unicode_literals -import inspect - import django -from django.apps import apps from django.conf import settings from django.core import validators -from django.core.exceptions import ImproperlyConfigured -from django.db import models from django.utils import six from django.views.generic import View @@ -107,30 +102,6 @@ def distinct(queryset, base): return queryset.distinct() -def _resolve_model(obj): - """ - Resolve supplied `obj` to a Django model class. - - `obj` must be a Django model class itself, or a string - representation of one. Useful in situations like GH #1225 where - Django may not have resolved a string-based reference to a model in - another model's foreign key definition. - - String representations should have the format: - 'appname.ModelName' - """ - if isinstance(obj, six.string_types) and len(obj.split('.')) == 2: - app_name, model_name = obj.split('.') - resolved_model = apps.get_model(app_name, model_name) - if resolved_model is None: - msg = "Django did not return a model for {0}.{1}" - raise ImproperlyConfigured(msg.format(app_name, model_name)) - return resolved_model - elif inspect.isclass(obj) and issubclass(obj, models.Model): - return obj - raise ValueError("{0} is not a Django model".format(obj)) - - # django.contrib.postgres requires psycopg2 try: from django.contrib.postgres import fields as postgres_fields diff --git a/tests/test_utils.py b/tests/test_utils.py index 4aed5ee738..d63f266d6f 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -2,12 +2,8 @@ from __future__ import unicode_literals from django.conf.urls import url -from django.core.exceptions import ImproperlyConfigured from django.test import TestCase, override_settings -from django.utils import six -import rest_framework.utils.model_meta -from rest_framework.compat import _resolve_model from rest_framework.routers import SimpleRouter from rest_framework.serializers import ModelSerializer from rest_framework.utils import json @@ -124,64 +120,6 @@ def test_modelviewset_resource_instance_breadcrumbs(self): ] -class ResolveModelTests(TestCase): - """ - `_resolve_model` should return a Django model class given the - provided argument is a Django model class itself, or a properly - formatted string representation of one. - """ - def test_resolve_django_model(self): - resolved_model = _resolve_model(BasicModel) - assert resolved_model == BasicModel - - def test_resolve_string_representation(self): - resolved_model = _resolve_model('tests.BasicModel') - assert resolved_model == BasicModel - - def test_resolve_unicode_representation(self): - resolved_model = _resolve_model(six.text_type('tests.BasicModel')) - assert resolved_model == BasicModel - - def test_resolve_non_django_model(self): - with self.assertRaises(ValueError): - _resolve_model(TestCase) - - def test_resolve_improper_string_representation(self): - with self.assertRaises(ValueError): - _resolve_model('BasicModel') - - -class ResolveModelWithPatchedDjangoTests(TestCase): - """ - Test coverage for when Django's `get_model` returns `None`. - - Under certain circumstances Django may return `None` with `get_model`: - http://git.io/get-model-source - - It usually happens with circular imports so it is important that DRF - excepts early, otherwise fault happens downstream and is much more - difficult to debug. - - """ - - def setUp(self): - """Monkeypatch get_model.""" - self.get_model = rest_framework.compat.apps.get_model - - def get_model(app_label, model_name): - return None - - rest_framework.compat.apps.get_model = get_model - - def tearDown(self): - """Revert monkeypatching.""" - rest_framework.compat.apps.get_model = self.get_model - - def test_blows_up_if_model_does_not_resolve(self): - with self.assertRaises(ImproperlyConfigured): - _resolve_model('tests.BasicModel') - - class JsonFloatTests(TestCase): """ Internaly, wrapped json functions should adhere to strict float handling From c1848d765d284ea66b6137112eeb9163a1679f31 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Mon, 8 Jan 2018 01:45:29 -0800 Subject: [PATCH 0028/1262] Drop compat workaround for unsupported Python 3.2 (#5734) --- rest_framework/compat.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 88a76000e0..c81fa62102 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -113,8 +113,7 @@ def distinct(queryset, base): try: import coreapi import uritemplate -except (ImportError, SyntaxError): - # SyntaxError is possible under python 3.2 +except ImportError: coreapi = None uritemplate = None From ffe3dbb1b15f35295924f5c2f0513fc4c81d5e6e Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Mon, 8 Jan 2018 01:49:46 -0800 Subject: [PATCH 0029/1262] Perfer iter(dict) over iter(dict.keys()) (#5736) Calling dict.keys() is unnecessary. The two are functionally equivalent on modern Pythons. Inspired by Lennart Regebro's talk "Prehistoric Patterns in Python" from PyCon 2017. https://www.youtube.com/watch?v=V5-JH23Vk0I --- docs/api-guide/serializers.md | 2 +- rest_framework/fields.py | 2 +- rest_framework/filters.py | 2 +- rest_framework/relations.py | 2 +- rest_framework/renderers.py | 4 ++-- rest_framework/schemas/generators.py | 2 +- rest_framework/schemas/inspectors.py | 4 ++-- rest_framework/serializers.py | 18 +++++++++--------- rest_framework/test.py | 2 +- rest_framework/utils/html.py | 2 +- rest_framework/utils/mediatypes.py | 4 ++-- tests/test_bound_fields.py | 2 +- tests/test_fields.py | 4 ++-- tests/test_metadata.py | 2 +- tests/test_multitable_inheritance.py | 4 ++-- tests/test_one_to_one_with_inheritance.py | 2 +- tests/test_permissions.py | 6 +++--- tests/test_request.py | 4 ++-- tests/test_serializer_lists.py | 2 +- 19 files changed, 35 insertions(+), 35 deletions(-) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 140de0fe2e..3c560d829b 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -1083,7 +1083,7 @@ For example, if you wanted to be able to set which fields should be used by a se if fields is not None: # Drop any fields that are not specified in the `fields` argument. allowed = set(fields) - existing = set(self.fields.keys()) + existing = set(self.fields) for field_name in existing - allowed: self.fields.pop(field_name) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 9b88784c83..c8f189ed17 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1420,7 +1420,7 @@ def _set_choices(self, choices): # Allows us to deal with eg. integer choices while supporting either # integer or string input, but still get the correct datatype out. self.choice_strings_to_values = { - six.text_type(key): key for key in self.choices.keys() + six.text_type(key): key for key in self.choices } choices = property(_get_choices, _set_choices) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index d30135d2e8..830d0a6169 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -218,7 +218,7 @@ def get_valid_fields(self, queryset, view, context={}): ] valid_fields += [ (key, key.title().split('__')) - for key in queryset.query.annotations.keys() + for key in queryset.query.annotations ] else: valid_fields = [ diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 22078e64a4..c87b9299ab 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -133,7 +133,7 @@ def many_init(cls, *args, **kwargs): return CustomManyRelatedField(*args, **kwargs) """ list_kwargs = {'child_relation': cls(*args, **kwargs)} - for key in kwargs.keys(): + for key in kwargs: if key in MANY_RELATION_KWARGS: list_kwargs[key] = kwargs[key] return ManyRelatedField(**list_kwargs) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index f071e7b4d3..a2db9f2283 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -805,8 +805,8 @@ def get_context(self, data, accepted_media_type, renderer_context): header = results style = 'detail' - columns = [key for key in header.keys() if key != 'url'] - details = [key for key in header.keys() if key != 'url'] + columns = [key for key in header if key != 'url'] + details = [key for key in header if key != 'url'] context['style'] = style context['columns'] = columns diff --git a/rest_framework/schemas/generators.py b/rest_framework/schemas/generators.py index 10af6ee04d..8807779185 100644 --- a/rest_framework/schemas/generators.py +++ b/rest_framework/schemas/generators.py @@ -228,7 +228,7 @@ def get_allowed_methods(self, callback): Return a list of the valid HTTP methods for this endpoint. """ if hasattr(callback, 'actions'): - actions = set(callback.actions.keys()) + actions = set(callback.actions) http_method_names = set(callback.cls.http_method_names) methods = [method.upper() for method in actions & http_method_names] else: diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index b2a5320bd2..2afef7a04c 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -54,13 +54,13 @@ def field_to_schema(field): return coreschema.String(title=title, description=description) elif isinstance(field, serializers.MultipleChoiceField): return coreschema.Array( - items=coreschema.Enum(enum=list(field.choices.keys())), + items=coreschema.Enum(enum=list(field.choices)), title=title, description=description ) elif isinstance(field, serializers.ChoiceField): return coreschema.Enum( - enum=list(field.choices.keys()), + enum=list(field.choices), title=title, description=description ) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index e2ea0d7440..57f916a17d 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1136,9 +1136,9 @@ def get_default_field_names(self, declared_fields, model_info): """ return ( [model_info.pk.name] + - list(declared_fields.keys()) + - list(model_info.fields.keys()) + - list(model_info.forward_relations.keys()) + list(declared_fields) + + list(model_info.fields) + + list(model_info.forward_relations) ) # Methods for constructing serializer fields... @@ -1194,7 +1194,7 @@ def build_standard_field(self, field_name, model_field): 'error_messages', 'validators', 'allow_null', 'allow_blank', 'choices' } - for key in list(field_kwargs.keys()): + for key in list(field_kwargs): if key not in valid_kwargs: field_kwargs.pop(key) @@ -1364,7 +1364,7 @@ def get_uniqueness_extra_kwargs(self, field_names, declared_fields, extra_kwargs # Include each of the `unique_together` field names, # so long as all the field names are included on the serializer. - for parent_class in [model] + list(model._meta.parents.keys()): + 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)): unique_constraint_names |= set(unique_together_list) @@ -1466,7 +1466,7 @@ def get_unique_together_validators(self): """ model_class_inheritance_tree = ( [self.Meta.model] + - list(self.Meta.model._meta.parents.keys()) + list(self.Meta.model._meta.parents) ) # The field names we're passing though here only include fields @@ -1566,9 +1566,9 @@ def get_default_field_names(self, declared_fields, model_info): """ return ( [self.url_field_name] + - list(declared_fields.keys()) + - list(model_info.fields.keys()) + - list(model_info.forward_relations.keys()) + list(declared_fields) + + list(model_info.fields) + + list(model_info.forward_relations) ) def build_nested_field(self, field_name, relation_info, nested_depth): diff --git a/rest_framework/test.py b/rest_framework/test.py index 3b745bd622..c58df34c6a 100644 --- a/rest_framework/test.py +++ b/rest_framework/test.py @@ -175,7 +175,7 @@ def _encode_data(self, data, format=None, content_type=None): "Set TEST_REQUEST_RENDERER_CLASSES to enable " "extra request formats.".format( format, - ', '.join(["'" + fmt + "'" for fmt in self.renderer_classes.keys()]) + ', '.join(["'" + fmt + "'" for fmt in self.renderer_classes]) ) ) diff --git a/rest_framework/utils/html.py b/rest_framework/utils/html.py index ca062c9fc1..77167e4708 100644 --- a/rest_framework/utils/html.py +++ b/rest_framework/utils/html.py @@ -59,7 +59,7 @@ def parse_html_list(dictionary, prefix=''): ret[index][key] = value else: ret[index] = MultiValueDict({key: [value]}) - return [ret[item] for item in sorted(ret.keys())] + return [ret[item] for item in sorted(ret)] def parse_html_dict(dictionary, prefix=''): diff --git a/rest_framework/utils/mediatypes.py b/rest_framework/utils/mediatypes.py index 9fe3da05b4..7d9d24f2b4 100644 --- a/rest_framework/utils/mediatypes.py +++ b/rest_framework/utils/mediatypes.py @@ -55,7 +55,7 @@ def __init__(self, media_type_str): def match(self, other): """Return true if this MediaType satisfies the given MediaType.""" - for key in self.params.keys(): + for key in self.params: if key != 'q' and other.params.get(key, None) != self.params.get(key, None): return False @@ -76,7 +76,7 @@ def precedence(self): return 0 elif self.sub_type == '*': return 1 - elif not self.params or list(self.params.keys()) == ['q']: + elif not self.params or list(self.params) == ['q']: return 2 return 3 diff --git a/tests/test_bound_fields.py b/tests/test_bound_fields.py index e90e65edf3..4cb75dc2c8 100644 --- a/tests/test_bound_fields.py +++ b/tests/test_bound_fields.py @@ -54,7 +54,7 @@ class ExampleSerializer(serializers.Serializer): serializer = ExampleSerializer() del serializer.fields['text'] - assert 'text' not in serializer.fields.keys() + assert 'text' not in serializer.fields def test_as_form_fields(self): class ExampleSerializer(serializers.Serializer): diff --git a/tests/test_fields.py b/tests/test_fields.py index bc11cd133a..fd12a33f94 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -401,7 +401,7 @@ class TestSerializer(serializers.Serializer): serializer = TestSerializer(data=QueryDict('message=')) assert serializer.is_valid() - assert list(serializer.validated_data.keys()) == ['message'] + assert list(serializer.validated_data) == ['message'] def test_empty_html_uuidfield_with_optional(self): class TestSerializer(serializers.Serializer): @@ -409,7 +409,7 @@ class TestSerializer(serializers.Serializer): serializer = TestSerializer(data=QueryDict('message=')) assert serializer.is_valid() - assert list(serializer.validated_data.keys()) == [] + assert list(serializer.validated_data) == [] def test_empty_html_charfield_allow_null(self): class TestSerializer(serializers.Serializer): diff --git a/tests/test_metadata.py b/tests/test_metadata.py index b9db36e81d..647a30dbd7 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -207,7 +207,7 @@ def check_permissions(self, request): view = ExampleView.as_view() response = view(request=request) assert response.status_code == status.HTTP_200_OK - assert list(response.data['actions'].keys()) == ['PUT'] + assert list(response.data['actions']) == ['PUT'] def test_object_permissions(self): """ diff --git a/tests/test_multitable_inheritance.py b/tests/test_multitable_inheritance.py index c406fcedaf..2ddd37ebba 100644 --- a/tests/test_multitable_inheritance.py +++ b/tests/test_multitable_inheritance.py @@ -44,7 +44,7 @@ def test_multitable_inherited_model_fields_as_expected(self): """ child = ChildModel(name1='parent name', name2='child name') serializer = DerivedModelSerializer(child) - assert set(serializer.data.keys()) == {'name1', 'name2', 'id'} + assert set(serializer.data) == {'name1', 'name2', 'id'} def test_onetoone_primary_key_model_fields_as_expected(self): """ @@ -54,7 +54,7 @@ def test_onetoone_primary_key_model_fields_as_expected(self): parent = ParentModel.objects.create(name1='parent name') associate = AssociatedModel.objects.create(name='hello', ref=parent) serializer = AssociatedModelSerializer(associate) - assert set(serializer.data.keys()) == {'name', 'ref'} + assert set(serializer.data) == {'name', 'ref'} def test_data_is_valid_without_parent_ptr(self): """ diff --git a/tests/test_one_to_one_with_inheritance.py b/tests/test_one_to_one_with_inheritance.py index 4bc9cec0bd..789c7fcb97 100644 --- a/tests/test_one_to_one_with_inheritance.py +++ b/tests/test_one_to_one_with_inheritance.py @@ -39,5 +39,5 @@ def test_multitable_inherited_model_fields_as_expected(self): """ child = ChildModel(name1='parent name', name2='child name') serializer = DerivedModelSerializer(child) - self.assertEqual(set(serializer.data.keys()), + self.assertEqual(set(serializer.data), {'name1', 'name2', 'id', 'childassociatedmodel'}) diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 5d7cb20eaa..80b666180a 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -151,7 +151,7 @@ def test_options_permitted(self): response = root_view(request, pk='1') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn('actions', response.data) - self.assertEqual(list(response.data['actions'].keys()), ['POST']) + self.assertEqual(list(response.data['actions']), ['POST']) request = factory.options( '/1', @@ -160,7 +160,7 @@ def test_options_permitted(self): response = instance_view(request, pk='1') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn('actions', response.data) - self.assertEqual(list(response.data['actions'].keys()), ['PUT']) + self.assertEqual(list(response.data['actions']), ['PUT']) def test_options_disallowed(self): request = factory.options( @@ -195,7 +195,7 @@ def test_options_updateonly(self): response = instance_view(request, pk='1') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn('actions', response.data) - self.assertEqual(list(response.data['actions'].keys()), ['PUT']) + self.assertEqual(list(response.data['actions']), ['PUT']) def test_empty_view_does_not_assert(self): request = factory.get('/1', HTTP_AUTHORIZATION=self.permitted_credentials) diff --git a/tests/test_request.py b/tests/test_request.py index 76589a4401..8c680baa0e 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -103,8 +103,8 @@ def test_request_POST_with_files(self): upload = SimpleUploadedFile("file.txt", b"file_content") request = Request(factory.post('/', {'upload': upload})) request.parsers = (FormParser(), MultiPartParser()) - assert list(request.POST.keys()) == [] - assert list(request.FILES.keys()) == ['upload'] + assert list(request.POST) == [] + assert list(request.FILES) == ['upload'] def test_standard_behaviour_determines_form_content_PUT(self): """ diff --git a/tests/test_serializer_lists.py b/tests/test_serializer_lists.py index a7955d83c7..34c3d100b4 100644 --- a/tests/test_serializer_lists.py +++ b/tests/test_serializer_lists.py @@ -15,7 +15,7 @@ def __init__(self, **kwargs): def __eq__(self, other): if self._data.keys() != other._data.keys(): return False - for key in self._data.keys(): + for key in self._data: if self._data[key] != other._data[key]: return False return True From b2ec681d8d4c5a4a5ff3febb43449be5eea07883 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Wed, 10 Jan 2018 23:51:34 -0800 Subject: [PATCH 0030/1262] Pass python_requires argument to setuptools (#5739) Helps pip decide what version of the library to install. https://packaging.python.org/tutorials/distributing-packages/#python-requires > If your project only runs on certain Python versions, setting the > python_requires argument to the appropriate PEP 440 version specifier > string will prevent pip from installing the project on other Python > versions. https://setuptools.readthedocs.io/en/latest/setuptools.html#new-and-changed-setup-keywords > python_requires > > A string corresponding to a version specifier (as defined in PEP 440) > for the Python version, used to specify the Requires-Python defined in > PEP 345. --- docs/topics/project-management.md | 5 +++++ setup.py | 1 + 2 files changed, 6 insertions(+) diff --git a/docs/topics/project-management.md b/docs/topics/project-management.md index cc86c968ac..fe8baf774f 100644 --- a/docs/topics/project-management.md +++ b/docs/topics/project-management.md @@ -105,6 +105,11 @@ The following template should be used for the description of the issue, and serv Checklist: - [ ] Create pull request for [release notes](https://github.com/encode/django-rest-framework/blob/master/docs/topics/release-notes.md) based on the [*.*.* milestone](https://github.com/encode/django-rest-framework/milestones/***). + - [ ] Update supported versions: + - [ ] `setup.py` `python_requires` list + - [ ] `setup.py` Python & Django version trove classifiers + - [ ] `README` Python & Django versions + - [ ] `docs` Python & Django versions - [ ] Update the translations from [transifex](http://www.django-rest-framework.org/topics/project-management/#translations). - [ ] Ensure the pull request increments the version to `*.*.*` in [`restframework/__init__.py`](https://github.com/encode/django-rest-framework/blob/master/rest_framework/__init__.py). - [ ] Confirm with @tomchristie that release is finalized and ready to go. diff --git a/setup.py b/setup.py index 9223facc95..f2304b9d15 100755 --- a/setup.py +++ b/setup.py @@ -62,6 +62,7 @@ def get_version(package): packages=find_packages(exclude=['tests*']), include_package_data=True, install_requires=[], + python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", zip_safe=False, classifiers=[ 'Development Status :: 5 - Production/Stable', From d5c34aa8130164ecf07a786668a1272ddcf483af Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Mon, 15 Jan 2018 06:03:44 -0800 Subject: [PATCH 0031/1262] Remove unused links from docs (#5735) Each removed link has no inline use. --- docs/api-guide/authentication.md | 5 ----- docs/api-guide/caching.md | 2 -- docs/api-guide/fields.md | 2 -- docs/api-guide/filtering.md | 2 -- docs/api-guide/pagination.md | 1 - docs/api-guide/permissions.md | 3 --- docs/api-guide/relations.md | 1 - docs/index.md | 8 -------- docs/topics/2.2-announcement.md | 1 - docs/topics/browsable-api.md | 2 -- docs/topics/internationalization.md | 1 - docs/topics/release-notes.md | 10 ---------- docs/topics/rest-framework-2-announcement.md | 1 - docs/topics/rest-hypermedia-hateoas.md | 1 - docs/tutorial/quickstart.md | 1 - 15 files changed, 41 deletions(-) diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 63a789dfca..d7f2954b6d 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -407,7 +407,6 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a [http401]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2 [http403]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4 [basicauth]: http://tools.ietf.org/html/rfc2617 -[oauth]: http://oauth.net/2/ [permission]: permissions.md [throttling]: throttling.md [csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax @@ -419,10 +418,6 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a [juanriaza]: https://github.com/juanriaza [djangorestframework-digestauth]: https://github.com/juanriaza/django-rest-framework-digestauth [oauth-1.0a]: http://oauth.net/core/1.0a -[django-oauth-plus]: http://code.larlet.fr/django-oauth-plus -[django-oauth2-provider]: https://github.com/caffeinehit/django-oauth2-provider -[django-oauth2-provider-docs]: https://django-oauth2-provider.readthedocs.io/en/latest/ -[rfc6749]: http://tools.ietf.org/html/rfc6749 [django-oauth-toolkit]: https://github.com/evonove/django-oauth-toolkit [evonove]: https://github.com/evonove/ [oauthlib]: https://github.com/idan/oauthlib diff --git a/docs/api-guide/caching.md b/docs/api-guide/caching.md index ed3f62c21e..ff51aed061 100644 --- a/docs/api-guide/caching.md +++ b/docs/api-guide/caching.md @@ -47,8 +47,6 @@ class PostView(APIView): **NOTE:** The [`cache_page`][page] decorator only caches the `GET` and `HEAD` responses with status 200. - -[django]: https://docs.djangoproject.com/en/dev/topics/cache/ [page]: https://docs.djangoproject.com/en/dev/topics/cache/#the-per-view-cache [cookie]: https://docs.djangoproject.com/en/dev/topics/http/decorators/#django.views.decorators.vary.vary_on_cookie [decorator]: https://docs.djangoproject.com/en/dev/topics/class-based-views/intro/#decorating-the-class diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index a2f10ed19e..684ae517d5 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -806,9 +806,7 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide [cite]: https://docs.djangoproject.com/en/stable/ref/forms/api/#django.forms.Form.cleaned_data [html-and-forms]: ../topics/html-and-forms.md [FILE_UPLOAD_HANDLERS]: https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS -[ecma262]: http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 [strftime]: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior -[django-widgets]: https://docs.djangoproject.com/en/stable/ref/forms/widgets/ [iso8601]: http://www.w3.org/TR/NOTE-datetime [drf-compound-fields]: https://drf-compound-fields.readthedocs.io [drf-extra-fields]: https://github.com/Hipo/drf-extra-fields diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 93a142755d..83cbe7d1b7 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -379,13 +379,11 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter] [drf-url-filter][drf-url-filter] is a simple Django app to apply filters on drf `ModelViewSet`'s `Queryset` in a clean, simple and configurable way. It also supports validations on incoming query params and their values. A beautiful python package `Voluptuous` is being used for validations on the incoming query parameters. The best part about voluptuous is you can define your own validations as per your query params requirements. [cite]: https://docs.djangoproject.com/en/stable/topics/db/queries/#retrieving-specific-objects-with-filters -[django-filter]: https://github.com/alex/django-filter [django-filter-docs]: https://django-filter.readthedocs.io/en/latest/index.html [django-filter-drf-docs]: https://django-filter.readthedocs.io/en/latest/guide/rest_framework.html [guardian]: https://django-guardian.readthedocs.io/ [view-permissions]: https://django-guardian.readthedocs.io/en/latest/userguide/assign.html [view-permissions-blogpost]: http://blog.nyaruka.com/adding-a-view-permission-to-django-models -[nullbooleanselect]: https://github.com/django/django/blob/master/django/forms/widgets.py [search-django-admin]: https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields [django-rest-framework-filters]: https://github.com/philipn/django-rest-framework-filters [django-rest-framework-word-search-filter]: https://github.com/trollknurr/django-rest-framework-word-search-filter diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index b28f1616df..0f123cb89f 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -314,7 +314,6 @@ The [`drf-proxy-pagination` package][drf-proxy-pagination] includes a `ProxyPagi The [`django-rest-framework-link-header-pagination` package][drf-link-header-pagination] includes a `LinkHeaderPagination` class which provides pagination via an HTTP `Link` header as desribed in [Github's developer documentation](github-link-pagination). [cite]: https://docs.djangoproject.com/en/stable/topics/pagination/ -[github-link-pagination]: https://developer.github.com/guides/traversing-with-pagination/ [link-header]: ../img/link-header-pagination.png [drf-extensions]: http://chibisov.github.io/drf-extensions/docs/ [paginate-by-max-mixin]: http://chibisov.github.io/drf-extensions/docs/#paginatebymaxmixin diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 0a3d0ef657..72cbeab916 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -276,10 +276,7 @@ The [Django Rest Framework API Key][django-rest-framework-api-key] package allow [contribauth]: https://docs.djangoproject.com/en/stable/topics/auth/customizing/#custom-permissions [objectpermissions]: https://docs.djangoproject.com/en/stable/topics/auth/customizing/#handling-object-permissions [guardian]: https://github.com/lukaszb/django-guardian -[get_objects_for_user]: http://pythonhosted.org/django-guardian/api/guardian.shortcuts.html#get-objects-for-user -[2.2-announcement]: ../topics/2.2-announcement.md [filtering]: filtering.md -[drf-any-permissions]: https://github.com/kevin-brown/drf-any-permissions [composed-permissions]: https://github.com/niwibe/djangorestframework-composed-permissions [rest-condition]: https://github.com/caxap/rest_condition [dry-rest-permissions]: https://github.com/Helioscene/dry-rest-permissions diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index 662fd48095..ce249c32fd 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -596,6 +596,5 @@ The [rest-framework-generic-relations][drf-nested-relations] library provides re [reverse-relationships]: https://docs.djangoproject.com/en/stable/topics/db/queries/#following-relationships-backward [routers]: http://www.django-rest-framework.org/api-guide/routers#defaultrouter [generic-relations]: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#id1 -[2.2-announcement]: ../topics/2.2-announcement.md [drf-nested-routers]: https://github.com/alanjds/drf-nested-routers [drf-nested-relations]: https://github.com/Ian-Foote/rest-framework-generic-relations diff --git a/docs/index.md b/docs/index.md index 0b544e5bfa..fe0ec3a968 100644 --- a/docs/index.md +++ b/docs/index.md @@ -311,8 +311,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [django-filter]: http://pypi.python.org/pypi/django-filter [django-crispy-forms]: https://github.com/maraujop/django-crispy-forms [django-guardian]: https://github.com/django-guardian/django-guardian -[0.4]: https://github.com/encode/django-rest-framework/tree/0.4.X -[image]: img/quickstart.png [index]: . [oauth1-section]: api-guide/authentication/#django-rest-framework-oauth [oauth2-section]: api-guide/authentication/#django-oauth-toolkit @@ -385,15 +383,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [release-notes]: topics/release-notes.md [jobs]: topics/jobs.md -[tox]: http://testrun.org/tox/latest/ - [group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework [botbot]: https://botbot.me/freenode/restframework/ [stack-overflow]: http://stackoverflow.com/ [django-rest-framework-tag]: http://stackoverflow.com/questions/tagged/django-rest-framework -[django-tag]: http://stackoverflow.com/questions/tagged/django [security-mail]: mailto:rest-framework-security@googlegroups.com -[paid-support]: http://dabapps.com/services/build/api-development/ -[dabapps]: http://dabapps.com -[contact-dabapps]: http://dabapps.com/contact/ [twitter]: https://twitter.com/_tomchristie diff --git a/docs/topics/2.2-announcement.md b/docs/topics/2.2-announcement.md index 2d743af247..e945188b9c 100644 --- a/docs/topics/2.2-announcement.md +++ b/docs/topics/2.2-announcement.md @@ -155,5 +155,4 @@ From version 2.2 onwards, serializers with hyperlinked relationships *always* re [mailing-list]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework [django-rest-framework-docs]: https://github.com/marcgibbons/django-rest-framework-docs [marcgibbons]: https://github.com/marcgibbons/ -[issues]: https://github.com/encode/django-rest-framework/issues [564]: https://github.com/encode/django-rest-framework/issues/564 diff --git a/docs/topics/browsable-api.md b/docs/topics/browsable-api.md index a0ca6626b2..ec4e6b000f 100644 --- a/docs/topics/browsable-api.md +++ b/docs/topics/browsable-api.md @@ -155,10 +155,8 @@ There are [a variety of packages for autocomplete widgets][autocomplete-packages [bootstrap]: http://getbootstrap.com [cerulean]: ../img/cerulean.png [slate]: ../img/slate.png -[bcustomize]: http://getbootstrap.com/2.3.2/customize.html [bswatch]: http://bootswatch.com/ [bcomponents]: http://getbootstrap.com/2.3.2/components.html [bcomponentsnav]: http://getbootstrap.com/2.3.2/components.html#navbar [autocomplete-packages]: https://www.djangopackages.com/grids/g/auto-complete/ [django-autocomplete-light]: https://github.com/yourlabs/django-autocomplete-light -[django-autocomplete-light-install]: https://django-autocomplete-light.readthedocs.io/en/master/install.html diff --git a/docs/topics/internationalization.md b/docs/topics/internationalization.md index f7efbf6977..fe85c6523a 100644 --- a/docs/topics/internationalization.md +++ b/docs/topics/internationalization.md @@ -110,4 +110,3 @@ For API clients the most appropriate of these will typically be to use the `Acce [django-language-preference]: https://docs.djangoproject.com/en/1.7/topics/i18n/translation/#how-django-discovers-language-preference [django-locale-paths]: https://docs.djangoproject.com/en/1.7/ref/settings/#std:setting-LOCALE_PATHS [django-locale-name]: https://docs.djangoproject.com/en/1.7/topics/i18n/#term-locale-name -[contributing]: ../../CONTRIBUTING.md diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 244adef0b3..128175ef5e 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -892,13 +892,6 @@ For older release notes, [please see the version 2.x documentation][old-release- [cite]: http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/ar01s04.html [deprecation-policy]: #deprecation-policy [django-deprecation-policy]: https://docs.djangoproject.com/en/stable/internals/release-process/#internal-release-deprecation-policy -[defusedxml-announce]: http://blog.python.org/2013/02/announcing-defusedxml-fixes-for-xml.html -[743]: https://github.com/encode/django-rest-framework/pull/743 -[staticfiles14]: https://docs.djangoproject.com/en/1.4/howto/static-files/#with-a-template-tag -[staticfiles13]: https://docs.djangoproject.com/en/1.3/howto/static-files/#with-a-template-tag -[2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion -[ticket-582]: https://github.com/encode/django-rest-framework/issues/582 -[rfc-6266]: http://tools.ietf.org/html/rfc6266#section-4.3 [old-release-notes]: https://github.com/encode/django-rest-framework/blob/version-2.4.x/docs/topics/release-notes.md [3.6-release]: 3.6-announcement.md @@ -1153,7 +1146,6 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh3249]: https://github.com/encode/django-rest-framework/issues/3249 [gh3250]: https://github.com/encode/django-rest-framework/issues/3250 [gh3275]: https://github.com/encode/django-rest-framework/issues/3275 -[gh3288]: https://github.com/encode/django-rest-framework/issues/3288 [gh3290]: https://github.com/encode/django-rest-framework/issues/3290 [gh3303]: https://github.com/encode/django-rest-framework/issues/3303 [gh3313]: https://github.com/encode/django-rest-framework/issues/3313 @@ -1410,7 +1402,6 @@ For older release notes, [please see the version 2.x documentation][old-release- -[gh2829]: https://github.com/encode/django-rest-framework/issues/2829 [gh3329]: https://github.com/encode/django-rest-framework/issues/3329 [gh3330]: https://github.com/encode/django-rest-framework/issues/3330 [gh3365]: https://github.com/encode/django-rest-framework/issues/3365 @@ -1651,7 +1642,6 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh5457]: https://github.com/encode/django-rest-framework/issues/5457 [gh5376]: https://github.com/encode/django-rest-framework/issues/5376 [gh5422]: https://github.com/encode/django-rest-framework/issues/5422 -[gh5408]: https://github.com/encode/django-rest-framework/issues/5408 [gh3732]: https://github.com/encode/django-rest-framework/issues/3732 [djangodocs-set-timezone]: https://docs.djangoproject.com/en/1.11/topics/i18n/timezones/#default-time-zone-and-current-time-zone [gh5273]: https://github.com/encode/django-rest-framework/issues/5273 diff --git a/docs/topics/rest-framework-2-announcement.md b/docs/topics/rest-framework-2-announcement.md index ed41bb4864..98194adfab 100644 --- a/docs/topics/rest-framework-2-announcement.md +++ b/docs/topics/rest-framework-2-announcement.md @@ -93,6 +93,5 @@ There's also a [live sandbox version of the tutorial API][sandbox] available for [quote2]: https://groups.google.com/d/msg/django-rest-framework/heRGHzG6BWQ/ooVURgpwVC0J [quote3]: https://groups.google.com/d/msg/django-rest-framework/flsXbvYqRoY/9lSyntOf5cUJ [image]: ../img/quickstart.png -[readthedocs]: https://readthedocs.org/ [tut]: ../tutorial/1-serialization.md [sandbox]: http://restframework.herokuapp.com/ diff --git a/docs/topics/rest-hypermedia-hateoas.md b/docs/topics/rest-hypermedia-hateoas.md index 5517b150cd..4481254f41 100644 --- a/docs/topics/rest-hypermedia-hateoas.md +++ b/docs/topics/rest-hypermedia-hateoas.md @@ -40,7 +40,6 @@ What REST framework doesn't do is give you machine readable hypermedia formats s [restful-web-apis]: http://restfulwebapis.org/ [building-hypermedia-apis]: http://www.amazon.com/Building-Hypermedia-APIs-HTML5-Node/dp/1449306578 [designing-hypermedia-apis]: http://designinghypermediaapis.com/ -[restisover]: http://blog.steveklabnik.com/posts/2012-02-23-rest-is-over [readinglist]: http://blog.steveklabnik.com/posts/2012-02-27-hypermedia-api-reading-list [maturitymodel]: http://martinfowler.com/articles/richardsonMaturityModel.html diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md index ab789518d0..35d5642c7a 100644 --- a/docs/tutorial/quickstart.md +++ b/docs/tutorial/quickstart.md @@ -211,7 +211,6 @@ Great, that was easy! If you want to get a more in depth understanding of how REST framework fits together head on over to [the tutorial][tutorial], or start browsing the [API guide][guide]. -[readme-example-api]: ../#example [image]: ../img/quickstart.png [tutorial]: 1-serialization.md [guide]: ../#api-guide From d3f3c3d9c1b91e9fcc72874d33c004f8ed7fa7fe Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Mon, 8 Jan 2018 07:22:32 -0800 Subject: [PATCH 0032/1262] Prefer https protocol for links in docs when available --- CONTRIBUTING.md | 8 ++-- README.md | 8 ++-- docs/api-guide/authentication.md | 22 +++++----- docs/api-guide/content-negotiation.md | 4 +- docs/api-guide/fields.md | 2 +- docs/api-guide/filtering.md | 2 +- docs/api-guide/metadata.md | 2 +- docs/api-guide/pagination.md | 4 +- docs/api-guide/parsers.md | 6 +-- docs/api-guide/relations.md | 2 +- docs/api-guide/renderers.md | 20 ++++----- docs/api-guide/reverse.md | 2 +- docs/api-guide/routers.md | 14 +++---- docs/api-guide/serializers.md | 8 ++-- docs/api-guide/settings.md | 2 +- docs/api-guide/status-codes.md | 6 +-- docs/api-guide/testing.md | 2 +- docs/api-guide/throttling.md | 2 +- docs/api-guide/versioning.md | 8 ++-- docs/api-guide/views.md | 2 +- docs/index.md | 20 ++++----- docs/topics/2.4-announcement.md | 4 +- docs/topics/3.0-announcement.md | 4 +- docs/topics/3.1-announcement.md | 8 ++-- docs/topics/3.5-announcement.md | 8 ++-- docs/topics/ajax-csrf-cors.md | 4 +- docs/topics/browsable-api.md | 12 +++--- docs/topics/browser-enhancements.md | 4 +- docs/topics/contributing.md | 8 ++-- docs/topics/documenting-your-api.md | 6 +-- docs/topics/internationalization.md | 4 +- docs/topics/jobs.md | 4 +- docs/topics/kickstarter-announcement.md | 42 +++++++++---------- docs/topics/mozilla-grant.md | 2 +- docs/topics/project-management.md | 4 +- docs/topics/rest-framework-2-announcement.md | 2 +- docs/topics/rest-hypermedia-hateoas.md | 8 ++-- docs/topics/third-party-packages.md | 4 +- docs/topics/tutorials-and-resources.md | 24 +++++------ docs/tutorial/1-serialization.md | 4 +- docs/tutorial/3-class-based-views.md | 2 +- .../7-schemas-and-client-libraries.md | 2 +- licenses/jquery.json-view.md | 2 +- rest_framework/compat.py | 2 +- rest_framework/parsers.py | 2 +- rest_framework/renderers.py | 2 +- rest_framework/status.py | 4 +- rest_framework/templatetags/rest_framework.py | 2 +- rest_framework/utils/encoders.py | 2 +- rest_framework/utils/mediatypes.py | 2 +- 50 files changed, 162 insertions(+), 162 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ba579568d8..29ba53955a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -194,14 +194,14 @@ If you want to draw attention to a note or warning, use a pair of enclosing line --- -[cite]: http://www.w3.org/People/Berners-Lee/FAQ.html +[cite]: https://www.w3.org/People/Berners-Lee/FAQ.html [code-of-conduct]: https://www.djangoproject.com/conduct/ [google-group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework -[so-filter]: http://stackexchange.com/filters/66475/rest-framework +[so-filter]: https://stackexchange.com/filters/66475/rest-framework [issues]: https://github.com/encode/django-rest-framework/issues?state=open -[pep-8]: http://www.python.org/dev/peps/pep-0008/ +[pep-8]: https://www.python.org/dev/peps/pep-0008/ [pull-requests]: https://help.github.com/articles/using-pull-requests [tox]: https://tox.readthedocs.io/en/latest/ -[markdown]: http://daringfireball.net/projects/markdown/basics +[markdown]: https://daringfireball.net/projects/markdown/basics [docs]: https://github.com/encode/django-rest-framework/tree/master/docs [mou]: http://mouapp.com/ diff --git a/README.md b/README.md index 92aad74995..7a5e4f0b71 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ That's it, we're done! You can now open the API in your browser at `http://127.0.0.1:8000/`, and view your new 'users' API. If you use the `Login` control in the top right corner you'll also be able to add, create and delete users from the system. -You can also interact with the API using command line tools such as [`curl`](http://curl.haxx.se/). For example, to list the users endpoint: +You can also interact with the API using command line tools such as [`curl`](https://curl.haxx.se/). For example, to list the users endpoint: $ curl -H 'Accept: application/json; indent=4' -u admin:password http://127.0.0.1:8000/users/ [ @@ -176,14 +176,14 @@ If you believe you've found something in Django REST framework which has securit Send a description of the issue via email to [rest-framework-security@googlegroups.com][security-mail]. The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure. [build-status-image]: https://secure.travis-ci.org/encode/django-rest-framework.svg?branch=master -[travis]: http://travis-ci.org/encode/django-rest-framework?branch=master +[travis]: https://travis-ci.org/encode/django-rest-framework?branch=master [coverage-status-image]: https://img.shields.io/codecov/c/github/encode/django-rest-framework/master.svg -[codecov]: http://codecov.io/github/encode/django-rest-framework?branch=master +[codecov]: https://codecov.io/github/encode/django-rest-framework?branch=master [pypi-version]: https://img.shields.io/pypi/v/djangorestframework.svg [pypi]: https://pypi.python.org/pypi/djangorestframework [twitter]: https://twitter.com/_tomchristie [group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework -[sandbox]: http://restframework.herokuapp.com/ +[sandbox]: https://restframework.herokuapp.com/ [funding]: https://fund.django-rest-framework.org/topics/funding/ [sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index d7f2954b6d..0704118bdc 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -403,21 +403,21 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a [drfpasswordless][drfpasswordless] adds (Medium, Square Cash inspired) passwordless support to Django REST Framework's own TokenAuthentication scheme. Users log in and sign up with a token sent to a contact point like an email address or a mobile number. -[cite]: http://jacobian.org/writing/rest-worst-practices/ -[http401]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2 -[http403]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4 -[basicauth]: http://tools.ietf.org/html/rfc2617 +[cite]: https://jacobian.org/writing/rest-worst-practices/ +[http401]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2 +[http403]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4 +[basicauth]: https://tools.ietf.org/html/rfc2617 [permission]: permissions.md [throttling]: throttling.md [csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax -[mod_wsgi_official]: http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIPassAuthorization +[mod_wsgi_official]: https://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIPassAuthorization.html [django-oauth-toolkit-getting-started]: https://django-oauth-toolkit.readthedocs.io/en/latest/rest-framework/getting_started.html -[django-rest-framework-oauth]: http://jpadilla.github.io/django-rest-framework-oauth/ -[django-rest-framework-oauth-authentication]: http://jpadilla.github.io/django-rest-framework-oauth/authentication/ -[django-rest-framework-oauth-permissions]: http://jpadilla.github.io/django-rest-framework-oauth/permissions/ +[django-rest-framework-oauth]: https://jpadilla.github.io/django-rest-framework-oauth/ +[django-rest-framework-oauth-authentication]: https://jpadilla.github.io/django-rest-framework-oauth/authentication/ +[django-rest-framework-oauth-permissions]: https://jpadilla.github.io/django-rest-framework-oauth/permissions/ [juanriaza]: https://github.com/juanriaza [djangorestframework-digestauth]: https://github.com/juanriaza/django-rest-framework-digestauth -[oauth-1.0a]: http://oauth.net/core/1.0a +[oauth-1.0a]: https://oauth.net/core/1.0a/ [django-oauth-toolkit]: https://github.com/evonove/django-oauth-toolkit [evonove]: https://github.com/evonove/ [oauthlib]: https://github.com/idan/oauthlib @@ -429,12 +429,12 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a [djangorestframework-simplejwt]: https://github.com/davesque/django-rest-framework-simplejwt [etoccalino]: https://github.com/etoccalino/ [djangorestframework-httpsignature]: https://github.com/etoccalino/django-rest-framework-httpsignature -[amazon-http-signature]: http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html +[amazon-http-signature]: https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html [http-signature-ietf-draft]: https://datatracker.ietf.org/doc/draft-cavage-http-signatures/ [hawkrest]: https://hawkrest.readthedocs.io/en/latest/ [hawk]: https://github.com/hueniverse/hawk [mohawk]: https://mohawk.readthedocs.io/en/latest/ -[mac]: http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05 +[mac]: https://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05 [djoser]: https://github.com/sunscrapers/djoser [django-rest-auth]: https://github.com/Tivix/django-rest-auth [django-rest-framework-social-oauth2]: https://github.com/PhilipGarnero/django-rest-framework-social-oauth2 diff --git a/docs/api-guide/content-negotiation.md b/docs/api-guide/content-negotiation.md index bd408feba5..8112a2e803 100644 --- a/docs/api-guide/content-negotiation.md +++ b/docs/api-guide/content-negotiation.md @@ -6,7 +6,7 @@ source: negotiation.py > > — [RFC 2616][cite], Fielding et al. -[cite]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html +[cite]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html Content negotiation is the process of selecting one of multiple possible representations to return to a client, based on client or server preferences. @@ -94,4 +94,4 @@ You can also set the content negotiation used for an individual view, or viewset 'accepted media type': request.accepted_renderer.media_type }) -[accept-header]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html +[accept-header]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 684ae517d5..bf5fc0ca7b 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -807,7 +807,7 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide [html-and-forms]: ../topics/html-and-forms.md [FILE_UPLOAD_HANDLERS]: https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS [strftime]: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior -[iso8601]: http://www.w3.org/TR/NOTE-datetime +[iso8601]: https://www.w3.org/TR/NOTE-datetime [drf-compound-fields]: https://drf-compound-fields.readthedocs.io [drf-extra-fields]: https://github.com/Hipo/drf-extra-fields [djangorestframework-recursive]: https://github.com/heywbj/django-rest-framework-recursive diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 83cbe7d1b7..723b2fb357 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -383,7 +383,7 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter] [django-filter-drf-docs]: https://django-filter.readthedocs.io/en/latest/guide/rest_framework.html [guardian]: https://django-guardian.readthedocs.io/ [view-permissions]: https://django-guardian.readthedocs.io/en/latest/userguide/assign.html -[view-permissions-blogpost]: http://blog.nyaruka.com/adding-a-view-permission-to-django-models +[view-permissions-blogpost]: https://blog.nyaruka.com/adding-a-view-permission-to-django-models [search-django-admin]: https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields [django-rest-framework-filters]: https://github.com/philipn/django-rest-framework-filters [django-rest-framework-word-search-filter]: https://github.com/trollknurr/django-rest-framework-word-search-filter diff --git a/docs/api-guide/metadata.md b/docs/api-guide/metadata.md index de28ffd8a9..d5c3c1a67e 100644 --- a/docs/api-guide/metadata.md +++ b/docs/api-guide/metadata.md @@ -115,7 +115,7 @@ The following third party packages provide additional metadata implementations. You can also write your own adapter to work with your specific frontend. If you wish to do so, it also provides an exporter that can export those schema information to json files. -[cite]: http://tools.ietf.org/html/rfc7231#section-4.3.7 +[cite]: https://tools.ietf.org/html/rfc7231#section-4.3.7 [no-options]: https://www.mnot.net/blog/2012/10/29/NO_OPTIONS [json-schema]: http://json-schema.org/ [drf-schema-adapter]: https://github.com/drf-forms/drf-schema-adapter diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 0f123cb89f..658e94d916 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -315,8 +315,8 @@ The [`django-rest-framework-link-header-pagination` package][drf-link-header-pag [cite]: https://docs.djangoproject.com/en/stable/topics/pagination/ [link-header]: ../img/link-header-pagination.png -[drf-extensions]: http://chibisov.github.io/drf-extensions/docs/ -[paginate-by-max-mixin]: http://chibisov.github.io/drf-extensions/docs/#paginatebymaxmixin +[drf-extensions]: https://chibisov.github.io/drf-extensions/docs/ +[paginate-by-max-mixin]: https://chibisov.github.io/drf-extensions/docs/#paginatebymaxmixin [drf-proxy-pagination]: https://github.com/tuffnatty/drf-proxy-pagination [drf-link-header-pagination]: https://github.com/tbeadle/django-rest-framework-link-header-pagination [disqus-cursor-api]: http://cramer.io/2011/03/08/building-cursors-for-the-disqus-api diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index cd5ef1a046..09ce4556fe 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -223,11 +223,11 @@ Modify your REST framework settings. [djangorestframework-camel-case] provides camel case JSON renderers and parsers for REST framework. This allows serializers to use Python-style underscored field names, but be exposed in the API as Javascript-style camel case field names. It is maintained by [Vitaly Babiy][vbabiy]. -[jquery-ajax]: http://api.jquery.com/jQuery.ajax/ +[jquery-ajax]: https://api.jquery.com/jQuery.ajax/ [cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion [upload-handlers]: https://docs.djangoproject.com/en/stable/topics/http/file-uploads/#upload-handlers -[rest-framework-yaml]: http://jpadilla.github.io/django-rest-framework-yaml/ -[rest-framework-xml]: http://jpadilla.github.io/django-rest-framework-xml/ +[rest-framework-yaml]: https://jpadilla.github.io/django-rest-framework-yaml/ +[rest-framework-xml]: https://jpadilla.github.io/django-rest-framework-xml/ [yaml]: http://www.yaml.org/ [messagepack]: https://github.com/juanriaza/django-rest-framework-msgpack [juanriaza]: https://github.com/juanriaza diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index ce249c32fd..77649afd62 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -592,7 +592,7 @@ The [drf-nested-routers package][drf-nested-routers] provides routers and relati The [rest-framework-generic-relations][drf-nested-relations] library provides read/write serialization for generic foreign keys. -[cite]: http://lwn.net/Articles/193245/ +[cite]: https://lwn.net/Articles/193245/ [reverse-relationships]: https://docs.djangoproject.com/en/stable/topics/db/queries/#following-relationships-backward [routers]: http://www.django-rest-framework.org/api-guide/routers#defaultrouter [generic-relations]: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#id1 diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 236504850d..d547a7bbf3 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -485,21 +485,21 @@ Comma-separated values are a plain-text tabular data format, that can be easily [testing]: testing.md [HATEOAS]: http://timelessrepo.com/haters-gonna-hateoas [quote]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven -[application/vnd.github+json]: http://developer.github.com/v3/media/ +[application/vnd.github+json]: https://developer.github.com/v3/media/ [application/vnd.collection+json]: http://www.amundsen.com/media-types/collection/ [django-error-views]: https://docs.djangoproject.com/en/stable/topics/http/views/#customizing-error-views -[rest-framework-jsonp]: http://jpadilla.github.io/django-rest-framework-jsonp/ -[cors]: http://www.w3.org/TR/cors/ +[rest-framework-jsonp]: https://jpadilla.github.io/django-rest-framework-jsonp/ +[cors]: https://www.w3.org/TR/cors/ [cors-docs]: http://www.django-rest-framework.org/topics/ajax-csrf-cors/ -[jsonp-security]: http://stackoverflow.com/questions/613962/is-jsonp-safe-to-use -[rest-framework-yaml]: http://jpadilla.github.io/django-rest-framework-yaml/ -[rest-framework-xml]: http://jpadilla.github.io/django-rest-framework-xml/ -[messagepack]: http://msgpack.org/ +[jsonp-security]: https://stackoverflow.com/questions/613962/is-jsonp-safe-to-use +[rest-framework-yaml]: https://jpadilla.github.io/django-rest-framework-yaml/ +[rest-framework-xml]: https://jpadilla.github.io/django-rest-framework-xml/ +[messagepack]: https://msgpack.org/ [juanriaza]: https://github.com/juanriaza [mjumbewu]: https://github.com/mjumbewu [vbabiy]: https://github.com/vbabiy -[rest-framework-yaml]: http://jpadilla.github.io/django-rest-framework-yaml/ -[rest-framework-xml]: http://jpadilla.github.io/django-rest-framework-xml/ +[rest-framework-yaml]: https://jpadilla.github.io/django-rest-framework-yaml/ +[rest-framework-xml]: https://jpadilla.github.io/django-rest-framework-xml/ [yaml]: http://www.yaml.org/ [djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack [djangorestframework-csv]: https://github.com/mjumbewu/django-rest-framework-csv @@ -508,7 +508,7 @@ Comma-separated values are a plain-text tabular data format, that can be easily [drf-ujson-renderer]: https://github.com/gizmag/drf-ujson-renderer [djangorestframework-camel-case]: https://github.com/vbabiy/djangorestframework-camel-case [Django REST Pandas]: https://github.com/wq/django-rest-pandas -[Pandas]: http://pandas.pydata.org/ +[Pandas]: https://pandas.pydata.org/ [other formats]: https://github.com/wq/django-rest-pandas#supported-formats [sheppard]: https://github.com/sheppard [wq]: https://github.com/wq diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index ee0b2054f2..00abcf571b 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -50,6 +50,6 @@ As with the `reverse` function, you should **include the request as a keyword ar api_root = reverse_lazy('api-root', request=request) -[cite]: http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5 +[cite]: https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5 [reverse]: https://docs.djangoproject.com/en/stable/topics/http/urls/#reverse [reverse-lazy]: https://docs.djangoproject.com/en/stable/topics/http/urls/#reverse-lazy diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md index 84ca82a3f7..e4415df155 100644 --- a/docs/api-guide/routers.md +++ b/docs/api-guide/routers.md @@ -326,10 +326,10 @@ The [`DRF-extensions` package][drf-extensions] provides [routers][drf-extensions [cite]: http://guides.rubyonrails.org/routing.html [route-decorators]: viewsets.md#marking-extra-actions-for-routing [drf-nested-routers]: https://github.com/alanjds/drf-nested-routers -[wq.db]: http://wq.io/wq.db -[wq.db-router]: http://wq.io/docs/router -[drf-extensions]: http://chibisov.github.io/drf-extensions/docs/ -[drf-extensions-routers]: http://chibisov.github.io/drf-extensions/docs/#routers -[drf-extensions-nested-viewsets]: http://chibisov.github.io/drf-extensions/docs/#nested-routes -[drf-extensions-collection-level-controllers]: http://chibisov.github.io/drf-extensions/docs/#collection-level-controllers -[drf-extensions-customizable-endpoint-names]: http://chibisov.github.io/drf-extensions/docs/#controller-endpoint-name +[wq.db]: https://wq.io/wq.db +[wq.db-router]: https://wq.io/docs/router +[drf-extensions]: https://chibisov.github.io/drf-extensions/docs/ +[drf-extensions-routers]: https://chibisov.github.io/drf-extensions/docs/#routers +[drf-extensions-nested-viewsets]: https://chibisov.github.io/drf-extensions/docs/#nested-routes +[drf-extensions-collection-level-controllers]: https://chibisov.github.io/drf-extensions/docs/#collection-level-controllers +[drf-extensions-customizable-endpoint-names]: https://chibisov.github.io/drf-extensions/docs/#controller-endpoint-name diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 3c560d829b..d4f058ebec 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -1172,9 +1172,9 @@ The [drf-writable-nested][drf-writable-nested] package provides writable nested [cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion [relations]: relations.md [model-managers]: https://docs.djangoproject.com/en/stable/topics/db/managers/ -[encapsulation-blogpost]: http://www.dabapps.com/blog/django-models-and-encapsulation/ +[encapsulation-blogpost]: https://www.dabapps.com/blog/django-models-and-encapsulation/ [thirdparty-writable-nested]: serializers.md#drf-writable-nested -[django-rest-marshmallow]: http://tomchristie.github.io/django-rest-marshmallow/ +[django-rest-marshmallow]: https://tomchristie.github.io/django-rest-marshmallow/ [marshmallow]: https://marshmallow.readthedocs.io/en/latest/ [serpy]: https://github.com/clarkduvall/serpy [mongoengine]: https://github.com/umutbozkurt/django-rest-framework-mongoengine @@ -1188,5 +1188,5 @@ The [drf-writable-nested][drf-writable-nested] package provides writable nested [drf-dynamic-fields]: https://github.com/dbrgn/drf-dynamic-fields [drf-base64]: https://bitbucket.org/levit_scs/drf_base64 [drf-serializer-extensions]: https://github.com/evenicoulddoit/django-rest-framework-serializer-extensions -[djangorestframework-queryfields]: http://djangorestframework-queryfields.readthedocs.io/ -[drf-writable-nested]: http://github.com/beda-software/drf-writable-nested +[djangorestframework-queryfields]: https://djangorestframework-queryfields.readthedocs.io/ +[drf-writable-nested]: https://github.com/beda-software/drf-writable-nested diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index caa0653894..ea150e10fa 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -473,6 +473,6 @@ An integer of 0 or more, that may be used to specify the number of application p Default: `None` [cite]: https://www.python.org/dev/peps/pep-0020/ -[rfc4627]: http://www.ietf.org/rfc/rfc4627.txt +[rfc4627]: https://www.ietf.org/rfc/rfc4627.txt [heroku-minified-json]: https://github.com/interagent/http-api-design#keep-json-minified-in-all-responses [strftime]: https://docs.python.org/3/library/time.html#time.strftime diff --git a/docs/api-guide/status-codes.md b/docs/api-guide/status-codes.md index 16a0e63c87..1016f3374d 100644 --- a/docs/api-guide/status-codes.md +++ b/docs/api-guide/status-codes.md @@ -118,6 +118,6 @@ The following helper functions are available for identifying the category of the is_client_error() # 4xx is_server_error() # 5xx -[rfc2324]: http://www.ietf.org/rfc/rfc2324.txt -[rfc2616]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html -[rfc6585]: http://tools.ietf.org/html/rfc6585 +[rfc2324]: https://www.ietf.org/rfc/rfc2324.txt +[rfc2616]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html +[rfc6585]: https://tools.ietf.org/html/rfc6585 diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md index d2ff6e7cb5..b31d2c9721 100644 --- a/docs/api-guide/testing.md +++ b/docs/api-guide/testing.md @@ -404,7 +404,7 @@ For example, to add support for using `format='html'` in test requests, you migh ) } -[cite]: http://jacobian.org/writing/django-apps-with-buildout/#s-create-a-test-wrapper +[cite]: https://jacobian.org/writing/django-apps-with-buildout/#s-create-a-test-wrapper [client]: https://docs.djangoproject.com/en/stable/topics/testing/tools/#the-test-client [requestfactory]: https://docs.djangoproject.com/en/stable/topics/testing/advanced/#django.test.client.RequestFactory [configuration]: #configuration diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 10a0bb0874..9657b00eb0 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -72,7 +72,7 @@ The `X-Forwarded-For` HTTP header and `REMOTE_ADDR` WSGI variable are used to un If you need to strictly identify unique client IP addresses, you'll need to first configure the number of application proxies that the API runs behind by setting the `NUM_PROXIES` setting. This setting should be an integer of zero or more. If set to non-zero then the client IP will be identified as being the last IP address in the `X-Forwarded-For` header, once any application proxy IP addresses have first been excluded. If set to zero, then the `REMOTE_ADDR` value will always be used as the identifying IP address. -It is important to understand that if you configure the `NUM_PROXIES` setting, then all clients behind a unique [NAT'd](http://en.wikipedia.org/wiki/Network_address_translation) gateway will be treated as a single client. +It is important to understand that if you configure the `NUM_PROXIES` setting, then all clients behind a unique [NAT'd](https://en.wikipedia.org/wiki/Network_address_translation) gateway will be treated as a single client. Further context on how the `X-Forwarded-For` header works, and identifying a remote client IP can be [found here][identifing-clients]. diff --git a/docs/api-guide/versioning.md b/docs/api-guide/versioning.md index 4a875b87e9..8e62c70f17 100644 --- a/docs/api-guide/versioning.md +++ b/docs/api-guide/versioning.md @@ -211,10 +211,10 @@ The following example uses a custom `X-API-Version` header to determine the requ If your versioning scheme is based on the request URL, you will also want to alter how versioned URLs are determined. In order to do so you should override the `.reverse()` method on the class. See the source code for examples. -[cite]: http://www.slideshare.net/evolve_conference/201308-fielding-evolve/31 -[roy-fielding-on-versioning]: http://www.infoq.com/articles/roy-fielding-on-versioning +[cite]: https://www.slideshare.net/evolve_conference/201308-fielding-evolve/31 +[roy-fielding-on-versioning]: https://www.infoq.com/articles/roy-fielding-on-versioning [klabnik-guidelines]: http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http#i_want_my_api_to_be_versioned [heroku-guidelines]: https://github.com/interagent/http-api-design/blob/master/en/foundations/require-versioning-in-the-accepts-header.md -[json-parameters]: http://tools.ietf.org/html/rfc4627#section-6 -[vendor-media-type]: http://en.wikipedia.org/wiki/Internet_media_type#Vendor_tree +[json-parameters]: https://tools.ietf.org/html/rfc4627#section-6 +[vendor-media-type]: https://en.wikipedia.org/wiki/Internet_media_type#Vendor_tree [lvh]: https://reinteractive.net/posts/199-developing-and-testing-rails-applications-with-subdomains diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 017bcad0c7..7b2c4eff7b 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -214,7 +214,7 @@ You may pass `None` in order to exclude the view from schema generation. return Response({"message": "Will not appear in schema!"}) -[cite]: http://reinout.vanrees.org/weblog/2011/08/24/class-based-views-usage.html +[cite]: https://reinout.vanrees.org/weblog/2011/08/24/class-based-views-usage.html [cite2]: http://www.boredomandlaziness.org/2012/05/djangos-cbvs-are-not-mistake-but.html [settings]: settings.md [throttling]: throttling.md diff --git a/docs/index.md b/docs/index.md index fe0ec3a968..1b494fcfbb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -18,9 +18,9 @@

- + - + @@ -31,7 +31,7 @@ --- -**Note**: This is the documentation for the **version 3** of REST framework. Documentation for [version 2](http://tomchristie.github.io/rest-framework-2-docs/) is also available. +**Note**: This is the documentation for the **version 3** of REST framework. Documentation for [version 2](https://tomchristie.github.io/rest-framework-2-docs/) is also available. --- @@ -302,13 +302,13 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -[mozilla]: http://www.mozilla.org/en-US/about/ +[mozilla]: https://www.mozilla.org/en-US/about/ [redhat]: https://www.redhat.com/ [heroku]: https://www.heroku.com/ [eventbrite]: https://www.eventbrite.co.uk/about/ -[coreapi]: http://pypi.python.org/pypi/coreapi/ -[markdown]: http://pypi.python.org/pypi/Markdown/ -[django-filter]: http://pypi.python.org/pypi/django-filter +[coreapi]: https://pypi.python.org/pypi/coreapi/ +[markdown]: https://pypi.python.org/pypi/Markdown/ +[django-filter]: https://pypi.python.org/pypi/django-filter [django-crispy-forms]: https://github.com/maraujop/django-crispy-forms [django-guardian]: https://github.com/django-guardian/django-guardian [index]: . @@ -317,7 +317,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [serializer-section]: api-guide/serializers#serializers [modelserializer-section]: api-guide/serializers#modelserializer [functionview-section]: api-guide/views#function-based-views -[sandbox]: http://restframework.herokuapp.com/ +[sandbox]: https://restframework.herokuapp.com/ [sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors [quickstart]: tutorial/quickstart.md @@ -385,7 +385,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework [botbot]: https://botbot.me/freenode/restframework/ -[stack-overflow]: http://stackoverflow.com/ -[django-rest-framework-tag]: http://stackoverflow.com/questions/tagged/django-rest-framework +[stack-overflow]: https://stackoverflow.com/ +[django-rest-framework-tag]: https://stackoverflow.com/questions/tagged/django-rest-framework [security-mail]: mailto:rest-framework-security@googlegroups.com [twitter]: https://twitter.com/_tomchristie diff --git a/docs/topics/2.4-announcement.md b/docs/topics/2.4-announcement.md index 8bbe61335f..eb1037f176 100644 --- a/docs/topics/2.4-announcement.md +++ b/docs/topics/2.4-announcement.md @@ -29,7 +29,7 @@ The `DEFAULT_MODEL_SERIALIZER_CLASS` API setting is now also deprecated. ## Updated test runner -We now have a new test runner for developing against the project,, that uses the excellent [py.test](http://pytest.org) library. +We now have a new test runner for developing against the project,, that uses the excellent [py.test](https://docs.pytest.org/) library. To use it make sure you have first installed the test requirements. @@ -128,7 +128,7 @@ There are also a number of other features and bugfixes as [listed in the release Smarter [client IP identification for throttling][client-ip-identification], with the addition of the `NUM_PROXIES` setting. -Added the standardized `Retry-After` header to throttled responses, as per [RFC 6585](http://tools.ietf.org/html/rfc6585). This should now be used in preference to the custom `X-Throttle-Wait-Seconds` header which will be fully deprecated in 3.0. +Added the standardized `Retry-After` header to throttled responses, as per [RFC 6585](https://tools.ietf.org/html/rfc6585). This should now be used in preference to the custom `X-Throttle-Wait-Seconds` header which will be fully deprecated in 3.0. ## Deprecations diff --git a/docs/topics/3.0-announcement.md b/docs/topics/3.0-announcement.md index 25c36b2ca4..03a2c281c9 100644 --- a/docs/topics/3.0-announcement.md +++ b/docs/topics/3.0-announcement.md @@ -32,7 +32,7 @@ Significant new functionality continues to be planned for the 3.1 and 3.2 releas #### REST framework: Under the hood. -This talk from the [Django: Under the Hood](http://www.djangounderthehood.com/) event in Amsterdam, Nov 2014, gives some good background context on the design decisions behind 3.0. +This talk from the [Django: Under the Hood](https://www.djangounderthehood.com/) event in Amsterdam, Nov 2014, gives some good background context on the design decisions behind 3.0. @@ -959,7 +959,7 @@ The 3.2 release is planned to introduce an alternative admin-style interface to You can follow development on the GitHub site, where we use [milestones to indicate planning timescales](https://github.com/encode/django-rest-framework/milestones). -[kickstarter]: http://kickstarter.com/projects/tomchristie/django-rest-framework-3 +[kickstarter]: https://www.kickstarter.com/projects/tomchristie/django-rest-framework-3 [sponsors]: http://www.django-rest-framework.org/topics/kickstarter-announcement/#sponsors [mixins.py]: https://github.com/encode/django-rest-framework/blob/master/rest_framework/mixins.py [django-localization]: https://docs.djangoproject.com/en/stable/topics/i18n/translation/#localization-how-to-create-language-files diff --git a/docs/topics/3.1-announcement.md b/docs/topics/3.1-announcement.md index 6cca406651..a86fa943ae 100644 --- a/docs/topics/3.1-announcement.md +++ b/docs/topics/3.1-announcement.md @@ -159,10 +159,10 @@ The change also means we can be more flexible with which external packages we re The following packages are now moved out of core and should be separately installed: -* OAuth - [djangorestframework-oauth](http://jpadilla.github.io/django-rest-framework-oauth/) -* XML - [djangorestframework-xml](http://jpadilla.github.io/django-rest-framework-xml) -* YAML - [djangorestframework-yaml](http://jpadilla.github.io/django-rest-framework-yaml) -* JSONP - [djangorestframework-jsonp](http://jpadilla.github.io/django-rest-framework-jsonp) +* OAuth - [djangorestframework-oauth](https://jpadilla.github.io/django-rest-framework-oauth/) +* XML - [djangorestframework-xml](https://jpadilla.github.io/django-rest-framework-xml) +* YAML - [djangorestframework-yaml](https://jpadilla.github.io/django-rest-framework-yaml) +* JSONP - [djangorestframework-jsonp](https://jpadilla.github.io/django-rest-framework-jsonp) It's worth reiterating that this change in policy shouldn't mean any work in your codebase other than adding a new requirement and modifying some import paths. For example to install XML rendering, you would now do: diff --git a/docs/topics/3.5-announcement.md b/docs/topics/3.5-announcement.md index 701ab48ebd..dca371b845 100644 --- a/docs/topics/3.5-announcement.md +++ b/docs/topics/3.5-announcement.md @@ -35,11 +35,11 @@ we strongly encourage you to invest in its continued development by

  • Rover.com
  • Sentry
  • Stream
  • -
  • Machinalis
  • +
  • Machinalis
  • -*Many thanks to all our [sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), and [Machinalis](http://www.machinalis.com/#services).* +*Many thanks to all our [sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), and [Machinalis](https://www.machinalis.com/#services).* --- @@ -256,8 +256,8 @@ in version 3.3 and raised a deprecation warning in 3.4. Its usage is now mandato [sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors [funding]: funding.md -[uploads]: http://core-api.github.io/python-client/api-guide/utils/#file -[downloads]: http://core-api.github.io/python-client/api-guide/codecs/#downloadcodec +[uploads]: https://core-api.github.io/python-client/api-guide/utils/#file +[downloads]: https://core-api.github.io/python-client/api-guide/codecs/#downloadcodec [schema-generation-api]: ../api-guide/schemas/#schemagenerator [schema-docs]: ../api-guide/schemas/#schemas-as-documentation [schema-view]: ../api-guide/schemas/#the-get_schema_view-shortcut diff --git a/docs/topics/ajax-csrf-cors.md b/docs/topics/ajax-csrf-cors.md index 4960e08810..646f3f5638 100644 --- a/docs/topics/ajax-csrf-cors.md +++ b/docs/topics/ajax-csrf-cors.md @@ -33,9 +33,9 @@ The best way to deal with CORS in REST framework is to add the required response [Otto Yiu][ottoyiu] maintains the [django-cors-headers] package, which is known to work correctly with REST framework APIs. -[cite]: http://www.codinghorror.com/blog/2008/10/preventing-csrf-and-xsrf-attacks.html +[cite]: https://blog.codinghorror.com/preventing-csrf-and-xsrf-attacks/ [csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) [csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax -[cors]: http://www.w3.org/TR/cors/ +[cors]: https://www.w3.org/TR/cors/ [ottoyiu]: https://github.com/ottoyiu/ [django-cors-headers]: https://github.com/ottoyiu/django-cors-headers/ diff --git a/docs/topics/browsable-api.md b/docs/topics/browsable-api.md index ec4e6b000f..23ac25bc97 100644 --- a/docs/topics/browsable-api.md +++ b/docs/topics/browsable-api.md @@ -44,7 +44,7 @@ Full example: {% extends "rest_framework/base.html" %} {% block bootstrap_theme %} - + {% endblock %} {% block bootstrap_navbar_variant %}{% endblock %} @@ -148,15 +148,15 @@ There are [a variety of packages for autocomplete widgets][autocomplete-packages --- -[cite]: http://en.wikiquote.org/wiki/Alfred_North_Whitehead +[cite]: https://en.wikiquote.org/wiki/Alfred_North_Whitehead [drfreverse]: ../api-guide/reverse.md [ffjsonview]: https://addons.mozilla.org/en-US/firefox/addon/jsonview/ [chromejsonview]: https://chrome.google.com/webstore/detail/chklaanhfefbnpoihckbnefhakgolnmc -[bootstrap]: http://getbootstrap.com +[bootstrap]: https://getbootstrap.com/ [cerulean]: ../img/cerulean.png [slate]: ../img/slate.png -[bswatch]: http://bootswatch.com/ -[bcomponents]: http://getbootstrap.com/2.3.2/components.html -[bcomponentsnav]: http://getbootstrap.com/2.3.2/components.html#navbar +[bswatch]: https://bootswatch.com/ +[bcomponents]: https://getbootstrap.com/2.3.2/components.html +[bcomponentsnav]: https://getbootstrap.com/2.3.2/components.html#navbar [autocomplete-packages]: https://www.djangopackages.com/grids/g/auto-complete/ [django-autocomplete-light]: https://github.com/yourlabs/django-autocomplete-light diff --git a/docs/topics/browser-enhancements.md b/docs/topics/browser-enhancements.md index 7517ecbc09..32eab43a83 100644 --- a/docs/topics/browser-enhancements.md +++ b/docs/topics/browser-enhancements.md @@ -80,8 +80,8 @@ was later [dropped from the spec][html5]. There remains [ongoing discussion][put_delete] about adding support for `PUT` and `DELETE`, as well as how to support content types other than form-encoded data. -[cite]: http://www.amazon.com/Restful-Web-Services-Leonard-Richardson/dp/0596529260 +[cite]: https://www.amazon.com/RESTful-Web-Services-Leonard-Richardson/dp/0596529260 [ajax-form]: https://github.com/encode/ajax-form [rails]: http://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-put-or-delete-methods-work -[html5]: http://www.w3.org/TR/html5-diff/#changes-2010-06-24 +[html5]: https://www.w3.org/TR/html5-diff/#changes-2010-06-24 [put_delete]: http://amundsen.com/examples/put-delete-forms/ diff --git a/docs/topics/contributing.md b/docs/topics/contributing.md index 9a413832fd..19976b00b7 100644 --- a/docs/topics/contributing.md +++ b/docs/topics/contributing.md @@ -198,15 +198,15 @@ If you want to draw attention to a note or warning, use a pair of enclosing line --- -[cite]: http://www.w3.org/People/Berners-Lee/FAQ.html +[cite]: https://www.w3.org/People/Berners-Lee/FAQ.html [code-of-conduct]: https://www.djangoproject.com/conduct/ [google-group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework -[so-filter]: http://stackexchange.com/filters/66475/rest-framework +[so-filter]: https://stackexchange.com/filters/66475/rest-framework [issues]: https://github.com/encode/django-rest-framework/issues?state=open -[pep-8]: http://www.python.org/dev/peps/pep-0008/ +[pep-8]: https://www.python.org/dev/peps/pep-0008/ [travis-status]: ../img/travis-status.png [pull-requests]: https://help.github.com/articles/using-pull-requests [tox]: https://tox.readthedocs.io/en/latest/ -[markdown]: http://daringfireball.net/projects/markdown/basics +[markdown]: https://daringfireball.net/projects/markdown/basics [docs]: https://github.com/encode/django-rest-framework/tree/master/docs [mou]: http://mouapp.com/ diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md index b8b9b29f93..fff6c4452d 100644 --- a/docs/topics/documenting-your-api.md +++ b/docs/topics/documenting-your-api.md @@ -298,15 +298,15 @@ To implement a hypermedia API you'll need to decide on an appropriate media type [drf-openapi]: https://github.com/limdauto/drf_openapi/ [image-drf-openapi]: ../img/drf-openapi.png [drfdocs-repo]: https://github.com/ekonstantinidis/django-rest-framework-docs -[drfdocs-website]: http://www.drfdocs.com/ +[drfdocs-website]: https://www.drfdocs.com/ [drfdocs-demo]: http://demo.drfdocs.com/ [drfautodocs-repo]: https://github.com/iMakedonsky/drf-autodocs [django-rest-swagger]: https://github.com/marcgibbons/django-rest-swagger [swagger]: https://swagger.io/ [open-api]: https://openapis.org/ [rest-framework-docs]: https://github.com/marcgibbons/django-rest-framework-docs -[apiary]: http://apiary.io/ -[markdown]: http://daringfireball.net/projects/markdown/ +[apiary]: https://apiary.io/ +[markdown]: https://daringfireball.net/projects/markdown/ [hypermedia-docs]: rest-hypermedia-hateoas.md [image-drf-docs]: ../img/drfdocs.png [image-django-rest-swagger]: ../img/django-rest-swagger.png diff --git a/docs/topics/internationalization.md b/docs/topics/internationalization.md index fe85c6523a..ed8b858364 100644 --- a/docs/topics/internationalization.md +++ b/docs/topics/internationalization.md @@ -102,9 +102,9 @@ You can find more information on how the language preference is determined in th For API clients the most appropriate of these will typically be to use the `Accept-Language` header; Sessions and cookies will not be available unless using session authentication, and generally better practice to prefer an `Accept-Language` header for API clients rather than using language URL prefixes. -[cite]: http://youtu.be/Wa0VfS2q94Y +[cite]: https://youtu.be/Wa0VfS2q94Y [django-translation]: https://docs.djangoproject.com/en/1.7/topics/i18n/translation -[custom-exception-handler]: ../api-guide/exceptions.md#custom-exception-handling +[custom-exception-handler]: ../api-guide/exceptions.md#custom-exception-handling [transifex-project]: https://www.transifex.com/projects/p/django-rest-framework/ [django-po-source]: https://raw.githubusercontent.com/encode/django-rest-framework/master/rest_framework/locale/en_US/LC_MESSAGES/django.po [django-language-preference]: https://docs.djangoproject.com/en/1.7/topics/i18n/translation/#how-django-discovers-language-preference diff --git a/docs/topics/jobs.md b/docs/topics/jobs.md index 8ffe059ad9..1012738d20 100644 --- a/docs/topics/jobs.md +++ b/docs/topics/jobs.md @@ -11,7 +11,7 @@ Looking for a new Django REST Framework related role? On this site we provide a * [https://djangojobs.net/jobs/][django-jobs-net] * [http://djangojobbers.com][django-jobbers-com] * [https://www.indeed.com/q-Django-jobs.html][indeed-com] -* [http://stackoverflow.com/jobs/developer-jobs-using-django][stackoverflow-com] +* [https://stackoverflow.com/jobs/developer-jobs-using-django][stackoverflow-com] * [https://www.upwork.com/o/jobs/browse/skill/django-framework/][upwork-com] * [https://www.technojobs.co.uk/django-jobs][technobjobs-co-uk] * [https://remoteok.io/remote-django-jobs][remoteok-io] @@ -29,7 +29,7 @@ Wonder how else you can help? One of the best ways you can help Django REST Fram [django-jobs-net]: https://djangojobs.net/jobs/ [django-jobbers-com]: http://djangojobbers.com [indeed-com]: https://www.indeed.com/q-Django-jobs.html -[stackoverflow-com]: http://stackoverflow.com/jobs/developer-jobs-using-django +[stackoverflow-com]: https://stackoverflow.com/jobs/developer-jobs-using-django [upwork-com]: https://www.upwork.com/o/jobs/browse/skill/django-framework/ [technobjobs-co-uk]: https://www.technojobs.co.uk/django-jobs [remoteok-io]: https://remoteok.io/remote-django-jobs diff --git a/docs/topics/kickstarter-announcement.md b/docs/topics/kickstarter-announcement.md index 6b7b428d1e..e2a283e293 100644 --- a/docs/topics/kickstarter-announcement.md +++ b/docs/topics/kickstarter-announcement.md @@ -72,12 +72,12 @@ Our gold sponsors include companies large and small. Many thanks for their signi
  • Schuberg Philis
  • ProReNata AB
  • SGA Websites
  • -
  • Sirono
  • -
  • Vinta Software Studio
  • -
  • Rapasso
  • +
  • Sirono
  • +
  • Vinta Software Studio
  • +
  • Rapasso
  • Mirus Research
  • -
  • Hipo
  • -
  • Byte
  • +
  • Hipo
  • +
  • Byte
  • Lightning Kite
  • Opbeat
  • Koordinates
  • @@ -85,7 +85,7 @@ Our gold sponsors include companies large and small. Many thanks for their signi
  • Singing Horse Studio Ltd.
  • Heroku
  • Rheinwerk Verlag
  • -
  • Security Compass
  • +
  • Security Compass
  • Django Software Foundation
  • Hipflask
  • Crate
  • @@ -105,38 +105,38 @@ Our gold sponsors include companies large and small. Many thanks for their signi The serious financial contribution that our silver sponsors have made is very much appreciated. I'd like to say a particular thank you to individuals who have chosen to privately support the project at this level. From 73203e6b5920dcbe78e3309b7bf2803eb56db536 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Thu, 25 Jan 2018 03:40:49 -0500 Subject: [PATCH 0037/1262] Rework dynamic list/detail actions (#5705) * Merge list/detail route decorators into 'action' * Merge dynamic routes, add 'detail' attribute * Add 'ViewSet.get_extra_actions()' * Refactor dynamic route checking & collection * Refactor dynamic route generation * Add 'ViewSet.detail' initkwarg * Fixup schema test * Add release notes for dynamic action changes * Replace list/detail route decorators in tests * Convert tabs to spaces in router docs * Update docs * Make 'detail' a required argument of 'action' * Improve router docs --- docs/api-guide/metadata.md | 2 +- docs/api-guide/routers.md | 96 ++++++--------- docs/api-guide/viewsets.md | 46 ++++--- docs/topics/release-notes.md | 29 +++++ docs/tutorial/6-viewsets-and-routers.md | 10 +- rest_framework/decorators.py | 42 +++++-- rest_framework/routers.py | 152 ++++++++++++------------ rest_framework/viewsets.py | 18 ++- tests/test_decorators.py | 53 ++++++++- tests/test_routers.py | 40 ++++--- tests/test_schemas.py | 27 +++-- tests/test_viewsets.py | 20 +++- 12 files changed, 331 insertions(+), 204 deletions(-) diff --git a/docs/api-guide/metadata.md b/docs/api-guide/metadata.md index d5c3c1a67e..affeea61c9 100644 --- a/docs/api-guide/metadata.md +++ b/docs/api-guide/metadata.md @@ -67,7 +67,7 @@ If you have specific requirements for creating schema endpoints that are accesse For example, the following additional route could be used on a viewset to provide a linkable schema endpoint. - @list_route(methods=['GET']) + @action(methods=['GET'], detail=False) def schema(self, request): meta = self.metadata_class() data = meta.determine_metadata(request, self) diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md index e4415df155..09e06ff1db 100644 --- a/docs/api-guide/routers.md +++ b/docs/api-guide/routers.md @@ -81,81 +81,62 @@ Router URL patterns can also be namespaces. If using namespacing with hyperlinked serializers you'll also need to ensure that any `view_name` parameters on the serializers correctly reflect the namespace. In the example above you'd need to include a parameter such as `view_name='api:user-detail'` for serializer fields hyperlinked to the user detail view. -### Extra link and actions +### Routing for extra actions -Any methods on the viewset decorated with `@detail_route` or `@list_route` will also be routed. -For example, given a method like this on the `UserViewSet` class: +A viewset may [mark extra actions for routing][route-decorators] by decorating a method with the `@action` decorator. These extra actions will be included in the generated routes. For example, given the `set_password` method on the `UserViewSet` class: from myapp.permissions import IsAdminOrIsSelf - from rest_framework.decorators import detail_route + from rest_framework.decorators import action class UserViewSet(ModelViewSet): ... - @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf]) + @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf]) def set_password(self, request, pk=None): ... -The following URL pattern would additionally be generated: +The following route would be generated: -* URL pattern: `^users/{pk}/set_password/$` Name: `'user-set-password'` +* URL pattern: `^users/{pk}/set_password/$` +* URL name: `'user-set-password'` -If you do not want to use the default URL generated for your custom action, you can instead use the url_path parameter to customize it. +By default, the URL pattern is based on the method name, and the URL name is the combination of the `ViewSet.basename` and the hyphenated method name. +If you don't want to use the defaults for either of these values, you can instead provide the `url_path` and `url_name` arguments to the `@action` decorator. For example, if you want to change the URL for our custom action to `^users/{pk}/change-password/$`, you could write: from myapp.permissions import IsAdminOrIsSelf - from rest_framework.decorators import detail_route + from rest_framework.decorators import action class UserViewSet(ModelViewSet): ... - @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_path='change-password') + @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf], + url_path='change-password', url_name='change_password') def set_password(self, request, pk=None): ... The above example would now generate the following URL pattern: -* URL pattern: `^users/{pk}/change-password/$` Name: `'user-change-password'` - -In the case you do not want to use the default name generated for your custom action, you can use the url_name parameter to customize it. - -For example, if you want to change the name of our custom action to `'user-change-password'`, you could write: - - from myapp.permissions import IsAdminOrIsSelf - from rest_framework.decorators import detail_route - - class UserViewSet(ModelViewSet): - ... - - @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_name='change-password') - def set_password(self, request, pk=None): - ... - -The above example would now generate the following URL pattern: - -* URL pattern: `^users/{pk}/set_password/$` Name: `'user-change-password'` - -You can also use url_path and url_name parameters together to obtain extra control on URL generation for custom views. - -For more information see the viewset documentation on [marking extra actions for routing][route-decorators]. +* URL path: `^users/{pk}/change-password/$` +* URL name: `'user-change_password'` # API Guide ## SimpleRouter -This router includes routes for the standard set of `list`, `create`, `retrieve`, `update`, `partial_update` and `destroy` actions. The viewset can also mark additional methods to be routed, using the `@detail_route` or `@list_route` decorators. +This router includes routes for the standard set of `list`, `create`, `retrieve`, `update`, `partial_update` and `destroy` actions. The viewset can also mark additional methods to be routed, using the `@action` decorator. - + - +
    URL StyleHTTP MethodActionURL Name
    {prefix}/GETlist{basename}-list
    POSTcreate
    {prefix}/{methodname}/GET, or as specified by `methods` argument`@list_route` decorated method{basename}-{methodname}
    {prefix}/{url_path}/GET, or as specified by `methods` argument`@action(detail=False)` decorated method{basename}-{url_name}
    {prefix}/{lookup}/GETretrieve{basename}-detail
    PUTupdate
    PATCHpartial_update
    DELETEdestroy
    {prefix}/{lookup}/{methodname}/GET, or as specified by `methods` argument`@detail_route` decorated method{basename}-{methodname}
    {prefix}/{lookup}/{url_path}/GET, or as specified by `methods` argument`@action(detail=True)` decorated method{basename}-{url_name}
    By default the URLs created by `SimpleRouter` are appended with a trailing slash. @@ -180,12 +161,12 @@ This router is similar to `SimpleRouter` as above, but additionally includes a d [.format]GETautomatically generated root viewapi-root {prefix}/[.format]GETlist{basename}-list POSTcreate - {prefix}/{methodname}/[.format]GET, or as specified by `methods` argument`@list_route` decorated method{basename}-{methodname} + {prefix}/{url_path}/[.format]GET, or as specified by `methods` argument`@action(detail=False)` decorated method{basename}-{url_name} {prefix}/{lookup}/[.format]GETretrieve{basename}-detail PUTupdate PATCHpartial_update DELETEdestroy - {prefix}/{lookup}/{methodname}/[.format]GET, or as specified by `methods` argument`@detail_route` decorated method{basename}-{methodname} + {prefix}/{lookup}/{url_path}/[.format]GET, or as specified by `methods` argument`@action(detail=True)` decorated method{basename}-{url_name} As with `SimpleRouter` the trailing slashes on the URL routes can be removed by setting the `trailing_slash` argument to `False` when instantiating the router. @@ -212,18 +193,18 @@ The arguments to the `Route` named tuple are: * `{basename}` - The base to use for the URL names that are created. -**initkwargs**: A dictionary of any additional arguments that should be passed when instantiating the view. Note that the `suffix` argument is reserved for identifying the viewset type, used when generating the view name and breadcrumb links. +**initkwargs**: A dictionary of any additional arguments that should be passed when instantiating the view. Note that the `detail`, `basename`, and `suffix` arguments are reserved for viewset introspection and are also used by the browsable API to generate the view name and breadcrumb links. ## Customizing dynamic routes -You can also customize how the `@list_route` and `@detail_route` decorators are routed. -To route either or both of these decorators, include a `DynamicListRoute` and/or `DynamicDetailRoute` named tuple in the `.routes` list. +You can also customize how the `@action` decorator is routed. Include the `DynamicRoute` named tuple in the `.routes` list, setting the `detail` argument as appropriate for the list-based and detail-based routes. In addition to `detail`, the arguments to `DynamicRoute` are: -The arguments to `DynamicListRoute` and `DynamicDetailRoute` are: +**url**: A string representing the URL to be routed. May include the same format strings as `Route`, and additionally accepts the `{url_path}` format string. -**url**: A string representing the URL to be routed. May include the same format strings as `Route`, and additionally accepts the `{methodname}` and `{methodnamehyphen}` format strings. +**name**: The name of the URL as used in `reverse` calls. May include the following format strings: -**name**: The name of the URL as used in `reverse` calls. May include the following format strings: `{basename}`, `{methodname}` and `{methodnamehyphen}`. +* `{basename}` - The base to use for the URL names that are created. +* `{url_name}` - The `url_name` provided to the `@action`. **initkwargs**: A dictionary of any additional arguments that should be passed when instantiating the view. @@ -231,7 +212,7 @@ The arguments to `DynamicListRoute` and `DynamicDetailRoute` are: The following example will only route to the `list` and `retrieve` actions, and does not use the trailing slash convention. - from rest_framework.routers import Route, DynamicDetailRoute, SimpleRouter + from rest_framework.routers import Route, DynamicRoute, SimpleRouter class CustomReadOnlyRouter(SimpleRouter): """ @@ -239,22 +220,23 @@ The following example will only route to the `list` and `retrieve` actions, and """ routes = [ Route( - url=r'^{prefix}$', - mapping={'get': 'list'}, - name='{basename}-list', - initkwargs={'suffix': 'List'} + url=r'^{prefix}$', + mapping={'get': 'list'}, + name='{basename}-list', + initkwargs={'suffix': 'List'} ), Route( - url=r'^{prefix}/{lookup}$', + url=r'^{prefix}/{lookup}$', mapping={'get': 'retrieve'}, name='{basename}-detail', initkwargs={'suffix': 'Detail'} ), - DynamicDetailRoute( - url=r'^{prefix}/{lookup}/{methodnamehyphen}$', - name='{basename}-{methodnamehyphen}', - initkwargs={} - ) + DynamicRoute( + url=r'^{prefix}/{lookup}/{url_path}$', + name='{basename}-{url_name}', + detail=True, + initkwargs={} + ) ] Let's take a look at the routes our `CustomReadOnlyRouter` would generate for a simple viewset. @@ -269,7 +251,7 @@ Let's take a look at the routes our `CustomReadOnlyRouter` would generate for a serializer_class = UserSerializer lookup_field = 'username' - @detail_route() + @action(detail=True) def group_names(self, request, pk=None): """ Returns a list of all the group names that the given @@ -283,7 +265,7 @@ Let's take a look at the routes our `CustomReadOnlyRouter` would generate for a router = CustomReadOnlyRouter() router.register('users', UserViewSet) - urlpatterns = router.urls + urlpatterns = router.urls The following mappings would be generated... diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index 27fb1d7805..503459a963 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -102,10 +102,16 @@ The default routers included with REST framework will provide routes for a stand def destroy(self, request, pk=None): pass -During dispatch the name of the current action is available via the `.action` attribute. -You may inspect `.action` to adjust behaviour based on the current action. +## Introspecting ViewSet actions -For example, you could restrict permissions to everything except the `list` action similar to this: +During dispatch, the following attributes are available on the `ViewSet`. + +* `basename` - the base to use for the URL names that are created. +* `action` - the name of the current action (e.g., `list`, `create`). +* `detail` - boolean indicating if the current action is configured for a list or detail view. +* `suffix` - the display suffix for the viewset type - mirrors the `detail` attribute. + +You may inspect these attributes to adjust behaviour based on the current action. For example, you could restrict permissions to everything except the `list` action similar to this: def get_permissions(self): """ @@ -119,16 +125,13 @@ For example, you could restrict permissions to everything except the `list` acti ## Marking extra actions for routing -If you have ad-hoc methods that you need to be routed to, you can mark them as requiring routing using the `@detail_route` or `@list_route` decorators. - -The `@detail_route` decorator contains `pk` in its URL pattern and is intended for methods which require a single instance. The `@list_route` decorator is intended for methods which operate on a list of objects. +If you have ad-hoc methods that should be routable, you can mark them as such with the `@action` decorator. Like regular actions, extra actions may be intended for either a list of objects, or a single instance. To indicate this, set the `detail` argument to `True` or `False`. The router will configure its URL patterns accordingly. e.g., the `DefaultRouter` will configure detail actions to contain `pk` in their URL patterns. -For example: +A more complete example of extra actions: from django.contrib.auth.models import User - from rest_framework import status - from rest_framework import viewsets - from rest_framework.decorators import detail_route, list_route + from rest_framework import status, viewsets + from rest_framework.decorators import action from rest_framework.response import Response from myapp.serializers import UserSerializer, PasswordSerializer @@ -139,7 +142,7 @@ For example: queryset = User.objects.all() serializer_class = UserSerializer - @detail_route(methods=['post']) + @action(methods=['post'], detail=True) def set_password(self, request, pk=None): user = self.get_object() serializer = PasswordSerializer(data=request.data) @@ -151,7 +154,7 @@ For example: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - @list_route() + @action(detail=False) def recent_users(self, request): recent_users = User.objects.all().order('-last_login') @@ -163,20 +166,22 @@ For example: serializer = self.get_serializer(recent_users, many=True) return Response(serializer.data) -The decorators can additionally take extra arguments that will be set for the routed view only. For example... +The decorator can additionally take extra arguments that will be set for the routed view only. For example: - @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf]) + @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf]) def set_password(self, request, pk=None): ... -These decorators will route `GET` requests by default, but may also accept other HTTP methods, by using the `methods` argument. For example: +These decorator will route `GET` requests by default, but may also accept other HTTP methods by setting the `methods` argument. For example: - @detail_route(methods=['post', 'delete']) + @action(methods=['post', 'delete'], detail=True) 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/$` +To view all extra actions, call the `.get_extra_actions()` method. + ## Reversing action URLs If you need to get the URL of an action, use the `.reverse_action()` method. This is a convenience wrapper for `reverse()`, automatically passing the view's `request` object and prepending the `url_name` with the `.basename` attribute. @@ -190,7 +195,14 @@ Using the example from the previous section: 'http://localhost:8000/api/users/1/set_password' ``` -The `url_name` argument should match the same argument to the `@list_route` and `@detail_route` decorators. Additionally, this can be used to reverse the default `list` and `detail` routes. +Alternatively, you can use the `url_name` attribute set by the `@action` decorator. + +```python +>>> view.reverse_action(view.set_password.url_name, args=['1']) +'http://localhost:8000/api/users/1/set_password' +``` + +The `url_name` argument for `.reverse_action()` should match the same argument to the `@action` decorator. Additionally, this method can be used to reverse the default actions, such as `list` and `create`. --- diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 128175ef5e..2c45b70afd 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -38,6 +38,31 @@ You can determine your currently installed version using `pip freeze`: --- +## 3.8.x series + +### 3.8.0 + +**Date**: [unreleased][3.8.0-milestone] + +* Refactor dynamic route generation and improve viewset action introspectibility. [#5705][gh5705] + + `ViewSet`s have been provided with new attributes and methods that allow + it to introspect its set of actions and the details of the current action. + + * Merged `list_route` and `detail_route` into a single `action` decorator. + * Get all extra actions on a `ViewSet` with `.get_extra_actions()`. + * Extra actions now set the `url_name` and `url_path` on the decorated method. + * Enable action url reversing through `.reverse_action()` method (added in 3.7.4) + * Example reverse call: `self.reverse_action(self.custom_action.url_name)` + * Add `detail` initkwarg to indicate if the current action is operating on a + collection or a single instance. + + Additional changes: + + * Deprecated `list_route` & `detail_route` in favor of `action` decorator with `detail` boolean. + * Deprecated dynamic list/detail route variants in favor of `DynamicRoute` with `detail` boolean. + * Refactored the router's dynamic route generation. + ## 3.7.x series ### 3.7.7 @@ -940,6 +965,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.7.5-milestone]: https://github.com/encode/django-rest-framework/milestone/63?closed=1 [3.7.6-milestone]: https://github.com/encode/django-rest-framework/milestone/64?closed=1 [3.7.7-milestone]: https://github.com/encode/django-rest-framework/milestone/65?closed=1 +[3.8.0-milestone]: https://github.com/encode/django-rest-framework/milestone/61?closed=1 [gh2013]: https://github.com/encode/django-rest-framework/issues/2013 @@ -1750,3 +1776,6 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh5695]: https://github.com/encode/django-rest-framework/issues/5695 [gh5696]: https://github.com/encode/django-rest-framework/issues/5696 [gh5697]: https://github.com/encode/django-rest-framework/issues/5697 + + +[gh5705]: https://github.com/encode/django-rest-framework/issues/5705 diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md index 7d87c02129..9452b49472 100644 --- a/docs/tutorial/6-viewsets-and-routers.md +++ b/docs/tutorial/6-viewsets-and-routers.md @@ -25,7 +25,7 @@ Here we've used the `ReadOnlyModelViewSet` class to automatically provide the de Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighlight` view classes. We can remove the three views, and again replace them with a single class. - from rest_framework.decorators import detail_route + from rest_framework.decorators import action from rest_framework.response import Response class SnippetViewSet(viewsets.ModelViewSet): @@ -40,7 +40,7 @@ Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighl permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,) - @detail_route(renderer_classes=[renderers.StaticHTMLRenderer]) + @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer]) def highlight(self, request, *args, **kwargs): snippet = self.get_object() return Response(snippet.highlighted) @@ -50,11 +50,11 @@ Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighl This time we've used the `ModelViewSet` class in order to get the complete set of default read and write operations. -Notice that we've also used the `@detail_route` decorator to create a custom action, named `highlight`. This decorator can be used to add any custom endpoints that don't fit into the standard `create`/`update`/`delete` style. +Notice that we've also used the `@action` decorator to create a custom action, named `highlight`. This decorator can be used to add any custom endpoints that don't fit into the standard `create`/`update`/`delete` style. -Custom actions which use the `@detail_route` decorator will respond to `GET` requests by default. We can use the `methods` argument if we wanted an action that responded to `POST` requests. +Custom actions which use the `@action` decorator will respond to `GET` requests by default. We can use the `methods` argument if we wanted an action that responded to `POST` requests. -The URLs for custom actions by default depend on the method name itself. If you want to change the way url should be constructed, you can include url_path as a decorator keyword argument. +The URLs for custom actions by default depend on the method name itself. If you want to change the way url should be constructed, you can include `url_path` as a decorator keyword argument. ## Binding ViewSets to URLs explicitly diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index 2f93fdd976..62afa05979 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -130,29 +130,49 @@ def decorator(func): return decorator -def detail_route(methods=None, **kwargs): +def action(methods=None, detail=None, url_path=None, url_name=None, **kwargs): """ - Used to mark a method on a ViewSet that should be routed for detail requests. + Mark a ViewSet method as a routable action. + + Set the `detail` boolean to determine if this action should apply to + instance/detail requests or collection/list requests. """ methods = ['get'] if (methods is None) else methods + methods = [method.lower() for method in methods] + + assert detail is not None, ( + "@action() missing required argument: 'detail'" + ) def decorator(func): func.bind_to_methods = methods - func.detail = True + func.detail = detail + func.url_path = url_path or func.__name__ + func.url_name = url_name or func.__name__.replace('_', '-') func.kwargs = kwargs return func return decorator +def detail_route(methods=None, **kwargs): + """ + Used to mark a method on a ViewSet that should be routed for detail requests. + """ + warnings.warn( + "`detail_route` is pending deprecation and will be removed in 3.10 in favor of " + "`action`, which accepts a `detail` bool. Use `@action(detail=True)` instead.", + PendingDeprecationWarning, stacklevel=2 + ) + return action(methods, detail=True, **kwargs) + + def list_route(methods=None, **kwargs): """ Used to mark a method on a ViewSet that should be routed for list requests. """ - methods = ['get'] if (methods is None) else methods - - def decorator(func): - func.bind_to_methods = methods - func.detail = False - func.kwargs = kwargs - return func - return decorator + warnings.warn( + "`list_route` is pending deprecation and will be removed in 3.10 in favor of " + "`action`, which accepts a `detail` bool. Use `@action(detail=False)` instead.", + PendingDeprecationWarning, stacklevel=2 + ) + return action(methods, detail=False, **kwargs) diff --git a/rest_framework/routers.py b/rest_framework/routers.py index f4d2fab383..9007788f85 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -16,6 +16,7 @@ from __future__ import unicode_literals import itertools +import warnings from collections import OrderedDict, namedtuple from django.conf.urls import url @@ -30,9 +31,30 @@ from rest_framework.settings import api_settings from rest_framework.urlpatterns import format_suffix_patterns -Route = namedtuple('Route', ['url', 'mapping', 'name', 'initkwargs']) -DynamicDetailRoute = namedtuple('DynamicDetailRoute', ['url', 'name', 'initkwargs']) -DynamicListRoute = namedtuple('DynamicListRoute', ['url', 'name', 'initkwargs']) +Route = namedtuple('Route', ['url', 'mapping', 'name', 'detail', 'initkwargs']) +DynamicRoute = namedtuple('DynamicRoute', ['url', 'name', 'detail', 'initkwargs']) + + +class DynamicDetailRoute(object): + def __new__(cls, url, name, initkwargs): + warnings.warn( + "`DynamicDetailRoute` is pending deprecation and will be removed in 3.10 " + "in favor of `DynamicRoute`, which accepts a `detail` boolean. Use " + "`DynamicRoute(url, name, True, initkwargs)` instead.", + PendingDeprecationWarning, stacklevel=2 + ) + return DynamicRoute(url, name, True, initkwargs) + + +class DynamicListRoute(object): + def __new__(cls, url, name, initkwargs): + warnings.warn( + "`DynamicListRoute` is pending deprecation and will be removed in 3.10 in " + "favor of `DynamicRoute`, which accepts a `detail` boolean. Use " + "`DynamicRoute(url, name, False, initkwargs)` instead.", + PendingDeprecationWarning, stacklevel=2 + ) + return DynamicRoute(url, name, False, initkwargs) def escape_curly_brackets(url_path): @@ -44,18 +66,6 @@ def escape_curly_brackets(url_path): return url_path -def replace_methodname(format_string, methodname): - """ - Partially format a format_string, swapping out any - '{methodname}' or '{methodnamehyphen}' components. - """ - methodnamehyphen = methodname.replace('_', '-') - ret = format_string - ret = ret.replace('{methodname}', methodname) - ret = ret.replace('{methodnamehyphen}', methodnamehyphen) - return ret - - def flatten(list_of_lists): """ Takes an iterable of iterables, returns a single iterable containing all items @@ -103,14 +113,15 @@ class SimpleRouter(BaseRouter): 'post': 'create' }, name='{basename}-list', + detail=False, initkwargs={'suffix': 'List'} ), - # Dynamically generated list routes. - # Generated using @list_route decorator - # on methods of the viewset. - DynamicListRoute( - url=r'^{prefix}/{methodname}{trailing_slash}$', - name='{basename}-{methodnamehyphen}', + # Dynamically generated list routes. Generated using + # @action(detail=False) decorator on methods of the viewset. + DynamicRoute( + url=r'^{prefix}/{url_path}{trailing_slash}$', + name='{basename}-{url_name}', + detail=False, initkwargs={} ), # Detail route. @@ -123,13 +134,15 @@ class SimpleRouter(BaseRouter): 'delete': 'destroy' }, name='{basename}-detail', + detail=True, initkwargs={'suffix': 'Instance'} ), - # Dynamically generated detail routes. - # Generated using @detail_route decorator on methods of the viewset. - DynamicDetailRoute( - url=r'^{prefix}/{lookup}/{methodname}{trailing_slash}$', - name='{basename}-{methodnamehyphen}', + # Dynamically generated detail routes. Generated using + # @action(detail=True) decorator on methods of the viewset. + DynamicRoute( + url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$', + name='{basename}-{url_name}', + detail=True, initkwargs={} ), ] @@ -160,57 +173,47 @@ def get_routes(self, viewset): # converting to list as iterables are good for one pass, known host needs to be checked again and again for # different functions. known_actions = list(flatten([route.mapping.values() for route in self.routes if isinstance(route, Route)])) - - # Determine any `@detail_route` or `@list_route` decorated methods on the viewset - detail_routes = [] - list_routes = [] - for methodname in dir(viewset): - attr = getattr(viewset, methodname) - httpmethods = getattr(attr, 'bind_to_methods', None) - detail = getattr(attr, 'detail', True) - if httpmethods: - # checking method names against the known actions list - if methodname in known_actions: - raise ImproperlyConfigured('Cannot use @detail_route or @list_route ' - 'decorators on method "%s" ' - 'as it is an existing route' % methodname) - httpmethods = [method.lower() for method in httpmethods] - if detail: - detail_routes.append((httpmethods, methodname)) - else: - list_routes.append((httpmethods, methodname)) - - def _get_dynamic_routes(route, dynamic_routes): - ret = [] - for httpmethods, methodname in dynamic_routes: - method_kwargs = getattr(viewset, methodname).kwargs - initkwargs = route.initkwargs.copy() - initkwargs.update(method_kwargs) - url_path = initkwargs.pop("url_path", None) or methodname - url_path = escape_curly_brackets(url_path) - url_name = initkwargs.pop("url_name", None) or url_path - ret.append(Route( - url=replace_methodname(route.url, url_path), - mapping={httpmethod: methodname for httpmethod in httpmethods}, - name=replace_methodname(route.name, url_name), - initkwargs=initkwargs, - )) - - return ret - - ret = [] + extra_actions = viewset.get_extra_actions() + + # checking action names against the known actions list + not_allowed = [ + action.__name__ for action in extra_actions + if action.__name__ in known_actions + ] + if not_allowed: + msg = ('Cannot use the @action decorator on the following ' + 'methods, as they are existing routes: %s') + raise ImproperlyConfigured(msg % ', '.join(not_allowed)) + + # partition detail and list actions + detail_actions = [action for action in extra_actions if action.detail] + list_actions = [action for action in extra_actions if not action.detail] + + routes = [] for route in self.routes: - if isinstance(route, DynamicDetailRoute): - # Dynamic detail routes (@detail_route decorator) - ret += _get_dynamic_routes(route, detail_routes) - elif isinstance(route, DynamicListRoute): - # Dynamic list routes (@list_route decorator) - ret += _get_dynamic_routes(route, list_routes) + if isinstance(route, DynamicRoute) and route.detail: + routes += [self._get_dynamic_route(route, action) for action in detail_actions] + elif isinstance(route, DynamicRoute) and not route.detail: + routes += [self._get_dynamic_route(route, action) for action in list_actions] else: - # Standard route - ret.append(route) + routes.append(route) - return ret + return routes + + def _get_dynamic_route(self, route, action): + initkwargs = route.initkwargs.copy() + initkwargs.update(action.kwargs) + + url_path = escape_curly_brackets(action.url_path) + + return Route( + url=route.url.replace('{url_path}', url_path), + mapping={http_method: action.__name__ + for http_method in action.bind_to_methods}, + name=route.name.replace('{url_name}', action.url_name), + detail=route.detail, + initkwargs=initkwargs, + ) def get_method_map(self, viewset, method_map): """ @@ -281,6 +284,7 @@ def get_urls(self): initkwargs = route.initkwargs.copy() initkwargs.update({ 'basename': basename, + 'detail': route.detail, }) view = viewset.as_view(mapping, **initkwargs) diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py index 4ee7cdaf86..9a85049bcc 100644 --- a/rest_framework/viewsets.py +++ b/rest_framework/viewsets.py @@ -19,6 +19,7 @@ from __future__ import unicode_literals from functools import update_wrapper +from inspect import getmembers from django.utils.decorators import classonlymethod from django.views.decorators.csrf import csrf_exempt @@ -27,6 +28,10 @@ from rest_framework.reverse import reverse +def _is_extra_action(attr): + return hasattr(attr, 'bind_to_methods') + + class ViewSetMixin(object): """ This is the magic. @@ -51,6 +56,9 @@ def as_view(cls, actions=None, **initkwargs): # eg. 'List' or 'Instance'. cls.suffix = None + # The detail initkwarg is reserved for introspecting the viewset type. + cls.detail = None + # Setting a basename allows a view to reverse its action urls. This # value is provided by the router through the initkwargs. cls.basename = None @@ -112,8 +120,7 @@ def view(request, *args, **kwargs): def initialize_request(self, request, *args, **kwargs): """ - Set the `.action` attribute on the view, - depending on the request method. + Set the `.action` attribute on the view, depending on the request method. """ request = super(ViewSetMixin, self).initialize_request(request, *args, **kwargs) method = request.method.lower() @@ -135,6 +142,13 @@ def reverse_action(self, url_name, *args, **kwargs): return reverse(url_name, *args, **kwargs) + @classmethod + def get_extra_actions(cls): + """ + Get the methods that are marked as an extra ViewSet `@action`. + """ + return [method for _, method in getmembers(cls, _is_extra_action)] + class ViewSet(ViewSetMixin, views.APIView): """ diff --git a/tests/test_decorators.py b/tests/test_decorators.py index 6331742db2..674990730c 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -1,12 +1,14 @@ from __future__ import unicode_literals +import pytest from django.test import TestCase from rest_framework import status from rest_framework.authentication import BasicAuthentication from rest_framework.decorators import ( - api_view, authentication_classes, parser_classes, permission_classes, - renderer_classes, schema, throttle_classes + action, api_view, authentication_classes, detail_route, list_route, + parser_classes, permission_classes, renderer_classes, schema, + throttle_classes ) from rest_framework.parsers import JSONParser from rest_framework.permissions import IsAuthenticated @@ -166,3 +168,50 @@ def view(request): return Response({}) assert isinstance(view.cls.schema, CustomSchema) + + +class ActionDecoratorTestCase(TestCase): + + def test_defaults(self): + @action(detail=True) + def test_action(request): + pass + + assert test_action.bind_to_methods == ['get'] + assert test_action.detail is True + assert test_action.url_path == 'test_action' + assert test_action.url_name == 'test-action' + + def test_detail_required(self): + with pytest.raises(AssertionError) as excinfo: + @action() + def test_action(request): + pass + + assert str(excinfo.value) == "@action() missing required argument: 'detail'" + + def test_detail_route_deprecation(self): + with pytest.warns(PendingDeprecationWarning) as record: + @detail_route() + def view(request): + pass + + assert len(record) == 1 + assert str(record[0].message) == ( + "`detail_route` is pending deprecation and will be removed in " + "3.10 in favor of `action`, which accepts a `detail` bool. Use " + "`@action(detail=True)` instead." + ) + + def test_list_route_deprecation(self): + with pytest.warns(PendingDeprecationWarning) as record: + @list_route() + def view(request): + pass + + assert len(record) == 1 + assert str(record[0].message) == ( + "`list_route` is pending deprecation and will be removed in " + "3.10 in favor of `action`, which accepts a `detail` bool. Use " + "`@action(detail=False)` instead." + ) diff --git a/tests/test_routers.py b/tests/test_routers.py index 55ccc647b3..36255f48f4 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -11,7 +11,7 @@ from rest_framework import permissions, serializers, viewsets from rest_framework.compat import get_regex_pattern -from rest_framework.decorators import detail_route, list_route +from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.routers import DefaultRouter, SimpleRouter from rest_framework.test import APIRequestFactory, URLPatternsTestCase @@ -67,12 +67,12 @@ def get_object(self, *args, **kwargs): class RegexUrlPathViewSet(viewsets.ViewSet): - @list_route(url_path='list/(?P[0-9]{4})') + @action(detail=False, url_path='list/(?P[0-9]{4})') def regex_url_path_list(self, request, *args, **kwargs): kwarg = self.kwargs.get('kwarg', '') return Response({'kwarg': kwarg}) - @detail_route(url_path='detail/(?P[0-9]{4})') + @action(detail=True, url_path='detail/(?P[0-9]{4})') def regex_url_path_detail(self, request, *args, **kwargs): pk = self.kwargs.get('pk', '') kwarg = self.kwargs.get('kwarg', '') @@ -99,23 +99,23 @@ class BasicViewSet(viewsets.ViewSet): def list(self, request, *args, **kwargs): return Response({'method': 'list'}) - @detail_route(methods=['post']) + @action(methods=['post'], detail=True) def action1(self, request, *args, **kwargs): return Response({'method': 'action1'}) - @detail_route(methods=['post']) + @action(methods=['post'], detail=True) def action2(self, request, *args, **kwargs): return Response({'method': 'action2'}) - @detail_route(methods=['post', 'delete']) + @action(methods=['post', 'delete'], detail=True) def action3(self, request, *args, **kwargs): return Response({'method': 'action2'}) - @detail_route() + @action(detail=True) def link1(self, request, *args, **kwargs): return Response({'method': 'link1'}) - @detail_route() + @action(detail=True) def link2(self, request, *args, **kwargs): return Response({'method': 'link2'}) @@ -297,7 +297,7 @@ def setUp(self): class TestViewSet(viewsets.ModelViewSet): permission_classes = [] - @detail_route(methods=['post'], permission_classes=[permissions.AllowAny]) + @action(methods=['post'], detail=True, permission_classes=[permissions.AllowAny]) def custom(self, request, *args, **kwargs): return Response({ 'permission_classes': self.permission_classes @@ -315,14 +315,14 @@ def test_action_kwargs(self): class TestActionAppliedToExistingRoute(TestCase): """ - Ensure `@detail_route` decorator raises an except when applied + Ensure `@action` decorator raises an except when applied to an existing route """ def test_exception_raised_when_action_applied_to_existing_route(self): class TestViewSet(viewsets.ModelViewSet): - @detail_route(methods=['post']) + @action(methods=['post'], detail=True) def retrieve(self, request, *args, **kwargs): return Response({ 'hello': 'world' @@ -339,27 +339,27 @@ class DynamicListAndDetailViewSet(viewsets.ViewSet): def list(self, request, *args, **kwargs): return Response({'method': 'list'}) - @list_route(methods=['post']) + @action(methods=['post'], detail=False) def list_route_post(self, request, *args, **kwargs): return Response({'method': 'action1'}) - @detail_route(methods=['post']) + @action(methods=['post'], detail=True) def detail_route_post(self, request, *args, **kwargs): return Response({'method': 'action2'}) - @list_route() + @action(detail=False) def list_route_get(self, request, *args, **kwargs): return Response({'method': 'link1'}) - @detail_route() + @action(detail=True) def detail_route_get(self, request, *args, **kwargs): return Response({'method': 'link2'}) - @list_route(url_path="list_custom-route") + @action(detail=False, url_path="list_custom-route") def list_custom_route_get(self, request, *args, **kwargs): return Response({'method': 'link1'}) - @detail_route(url_path="detail_custom-route") + @action(detail=True, url_path="detail_custom-route") def detail_custom_route_get(self, request, *args, **kwargs): return Response({'method': 'link2'}) @@ -455,6 +455,12 @@ def test_suffix(self): assert initkwargs['suffix'] == 'List' + def test_detail(self): + match = resolve('/example/notes/') + initkwargs = match.func.initkwargs + + assert not initkwargs['detail'] + def test_basename(self): match = resolve('/example/notes/') initkwargs = match.func.initkwargs diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 34cb20798a..1cbee0695c 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -10,9 +10,7 @@ filters, generics, pagination, permissions, serializers ) from rest_framework.compat import coreapi, coreschema, get_regex_pattern, path -from rest_framework.decorators import ( - api_view, detail_route, list_route, schema -) +from rest_framework.decorators import action, api_view, schema from rest_framework.request import Request from rest_framework.routers import DefaultRouter, SimpleRouter from rest_framework.schemas import ( @@ -67,25 +65,25 @@ class ExampleViewSet(ModelViewSet): filter_backends = [filters.OrderingFilter] serializer_class = ExampleSerializer - @detail_route(methods=['post'], serializer_class=AnotherSerializer) + @action(methods=['post'], detail=True, serializer_class=AnotherSerializer) def custom_action(self, request, pk): """ A description of custom action. """ return super(ExampleSerializer, self).retrieve(self, request) - @detail_route(methods=['post'], serializer_class=AnotherSerializerWithListFields) + @action(methods=['post'], detail=True, serializer_class=AnotherSerializerWithListFields) def custom_action_with_list_fields(self, request, pk): """ A custom action using both list field and list serializer in the serializer. """ return super(ExampleSerializer, self).retrieve(self, request) - @list_route() + @action(detail=False) def custom_list_action(self, request): return super(ExampleViewSet, self).list(self, request) - @list_route(methods=['post', 'get'], serializer_class=EmptySerializer) + @action(methods=['post', 'get'], detail=False, serializer_class=EmptySerializer) def custom_list_action_multiple_methods(self, request): return super(ExampleViewSet, self).list(self, request) @@ -865,11 +863,11 @@ class NamingCollisionViewSet(GenericViewSet): """ permision_class = () - @list_route() + @action(detail=False) def detail(self, request): return {} - @list_route(url_path='detail/export') + @action(detail=False, url_path='detail/export') def detail_export(self, request): return {} @@ -949,7 +947,10 @@ def test_from_router(self): generator = SchemaGenerator(title='Naming Colisions', patterns=patterns) schema = generator.get_schema() - desc = schema['detail_0'].description # not important here + + # not important here + desc_0 = schema['detail']['detail_export'].description + desc_1 = schema['detail_0'].description expected = coreapi.Document( url='', @@ -959,12 +960,12 @@ def test_from_router(self): 'detail_export': coreapi.Link( url='/from-routercollision/detail/export/', action='http://github.com/get', - description=desc) + description=desc_0) }, 'detail_0': coreapi.Link( url='/from-routercollision/detail/', action='http://github.com/get', - description=desc + description=desc_1 ) } ) @@ -1046,7 +1047,7 @@ def options(self, request, *args, **kwargs): class AViewSet(ModelViewSet): - @detail_route(methods=['options', 'get']) + @action(methods=['options', 'get'], detail=True) def custom_action(self, request, pk): pass diff --git a/tests/test_viewsets.py b/tests/test_viewsets.py index beff42cb89..25feb0f372 100644 --- a/tests/test_viewsets.py +++ b/tests/test_viewsets.py @@ -3,7 +3,7 @@ from django.test import TestCase, override_settings from rest_framework import status -from rest_framework.decorators import detail_route, list_route +from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.routers import SimpleRouter from rest_framework.test import APIRequestFactory @@ -39,19 +39,19 @@ def list(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs): pass - @list_route() + @action(detail=False) def list_action(self, request, *args, **kwargs): pass - @list_route(url_name='list-custom') + @action(detail=False, url_name='list-custom') def custom_list_action(self, request, *args, **kwargs): pass - @detail_route() + @action(detail=True) def detail_action(self, request, *args, **kwargs): pass - @detail_route(url_name='detail-custom') + @action(detail=True, url_name='detail-custom') def custom_detail_action(self, request, *args, **kwargs): pass @@ -111,6 +111,16 @@ def test_args_kwargs_request_action_map_on_self(self): self.assertIn(attribute, dir(view)) +class GetExtraActionTests(TestCase): + + def test_extra_actions(self): + view = ActionViewSet() + actual = [action.__name__ for action in view.get_extra_actions()] + expected = ['custom_detail_action', 'custom_list_action', 'detail_action', 'list_action'] + + self.assertEqual(actual, expected) + + @override_settings(ROOT_URLCONF='tests.test_viewsets') class ReverseActionTests(TestCase): def test_default_basename(self): From 16645885007cf02b9085766185764ab4f95c8142 Mon Sep 17 00:00:00 2001 From: Aseem Shrey Date: Sun, 21 Jan 2018 20:22:21 +0530 Subject: [PATCH 0038/1262] Updated docs to use `pip show` Show the current DRF version using `pip show` Closes #5757 --- docs/topics/release-notes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 2c45b70afd..820fa731b3 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -32,9 +32,9 @@ To upgrade Django REST framework to the latest version, use pip: pip install -U djangorestframework -You can determine your currently installed version using `pip freeze`: +You can determine your currently installed version using `pip show`: - pip freeze | grep djangorestframework + pip show djangorestframework --- From 052a20cd7b9a8410a147d4ef2ab5291ab0eb1d04 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Thu, 25 Jan 2018 21:43:55 -0800 Subject: [PATCH 0039/1262] Load 'static' instead of 'staticfiles' in templates (#5773) --- docs/topics/api-clients.md | 2 +- rest_framework/templates/rest_framework/admin.html | 2 +- rest_framework/templates/rest_framework/base.html | 2 +- rest_framework/templates/rest_framework/docs/error.html | 2 +- rest_framework/templates/rest_framework/docs/index.html | 2 +- .../templates/rest_framework/docs/langs/javascript-intro.html | 2 +- rest_framework/templates/rest_framework/login_base.html | 1 - 7 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/topics/api-clients.md b/docs/topics/api-clients.md index 019b48b3dc..1de669b42c 100644 --- a/docs/topics/api-clients.md +++ b/docs/topics/api-clients.md @@ -395,7 +395,7 @@ Once the API documentation URLs are installed, you'll be able to include both th /static/rest_framework/js/coreapi-0.1.1.js /docs/schema.js --> - {% load staticfiles %} + {% load static %} diff --git a/rest_framework/templates/rest_framework/admin.html b/rest_framework/templates/rest_framework/admin.html index de011cd09a..0f588c2d73 100644 --- a/rest_framework/templates/rest_framework/admin.html +++ b/rest_framework/templates/rest_framework/admin.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} {% load i18n %} {% load rest_framework %} diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index 14007aa52e..5d4d258bef 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} {% load i18n %} {% load rest_framework %} diff --git a/rest_framework/templates/rest_framework/docs/error.html b/rest_framework/templates/rest_framework/docs/error.html index 8015bd7f02..1155e7ece5 100644 --- a/rest_framework/templates/rest_framework/docs/error.html +++ b/rest_framework/templates/rest_framework/docs/error.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} diff --git a/rest_framework/templates/rest_framework/docs/index.html b/rest_framework/templates/rest_framework/docs/index.html index de704ab518..8149103448 100644 --- a/rest_framework/templates/rest_framework/docs/index.html +++ b/rest_framework/templates/rest_framework/docs/index.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} diff --git a/rest_framework/templates/rest_framework/docs/langs/javascript-intro.html b/rest_framework/templates/rest_framework/docs/langs/javascript-intro.html index 8ef880cbc8..a6938fc45d 100644 --- a/rest_framework/templates/rest_framework/docs/langs/javascript-intro.html +++ b/rest_framework/templates/rest_framework/docs/langs/javascript-intro.html @@ -1,5 +1,5 @@ {% load rest_framework %} -{% load staticfiles %} +{% load static %}
    {% code html %}
     
     {% endcode %}
    diff --git a/rest_framework/templates/rest_framework/login_base.html b/rest_framework/templates/rest_framework/login_base.html index 631ad71bad..ba48917088 100644 --- a/rest_framework/templates/rest_framework/login_base.html +++ b/rest_framework/templates/rest_framework/login_base.html @@ -1,5 +1,4 @@ {% extends "rest_framework/base.html" %} -{% load staticfiles %} {% load rest_framework %} {% block body %} From 3e5d3752e77af14acc009743c8648d06f09246e6 Mon Sep 17 00:00:00 2001 From: Max Goodridge Date: Mon, 29 Jan 2018 07:41:55 +0000 Subject: [PATCH 0040/1262] Fixed a typo (#5783) --- 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 403c2b8655..78fb3f83eb 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -801,7 +801,7 @@ The [drf-compound-fields][drf-compound-fields] package provides "compound" seria The [drf-extra-fields][drf-extra-fields] package provides extra serializer fields for REST framework, including `Base64ImageField` and `PointField` classes. -## djangrestframework-recursive +## djangorestframework-recursive the [djangorestframework-recursive][djangorestframework-recursive] package provides a `RecursiveField` for serializing and deserializing recursive structures From 2677f59d5d1cb1b0170eabb516478180da8aa610 Mon Sep 17 00:00:00 2001 From: Matt Prahl Date: Mon, 29 Jan 2018 09:33:14 -0500 Subject: [PATCH 0041/1262] Refer to "NamespaceVersioning" instead of "NamespacedVersioning" in the documentation (#5754) --- docs/api-guide/versioning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/versioning.md b/docs/api-guide/versioning.md index 8e62c70f17..c106e536d2 100644 --- a/docs/api-guide/versioning.md +++ b/docs/api-guide/versioning.md @@ -37,7 +37,7 @@ The `reverse` function included by REST framework ties in with the versioning sc The above function will apply any URL transformations appropriate to the request version. For example: -* If `NamespacedVersioning` was being used, and the API version was 'v1', then the URL lookup used would be `'v1:bookings-list'`, which might resolve to a URL like `http://example.org/v1/bookings/`. +* If `NamespaceVersioning` was being used, and the API version was 'v1', then the URL lookup used would be `'v1:bookings-list'`, which might resolve to a URL like `http://example.org/v1/bookings/`. * If `QueryParameterVersioning` was being used, and the API version was `1.0`, then the returned URL might be something like `http://example.org/bookings/?version=1.0` #### Versioned APIs and hyperlinked serializers From 769bc1336fd5d6a7fcf10d8be3b374c3e7a21bb3 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 30 Jan 2018 08:45:09 +0100 Subject: [PATCH 0042/1262] ErrorDetail: add __eq__/__ne__ and __repr__ (#5787) This adds `__eq__` to handle `code` in comparisons. When comparing an ErrorDetail to a string (missing `code` there) the ErrorDetail's `code` is ignored, but otherwise it is taken into account. --- rest_framework/exceptions.py | 17 +++++++++++++++++ tests/test_exceptions.py | 27 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index d885ba6432..492872ae5b 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -14,6 +14,7 @@ from django.utils.translation import ungettext from rest_framework import status +from rest_framework.compat import unicode_to_repr from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList @@ -73,6 +74,22 @@ def __new__(cls, string, code=None): self.code = code return self + def __eq__(self, other): + r = super(ErrorDetail, self).__eq__(other) + try: + return r and self.code == other.code + except AttributeError: + return r + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return unicode_to_repr('ErrorDetail(string=%r, code=%r)' % ( + six.text_type(self), + self.code, + )) + class APIException(Exception): """ diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 176aeb1746..006191a491 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -53,6 +53,33 @@ def test_get_full_details_with_throttling(self): 'code': 'throttled'} +class ErrorDetailTests(TestCase): + + def test_eq(self): + assert ErrorDetail('msg') == ErrorDetail('msg') + assert ErrorDetail('msg', 'code') == ErrorDetail('msg', code='code') + + assert ErrorDetail('msg') == 'msg' + assert ErrorDetail('msg', 'code') == 'msg' + + def test_ne(self): + assert ErrorDetail('msg1') != ErrorDetail('msg2') + assert ErrorDetail('msg') != ErrorDetail('msg', code='invalid') + + assert ErrorDetail('msg1') != 'msg2' + assert ErrorDetail('msg1', 'code') != 'msg2' + + def test_repr(self): + assert repr(ErrorDetail('msg1')) == \ + 'ErrorDetail(string={!r}, code=None)'.format('msg1') + assert repr(ErrorDetail('msg1', 'code')) == \ + 'ErrorDetail(string={!r}, code={!r})'.format('msg1', 'code') + + def test_str(self): + assert str(ErrorDetail('msg1')) == 'msg1' + assert str(ErrorDetail('msg1', 'code')) == 'msg1' + + class TranslationTests(TestCase): @translation.override('fr') From ece4171ae40e0956c9d69e317fee1f96c427b113 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 30 Jan 2018 03:08:06 -0500 Subject: [PATCH 0043/1262] Replace `background-attachment: fixed` in docs (#5777) Fixed backgrounds have performance issues on large displays. --- docs_theme/css/default.css | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/docs_theme/css/default.css b/docs_theme/css/default.css index a0a286b226..bb17a3a115 100644 --- a/docs_theme/css/default.css +++ b/docs_theme/css/default.css @@ -160,9 +160,19 @@ body, .navbar .navbar-inner .container-fluid{ margin: 0 auto; } -body{ - background: url("http://github.com/img/grid.png") repeat-x; - background-attachment: fixed; +/* Replacement for `body { background-attachment: fixed; }`, which + has performance issues when scrolling on large displays. */ +body::before { + content: ' '; + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; + background-color: #f8f8f8; + background: url(http://github.com/img/grid.png) repeat-x; + will-change: transform; + z-index: -1; } From df77f7bb9d40f83d92848fd0afd5c61e281eeb48 Mon Sep 17 00:00:00 2001 From: Si Feng Date: Tue, 30 Jan 2018 14:10:02 -0800 Subject: [PATCH 0044/1262] Make 404 & 403 responses consistent with `exceptions.APIException` output (#5763) --- rest_framework/views.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/rest_framework/views.py b/rest_framework/views.py index f9ee7fb539..1f51517db3 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -8,10 +8,8 @@ from django.db import connection, models, transaction from django.http import Http404 from django.http.response import HttpResponseBase -from django.utils import six from django.utils.cache import cc_delim_re, patch_vary_headers from django.utils.encoding import smart_text -from django.utils.translation import ugettext_lazy as _ from django.views.decorators.csrf import csrf_exempt from django.views.generic import View @@ -70,6 +68,11 @@ def exception_handler(exc, context): Any unhandled exceptions may return `None`, which will cause a 500 error to be raised. """ + if isinstance(exc, Http404): + exc = exceptions.NotFound() + elif isinstance(exc, PermissionDenied): + exc = exceptions.PermissionDenied() + if isinstance(exc, exceptions.APIException): headers = {} if getattr(exc, 'auth_header', None): @@ -85,20 +88,6 @@ def exception_handler(exc, context): set_rollback() return Response(data, status=exc.status_code, headers=headers) - elif isinstance(exc, Http404): - msg = _('Not found.') - data = {'detail': six.text_type(msg)} - - set_rollback() - return Response(data, status=status.HTTP_404_NOT_FOUND) - - elif isinstance(exc, PermissionDenied): - msg = _('Permission denied.') - data = {'detail': six.text_type(msg)} - - set_rollback() - return Response(data, status=status.HTTP_403_FORBIDDEN) - return None From 2fa04caf7c2482f7e8bbf69052ad48af304da9a2 Mon Sep 17 00:00:00 2001 From: Veli-Matti Helke Date: Wed, 31 Jan 2018 15:25:57 +0200 Subject: [PATCH 0045/1262] small fix to API documentation: schemas (#5796) adding missing parameters to get_manual_fields() --- docs/api-guide/schemas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 3284c9e1b0..eb27998597 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -631,7 +631,7 @@ def get_manual_fields(self, path, method): if method=='POST': extra_fields = # ... list of extra fields for POST ... - manual_fields = super().get_manual_fields() + manual_fields = super().get_manual_fields(path, method) return manual_fields + extra_fields ``` From 3c7b3ac6df1deadee57ec9c01ffd58d804559389 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Thu, 1 Feb 2018 16:02:29 +0100 Subject: [PATCH 0046/1262] Updated step 1 of contributing guide (#5799) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create a fork, then clone it. * Link to GitHub’s How-To. --- docs/topics/contributing.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/topics/contributing.md b/docs/topics/contributing.md index 19976b00b7..9cc6ccee08 100644 --- a/docs/topics/contributing.md +++ b/docs/topics/contributing.md @@ -48,9 +48,15 @@ Getting involved in triaging incoming issues is a good way to start contributing # Development -To start developing on Django REST framework, clone the repo: +To start developing on Django REST framework, first create a Fork from the +[Django REST Framework repo][repo] on GitHub. - git clone git@github.com:encode/django-rest-framework.git +Then clone your fork. The clone command will look like this, with your GitHub +username instead of YOUR-USERNAME: + + git clone https://github.com/YOUR-USERNAME/Spoon-Knife + +See GitHub's [_Fork a Repo_][how-to-fork] Guide for more help. Changes should broadly follow the [PEP 8][pep-8] style conventions, and we recommend you set up your editor to automatically indicate non-conforming styles. @@ -210,3 +216,5 @@ If you want to draw attention to a note or warning, use a pair of enclosing line [markdown]: https://daringfireball.net/projects/markdown/basics [docs]: https://github.com/encode/django-rest-framework/tree/master/docs [mou]: http://mouapp.com/ +[repo]: https://github.com/encode/django-rest-framework +[how-to-fork]: https://help.github.com/articles/fork-a-repo/ From 27f32faee4dd96c5baee920d3a8f42ab4364782b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jeremy=20Lain=C3=A9?= Date: Thu, 1 Feb 2018 16:14:35 +0100 Subject: [PATCH 0047/1262] Fix schema generation for PrimaryKeyRelatedField (#5764) By default all subclasses of RelatedField are output as string fields in the schema, which works well for StringRelatedField, SlugRelatedField or HyperlinkedRelatedField. Handle the common case of a PrimaryKeyRelatedField pointing to an AutoField. --- rest_framework/schemas/inspectors.py | 8 +++++ tests/test_schemas.py | 47 +++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index 413b9c0ac8..8ef783683d 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -50,6 +50,14 @@ def field_to_schema(field): title=title, description=description ) + elif isinstance(field, serializers.PrimaryKeyRelatedField): + schema_cls = coreschema.String + model = getattr(field.queryset, 'model', None) + if model is not None: + model_field = model._meta.pk + if isinstance(model_field, models.AutoField): + schema_cls = coreschema.Integer + return schema_cls(title=title, description=description) elif isinstance(field, serializers.RelatedField): return coreschema.String(title=title, description=description) elif isinstance(field, serializers.MultipleChoiceField): diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 1cbee0695c..1abe5d2a16 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -23,7 +23,7 @@ from rest_framework.views import APIView from rest_framework.viewsets import GenericViewSet, ModelViewSet -from .models import BasicModel +from .models import BasicModel, ForeignKeySource factory = APIRequestFactory() @@ -556,6 +556,51 @@ def test_schema_for_regular_views(self): assert schema == expected +class ForeignKeySourceSerializer(serializers.ModelSerializer): + class Meta: + model = ForeignKeySource + fields = ('id', 'name', 'target') + + +class ForeignKeySourceView(generics.CreateAPIView): + queryset = ForeignKeySource.objects.all() + serializer_class = ForeignKeySourceSerializer + + +@unittest.skipUnless(coreapi, 'coreapi is not installed') +class TestSchemaGeneratorWithForeignKey(TestCase): + def setUp(self): + self.patterns = [ + url(http://github.com/r'%5Eexample/?$%27,%20ForeignKeySourceView.as_view()), + ] + + def test_schema_for_regular_views(self): + """ + Ensure that AutoField foreign keys are output as Integer. + """ + generator = SchemaGenerator(title='Example API', patterns=self.patterns) + schema = generator.get_schema() + + expected = coreapi.Document( + url='', + title='Example API', + content={ + 'example': { + 'create': coreapi.Link( + url='/example/', + action='http://github.com/post', + encoding='application/json', + fields=[ + coreapi.Field('name', required=True, location='form', schema=coreschema.String(title='Name')), + coreapi.Field('target', required=True, location='form', schema=coreschema.Integer(description='Target', title='Target')), + ] + ) + } + } + ) + assert schema == expected + + @unittest.skipUnless(coreapi, 'coreapi is not installed') class Test4605Regression(TestCase): def test_4605_regression(self): From a8d129b7da847ab12a1ecaeefa54c0d6d330184e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jeremy=20Lain=C3=A9?= Date: Tue, 23 Jan 2018 21:06:24 +0100 Subject: [PATCH 0048/1262] Represent serializer DictField as an Object in schema DictFields were incorrectly being output as String in the schema. This pull request outputs an Object instead and adds a unit test. Update s/detail_route/action/ after rebase --- rest_framework/schemas/inspectors.py | 5 +++++ tests/test_schemas.py | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index 8ef783683d..86c6cb71a9 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -34,6 +34,11 @@ def field_to_schema(field): title=title, description=description ) + elif isinstance(field, serializers.DictField): + return coreschema.Object( + title=title, + description=description + ) elif isinstance(field, serializers.Serializer): return coreschema.Object( properties=OrderedDict([ diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 1abe5d2a16..267c445850 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -49,6 +49,10 @@ class ExampleSerializer(serializers.Serializer): hidden = serializers.HiddenField(default='hello') +class AnotherSerializerWithDictField(serializers.Serializer): + a = serializers.DictField() + + class AnotherSerializerWithListFields(serializers.Serializer): a = serializers.ListField(child=serializers.IntegerField()) b = serializers.ListSerializer(child=serializers.CharField()) @@ -72,6 +76,13 @@ def custom_action(self, request, pk): """ return super(ExampleSerializer, self).retrieve(self, request) + @action(methods=['post'], detail=True, serializer_class=AnotherSerializerWithDictField) + def custom_action_with_dict_field(self, request, pk): + """ + A custom action using a dict field in the serializer. + """ + return super(ExampleSerializer, self).retrieve(self, request) + @action(methods=['post'], detail=True, serializer_class=AnotherSerializerWithListFields) def custom_action_with_list_fields(self, request, pk): """ @@ -198,6 +209,16 @@ def test_authenticated_request(self): coreapi.Field('d', required=False, location='form', schema=coreschema.String(title='D')), ] ), + 'custom_action_with_dict_field': coreapi.Link( + url='/example/{id}/custom_action_with_dict_field/', + action='http://github.com/post', + encoding='application/json', + description='A custom action using a dict field in the serializer.', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()), + coreapi.Field('a', required=True, location='form', schema=coreschema.Object(title='A')), + ] + ), 'custom_action_with_list_fields': coreapi.Link( url='/example/{id}/custom_action_with_list_fields/', action='http://github.com/post', From 878fe895dcd35297be798083a704ee74dd8709ba Mon Sep 17 00:00:00 2001 From: "Fraire, Santiago" Date: Fri, 5 Jan 2018 13:57:52 +0100 Subject: [PATCH 0049/1262] Docs: Added example reimplementing ObtainAuthToken Closes #5802 --- docs/api-guide/authentication.md | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 0704118bdc..e33023c40d 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -205,11 +205,39 @@ The `obtain_auth_token` view will return a JSON response when valid `username` a { 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' } -Note that the default `obtain_auth_token` view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. If you need a customized version of the `obtain_auth_token` view, you can do so by overriding the `ObtainAuthToken` view class, and using that in your url conf instead. +Note that the default `obtain_auth_token` view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. By default there are no permissions or throttling applied to the `obtain_auth_token` view. If you do wish to apply throttling you'll need to override the view class, and include them using the `throttle_classes` attribute. +If you need a customized version of the `obtain_auth_token` view, you can do so by subclassing the `ObtainAuthToken` view class, and using that in your url conf instead. + +For example, you may return additional user information beyond the `token` value: + + from rest_framework.authtoken.views import ObtainAuthToken + from rest_framework.authtoken.models import Token + from rest_framework.response import Response + + class CustomAuthToken(ObtainAuthToken): + + def post(self, request, *args, **kwargs): + serializer = self.serializer_class(data=request.data, + context={'request': request}) + serializer.is_valid(raise_exception=True) + user = serializer.validated_data['user'] + token, created = Token.objects.get_or_create(user=user) + return Response({ + 'token': token.key, + 'user_id': user.pk, + 'email': user.email + }) + +And in your `urls.py`: + + urlpatterns += [ + url(http://github.com/r'%5Eapi-token-auth/',%20CustomAuthToken.as_view()) + ] + ##### With Django admin From 0d5a3a00b0465a27ecfa6947478cbd57ac2432f2 Mon Sep 17 00:00:00 2001 From: Paulo Scardine Date: Sun, 17 Dec 2017 02:11:15 -0200 Subject: [PATCH 0050/1262] Add schema to ObtainAuthToken Add encoding parameter to ManualSchema Closes #5676 * Fixed lint errors * Added docs for ManualSchema encoding parameter --- docs/api-guide/schemas.md | 2 ++ rest_framework/authtoken/views.py | 26 ++++++++++++++++++++++++++ rest_framework/schemas/inspectors.py | 5 +++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index eb27998597..aaefe3db8f 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -669,6 +669,8 @@ The `ManualSchema` constructor takes two arguments: **`description`**: A string description. Optional. +**`encoding`**: Default `None`. A string encoding, e.g `application/json`. Optional. + --- ## Core API diff --git a/rest_framework/authtoken/views.py b/rest_framework/authtoken/views.py index 6254d2f7f9..6eed3782eb 100644 --- a/rest_framework/authtoken/views.py +++ b/rest_framework/authtoken/views.py @@ -3,6 +3,9 @@ from rest_framework.authtoken.serializers import AuthTokenSerializer from rest_framework.response import Response from rest_framework.views import APIView +from rest_framework.schemas import ManualSchema +import coreapi +import coreschema class ObtainAuthToken(APIView): @@ -11,6 +14,29 @@ class ObtainAuthToken(APIView): parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,) renderer_classes = (renderers.JSONRenderer,) serializer_class = AuthTokenSerializer + schema = ManualSchema( + fields=[ + coreapi.Field( + name="username", + required=True, + location='form', + schema=coreschema.String( + title="Username", + description="Valid username for authentication", + ), + ), + coreapi.Field( + name="password", + required=True, + location='form', + schema=coreschema.String( + title="Password", + description="Valid password for authentication", + ), + ), + ], + encoding="application/json", + ) def post(self, request, *args, **kwargs): serializer = self.serializer_class(data=request.data, diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index 86c6cb71a9..171b88b0b3 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -445,7 +445,7 @@ class ManualSchema(ViewInspector): Allows providing a list of coreapi.Fields, plus an optional description. """ - def __init__(self, fields, description=''): + def __init__(self, fields, description='', encoding=None): """ Parameters: @@ -455,6 +455,7 @@ def __init__(self, fields, description=''): assert all(isinstance(f, coreapi.Field) for f in fields), "`fields` must be a list of coreapi.Field instances" self._fields = fields self._description = description + self._encoding = encoding def get_link(self, path, method, base_url): @@ -464,7 +465,7 @@ def get_link(self, path, method, base_url): return coreapi.Link( url=urlparse.urljoin(base_url, path), action=method.lower(), - encoding=None, + encoding=self._encoding, fields=self._fields, description=self._description ) From c456b3c510870ed8e0117cb69abc2360a7aa0fca Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 5 Feb 2018 10:24:13 -0500 Subject: [PATCH 0051/1262] Fix request formdata handling (#5800) * Rename 'wsgi' request test to more accurate 'http' * Test duplicate request stream parsing * Fix setting post/files on the underlying request --- rest_framework/request.py | 9 +++--- tests/test_request.py | 63 ++++++++++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/rest_framework/request.py b/rest_framework/request.py index 7e4daf2749..9d4f73d30e 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -278,10 +278,11 @@ def _load_data_and_files(self): else: self._full_data = self._data - # copy data & files refs to the underlying request so that closable - # objects are handled appropriately. - self._request._post = self.POST - self._request._files = self.FILES + # if a form media type, copy data & files refs to the underlying + # http request so that closable objects are handled appropriately. + if is_form_media_type(self.content_type): + self._request._post = self.POST + self._request._files = self.FILES def _load_stream(self): """ diff --git a/tests/test_request.py b/tests/test_request.py index 8c680baa0e..83d295a128 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -13,6 +13,7 @@ from django.contrib.auth.models import User 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.utils import six @@ -137,6 +138,11 @@ def post(self, request): return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR) +class EchoView(APIView): + def post(self, request): + return Response(status=status.HTTP_200_OK, data=request.data) + + class FileUploadView(APIView): def post(self, request): filenames = [file.temporary_file_path() for file in request.FILES.values()] @@ -149,6 +155,7 @@ def post(self, request): urlpatterns = [ url(http://github.com/r'%5E),%20MockView.as_view()), + url(http://github.com/r'%5Eecho/),%20EchoView.as_view()), url(http://github.com/r'%5Eupload/),%20FileUploadView.as_view()) ] @@ -271,24 +278,64 @@ def test_default_secure_true(self): assert request.scheme == 'https' -class TestWSGIRequestProxy(TestCase): - def test_attribute_access(self): - wsgi_request = factory.get('/') - request = Request(wsgi_request) +class TestHttpRequest(TestCase): + def test_attribute_access_proxy(self): + http_request = factory.get('/') + request = Request(http_request) inner_sentinel = object() - wsgi_request.inner_property = inner_sentinel + http_request.inner_property = inner_sentinel assert request.inner_property is inner_sentinel outer_sentinel = object() request.inner_property = outer_sentinel assert request.inner_property is outer_sentinel - def test_exception(self): + def test_exception_proxy(self): # ensure the exception message is not for the underlying WSGIRequest - wsgi_request = factory.get('/') - request = Request(wsgi_request) + http_request = factory.get('/') + request = Request(http_request) message = "'Request' object has no attribute 'inner_property'" with self.assertRaisesMessage(AttributeError, message): request.inner_property + + @override_settings(ROOT_URLCONF='tests.test_request') + def test_duplicate_request_stream_parsing_exception(self): + """ + Check assumption that duplicate stream parsing will result in a + `RawPostDataException` being raised. + """ + response = APIClient().post('/echo/', data={'a': 'b'}, format='json') + request = response.renderer_context['request'] + + # ensure that request stream was consumed by json parser + assert request.content_type.startswith('application/json') + assert response.data == {'a': 'b'} + + # pass same HttpRequest to view, stream already consumed + with pytest.raises(RawPostDataException): + EchoView.as_view()(request._request) + + @override_settings(ROOT_URLCONF='tests.test_request') + def test_duplicate_request_form_data_access(self): + """ + Form data is copied to the underlying django request for middleware + and file closing reasons. Duplicate processing of a request with form + data is 'safe' in so far as accessing `request.POST` does not trigger + the duplicate stream parse exception. + """ + response = APIClient().post('/echo/', data={'a': 'b'}) + request = response.renderer_context['request'] + + # ensure that request stream was consumed by form parser + assert request.content_type.startswith('multipart/form-data') + assert response.data == {'a': ['b']} + + # pass same HttpRequest to view, form data set on underlying request + response = EchoView.as_view()(request._request) + request = response.renderer_context['request'] + + # ensure that request stream was consumed by form parser + assert request.content_type.startswith('multipart/form-data') + assert response.data == {'a': ['b']} From 1bc826e6fd1c5fc70a3f9af2e89bb0a575e6ce7b Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 7 Feb 2018 14:46:17 -0500 Subject: [PATCH 0052/1262] Fix authtoken views imports (#5818) --- rest_framework/authtoken/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rest_framework/authtoken/views.py b/rest_framework/authtoken/views.py index 6eed3782eb..ff53c01baf 100644 --- a/rest_framework/authtoken/views.py +++ b/rest_framework/authtoken/views.py @@ -1,11 +1,11 @@ +import coreapi +import coreschema from rest_framework import parsers, renderers from rest_framework.authtoken.models import Token from rest_framework.authtoken.serializers import AuthTokenSerializer from rest_framework.response import Response -from rest_framework.views import APIView from rest_framework.schemas import ManualSchema -import coreapi -import coreschema +from rest_framework.views import APIView class ObtainAuthToken(APIView): From 1438719979c92e9c0895de6f29d557df774eccb3 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Thu, 8 Feb 2018 09:00:44 +0100 Subject: [PATCH 0053/1262] requirements-testing: update pytest to 3.4.0 (#5815) --- requirements/requirements-testing.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/requirements-testing.txt b/requirements/requirements-testing.txt index 72ce56d26a..e0db14d44a 100644 --- a/requirements/requirements-testing.txt +++ b/requirements/requirements-testing.txt @@ -1,4 +1,4 @@ -# PyTest for running the tests. -pytest==3.2.5 +# Pytest for running the tests. +pytest==3.4.0 pytest-django==3.1.2 pytest-cov==2.5.1 From d1c92c81ff86fb8c6e9ccf2b394852595ff52141 Mon Sep 17 00:00:00 2001 From: Allisson Azevedo Date: Thu, 8 Feb 2018 05:04:51 -0300 Subject: [PATCH 0054/1262] Add Django Rest Framework Role Filters to Third party packages (#5809) --- docs/api-guide/permissions.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 72cbeab916..f5fc214cd6 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -269,6 +269,10 @@ The [Django Rest Framework Roles][django-rest-framework-roles] package makes it The [Django Rest Framework API Key][django-rest-framework-api-key] package allows you to ensure that every request made to the server requires an API key header. You can generate one from the django admin interface. +## Django Rest Framework Role Filters + +The [Django Rest Framework Role Filters][django-rest-framework-role-filters] package provides simple filtering over multiple types of roles. + [cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html [authentication]: authentication.md [throttling]: throttling.md @@ -282,3 +286,4 @@ The [Django Rest Framework API Key][django-rest-framework-api-key] package allow [dry-rest-permissions]: https://github.com/Helioscene/dry-rest-permissions [django-rest-framework-roles]: https://github.com/computer-lab/django-rest-framework-roles [django-rest-framework-api-key]: https://github.com/manosim/django-rest-framework-api-key +[django-rest-framework-role-filters]: https://github.com/allisson/django-rest-framework-role-filters From 7d0d22ffaabfcfa57f49fcf086667faf7c2509c5 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Mon, 12 Feb 2018 15:14:44 +0100 Subject: [PATCH 0055/1262] Use single copy of static assets. Update jQuery (#5823) * Move font-awesome to top level. * Use top-level jQuery & Bootstrap * Update to jQuery v3.3.1 Compatible with Bootstrap v3.3.7 c.f. https://github.com/twbs/bootstrap/issues/16834#issuecomment-251996660 * Re-add bootstrap-theme --- .../{docs => }/css/bootstrap-theme.min.css | 0 .../{docs => }/css/font-awesome-4.0.3.css | 0 .../rest_framework/docs/css/bootstrap.min.css | 6 - .../fonts/glyphicons-halflings-regular.eot | Bin 20127 -> 0 bytes .../fonts/glyphicons-halflings-regular.svg | 288 ------------------ .../fonts/glyphicons-halflings-regular.ttf | Bin 45404 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 23424 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff2 | Bin 18028 -> 0 bytes .../rest_framework/docs/js/bootstrap.min.js | 7 - .../docs/js/jquery-1.10.2.min.js | 6 - .../{docs => }/fonts/fontawesome-webfont.eot | Bin .../{docs => }/fonts/fontawesome-webfont.svg | 0 .../{docs => }/fonts/fontawesome-webfont.ttf | Bin .../{docs => }/fonts/fontawesome-webfont.woff | Bin .../rest_framework/js/jquery-1.12.4.min.js | 5 - .../rest_framework/js/jquery-3.3.1.min.js | 2 + .../templates/rest_framework/admin.html | 2 +- .../templates/rest_framework/base.html | 2 +- .../templates/rest_framework/docs/error.html | 2 +- .../templates/rest_framework/docs/index.html | 10 +- 20 files changed, 10 insertions(+), 320 deletions(-) rename rest_framework/static/rest_framework/{docs => }/css/bootstrap-theme.min.css (100%) rename rest_framework/static/rest_framework/{docs => }/css/font-awesome-4.0.3.css (100%) delete mode 100644 rest_framework/static/rest_framework/docs/css/bootstrap.min.css delete mode 100644 rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.eot delete mode 100644 rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.svg delete mode 100644 rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.ttf delete mode 100644 rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.woff delete mode 100644 rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.woff2 delete mode 100644 rest_framework/static/rest_framework/docs/js/bootstrap.min.js delete mode 100644 rest_framework/static/rest_framework/docs/js/jquery-1.10.2.min.js rename rest_framework/static/rest_framework/{docs => }/fonts/fontawesome-webfont.eot (100%) rename rest_framework/static/rest_framework/{docs => }/fonts/fontawesome-webfont.svg (100%) rename rest_framework/static/rest_framework/{docs => }/fonts/fontawesome-webfont.ttf (100%) rename rest_framework/static/rest_framework/{docs => }/fonts/fontawesome-webfont.woff (100%) delete mode 100644 rest_framework/static/rest_framework/js/jquery-1.12.4.min.js create mode 100644 rest_framework/static/rest_framework/js/jquery-3.3.1.min.js diff --git a/rest_framework/static/rest_framework/docs/css/bootstrap-theme.min.css b/rest_framework/static/rest_framework/css/bootstrap-theme.min.css similarity index 100% rename from rest_framework/static/rest_framework/docs/css/bootstrap-theme.min.css rename to rest_framework/static/rest_framework/css/bootstrap-theme.min.css diff --git a/rest_framework/static/rest_framework/docs/css/font-awesome-4.0.3.css b/rest_framework/static/rest_framework/css/font-awesome-4.0.3.css similarity index 100% rename from rest_framework/static/rest_framework/docs/css/font-awesome-4.0.3.css rename to rest_framework/static/rest_framework/css/font-awesome-4.0.3.css diff --git a/rest_framework/static/rest_framework/docs/css/bootstrap.min.css b/rest_framework/static/rest_framework/docs/css/bootstrap.min.css deleted file mode 100644 index 82113bd8a0..0000000000 --- a/rest_framework/static/rest_framework/docs/css/bootstrap.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v3.3.7 (http://getbootstrap.com) - * Copyright 2011-2016 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(http://github.com/fonts/glyphicons-halflings-regular.eot);src:url(http://github.com/fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(http://github.com/fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(http://github.com/fonts/glyphicons-halflings-regular.woff) format('woff'),url(http://github.com/fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(http://github.com/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} -/*# sourceMappingURL=bootstrap.min.css.map */ diff --git a/rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.eot b/rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.eot deleted file mode 100644 index b93a4953fff68df523aa7656497ee339d6026d64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20127 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.ttf b/rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.ttf deleted file mode 100644 index 1413fc609ab6f21774de0cb7e01360095584f65b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45404 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 G`u_lkUM?>H diff --git a/rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.woff2 b/rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.woff2 deleted file mode 100644 index 64539b54c3751a6d9adb44c8e3a45ba5a73b77f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18028 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 diff --git a/rest_framework/static/rest_framework/docs/js/bootstrap.min.js b/rest_framework/static/rest_framework/docs/js/bootstrap.min.js deleted file mode 100644 index be9574d70b..0000000000 --- a/rest_framework/static/rest_framework/docs/js/bootstrap.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v3.3.7 (http://getbootstrap.com) - * Copyright 2011-2016 Twitter, Inc. - * Licensed under the MIT license - */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
    ',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); diff --git a/rest_framework/static/rest_framework/docs/js/jquery-1.10.2.min.js b/rest_framework/static/rest_framework/docs/js/jquery-1.10.2.min.js deleted file mode 100644 index da4170647d..0000000000 --- a/rest_framework/static/rest_framework/docs/js/jquery-1.10.2.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license -//@ sourceMappingURL=jquery-1.10.2.min.map -*/ -(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="
    ",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
    a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
    t
    ",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
    ",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t -}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); -u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x(" - - + + diff --git a/docs_theme/css/default.css b/docs_theme/css/default.css index 992bc60a42..7006f2a668 100644 --- a/docs_theme/css/default.css +++ b/docs_theme/css/default.css @@ -37,7 +37,7 @@ body.index-page #main-content iframe.github-star-button { margin-right: -15px; } -/* Travis CI and PyPI badge */ +/* CI and PyPI badge */ body.index-page #main-content img.status-badge { float: right; margin-right: 8px; From f628db383a1cb47e7910ac2547d4dd53f0902211 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Fri, 16 Apr 2021 17:23:18 +0100 Subject: [PATCH 0769/1262] Fix GitHub Actions to run on 'master' branch (#7926) The config I copied in #7903 was from a repo with the new name 'main', so tests have not been running on master since. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6686ce7593..1c9e49e348 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,7 +3,7 @@ name: CI on: push: branches: - - main + - master pull_request: jobs: From a0a2c5cb370ff80a95deaa8d23f099acc4e5e0c5 Mon Sep 17 00:00:00 2001 From: Terence Honles Date: Fri, 16 Apr 2021 09:27:22 -0700 Subject: [PATCH 0770/1262] Fix tests with mock timezone (#7911) After django/django#13877, Django no longer checks for `hasattr(timezone, 'localize')` and instead does an inheritance check. --- tests/test_fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_fields.py b/tests/test_fields.py index 5842553f02..78a9effb8c 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1485,7 +1485,7 @@ class TestNaiveDayLightSavingTimeTimeZoneDateTimeField(FieldValues): } outputs = {} - class MockTimezone: + class MockTimezone(pytz.BaseTzInfo): @staticmethod def localize(value, is_dst): raise pytz.InvalidTimeError() From 67b5093ca526d219b8f25abf427161e154c23c6e Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Fri, 16 Apr 2021 17:47:21 +0100 Subject: [PATCH 0771/1262] Fix pytest warnings (#7928) * Use `--strict-markers` instead of `--strict`, as per this warning: ``` /.../_pytest/config/__init__.py:1183: PytestDeprecationWarning: The --strict option is deprecated, use --strict-markers instead. ``` * Remove config option 'testspath' - pytest is logging a warning about this being unknown: ``` /.../_pytest/config/__init__.py:1233: PytestConfigWarning: Unknown config option: testspath ``` I can't find any reference to it in the pytest docs or changelog. --- setup.cfg | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index abb7cca908..46ffb13c52 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,8 +2,7 @@ license_file = LICENSE.md [tool:pytest] -addopts=--tb=short --strict -ra -testspath = tests +addopts=--tb=short --strict-markers -ra [flake8] ignore = E501,W504 From 010c8d4f084c1c3c5f712e731351604f301d6906 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Fri, 16 Apr 2021 17:59:27 +0100 Subject: [PATCH 0772/1262] Use tox-py in CI (#7925) --- .github/workflows/main.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1c9e49e348..fc166c434d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,12 +37,10 @@ jobs: run: python -m pip install --upgrade pip setuptools virtualenv wheel - name: Install dependencies - run: python -m pip install --upgrade codecov tox + run: python -m pip install --upgrade codecov tox tox-py - name: Run tox targets for ${{ matrix.python-version }} - run: | - ENV_PREFIX=$(tr -C -d "0-9" <<< "${{ matrix.python-version }}") - TOXENV=$(tox --listenvs | grep "^py$ENV_PREFIX" | tr '\n' ',') tox + run: tox --py current - name: Run extra tox targets if: ${{ matrix.python-version == '3.9' }} From 8812394ed83d7cce0ed5b2c5fcf093269d364b9b Mon Sep 17 00:00:00 2001 From: Denis Orehovsky Date: Tue, 20 Apr 2021 17:03:16 +0300 Subject: [PATCH 0773/1262] Add distinction between request and response serializers for OpenAPI (#7424) * Add distinction between request and response serializers * Add docs * document new functions in schemas.md * add a test case for different request vs response objects * Correct formatting for flake8 Co-authored-by: Shaun Gosse --- docs/api-guide/schemas.md | 14 +++++ rest_framework/schemas/openapi.py | 37 +++++++++++--- tests/schemas/test_openapi.py | 85 +++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 8 deletions(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index b4832b3690..acf2ecb932 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -375,6 +375,20 @@ operationIds. In order to work around this, you can override `get_operation_id_base()` to provide a different base for name part of the ID. +#### `get_serializer()` + +If the view has implemented `get_serializer()`, returns the result. + +#### `get_request_serializer()` + +By default returns `get_serializer()` but can be overridden to +differentiate between request and response objects. + +#### `get_response_serializer()` + +By default returns `get_serializer()` but can be overridden to +differentiate between request and response objects. + ### `AutoSchema.__init__()` kwargs `AutoSchema` provides a number of `__init__()` kwargs that can be used for diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 4ecb7a65f1..5e9d59f8bf 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -192,15 +192,22 @@ def get_components(self, path, method): if method.lower() == 'delete': return {} - serializer = self.get_serializer(path, method) + request_serializer = self.get_request_serializer(path, method) + response_serializer = self.get_response_serializer(path, method) - if not isinstance(serializer, serializers.Serializer): - return {} + components = {} + + if isinstance(request_serializer, serializers.Serializer): + component_name = self.get_component_name(request_serializer) + content = self.map_serializer(request_serializer) + components.setdefault(component_name, content) - component_name = self.get_component_name(serializer) + if isinstance(response_serializer, serializers.Serializer): + component_name = self.get_component_name(response_serializer) + content = self.map_serializer(response_serializer) + components.setdefault(component_name, content) - content = self.map_serializer(serializer) - return {component_name: content} + return components def _to_camel_case(self, snake_str): components = snake_str.split('_') @@ -615,6 +622,20 @@ def get_serializer(self, path, method): .format(view.__class__.__name__, method, path)) return None + def get_request_serializer(self, path, method): + """ + Override this method if your view uses a different serializer for + handling request body. + """ + return self.get_serializer(path, method) + + def get_response_serializer(self, path, method): + """ + Override this method if your view uses a different serializer for + populating response data. + """ + return self.get_serializer(path, method) + def _get_reference(self, serializer): return {'$ref': '#/components/schemas/{}'.format(self.get_component_name(serializer))} @@ -624,7 +645,7 @@ def get_request_body(self, path, method): self.request_media_types = self.map_parsers(path, method) - serializer = self.get_serializer(path, method) + serializer = self.get_request_serializer(path, method) if not isinstance(serializer, serializers.Serializer): item_schema = {} @@ -648,7 +669,7 @@ def get_responses(self, path, method): self.response_media_types = self.map_renderers(path, method) - serializer = self.get_serializer(path, method) + serializer = self.get_response_serializer(path, method) if not isinstance(serializer, serializers.Serializer): item_schema = {} diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index 871eb1b302..aef20670e6 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -712,6 +712,91 @@ def get_operation_id_base(self, path, method, action): operationId = inspector.get_operation_id(path, method) assert operationId == 'listItem' + def test_different_request_response_objects(self): + class RequestSerializer(serializers.Serializer): + text = serializers.CharField() + + class ResponseSerializer(serializers.Serializer): + text = serializers.BooleanField() + + class CustomSchema(AutoSchema): + def get_request_serializer(self, path, method): + return RequestSerializer() + + def get_response_serializer(self, path, method): + return ResponseSerializer() + + path = '/' + method = 'POST' + view = create_view( + views.ExampleGenericAPIView, + method, + create_request(path), + ) + inspector = CustomSchema() + inspector.view = view + + components = inspector.get_components(path, method) + assert components == { + 'Request': { + 'properties': { + 'text': { + 'type': 'string' + } + }, + 'required': ['text'], + 'type': 'object' + }, + 'Response': { + 'properties': { + 'text': { + 'type': 'boolean' + } + }, + 'required': ['text'], + 'type': 'object' + } + } + + operation = inspector.get_operation(path, method) + assert operation == { + 'operationId': 'createExample', + 'description': '', + 'parameters': [], + 'requestBody': { + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/Request' + } + }, + 'application/x-www-form-urlencoded': { + 'schema': { + '$ref': '#/components/schemas/Request' + } + }, + 'multipart/form-data': { + 'schema': { + '$ref': '#/components/schemas/Request' + } + } + } + }, + 'responses': { + '201': { + 'content': { + 'application/json': { + 'schema': { + '$ref': '#/components/schemas/Response' + } + } + }, + 'description': '' + } + }, + 'tags': [''] + } + def test_repeat_operation_ids(self): router = routers.SimpleRouter() router.register('account', views.ExampleGenericViewSet, basename="account") From 431f7dfa3dc108d449044a6c9eef715416d53059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Skar=C5=BCy=C5=84ski?= Date: Fri, 23 Apr 2021 10:34:58 +0200 Subject: [PATCH 0774/1262] fix typo in packaging requirements (#7949) --- requirements/requirements-packaging.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-packaging.txt b/requirements/requirements-packaging.txt index 3489c76ec0..fae03baab5 100644 --- a/requirements/requirements-packaging.txt +++ b/requirements/requirements-packaging.txt @@ -5,4 +5,4 @@ wheel>=0.35.1,<0.36 twine>=3.2.0,<3.3 # Transifex client for managing translation resources. -transifex-clien>=0.13.12,<0.14 +transifex-client>=0.13.12,<0.14 From a0083f7f9867113a37a5096a06ee69344781075a Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Mon, 26 Apr 2021 10:30:41 +0200 Subject: [PATCH 0775/1262] FIX: Broken cite. (#7951) --- docs/api-guide/schemas.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index acf2ecb932..b9de6745fe 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -421,6 +421,7 @@ If your views have related customizations that are needed frequently, you can create a base `AutoSchema` subclass for your project that takes additional `__init__()` kwargs to save subclassing `AutoSchema` for each view. +[cite]: https://blog.heroku.com/archives/2014/1/8/json_schema_for_heroku_platform_api [openapi]: https://github.com/OAI/OpenAPI-Specification [openapi-specification-extensions]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#specification-extensions [openapi-operation]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject From 3875d3284e73ed4d8e36c07d9b70c1b22c9d5998 Mon Sep 17 00:00:00 2001 From: Ishu Kumar Date: Mon, 10 May 2021 16:56:26 +0530 Subject: [PATCH 0776/1262] Punctuations and missing "to" preposition (#7966) Changes made in lines 221, 222, 223, and 224 for better readability. --- docs/api-guide/pagination.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 8d9eb22881..632af6a823 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -218,10 +218,10 @@ To set these attributes you should override the `CursorPagination` class, and th # Custom pagination styles -To create a custom pagination serializer class you should subclass `pagination.BasePagination` and override the `paginate_queryset(self, queryset, request, view=None)` and `get_paginated_response(self, data)` methods: +To create a custom pagination serializer class, you should inherit the subclass `pagination.BasePagination`, override the `paginate_queryset(self, queryset, request, view=None)`, and `get_paginated_response(self, data)` methods: -* The `paginate_queryset` method is passed the initial queryset and should return an iterable object that contains only the data in the requested page. -* The `get_paginated_response` method is passed the serialized page data and should return a `Response` instance. +* The `paginate_queryset` method is passed to the initial queryset and should return an iterable object. That object contains only the data in the requested page. +* The `get_paginated_response` method is passed to the serialized page data and should return a `Response` instance. Note that the `paginate_queryset` method may set state on the pagination instance, that may later be used by the `get_paginated_response` method. From bc075212cb05a52a2b2b2b4c909cfbd03c7ebd8e Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Mon, 24 May 2021 09:47:44 +0200 Subject: [PATCH 0777/1262] Fix running runtests.py without arguments. (#7954) Regression in aa12a5f967705f70b1dbe457bb2396d106e3570b. --- runtests.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtests.py b/runtests.py index c340b55d86..98f34c0673 100755 --- a/runtests.py +++ b/runtests.py @@ -45,5 +45,7 @@ def is_class(string): # `runtests.py TestCase [flags]` # `runtests.py test_function [flags]` pytest_args = ['tests', '-k', pytest_args[0]] + pytest_args[1:] + else: + pytest_args = [] sys.exit(pytest.main(pytest_args)) From 9d149f23177055b3b1ea12cf62de0d669739b544 Mon Sep 17 00:00:00 2001 From: Abduaziz <68025869+AbduazizZiyodov@users.noreply.github.com> Date: Wed, 2 Jun 2021 14:02:11 +0500 Subject: [PATCH 0778/1262] Fixed some punctuation marks & small typos (#8015) --- docs/api-guide/authentication.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 4497f73bd0..60544079f1 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -11,7 +11,7 @@ source: Authentication is the mechanism of associating an incoming request with a set of identifying credentials, such as the user the request came from, or the token that it was signed with. The [permission] and [throttling] policies can then use those credentials to determine if the request should be permitted. -REST framework provides a number of authentication schemes out of the box, and also allows you to implement custom schemes. +REST framework provides several authentication schemes out of the box, and also allows you to implement custom schemes. Authentication is always run at the very start of the view, before the permission and throttling checks occur, and before any other code is allowed to proceed. @@ -23,7 +23,7 @@ The `request.auth` property is used for any additional authentication informatio **Note:** Don't forget that **authentication by itself won't allow or disallow an incoming request**, it simply identifies the credentials that the request was made with. -For information on how to setup the permission polices for your API please see the [permissions documentation][permission]. +For information on how to set up the permission policies for your API please see the [permissions documentation][permission]. --- @@ -195,7 +195,7 @@ If you've already created some users, you can generate tokens for all existing u ##### By exposing an api endpoint -When using `TokenAuthentication`, you may want to provide a mechanism for clients to obtain a token given the username and password. REST framework provides a built-in view to provide this behavior. To use it, add the `obtain_auth_token` view to your URLconf: +When using `TokenAuthentication`, you may want to provide a mechanism for clients to obtain a token given the username and password. REST framework provides a built-in view to provide this behaviour. To use it, add the `obtain_auth_token` view to your URLconf: from rest_framework.authtoken import views urlpatterns += [ @@ -210,7 +210,7 @@ The `obtain_auth_token` view will return a JSON response when valid `username` a Note that the default `obtain_auth_token` view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. -By default there are no permissions or throttling applied to the `obtain_auth_token` view. If you do wish to apply throttling you'll need to override the view class, +By default, there are no permissions or throttling applied to the `obtain_auth_token` view. If you do wish to apply to throttle you'll need to override the view class, and include them using the `throttle_classes` attribute. If you need a customized version of the `obtain_auth_token` view, you can do so by subclassing the `ObtainAuthToken` view class, and using that in your url conf instead. @@ -244,7 +244,7 @@ And in your `urls.py`: ##### With Django admin -It is also possible to create Tokens manually through admin interface. In case you are using a large user base, we recommend that you monkey patch the `TokenAdmin` class to customize it to your needs, more specifically by declaring the `user` field as `raw_field`. +It is also possible to create Tokens manually through the admin interface. In case you are using a large user base, we recommend that you monkey patch the `TokenAdmin` class customize it to your needs, more specifically by declaring the `user` field as `raw_field`. `your_app/admin.py`: @@ -279,11 +279,11 @@ If successfully authenticated, `SessionAuthentication` provides the following cr Unauthenticated responses that are denied permission will result in an `HTTP 403 Forbidden` response. -If you're using an AJAX style API with SessionAuthentication, you'll need to make sure you include a valid CSRF token for any "unsafe" HTTP method calls, such as `PUT`, `PATCH`, `POST` or `DELETE` requests. See the [Django CSRF documentation][csrf-ajax] for more details. +If you're using an AJAX-style API with SessionAuthentication, you'll need to make sure you include a valid CSRF token for any "unsafe" HTTP method calls, such as `PUT`, `PATCH`, `POST` or `DELETE` requests. See the [Django CSRF documentation][csrf-ajax] for more details. **Warning**: Always use Django's standard login view when creating login pages. This will ensure your login views are properly protected. -CSRF validation in REST framework works slightly differently to standard Django due to the need to support both session and non-session based authentication to the same views. This means that only authenticated requests require CSRF tokens, and anonymous requests may be sent without CSRF tokens. This behaviour is not suitable for login views, which should always have CSRF validation applied. +CSRF validation in REST framework works slightly differently from standard Django due to the need to support both session and non-session based authentication to the same views. This means that only authenticated requests require CSRF tokens, and anonymous requests may be sent without CSRF tokens. This behaviour is not suitable for login views, which should always have CSRF validation applied. ## RemoteUserAuthentication @@ -316,7 +316,7 @@ In some circumstances instead of returning `None`, you may want to raise an `Aut Typically the approach you should take is: * If authentication is not attempted, return `None`. Any other authentication schemes also in use will still be checked. -* If authentication is attempted but fails, raise a `AuthenticationFailed` exception. An error response will be returned immediately, regardless of any permissions checks, and without checking any other authentication schemes. +* If authentication is attempted but fails, raise an `AuthenticationFailed` exception. An error response will be returned immediately, regardless of any permissions checks, and without checking any other authentication schemes. You *may* also override the `.authenticate_header(self, request)` method. If implemented, it should return a string that will be used as the value of the `WWW-Authenticate` header in a `HTTP 401 Unauthorized` response. @@ -353,7 +353,7 @@ The following example will authenticate any incoming request as the user given b # Third party packages -The following third party packages are also available. +The following third-party packages are also available. ## Django OAuth Toolkit @@ -384,7 +384,7 @@ For more details see the [Django REST framework - Getting started][django-oauth- The [Django REST framework OAuth][django-rest-framework-oauth] package provides both OAuth1 and OAuth2 support for REST framework. -This package was previously included directly in REST framework but is now supported and maintained as a third party package. +This package was previously included directly in the REST framework but is now supported and maintained as a third-party package. #### Installation & configuration @@ -408,7 +408,7 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a ## Djoser -[Djoser][djoser] library provides a set of views to handle basic actions such as registration, login, logout, password reset and account activation. The package works with a custom user model and it uses token based authentication. This is a ready to use REST implementation of Django authentication system. +[Djoser][djoser] library provides a set of views to handle basic actions such as registration, login, logout, password reset and account activation. The package works with a custom user model and uses token-based authentication. This is ready to use REST implementation of the Django authentication system. ## django-rest-auth / dj-rest-auth @@ -426,15 +426,15 @@ There are currently two forks of this project. ## django-rest-knox -[Django-rest-knox][django-rest-knox] library provides models and views to handle token based authentication in a more secure and extensible way than the built-in TokenAuthentication scheme - with Single Page Applications and Mobile clients in mind. It provides per-client tokens, and views to generate them when provided some other authentication (usually basic authentication), to delete the token (providing a server enforced logout) and to delete all tokens (logs out all clients that a user is logged into). +[Django-rest-knox][django-rest-knox] library provides models and views to handle token-based authentication in a more secure and extensible way than the built-in TokenAuthentication scheme - with Single Page Applications and Mobile clients in mind. It provides per-client tokens, and views to generate them when provided some other authentication (usually basic authentication), to delete the token (providing a server enforced logout) and to delete all tokens (logs out all clients that a user is logged into). ## drfpasswordless -[drfpasswordless][drfpasswordless] adds (Medium, Square Cash inspired) passwordless support to Django REST Framework's own TokenAuthentication scheme. Users log in and sign up with a token sent to a contact point like an email address or a mobile number. +[drfpasswordless][drfpasswordless] adds (Medium, Square Cash inspired) passwordless support to Django REST Framework's TokenAuthentication scheme. Users log in and sign up with a token sent to a contact point like an email address or a mobile number. ## django-rest-authemail -[django-rest-authemail][django-rest-authemail] provides a RESTful API interface for user signup and authentication. Email addresses are used for authentication, rather than usernames. API endpoints are available for signup, signup email verification, login, logout, password reset, password reset verification, email change, email change verification, password change, and user detail. A fully-functional example project and detailed instructions are included. +[django-rest-authemail][django-rest-authemail] provides a RESTful API interface for user signup and authentication. Email addresses are used for authentication, rather than usernames. API endpoints are available for signup, signup email verification, login, logout, password reset, password reset verification, email change, email change verification, password change, and user detail. A fully functional example project and detailed instructions are included. ## Django-Rest-Durin From 61e7a993bd0702d30e3049179000bc7c5f284781 Mon Sep 17 00:00:00 2001 From: Ian De Bie Date: Mon, 7 Jun 2021 04:30:23 -0500 Subject: [PATCH 0779/1262] fix comments by using correct css comment syntax (#8019) these intended comments were causing errors in sonarqube scans due to using wrong css comment syntax --- rest_framework/static/rest_framework/docs/css/base.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rest_framework/static/rest_framework/docs/css/base.css b/rest_framework/static/rest_framework/docs/css/base.css index 0be2bafa91..06b240c522 100644 --- a/rest_framework/static/rest_framework/docs/css/base.css +++ b/rest_framework/static/rest_framework/docs/css/base.css @@ -7,15 +7,15 @@ h1 { } pre.highlight code * { - white-space: nowrap; // this sets all children inside to nowrap + white-space: nowrap; /* this sets all children inside to nowrap */ } pre.highlight { - overflow-x: auto; // this sets the scrolling in x + overflow-x: auto; /* this sets the scrolling in x */ } pre.highlight code { - white-space: pre; // forces to respect
     formatting
    +  white-space: pre;       /* forces  to respect 
     formatting */
     }
     
     .main-container {
    
    From 24a938abaadd98b5482bec33defd285625842342 Mon Sep 17 00:00:00 2001
    From: Finn Gundlach 
    Date: Wed, 16 Jun 2021 15:53:29 +0200
    Subject: [PATCH 0780/1262] Update documentation to include Django 3.2 as
     supported version (#8037)
    
    ---
     README.md     | 2 +-
     docs/index.md | 2 +-
     2 files changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/README.md b/README.md
    index ff76a5525d..fce275256e 100644
    --- a/README.md
    +++ b/README.md
    @@ -53,7 +53,7 @@ There is a live example API for testing purposes, [available here][sandbox].
     # Requirements
     
     * Python (3.5, 3.6, 3.7, 3.8, 3.9)
    -* Django (2.2, 3.0, 3.1)
    +* Django (2.2, 3.0, 3.1, 3.2)
     
     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 530813684e..28e3302501 100644
    --- a/docs/index.md
    +++ b/docs/index.md
    @@ -84,7 +84,7 @@ continued development by **[signing up for a paid plan][funding]**.
     REST framework requires the following:
     
     * Python (3.5, 3.6, 3.7, 3.8, 3.9)
    -* Django (2.2, 3.0, 3.1)
    +* Django (2.2, 3.0, 3.1, 3.2)
     
     We **highly recommend** and only officially support the latest patch release of
     each Python and Django series.
    
    From e92016ac2e926483e05e296558fc3d1ea3279625 Mon Sep 17 00:00:00 2001
    From: Adam Johnson 
    Date: Mon, 21 Jun 2021 11:33:43 +0100
    Subject: [PATCH 0781/1262] Stop ignoring test outcome for Django 3.2 (#7927)
    
    ---
     requirements/requirements-optionals.txt |  2 +-
     setup.py                                |  1 +
     tox.ini                                 | 12 ------------
     3 files changed, 2 insertions(+), 13 deletions(-)
    
    diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt
    index 4cb0e54f4b..75b9ab4d60 100644
    --- a/requirements/requirements-optionals.txt
    +++ b/requirements/requirements-optionals.txt
    @@ -2,7 +2,7 @@
     coreapi==2.3.1
     coreschema==0.0.4
     django-filter>=2.4.0,<3.0
    -django-guardian>=2.3.0,<2.4
    +django-guardian>=2.4.0,<2.5
     markdown==3.3;python_version>="3.6"
     markdown==3.2.2;python_version=="3.5"
     psycopg2-binary>=2.8.5,<2.9
    diff --git a/setup.py b/setup.py
    index e2a1c0222c..5fd4df20db 100755
    --- a/setup.py
    +++ b/setup.py
    @@ -92,6 +92,7 @@ def get_version(package):
             'Framework :: Django :: 2.2',
             'Framework :: Django :: 3.0',
             'Framework :: Django :: 3.1',
    +        'Framework :: Django :: 3.2',
             'Intended Audience :: Developers',
             'License :: OSI Approved :: BSD License',
             'Operating System :: OS Independent',
    diff --git a/tox.ini b/tox.ini
    index bf4de90d03..f23486a685 100644
    --- a/tox.ini
    +++ b/tox.ini
    @@ -50,18 +50,6 @@ deps =
            -rrequirements/requirements-testing.txt
            -rrequirements/requirements-documentation.txt
     
    -[testenv:py36-django32]
    -ignore_outcome = true
    -
    -[testenv:py37-django32]
    -ignore_outcome = true
    -
    -[testenv:py38-django32]
    -ignore_outcome = true
    -
    -[testenv:py39-django32]
    -ignore_outcome = true
    -
     [testenv:py38-djangomain]
     ignore_outcome = true
     
    
    From c8a9c856c25a1a360a91d2c7bc11e0dacfb9c3a4 Mon Sep 17 00:00:00 2001
    From: Burak Kadir Er 
    Date: Mon, 28 Jun 2021 14:51:21 +0300
    Subject: [PATCH 0782/1262] fix a small typo (#8060)
    
    ---
     docs/api-guide/renderers.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md
    index 954fb3bb98..7dbc5eee8f 100644
    --- a/docs/api-guide/renderers.md
    +++ b/docs/api-guide/renderers.md
    @@ -105,7 +105,7 @@ The TemplateHTMLRenderer will create a `RequestContext`, using the `response.dat
     
     ---
     
    -**Note:** When used with a view that makes use of a serializer the `Response` sent for rendering may not be a dictionay and will need to be wrapped in a dict before returning to allow the TemplateHTMLRenderer to render it. For example:
    +**Note:** When used with a view that makes use of a serializer the `Response` sent for rendering may not be a dictionary and will need to be wrapped in a dict before returning to allow the TemplateHTMLRenderer to render it. For example:
     
     ```
     response.data = {'results': response.data}
    
    From d2977cff989f9b14f402ecf1e9235ee3d110977b Mon Sep 17 00:00:00 2001
    From: Nikita Sobolev 
    Date: Mon, 28 Jun 2021 15:07:41 +0300
    Subject: [PATCH 0783/1262] Fixes inconsistent headers in `serializer` docs
     (#8056)
    
    Some headers were using `.`, some - were not.
    Now, all of them are the same with `.`, because it was easier to fix.
    ---
     docs/api-guide/serializers.md | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md
    index f05fe7e7e9..13c0c87104 100644
    --- a/docs/api-guide/serializers.md
    +++ b/docs/api-guide/serializers.md
    @@ -605,13 +605,13 @@ For `ModelSerializer` this defaults to `PrimaryKeyRelatedField`.
     
     For `HyperlinkedModelSerializer` this defaults to `serializers.HyperlinkedRelatedField`.
     
    -### `serializer_url_field`
    +### `.serializer_url_field`
     
     The serializer field class that should be used for any `url` field on the serializer.
     
     Defaults to `serializers.HyperlinkedIdentityField`
     
    -### `serializer_choice_field`
    +### `.serializer_choice_field`
     
     The serializer field class that should be used for any choice fields on the serializer.
     
    
    From 98e56e0327596db352b35fa3b3dc8355dc9bd030 Mon Sep 17 00:00:00 2001
    From: Evgeny Panfilov 
    Date: Thu, 1 Jul 2021 17:04:44 +0300
    Subject: [PATCH 0784/1262] fix empty string as a value for a validated
     DecimalField (#8064) (#8067)
    
    ---
     rest_framework/fields.py |  8 +++++---
     tests/test_fields.py     | 24 ++++++++++++++++++++++++
     2 files changed, 29 insertions(+), 3 deletions(-)
    
    diff --git a/rest_framework/fields.py b/rest_framework/fields.py
    index e4be54751d..bedc02b94d 100644
    --- a/rest_framework/fields.py
    +++ b/rest_framework/fields.py
    @@ -1046,6 +1046,11 @@ def __init__(self, max_digits, decimal_places, coerce_to_string=None, max_value=
                     'Invalid rounding option %s. Valid values for rounding are: %s' % (rounding, valid_roundings))
             self.rounding = rounding
     
    +    def validate_empty_values(self, data):
    +        if smart_str(data).strip() == '' and self.allow_null:
    +            return (True, None)
    +        return super().validate_empty_values(data)
    +
         def to_internal_value(self, data):
             """
             Validate that the input is a decimal number and return a Decimal
    @@ -1063,9 +1068,6 @@ def to_internal_value(self, data):
             try:
                 value = decimal.Decimal(data)
             except decimal.DecimalException:
    -            if data == '' and self.allow_null:
    -                return None
    -
                 self.fail('invalid')
     
             if value.is_nan():
    diff --git a/tests/test_fields.py b/tests/test_fields.py
    index 78a9effb8c..d99ca9c40d 100644
    --- a/tests/test_fields.py
    +++ b/tests/test_fields.py
    @@ -1163,6 +1163,30 @@ class TestMinMaxDecimalField(FieldValues):
         )
     
     
    +class TestAllowEmptyStrDecimalFieldWithValidators(FieldValues):
    +    """
    +    Check that empty string ('', ' ') is acceptable value for the DecimalField
    +    if allow_null=True and there are max/min validators
    +    """
    +    valid_inputs = {
    +        None: None,
    +        '': None,
    +        ' ': None,
    +        '  ': None,
    +        5: Decimal('5'),
    +        '0': Decimal('0'),
    +        '10': Decimal('10'),
    +    }
    +    invalid_inputs = {
    +        -1: ['Ensure this value is greater than or equal to 0.'],
    +        11: ['Ensure this value is less than or equal to 10.'],
    +    }
    +    outputs = {
    +        None: '',
    +    }
    +    field = serializers.DecimalField(max_digits=3, decimal_places=1, allow_null=True, min_value=0, max_value=10)
    +
    +
     class TestNoMaxDigitsDecimalField(FieldValues):
         field = serializers.DecimalField(
             max_value=100, min_value=0,
    
    From b215375125980114482779b36dd825775ef7e482 Mon Sep 17 00:00:00 2001
    From: Nikhil Benesch 
    Date: Fri, 6 Aug 2021 05:10:58 -0400
    Subject: [PATCH 0785/1262] Propagate nullability in ModelSerializer (#8116)
    
    Propagate the nullability of underlying model fields in ModelSerializer
    when those fields are marked as read only. This ensures the correct
    generation of OpenAPI schemas.
    
    Fix #8041.
    ---
     rest_framework/serializers.py |  5 ++---
     tests/schemas/test_openapi.py | 19 +++++++++++++++++++
     2 files changed, 21 insertions(+), 3 deletions(-)
    
    diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
    index 49eec82591..9ea57f1aff 100644
    --- a/rest_framework/serializers.py
    +++ b/rest_framework/serializers.py
    @@ -1326,9 +1326,8 @@ def include_extra_kwargs(self, kwargs, extra_kwargs):
             """
             if extra_kwargs.get('read_only', False):
                 for attr in [
    -                'required', 'default', 'allow_blank', 'allow_null',
    -                'min_length', 'max_length', 'min_value', 'max_value',
    -                'validators', 'queryset'
    +                'required', 'default', 'allow_blank', 'min_length',
    +                'max_length', 'min_value', 'max_value', 'validators', 'queryset'
                 ]:
                     kwargs.pop(attr, None)
     
    diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py
    index aef20670e6..daa035a3f3 100644
    --- a/tests/schemas/test_openapi.py
    +++ b/tests/schemas/test_openapi.py
    @@ -2,6 +2,7 @@
     import warnings
     
     import pytest
    +from django.db import models
     from django.test import RequestFactory, TestCase, override_settings
     from django.urls import path
     from django.utils.translation import gettext_lazy as _
    @@ -110,6 +111,24 @@ class Serializer(serializers.Serializer):
             assert data['properties']['default_false']['default'] is False, "default must be false"
             assert 'default' not in data['properties']['without_default'], "default must not be defined"
     
    +    def test_nullable_fields(self):
    +        class Model(models.Model):
    +            rw_field = models.CharField(null=True)
    +            ro_field = models.CharField(null=True)
    +
    +        class Serializer(serializers.ModelSerializer):
    +            class Meta:
    +                model = Model
    +                fields = ["rw_field", "ro_field"]
    +                read_only_fields = ["ro_field"]
    +
    +        inspector = AutoSchema()
    +
    +        data = inspector.map_serializer(Serializer())
    +        assert data['properties']['rw_field']['nullable'], "rw_field nullable must be true"
    +        assert data['properties']['ro_field']['nullable'], "ro_field nullable must be true"
    +        assert data['properties']['ro_field']['readOnly'], "ro_field read_only must be true"
    +
     
     @pytest.mark.skipif(uritemplate is None, reason='uritemplate not installed.')
     class TestOperationIntrospection(TestCase):
    
    From fdb49314754ff13d91c6eec7ccdb8ece52bea9eb Mon Sep 17 00:00:00 2001
    From: Aarni Koskela 
    Date: Fri, 6 Aug 2021 12:14:52 +0300
    Subject: [PATCH 0786/1262] Make Field constructors keyword-only (#7632)
    
    ---
     rest_framework/fields.py | 46 ++++++++++++++++++++--------------------
     tests/test_fields.py     |  5 +++++
     2 files changed, 28 insertions(+), 23 deletions(-)
    
    diff --git a/rest_framework/fields.py b/rest_framework/fields.py
    index bedc02b94d..5cafed5556 100644
    --- a/rest_framework/fields.py
    +++ b/rest_framework/fields.py
    @@ -320,7 +320,7 @@ class Field:
         default_empty_html = empty
         initial = None
     
    -    def __init__(self, read_only=False, write_only=False,
    +    def __init__(self, *, read_only=False, write_only=False,
                      required=None, default=empty, initial=empty, source=None,
                      label=None, help_text=None, style=None,
                      error_messages=None, validators=None, allow_null=False):
    @@ -1163,14 +1163,14 @@ class DateTimeField(Field):
         }
         datetime_parser = datetime.datetime.strptime
     
    -    def __init__(self, format=empty, input_formats=None, default_timezone=None, *args, **kwargs):
    +    def __init__(self, format=empty, input_formats=None, default_timezone=None, **kwargs):
             if format is not empty:
                 self.format = format
             if input_formats is not None:
                 self.input_formats = input_formats
             if default_timezone is not None:
                 self.timezone = default_timezone
    -        super().__init__(*args, **kwargs)
    +        super().__init__(**kwargs)
     
         def enforce_timezone(self, value):
             """
    @@ -1249,12 +1249,12 @@ class DateField(Field):
         }
         datetime_parser = datetime.datetime.strptime
     
    -    def __init__(self, format=empty, input_formats=None, *args, **kwargs):
    +    def __init__(self, format=empty, input_formats=None, **kwargs):
             if format is not empty:
                 self.format = format
             if input_formats is not None:
                 self.input_formats = input_formats
    -        super().__init__(*args, **kwargs)
    +        super().__init__(**kwargs)
     
         def to_internal_value(self, value):
             input_formats = getattr(self, 'input_formats', api_settings.DATE_INPUT_FORMATS)
    @@ -1315,12 +1315,12 @@ class TimeField(Field):
         }
         datetime_parser = datetime.datetime.strptime
     
    -    def __init__(self, format=empty, input_formats=None, *args, **kwargs):
    +    def __init__(self, format=empty, input_formats=None, **kwargs):
             if format is not empty:
                 self.format = format
             if input_formats is not None:
                 self.input_formats = input_formats
    -        super().__init__(*args, **kwargs)
    +        super().__init__(**kwargs)
     
         def to_internal_value(self, value):
             input_formats = getattr(self, 'input_formats', api_settings.TIME_INPUT_FORMATS)
    @@ -1470,9 +1470,9 @@ class MultipleChoiceField(ChoiceField):
         }
         default_empty_html = []
     
    -    def __init__(self, *args, **kwargs):
    +    def __init__(self, **kwargs):
             self.allow_empty = kwargs.pop('allow_empty', True)
    -        super().__init__(*args, **kwargs)
    +        super().__init__(**kwargs)
     
         def get_value(self, dictionary):
             if self.field_name not in dictionary:
    @@ -1529,12 +1529,12 @@ class FileField(Field):
             'max_length': _('Ensure this filename has at most {max_length} characters (it has {length}).'),
         }
     
    -    def __init__(self, *args, **kwargs):
    +    def __init__(self, **kwargs):
             self.max_length = kwargs.pop('max_length', None)
             self.allow_empty_file = kwargs.pop('allow_empty_file', False)
             if 'use_url' in kwargs:
                 self.use_url = kwargs.pop('use_url')
    -        super().__init__(*args, **kwargs)
    +        super().__init__(**kwargs)
     
         def to_internal_value(self, data):
             try:
    @@ -1578,9 +1578,9 @@ class ImageField(FileField):
             ),
         }
     
    -    def __init__(self, *args, **kwargs):
    +    def __init__(self, **kwargs):
             self._DjangoImageField = kwargs.pop('_DjangoImageField', DjangoImageField)
    -        super().__init__(*args, **kwargs)
    +        super().__init__(**kwargs)
     
         def to_internal_value(self, data):
             # Image validation is a bit grungy, so we'll just outright
    @@ -1595,8 +1595,8 @@ def to_internal_value(self, data):
     # Composite field types...
     
     class _UnvalidatedField(Field):
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    +    def __init__(self, **kwargs):
    +        super().__init__(**kwargs)
             self.allow_blank = True
             self.allow_null = True
     
    @@ -1617,7 +1617,7 @@ class ListField(Field):
             'max_length': _('Ensure this field has no more than {max_length} elements.')
         }
     
    -    def __init__(self, *args, **kwargs):
    +    def __init__(self, **kwargs):
             self.child = kwargs.pop('child', copy.deepcopy(self.child))
             self.allow_empty = kwargs.pop('allow_empty', True)
             self.max_length = kwargs.pop('max_length', None)
    @@ -1629,7 +1629,7 @@ def __init__(self, *args, **kwargs):
                 "Remove `source=` from the field declaration."
             )
     
    -        super().__init__(*args, **kwargs)
    +        super().__init__(**kwargs)
             self.child.bind(field_name='', parent=self)
             if self.max_length is not None:
                 message = lazy_format(self.error_messages['max_length'], max_length=self.max_length)
    @@ -1694,7 +1694,7 @@ class DictField(Field):
             'empty': _('This dictionary may not be empty.'),
         }
     
    -    def __init__(self, *args, **kwargs):
    +    def __init__(self, **kwargs):
             self.child = kwargs.pop('child', copy.deepcopy(self.child))
             self.allow_empty = kwargs.pop('allow_empty', True)
     
    @@ -1704,7 +1704,7 @@ def __init__(self, *args, **kwargs):
                 "Remove `source=` from the field declaration."
             )
     
    -        super().__init__(*args, **kwargs)
    +        super().__init__(**kwargs)
             self.child.bind(field_name='', parent=self)
     
         def get_value(self, dictionary):
    @@ -1753,8 +1753,8 @@ def run_child_validation(self, data):
     class HStoreField(DictField):
         child = CharField(allow_blank=True, allow_null=True)
     
    -    def __init__(self, *args, **kwargs):
    -        super().__init__(*args, **kwargs)
    +    def __init__(self, **kwargs):
    +        super().__init__(**kwargs)
             assert isinstance(self.child, CharField), (
                 "The `child` argument must be an instance of `CharField`, "
                 "as the hstore extension stores values as strings."
    @@ -1769,11 +1769,11 @@ class JSONField(Field):
         # Workaround for isinstance calls when importing the field isn't possible
         _is_jsonfield = True
     
    -    def __init__(self, *args, **kwargs):
    +    def __init__(self, **kwargs):
             self.binary = kwargs.pop('binary', False)
             self.encoder = kwargs.pop('encoder', None)
             self.decoder = kwargs.pop('decoder', None)
    -        super().__init__(*args, **kwargs)
    +        super().__init__(**kwargs)
     
         def get_value(self, dictionary):
             if html.is_html_input(dictionary) and self.field_name in dictionary:
    diff --git a/tests/test_fields.py b/tests/test_fields.py
    index d99ca9c40d..2d4cc44ae0 100644
    --- a/tests/test_fields.py
    +++ b/tests/test_fields.py
    @@ -2010,6 +2010,11 @@ def test_collection_types_are_invalid_input(self):
                 field.to_internal_value(input_value)
             assert exc_info.value.detail == ['Expected a list of items but got type "dict".']
     
    +    def test_constructor_misuse_raises(self):
    +        # Test that `ListField` can only be instantiated with keyword arguments
    +        with pytest.raises(TypeError):
    +            serializers.ListField(serializers.CharField())
    +
     
     class TestNestedListField(FieldValues):
         """
    
    From 2942590ee3d3596683405dcdb1017e1833f5cb02 Mon Sep 17 00:00:00 2001
    From: Ma77heus <58952630+MattheusHenrique@users.noreply.github.com>
    Date: Fri, 6 Aug 2021 12:39:58 -0300
    Subject: [PATCH 0787/1262] fix: broken cite (#8086)
    
    Co-authored-by: MattheusHenrique 
    ---
     docs/api-guide/renderers.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md
    index 7dbc5eee8f..f13b7ba946 100644
    --- a/docs/api-guide/renderers.md
    +++ b/docs/api-guide/renderers.md
    @@ -528,7 +528,7 @@ Comma-separated values are a plain-text tabular data format, that can be easily
     [Rest Framework Latex] provides a renderer that outputs PDFs using Laulatex. It is maintained by [Pebble (S/F Software)][mypebble].
     
     
    -[cite]: https://docs.djangoproject.com/en/stable/stable/template-response/#the-rendering-process
    +[cite]: https://docs.djangoproject.com/en/stable/ref/template-response/#the-rendering-process
     [conneg]: content-negotiation.md
     [html-and-forms]: ../topics/html-and-forms.md
     [browser-accept-headers]: http://www.gethifi.com/blog/browser-rest-http-accept-headers
    
    From cba24464e8f63377627f3016df88ee74d11a817d Mon Sep 17 00:00:00 2001
    From: Paul Wayper 
    Date: Sat, 7 Aug 2021 01:45:15 +1000
    Subject: [PATCH 0788/1262] Botbot has been acquired, all paths now point to
     startupresources (#8050)
    
    Signed-off-by: Paul Wayper 
    ---
     docs/index.md | 3 +--
     1 file changed, 1 insertion(+), 2 deletions(-)
    
    diff --git a/docs/index.md b/docs/index.md
    index 28e3302501..ccbaf73731 100644
    --- a/docs/index.md
    +++ b/docs/index.md
    @@ -186,7 +186,7 @@ Framework.
     
     ## Support
     
    -For support please see the [REST framework discussion group][group], try the  `#restframework` channel on `irc.freenode.net`, search [the IRC archives][botbot], or raise a  question on [Stack Overflow][stack-overflow], making sure to include the ['django-rest-framework'][django-rest-framework-tag] tag.
    +For support please see the [REST framework discussion group][group], try the  `#restframework` channel on `irc.freenode.net`, or raise a  question on [Stack Overflow][stack-overflow], making sure to include the ['django-rest-framework'][django-rest-framework-tag] tag.
     
     For priority support please sign up for a [professional or premium sponsorship plan](https://fund.django-rest-framework.org/topics/funding/).
     
    @@ -257,7 +257,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     [funding]: community/funding.md
     
     [group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
    -[botbot]: https://botbot.me/freenode/restframework/
     [stack-overflow]: https://stackoverflow.com/
     [django-rest-framework-tag]: https://stackoverflow.com/questions/tagged/django-rest-framework
     [security-mail]: mailto:rest-framework-security@googlegroups.com
    
    From c4404f3d5d2df2a5c7450517b48c4e6dfeb3c89e Mon Sep 17 00:00:00 2001
    From: Paul Wayper 
    Date: Sat, 7 Aug 2021 01:46:26 +1000
    Subject: [PATCH 0789/1262] We now use Libera.chat rather than Freenode for IRC
     (#8049)
    
    Signed-off-by: Paul Wayper 
    
    Co-authored-by: Tom Christie 
    ---
     README.md     | 2 +-
     docs/index.md | 2 +-
     2 files changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/README.md b/README.md
    index fce275256e..b8d8ca61b1 100644
    --- a/README.md
    +++ b/README.md
    @@ -168,7 +168,7 @@ Or to create a new user:
     
     Full documentation for the project is available at [https://www.django-rest-framework.org/][docs].
     
    -For questions and support, use the [REST framework discussion group][group], or `#restframework` on freenode IRC.
    +For questions and support, use the [REST framework discussion group][group], or `#restframework` on libera.chat IRC.
     
     You may also want to [follow the author on Twitter][twitter].
     
    diff --git a/docs/index.md b/docs/index.md
    index ccbaf73731..641800b93c 100644
    --- a/docs/index.md
    +++ b/docs/index.md
    @@ -186,7 +186,7 @@ Framework.
     
     ## Support
     
    -For support please see the [REST framework discussion group][group], try the  `#restframework` channel on `irc.freenode.net`, or raise a  question on [Stack Overflow][stack-overflow], making sure to include the ['django-rest-framework'][django-rest-framework-tag] tag.
    +For support please see the [REST framework discussion group][group], try the  `#restframework` channel on `irc.libera.chat`, or raise a  question on [Stack Overflow][stack-overflow], making sure to include the ['django-rest-framework'][django-rest-framework-tag] tag.
     
     For priority support please sign up for a [professional or premium sponsorship plan](https://fund.django-rest-framework.org/topics/funding/).
     
    
    From b824b33dc3a3facad3ef2b41fbe02ab2a7578bc3 Mon Sep 17 00:00:00 2001
    From: Thomas Grainger 
    Date: Fri, 6 Aug 2021 16:46:57 +0100
    Subject: [PATCH 0790/1262] add changelog project_url (#8085)
    
    ---
     setup.py | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/setup.py b/setup.py
    index 5fd4df20db..d755a00fe2 100755
    --- a/setup.py
    +++ b/setup.py
    @@ -109,6 +109,7 @@ def get_version(package):
         project_urls={
             'Funding': 'https://fund.django-rest-framework.org/topics/funding/',
             'Source': 'https://github.com/encode/django-rest-framework',
    +        'Changelog': 'https://www.django-rest-framework.org/community/release-notes/',
         },
     )
     
    
    From e95e91ccf2065cbf474892b73ebd5790e5a4ae14 Mon Sep 17 00:00:00 2001
    From: Ben Hampson <77866043+Ben-Hampson@users.noreply.github.com>
    Date: Fri, 6 Aug 2021 17:49:41 +0200
    Subject: [PATCH 0791/1262] Use correct link for httpie (#8005)
    
    Before it was linking to a fork of a fork of httpie. I've changed it to the right URL.
    ---
     docs/tutorial/1-serialization.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md
    index 85d8676b1d..908b7474a0 100644
    --- a/docs/tutorial/1-serialization.md
    +++ b/docs/tutorial/1-serialization.md
    @@ -374,5 +374,5 @@ We'll see how we can start to improve things in [part 2 of the tutorial][tut-2].
     [sandbox]: https://restframework.herokuapp.com/
     [venv]: https://docs.python.org/3/library/venv.html
     [tut-2]: 2-requests-and-responses.md
    -[httpie]: https://github.com/jakubroztocil/httpie#installation
    +[httpie]: https://github.com/httpie/httpie#installation
     [curl]: https://curl.haxx.se/
    
    From cdd53c7de912d5868c96f4e3883df248a3e6341d Mon Sep 17 00:00:00 2001
    From: juliangeissler <81534590+juliangeissler@users.noreply.github.com>
    Date: Sun, 8 Aug 2021 15:45:00 +0200
    Subject: [PATCH 0792/1262] Update Tutorial - Relationships & Hyperlinked APIs
     (#7950)
    
    unnecessary import, because it is already added in the previous section
    ---
     docs/tutorial/5-relationships-and-hyperlinked-apis.md | 1 -
     1 file changed, 1 deletion(-)
    
    diff --git a/docs/tutorial/5-relationships-and-hyperlinked-apis.md b/docs/tutorial/5-relationships-and-hyperlinked-apis.md
    index b0f3380859..f999fdf507 100644
    --- a/docs/tutorial/5-relationships-and-hyperlinked-apis.md
    +++ b/docs/tutorial/5-relationships-and-hyperlinked-apis.md
    @@ -31,7 +31,6 @@ The other thing we need to consider when creating the code highlight view is tha
     Instead of using a concrete generic view, we'll use the base class for representing instances, and create our own `.get()` method.  In your `snippets/views.py` add:
     
         from rest_framework import renderers
    -    from rest_framework.response import Response
     
         class SnippetHighlight(generics.GenericAPIView):
             queryset = Snippet.objects.all()
    
    From c5d9144aef1144825942ddffe0a6af23102ef44a Mon Sep 17 00:00:00 2001
    From: Mark <33526445+mark-gold@users.noreply.github.com>
    Date: Wed, 11 Aug 2021 13:30:09 +0300
    Subject: [PATCH 0793/1262] fix typo (#8122)
    
    Co-authored-by: mgold 
    ---
     docs/api-guide/validators.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md
    index 4451489d4d..76dcb0d541 100644
    --- a/docs/api-guide/validators.md
    +++ b/docs/api-guide/validators.md
    @@ -238,7 +238,7 @@ In the case of update operations on *nested* serializers there's no way of
     applying this exclusion, because the instance is not available.
     
     Again, you'll probably want to explicitly remove the validator from the
    -serializer class, and write the code the for the validation constraint
    +serializer class, and write the code for the validation constraint
     explicitly, in a `.validate()` method, or in the view.
     
     ## Debugging complex cases
    
    From c927053d4b99ada6b3fd5d70c6536554ff5fe8c0 Mon Sep 17 00:00:00 2001
    From: jefcolbi 
    Date: Tue, 31 Aug 2021 12:51:47 +0100
    Subject: [PATCH 0794/1262] Replacing django-rest-auth with dj-rest-auth
     (#8146)
    
    ---
     docs/community/third-party-packages.md | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/docs/community/third-party-packages.md b/docs/community/third-party-packages.md
    index e53fc3d50c..933244a6a9 100644
    --- a/docs/community/third-party-packages.md
    +++ b/docs/community/third-party-packages.md
    @@ -54,7 +54,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
     * [hawkrest][hawkrest] - Provides Hawk HTTP Authorization.
     * [djangorestframework-httpsignature][djangorestframework-httpsignature] - Provides an easy to use HTTP Signature Authentication mechanism.
     * [djoser][djoser] - Provides a set of views to handle basic actions such as registration, login, logout, password reset and account activation.
    -* [django-rest-auth][django-rest-auth] - Provides a set of REST API endpoints for registration, authentication (including social media authentication), password reset, retrieve and update user details, etc.
    +* [dj-rest-auth][dj-rest-auth] - Provides a set of REST API endpoints for registration, authentication (including social media authentication), password reset, retrieve and update user details, etc.
     * [drf-oidc-auth][drf-oidc-auth] - Implements OpenID Connect token authentication for DRF.
     * [drfpasswordless][drfpasswordless] - Adds (Medium, Square Cash inspired) passwordless logins and signups via email and mobile numbers.
     * [django-rest-authemail][django-rest-authemail] - Provides a RESTful API for user signup and authentication using email addresses.
    @@ -193,7 +193,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
     [gaiarestframework]: https://github.com/AppsFuel/gaiarestframework
     [drf-extensions]: https://github.com/chibisov/drf-extensions
     [ember-django-adapter]: https://github.com/dustinfarris/ember-django-adapter
    -[django-rest-auth]: https://github.com/Tivix/django-rest-auth/
    +[dj-rest-auth]: https://github.com/iMerica/dj-rest-auth
     [django-versatileimagefield]: https://github.com/WGBH/django-versatileimagefield
     [django-versatileimagefield-drf-docs]:https://django-versatileimagefield.readthedocs.io/en/latest/drf_integration.html
     [drf-tracking]: https://github.com/aschn/drf-tracking
    
    From 88666629a70f5c3fbe31e11aecd9817338de9c92 Mon Sep 17 00:00:00 2001
    From: Asif Saif Uddin 
    Date: Tue, 31 Aug 2021 18:56:08 +0600
    Subject: [PATCH 0795/1262] stop testing django 3.0 as its EOL (#8136)
    
    ---
     tox.ini | 5 +----
     1 file changed, 1 insertion(+), 4 deletions(-)
    
    diff --git a/tox.ini b/tox.ini
    index f23486a685..25f8418219 100644
    --- a/tox.ini
    +++ b/tox.ini
    @@ -1,7 +1,6 @@
     [tox]
     envlist =
            {py35,py36,py37}-django22,
    -       {py36,py37,py38}-django30,
            {py36,py37,py38,py39}-django31,
            {py36,py37,py38,py39}-django32,
            {py38,py39}-djangomain,
    @@ -10,7 +9,6 @@ envlist =
     [travis:env]
     DJANGO =
         2.2: django22
    -    3.0: django30
         3.1: django31
         3.2: django32
         main: djangomain
    @@ -23,9 +21,8 @@ setenv =
            PYTHONWARNINGS=once
     deps =
             django22: Django>=2.2,<3.0
    -        django30: Django>=3.0,<3.1
             django31: Django>=3.1,<3.2
    -        django32: Django>=3.2a1,<4.0
    +        django32: Django>=3.2,<4.0
             djangomain: https://github.com/django/django/archive/main.tar.gz
             -rrequirements/requirements-testing.txt
             -rrequirements/requirements-optionals.txt
    
    From 6b392a46ea025148a24ce665e9c18e4386dde8fa Mon Sep 17 00:00:00 2001
    From: Aditya Mitra <55396651+aditya-mitra@users.noreply.github.com>
    Date: Tue, 31 Aug 2021 18:27:02 +0530
    Subject: [PATCH 0796/1262] [FIX] Typo in api-guide/authentication (#8144)
    
    ---
     docs/api-guide/authentication.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md
    index 60544079f1..57bbaeb679 100644
    --- a/docs/api-guide/authentication.md
    +++ b/docs/api-guide/authentication.md
    @@ -13,7 +13,7 @@ Authentication is the mechanism of associating an incoming request with a set of
     
     REST framework provides several authentication schemes out of the box, and also allows you to implement custom schemes.
     
    -Authentication is always run at the very start of the view, before the permission and throttling checks occur, and before any other code is allowed to proceed.
    +Authentication always runs at the very start of the view, before the permission and throttling checks occur, and before any other code is allowed to proceed.
     
     The `request.user` property will typically be set to an instance of the `contrib.auth` package's `User` class.
     
    
    From 4632b5daaed5a71a1be3e7d412a7f9a2e5520b90 Mon Sep 17 00:00:00 2001
    From: Ryan Nowakowski 
    Date: Tue, 31 Aug 2021 08:18:49 -0500
    Subject: [PATCH 0797/1262] Fix subtitle of schemas for filtering (#8145)
    
    Fix a likely copy/paste error
    ---
     docs/api-guide/filtering.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md
    index 478e3bcf95..3541388ca2 100644
    --- a/docs/api-guide/filtering.md
    +++ b/docs/api-guide/filtering.md
    @@ -335,7 +335,7 @@ Generic filters may also present an interface in the browsable API. To do so you
     
     The method should return a rendered HTML string.
     
    -## Pagination & schemas
    +## Filtering & schemas
     
     You can also make the filter controls available to the schema autogeneration
     that REST framework provides, by implementing a `get_schema_fields()` method. This method should have the following signature:
    
    From cb206e4701dd67f859c015bea111d0e77e364c4a Mon Sep 17 00:00:00 2001
    From: Juan Benitez 
    Date: Fri, 3 Sep 2021 07:00:23 -0500
    Subject: [PATCH 0798/1262] fix: change View class to Throttle class on
     SimpleRateThrottle Docstring (#8147)
    
    ---
     rest_framework/throttling.py | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/rest_framework/throttling.py b/rest_framework/throttling.py
    index 0ba2ba66b1..e262b886bc 100644
    --- a/rest_framework/throttling.py
    +++ b/rest_framework/throttling.py
    @@ -52,7 +52,7 @@ class SimpleRateThrottle(BaseThrottle):
         A simple cache implementation, that only requires `.get_cache_key()`
         to be overridden.
     
    -    The rate (requests / seconds) is set by a `rate` attribute on the View
    +    The rate (requests / seconds) is set by a `rate` attribute on the Throttle
         class.  The attribute is a string of the form 'number_of_requests/period'.
     
         Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')
    
    From 96001c5de61b5fe7c083bdd8e5810105e3575014 Mon Sep 17 00:00:00 2001
    From: Anthony Randall 
    Date: Fri, 3 Sep 2021 06:23:19 -0600
    Subject: [PATCH 0799/1262] Added an article - implementing rest apis with
     embedded privacy from doordash engineering blog (#7956)
    
    * Update tutorials-and-resources.md
    
    * Update tutorials-and-resources.md
    ---
     docs/community/tutorials-and-resources.md | 2 ++
     1 file changed, 2 insertions(+)
    
    diff --git a/docs/community/tutorials-and-resources.md b/docs/community/tutorials-and-resources.md
    index dae292f50c..23faf79128 100644
    --- a/docs/community/tutorials-and-resources.md
    +++ b/docs/community/tutorials-and-resources.md
    @@ -76,6 +76,7 @@ There are a wide range of resources available for learning and using Django REST
     * [Chatbot Using Django REST Framework + api.ai + Slack — Part 1/3][chatbot-using-drf-part1]
     * [New Django Admin with DRF and EmberJS... What are the News?][new-django-admin-with-drf-and-emberjs]
     * [Blog posts about Django REST Framework][medium-django-rest-framework]
    +* [Implementing Rest APIs With Embedded Privacy][doordash-implementing-rest-apis]
     
     ### Documentations
     * [Classy Django REST Framework][cdrf.co]
    @@ -128,3 +129,4 @@ Want your Django REST Framework talk/tutorial/article to be added to our website
     [anna-email]: mailto:anna@django-rest-framework.org
     [pycon-us-2017]: https://www.youtube.com/watch?v=Rk6MHZdust4
     [django-rest-react-valentinog]: https://www.valentinog.com/blog/tutorial-api-django-rest-react/
    +[doordash-implementing-rest-apis]: https://doordash.engineering/2013/10/07/implementing-rest-apis-with-embedded-privacy/
    
    From 655e803adfb19b8cb5b94a4895f1baffed55a958 Mon Sep 17 00:00:00 2001
    From: Peter Uittenbroek 
    Date: Fri, 3 Sep 2021 15:37:03 +0200
    Subject: [PATCH 0800/1262] #7157: Fix RemoteUserAuthentication calling django
     authenticate with request argument (#7158)
    
    ---
     rest_framework/authentication.py | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py
    index 9111007c09..382abf1580 100644
    --- a/rest_framework/authentication.py
    +++ b/rest_framework/authentication.py
    @@ -227,6 +227,6 @@ class RemoteUserAuthentication(BaseAuthentication):
         header = "REMOTE_USER"
     
         def authenticate(self, request):
    -        user = authenticate(remote_user=request.META.get(self.header))
    +        user = authenticate(request=request, remote_user=request.META.get(self.header))
             if user and user.is_active:
                 return (user, None)
    
    From 9716b1b6b7779543c134856e59f1c1393963e46f Mon Sep 17 00:00:00 2001
    From: Ivan Trushin <33528037+WannaFight@users.noreply.github.com>
    Date: Mon, 6 Sep 2021 14:18:13 +0300
    Subject: [PATCH 0801/1262] Fix arguments (#7995)
    
    `path()` has no argument `namespace`, it has `name` argument
    ---
     docs/tutorial/quickstart.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md
    index ee839790f1..e19577f617 100644
    --- a/docs/tutorial/quickstart.md
    +++ b/docs/tutorial/quickstart.md
    @@ -126,7 +126,7 @@ Okay, now let's wire up the API URLs.  On to `tutorial/urls.py`...
         # Additionally, we include login URLs for the browsable API.
         urlpatterns = [
             path('', include(router.urls)),
    -        path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
    +        path('api-auth/', include('rest_framework.urls', name='rest_framework'))
         ]
     
     Because we're using viewsets instead of views, we can automatically generate the URL conf for our API, by simply registering the viewsets with a router class.
    
    From 9ce541e90990307e06da1b7f5a2576406366a5e5 Mon Sep 17 00:00:00 2001
    From: Tom Christie 
    Date: Mon, 6 Sep 2021 12:19:20 +0100
    Subject: [PATCH 0802/1262] Revert "Fix arguments (#7995)" (#8156)
    
    This reverts commit 9716b1b6b7779543c134856e59f1c1393963e46f.
    ---
     docs/tutorial/quickstart.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md
    index e19577f617..ee839790f1 100644
    --- a/docs/tutorial/quickstart.md
    +++ b/docs/tutorial/quickstart.md
    @@ -126,7 +126,7 @@ Okay, now let's wire up the API URLs.  On to `tutorial/urls.py`...
         # Additionally, we include login URLs for the browsable API.
         urlpatterns = [
             path('', include(router.urls)),
    -        path('api-auth/', include('rest_framework.urls', name='rest_framework'))
    +        path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
         ]
     
     Because we're using viewsets instead of views, we can automatically generate the URL conf for our API, by simply registering the viewsets with a router class.
    
    From 73f3325f80a381d1d62ab1b84956295963f445ed Mon Sep 17 00:00:00 2001
    From: Tom Christie 
    Date: Fri, 10 Sep 2021 11:32:27 +0100
    Subject: [PATCH 0803/1262] Update stream.io link (#8161)
    
    ---
     README.md     | 2 +-
     docs/index.md | 2 +-
     2 files changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/README.md b/README.md
    index b8d8ca61b1..e3bcc2a1c2 100644
    --- a/README.md
    +++ b/README.md
    @@ -197,7 +197,7 @@ Please see the [security policy][security-policy].
     [bitio-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/bitio-readme.png
     
     [sentry-url]: https://getsentry.com/welcome/
    -[stream-url]: https://getstream.io/try-the-api/?utm_source=drf&utm_medium=banner&utm_campaign=drf
    +[stream-url]: https://getstream.io/?utm_source=drf&utm_medium=sponsorship&utm_content=developer
     [rollbar-url]: https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial
     [esg-url]: https://software.esg-usa.com/
     [retool-url]: https://retool.com/?utm_source=djangorest&utm_medium=sponsorship
    diff --git a/docs/index.md b/docs/index.md
    index 641800b93c..9b667c6691 100644
    --- a/docs/index.md
    +++ b/docs/index.md
    @@ -75,7 +75,7 @@ continued development by **[signing up for a paid plan][funding]**.
     
     
    -*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), and [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship).* +*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=sponsorship&utm_content=developer), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), and [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship).* --- From 761f56ef4025543e9cf39346d25641305e7d957d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 10 Sep 2021 14:45:06 +0100 Subject: [PATCH 0804/1262] Update stream.io link --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 9b667c6691..f000d2e093 100644 --- a/docs/index.md +++ b/docs/index.md @@ -67,7 +67,7 @@ continued development by **[signing up for a paid plan][funding]**.
    • Sentry
    • -
    • Stream
    • +
    • Stream
    • ESG
    • Rollbar
    • Retool
    • From f0a5b958a134e8cd94e3ef3263e8fa623ac9b82f Mon Sep 17 00:00:00 2001 From: Dan Lousqui Date: Tue, 14 Sep 2021 14:45:55 +0200 Subject: [PATCH 0805/1262] Add max_length and min_length options to ListSerializer (#8165) --- docs/api-guide/serializers.md | 8 ++++ rest_framework/serializers.py | 27 +++++++++++++- tests/test_serializer_lists.py | 67 ++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 13c0c87104..cf8525748c 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -755,6 +755,14 @@ The following argument can also be passed to a `ListSerializer` field or a seria This is `True` by default, but can be set to `False` if you want to disallow empty lists as valid input. +### `max_length` + +This is `None` by default, but can be set to a positive integer if you want to validates that the list contains no more than this number of elements. + +### `min_length` + +This is `None` by default, but can be set to a positive integer if you want to validates that the list contains no fewer than this number of elements. + ### Customizing `ListSerializer` behavior There *are* a few use cases when you might want to customize the `ListSerializer` behavior. For example: diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 9ea57f1aff..3896805177 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -71,7 +71,8 @@ LIST_SERIALIZER_KWARGS = ( 'read_only', 'write_only', 'required', 'default', 'initial', 'source', 'label', 'help_text', 'style', 'error_messages', 'allow_empty', - 'instance', 'data', 'partial', 'context', 'allow_null' + 'instance', 'data', 'partial', 'context', 'allow_null', + 'max_length', 'min_length' ) ALL_FIELDS = '__all__' @@ -143,12 +144,18 @@ def many_init(cls, *args, **kwargs): return CustomListSerializer(*args, **kwargs) """ allow_empty = kwargs.pop('allow_empty', None) + max_length = kwargs.pop('max_length', None) + min_length = kwargs.pop('min_length', None) child_serializer = cls(*args, **kwargs) list_kwargs = { 'child': child_serializer, } if allow_empty is not None: list_kwargs['allow_empty'] = allow_empty + if max_length is not None: + list_kwargs['max_length'] = max_length + if min_length is not None: + list_kwargs['min_length'] = min_length list_kwargs.update({ key: value for key, value in kwargs.items() if key in LIST_SERIALIZER_KWARGS @@ -568,12 +575,16 @@ class ListSerializer(BaseSerializer): default_error_messages = { 'not_a_list': _('Expected a list of items but got type "{input_type}".'), - 'empty': _('This list may not be empty.') + 'empty': _('This list may not be empty.'), + 'max_length': _('Ensure this field has no more than {max_length} elements.'), + 'min_length': _('Ensure this field has at least {min_length} elements.') } def __init__(self, *args, **kwargs): self.child = kwargs.pop('child', copy.deepcopy(self.child)) self.allow_empty = kwargs.pop('allow_empty', True) + self.max_length = kwargs.pop('max_length', None) + self.min_length = kwargs.pop('min_length', None) assert self.child is not None, '`child` is a required argument.' assert not inspect.isclass(self.child), '`child` has not been instantiated.' super().__init__(*args, **kwargs) @@ -635,6 +646,18 @@ def to_internal_value(self, data): api_settings.NON_FIELD_ERRORS_KEY: [message] }, code='empty') + if self.max_length is not None and len(data) > self.max_length: + message = self.error_messages['max_length'].format(max_length=self.max_length) + raise ValidationError({ + api_settings.NON_FIELD_ERRORS_KEY: [message] + }, code='max_length') + + if self.min_length is not None and len(data) < self.min_length: + message = self.error_messages['min_length'].format(min_length=self.min_length) + raise ValidationError({ + api_settings.NON_FIELD_ERRORS_KEY: [message] + }, code='min_length') + ret = [] errors = [] diff --git a/tests/test_serializer_lists.py b/tests/test_serializer_lists.py index f35c4fcc9e..551f626662 100644 --- a/tests/test_serializer_lists.py +++ b/tests/test_serializer_lists.py @@ -616,3 +616,70 @@ def test_nested_serializer_with_list_multipart(self): assert serializer.is_valid() assert serializer.validated_data == [] + + +class TestMaxMinLengthListSerializer: + """ + Tests the behaviour of ListSerializers when max_length and min_length are used + """ + + def setup(self): + class IntegerSerializer(serializers.Serializer): + some_int = serializers.IntegerField() + + class MaxLengthSerializer(serializers.Serializer): + many_int = IntegerSerializer(many=True, max_length=5) + + class MinLengthSerializer(serializers.Serializer): + many_int = IntegerSerializer(many=True, min_length=3) + + class MaxMinLengthSerializer(serializers.Serializer): + many_int = IntegerSerializer(many=True, min_length=3, max_length=5) + + self.MaxLengthSerializer = MaxLengthSerializer + self.MinLengthSerializer = MinLengthSerializer + self.MaxMinLengthSerializer = MaxMinLengthSerializer + + def test_min_max_length_two_items(self): + input_data = {'many_int': [{'some_int': i} for i in range(2)]} + + max_serializer = self.MaxLengthSerializer(data=input_data) + min_serializer = self.MinLengthSerializer(data=input_data) + max_min_serializer = self.MaxMinLengthSerializer(data=input_data) + + assert max_serializer.is_valid() + assert max_serializer.validated_data == input_data + + assert not min_serializer.is_valid() + + assert not max_min_serializer.is_valid() + + def test_min_max_length_four_items(self): + input_data = {'many_int': [{'some_int': i} for i in range(4)]} + + max_serializer = self.MaxLengthSerializer(data=input_data) + min_serializer = self.MinLengthSerializer(data=input_data) + max_min_serializer = self.MaxMinLengthSerializer(data=input_data) + + assert max_serializer.is_valid() + assert max_serializer.validated_data == input_data + + assert min_serializer.is_valid() + assert min_serializer.validated_data == input_data + + assert max_min_serializer.is_valid() + assert min_serializer.validated_data == input_data + + def test_min_max_length_six_items(self): + input_data = {'many_int': [{'some_int': i} for i in range(6)]} + + max_serializer = self.MaxLengthSerializer(data=input_data) + min_serializer = self.MinLengthSerializer(data=input_data) + max_min_serializer = self.MaxMinLengthSerializer(data=input_data) + + assert not max_serializer.is_valid() + + assert min_serializer.is_valid() + assert min_serializer.validated_data == input_data + + assert not max_min_serializer.is_valid() From 250479dc3799a281429c2c10d9605a1a85d3e517 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 22 Sep 2021 09:57:17 +0200 Subject: [PATCH 0806/1262] Added pytz to install_requires. pytz will not automatically be installed with Django from v4.0. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d755a00fe2..b8e220cb43 100755 --- a/setup.py +++ b/setup.py @@ -82,7 +82,7 @@ def get_version(package): author_email='tom@tomchristie.com', # SEE NOTE BELOW (*) packages=find_packages(exclude=['tests*']), include_package_data=True, - install_requires=["django>=2.2"], + install_requires=["django>=2.2", "pytz"], python_requires=">=3.5", zip_safe=False, classifiers=[ From f651878df33bf12d3b637f2377e234a2f0a0523c Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 22 Sep 2021 09:58:34 +0200 Subject: [PATCH 0807/1262] Adjusted DateTimeField docs for zoneinfo. --- 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 04f9939425..b986009f9b 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -325,7 +325,7 @@ Corresponds to `django.db.models.fields.DateTimeField`. * `format` - A string representing the output format. If not specified, this defaults to the same value as the `DATETIME_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 `datetime` objects should be returned by `to_representation`. In this case the datetime 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 `DATETIME_INPUT_FORMATS` setting will be used, which defaults to `['iso-8601']`. -* `default_timezone` - A `pytz.timezone` representing the timezone. If not specified and the `USE_TZ` setting is enabled, this defaults to the [current timezone][django-current-timezone]. If `USE_TZ` is disabled, then datetime objects will be naive. +* `default_timezone` - A `tzinfo` subclass (`zoneinfo` or `pytz`) prepresenting the timezone. If not specified and the `USE_TZ` setting is enabled, this defaults to the [current timezone][django-current-timezone]. If `USE_TZ` is disabled, then datetime objects will be naive. #### `DateTimeField` format strings. From 2d9eee5d022bf77d6bb25e1d8b57da9e9c94f96f Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 22 Sep 2021 10:00:49 +0200 Subject: [PATCH 0808/1262] Adjusted URLPatternsTestCase to use addClassCleanup() from Django 4.0. Refs https://github.com/django/django/commit/faba5b702a9c5bb9452a543100928bcb5f66ebcf. addClassCleanup() is available from Python 3.8, which is the minimum supported Python from Django 4.0. --- rest_framework/test.py | 30 ++++++++++++++++++++++-------- tests/test_testing.py | 22 +++++++++++++++++----- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/rest_framework/test.py b/rest_framework/test.py index e934eff55d..0212348ee0 100644 --- a/rest_framework/test.py +++ b/rest_framework/test.py @@ -3,6 +3,7 @@ import io from importlib import import_module +import django from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.core.handlers.wsgi import WSGIHandler @@ -357,6 +358,13 @@ class APILiveServerTestCase(testcases.LiveServerTestCase): client_class = APIClient +def cleanup_url_patterns(cls): + if hasattr(cls, '_module_urlpatterns'): + cls._module.urlpatterns = cls._module_urlpatterns + else: + del cls._module.urlpatterns + + class URLPatternsTestCase(testcases.SimpleTestCase): """ Isolate URL patterns on a per-TestCase basis. For example, @@ -385,14 +393,20 @@ def setUpClass(cls): cls._module.urlpatterns = cls.urlpatterns cls._override.enable() + + if django.VERSION > (4, 0): + cls.addClassCleanup(cls._override.disable) + cls.addClassCleanup(cleanup_url_patterns, cls) + super().setUpClass() - @classmethod - def tearDownClass(cls): - super().tearDownClass() - cls._override.disable() + if django.VERSION < (4, 0): + @classmethod + def tearDownClass(cls): + super().tearDownClass() + cls._override.disable() - if hasattr(cls, '_module_urlpatterns'): - cls._module.urlpatterns = cls._module_urlpatterns - else: - del cls._module.urlpatterns + if hasattr(cls, '_module_urlpatterns'): + cls._module.urlpatterns = cls._module_urlpatterns + else: + del cls._module.urlpatterns diff --git a/tests/test_testing.py b/tests/test_testing.py index cc60e4f003..5066ee142e 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -1,5 +1,6 @@ from io import BytesIO +import django from django.contrib.auth.models import User from django.shortcuts import redirect from django.test import TestCase, override_settings @@ -282,6 +283,10 @@ def test_empty_request_content_type(self): assert request.META['CONTENT_TYPE'] == 'application/json' +def check_urlpatterns(cls): + assert urlpatterns is not cls.urlpatterns + + class TestUrlPatternTestCase(URLPatternsTestCase): urlpatterns = [ path('', view), @@ -293,11 +298,18 @@ def setUpClass(cls): super().setUpClass() assert urlpatterns is cls.urlpatterns - @classmethod - def tearDownClass(cls): - assert urlpatterns is cls.urlpatterns - super().tearDownClass() - assert urlpatterns is not cls.urlpatterns + if django.VERSION > (4, 0): + cls.addClassCleanup( + check_urlpatterns, + cls + ) + + if django.VERSION < (4, 0): + @classmethod + def tearDownClass(cls): + assert urlpatterns is cls.urlpatterns + super().tearDownClass() + assert urlpatterns is not cls.urlpatterns def test_urlpatterns(self): assert self.client.get('/').status_code == 200 From 4916854492e6c999977b8577b5a15e6ffc784550 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 22 Sep 2021 10:02:28 +0200 Subject: [PATCH 0809/1262] Removed USE_L10N setting from Django 4.0. USE_L10N defaults to True from Django 4.0, and will be removed in Django 5.0. --- tests/conftest.py | 4 +++- tests/test_fields.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index cc32cc6373..79cabd5e1a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,6 +18,8 @@ def pytest_addoption(parser): def pytest_configure(config): from django.conf import settings + # USE_L10N is deprecated, and will be removed in Django 5.0. + use_l10n = {"USE_L10N": True} if django.VERSION < (4, 0) else {} settings.configure( DEBUG_PROPAGATE_EXCEPTIONS=True, DATABASES={ @@ -33,7 +35,6 @@ def pytest_configure(config): SITE_ID=1, SECRET_KEY='not very secret in tests', USE_I18N=True, - USE_L10N=True, STATIC_URL='/static/', ROOT_URLCONF='tests.urls', TEMPLATES=[ @@ -68,6 +69,7 @@ def pytest_configure(config): PASSWORD_HASHERS=( 'django.contrib.auth.hashers.MD5PasswordHasher', ), + **use_l10n, ) # guardian is optional diff --git a/tests/test_fields.py b/tests/test_fields.py index 2d4cc44ae0..a3b37584bc 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1220,12 +1220,12 @@ class TestNoStringCoercionDecimalField(FieldValues): class TestLocalizedDecimalField(TestCase): - @override_settings(USE_L10N=True, LANGUAGE_CODE='pl') + @override_settings(LANGUAGE_CODE='pl') def test_to_internal_value(self): field = serializers.DecimalField(max_digits=2, decimal_places=1, localize=True) assert field.to_internal_value('1,1') == Decimal('1.1') - @override_settings(USE_L10N=True, LANGUAGE_CODE='pl') + @override_settings(LANGUAGE_CODE='pl') def test_to_representation(self): field = serializers.DecimalField(max_digits=2, decimal_places=1, localize=True) assert field.to_representation(Decimal('1.1')) == '1,1' From 19b609155479f967a41ffc20a58bb09229f1e64b Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 22 Sep 2021 10:06:10 +0200 Subject: [PATCH 0810/1262] Adjusted authentication test for internal CSRF changes. Private _get_new_csrf_token() was removed in https://github.com/django/django/commit/231de683d86374c2b74da2185efc6ddfb5eb3341. --- tests/authentication/test_authentication.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/authentication/test_authentication.py b/tests/authentication/test_authentication.py index a73e0d79c7..d771aaf8b4 100644 --- a/tests/authentication/test_authentication.py +++ b/tests/authentication/test_authentication.py @@ -1,5 +1,6 @@ import base64 +import django import pytest from django.conf import settings from django.contrib.auth.models import User @@ -218,7 +219,16 @@ def test_post_form_session_auth_passing_csrf(self): Ensure POSTing form over session authentication with CSRF token succeeds. Regression test for #6088 """ - from django.middleware.csrf import _get_new_csrf_token + # Remove this shim when dropping support for Django 2.2. + if django.VERSION < (3, 0): + from django.middleware.csrf import _get_new_csrf_token + else: + from django.middleware.csrf import ( + _get_new_csrf_string, _mask_cipher_secret + ) + + def _get_new_csrf_token(): + return _mask_cipher_secret(_get_new_csrf_string()) self.csrf_client.login(username=self.username, password=self.password) From f46c33e4e2c545e3fd90de692242c53a6fe6e7e9 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 22 Sep 2021 10:07:00 +0200 Subject: [PATCH 0811/1262] Fixed TestDefaultTZDateTimeField to allow multiple tzinfo implementations. zoneinfo was made the default time zone implementation in https://github.com/django/django/commit/306607d5b99b6eca6ae2c1e726d8eb32b9b2ca1b. --- tests/test_fields.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/test_fields.py b/tests/test_fields.py index a3b37584bc..7a5304a82a 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1464,15 +1464,24 @@ def setup_class(cls): cls.field = serializers.DateTimeField() cls.kolkata = pytz.timezone('Asia/Kolkata') + def assertUTC(self, tzinfo): + """ + Check UTC for datetime.timezone, ZoneInfo, and pytz tzinfo instances. + """ + assert ( + tzinfo is utc or + (getattr(tzinfo, "key", None) or getattr(tzinfo, "zone", None)) == "UTC" + ) + def test_default_timezone(self): - assert self.field.default_timezone() == utc + self.assertUTC(self.field.default_timezone()) def test_current_timezone(self): - assert self.field.default_timezone() == utc + self.assertUTC(self.field.default_timezone()) activate(self.kolkata) assert self.field.default_timezone() == self.kolkata deactivate() - assert self.field.default_timezone() == utc + self.assertUTC(self.field.default_timezone()) @pytest.mark.skipif(pytz is None, reason='pytz not installed') From c62e3ca764d90e7b8402cc4022cffad2a07fa5be Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 22 Sep 2021 10:08:14 +0200 Subject: [PATCH 0812/1262] Added Django 4.0 to test matrix. --- tox.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 25f8418219..6f49d373fc 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ envlist = {py35,py36,py37}-django22, {py36,py37,py38,py39}-django31, {py36,py37,py38,py39}-django32, - {py38,py39}-djangomain, + {py38,py39}-{django40,djangomain}, base,dist,docs, [travis:env] @@ -11,6 +11,7 @@ DJANGO = 2.2: django22 3.1: django31 3.2: django32 + 4.0: django40 main: djangomain [testenv] @@ -23,6 +24,7 @@ deps = django22: Django>=2.2,<3.0 django31: Django>=3.1,<3.2 django32: Django>=3.2,<4.0 + django40: Django>=4.0a1,<5.0 djangomain: https://github.com/django/django/archive/main.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From 1fa5bc31c0c047eb2109929b17f8e53e84b7d40b Mon Sep 17 00:00:00 2001 From: Akhil Kokani Date: Mon, 27 Sep 2021 14:31:47 +0530 Subject: [PATCH 0813/1262] Update serializers.md (#8189) * Update serializers.md Removed unwanted word, "neither". * Update docs/api-guide/serializers.md Co-authored-by: Tom Christie --- 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 cf8525748c..377f732acd 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -116,7 +116,7 @@ Calling `.save()` will either create a new instance, or update an existing insta # .save() will update the existing `comment` instance. serializer = CommentSerializer(comment, data=data) -Both the `.create()` and `.update()` methods are optional. You can implement either neither, one, or both of them, depending on the use-case for your serializer class. +Both the `.create()` and `.update()` methods are optional. You can implement either none, one, or both of them, depending on the use-case for your serializer class. #### Passing additional attributes to `.save()` From 605a624da6958bd5e633a391eb65e72d0526f37c Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Tue, 5 Oct 2021 14:02:34 +0100 Subject: [PATCH 0814/1262] Add PostHog as premium sponsors (#8193) * Add PostHog as premium sponsors * Adding 275x250 PostHog image --- README.md | 5 ++++- docs/img/premium/posthog-readme.png | Bin 0 -> 2402 bytes docs/index.md | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 docs/img/premium/posthog-readme.png diff --git a/README.md b/README.md index e3bcc2a1c2..8ba0a5e1d3 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,9 @@ The initial aim is to provide a single full-time position on REST framework. [![][esg-img]][esg-url] [![][retool-img]][retool-url] [![][bitio-img]][bitio-url] +[![][posthog-img]][posthog-url] -Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Rollbar][rollbar-url], [ESG][esg-url], [Retool][retool-url], and [bit.io][bitio-url]. +Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Rollbar][rollbar-url], [ESG][esg-url], [Retool][retool-url], [bit.io][bitio-url], and [PostHog][posthog-url]. --- @@ -195,6 +196,7 @@ Please see the [security policy][security-policy]. [esg-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/esg-readme.png [retool-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/retool-readme.png [bitio-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/bitio-readme.png +[posthog-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/posthog-readme.png [sentry-url]: https://getsentry.com/welcome/ [stream-url]: https://getstream.io/?utm_source=drf&utm_medium=sponsorship&utm_content=developer @@ -202,6 +204,7 @@ Please see the [security policy][security-policy]. [esg-url]: https://software.esg-usa.com/ [retool-url]: https://retool.com/?utm_source=djangorest&utm_medium=sponsorship [bitio-url]: https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship +[posthog-url]: https://posthog.com?utm_source=drf&utm_medium=sponsorship&utm_campaign=open-source-sponsorship [oauth1-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth [oauth2-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit diff --git a/docs/img/premium/posthog-readme.png b/docs/img/premium/posthog-readme.png new file mode 100644 index 0000000000000000000000000000000000000000..9ca8b0ecf04599491790c65f6ac2382a1f45cb34 GIT binary patch literal 2402 zcmai$dpy(oAIIm?kqEUVMI1|COS+h2jFQXTx1B9IU5@L*+;d$jLMd%T+LDdA=Y9!U zO`&8o_mayIDdv_-A-UVxJbwN6dpyqL@p*i{ulM)+e7#=Z@At0{8Hcslxnuth5D2u> z$`W-31QG!Y^y+po!Cqx!8!Whp6OA#(AW(UN)Vj+y!7LJV#=-^wfTCV04r1+CN^`RT6k>eVYF zBcruOb2^VqMINXu5{N;Nm zqHJ#!fI!<5tx(2xq5U(1Q-n$wWLu}W2EJU6rPH6guvvy==MQlDuww4toQBkVvx=gB zS56+3F_shmk9B{jb$qEsJ>}{Re&V>w8v9)6amureTuaCH72!MtnmxS`rtIbfQ8e9$UWfTA8?9)t?!XzL_}Y0;vq!}N8n+<^&H^8AcCFY4@fNOCpmAEU**@Zl%mYXyJ-UgseWGAX^U|40*v+ zfXPrB@bc8vNIp9*MDikKcSEHwu|0PLVtGV@`6R2u#yu$wc8g+LOl>S2ad zNlq75jnbj!Li+}4Hk8eTq?md-io$%ik8Vif4I@P%8K6VEX@g_9_%%>-ohogqEmNrl zWWBQA=;}w`oukPTxMpbNgHp2WFZqNl@k?fK$Cs8z-!G%x@j@t#e39C>$l zgZtJ{NLKmfDJJjzpM)p-zow=T7c!IAXmXAAa3Y5p5EM;Z>c zOqrTzK1V;PP3h$!@uGlamb4CXM%Fvf9sS;PpLbw?dN;3?t`-CD<~==K$ffI8?KJBV z$O0~1mn?g`EfXfhy#hsnfXpWyk2r#9m7z44^L5I$0d_@TD)J6|dUee^N(9NF1QIdW z7c8JDBqEOj8U4gaCAneZ3DCwRAn``nY$?~Xx0?q80yo_Ds5fs2k_s70qc`RT8gvp` zTmuBwT)-mjhWgODYlM1lVq4QO!B6S!<=I0&irx##s3-*T9Gs{@SVZkJf7v0pGL<_l zXb8u|uZMka*Uc{xCpT@S)B;2g$(-4-#?`YOSJi-6!ACCYAY)CZ^w9FXyr)y%fk_sq zKox&aL8E}ZYC|6VH1U2bs(rYDQ1M*QV@F-69=a(k=n+(2Uo7kG{ah1=LObq9E{v5P z@wfuEW_yVEt^(zEYC-g_v**Icv2Uw@^!n2qc0lw;GgSyEajkc()wgzg zMwx4?mmd*ZSzv-sS*PrXmPwoHCu{J#mEU6f3zKHcfstl%v;N|g@enS*%Ae54Yj}uj z%lRoIKV@-jL<`+`t5!Mnl8aSt*vsldhPXT0zMwmyK)w{wBavcnpw(X>_Rr*HGi{_F z`~2dwy1Ho@*a$7Y4t*SX^aNdgA^0qU)M;vy`e}AuIy{*?sC@UQs*0i<6PuK90L3BJ z#nu2OGmkAxPU;{s;ymw-l>?7T5Yauir!94LW*z^DztyXG!f=i;5#G|L`JzA-TT^-W zHSb#W!vG3FzjIEZh*9}Hxs6!gW;WIx?N=MJ7)t$m?ZjAUQpgDMNjqCLrYR@e8Ul8~ z+Psy>pX(WZ4Gf-eKGYK;%Zx|s>8`Sae1=jgch4VabK)gvvD*?Qv2i2$B^`H8g(+00 z8erfHDDIDivGiM~LzTQwW+tc7($y{!-|=5XBhem8GYv~9_Zy_fot1a<^}{Qx3@~0n zS>`OXmrvV6GeX5woY=vOc{V)GTp#|EC$#lWGRBhLZsws0(C2FIb2}u9JexkenOO|F z=vsfrkhza|EXMqeXfR2?fdF^x_TMO+4y!qL_>}X}ju(jP#?Fv{$BUg`IpaUv(gW(Z zokCrLI|RF?YB?TgRZY3pmX*-GG86H1Km1GAOX<@~Si73-#j?8`_8RP`)TGykPIkqn zX{gu7UC#Qv&Yk?s{{C}%Q7(!+Rdy)#y!uRZldTj(uRUy_tCLN2kOmNxK6b8dBUqK0 z3rQnZ0wp{|!dKhD!%!+SW46E;%T$M|%&@3Z6X%=% E0-^VOCIA2c literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md index f000d2e093..54b7881072 100644 --- a/docs/index.md +++ b/docs/index.md @@ -72,10 +72,11 @@ continued development by **[signing up for a paid plan][funding]**.
    • Rollbar
    • Retool
    • bit.io
    • +
    • PostHog
    -*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=sponsorship&utm_content=developer), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), and [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship).* +*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=sponsorship&utm_content=developer), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), and [bit.io](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship).* --- From 6ea95b6ad1bc0d4a4234a267b1ba32701878c6bb Mon Sep 17 00:00:00 2001 From: thetarby <45286577+thetarby@users.noreply.github.com> Date: Tue, 5 Oct 2021 17:33:55 +0300 Subject: [PATCH 0815/1262] Highlight `select_related` and `prefetch_related` usage in documentation (#7610) * docs updated to highlight use of select_related and prefetch related to avoid n+1 problems * Apply suggestions from code review cosmetic changes Co-authored-by: Xavier Ordoquy * cosmetic changes Co-authored-by: Xavier Ordoquy --- docs/api-guide/fields.md | 10 +++++++++- docs/api-guide/generic-views.md | 7 +++++++ docs/api-guide/relations.md | 31 +++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index b986009f9b..5b9688dcab 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -78,7 +78,14 @@ Defaults to `False` ### `source` -The name of the attribute that will be used to populate the field. May be a method that only takes a `self` argument, such as `URLField(source='get_absolute_url')`, or may use dotted notation to traverse attributes, such as `EmailField(source='user.email')`. When serializing fields with dotted notation, it may be necessary to provide a `default` value if any object is not present or is empty during attribute traversal. +The name of the attribute that will be used to populate the field. May be a method that only takes a `self` argument, such as `URLField(source='get_absolute_url')`, or may use dotted notation to traverse attributes, such as `EmailField(source='user.email')`. + +When serializing fields with dotted notation, it may be necessary to provide a `default` value if any object is not present or is empty during attribute traversal. Beware of possible n+1 problems when using source attribute if you are accessing a relational orm model. For example: + + class CommentSerializer(serializers.Serializer): + email = serializers.EmailField(source="user.email") + +would require user object to be fetched from database when it is not prefetched. If that is not wanted, be sure to be using `prefetch_related` and `select_related` methods appropriately. For more information about the methods refer to [django documentation][django-docs-select-related]. The value `source='*'` has a special meaning, and is used to indicate that the entire object should be passed through to the field. This can be useful for creating nested representations, or for fields which require access to the complete object in order to determine the output representation. @@ -855,3 +862,4 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide [django-hstore]: https://github.com/djangonauts/django-hstore [python-decimal-rounding-modes]: https://docs.python.org/3/library/decimal.html#rounding-modes [django-current-timezone]: https://docs.djangoproject.com/en/stable/topics/i18n/timezones/#default-time-zone-and-current-time-zone +[django-docs-select-related]: https://docs.djangoproject.com/en/3.1/ref/models/querysets/#django.db.models.query.QuerySet.select_related diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index afc2cab563..fbafec93ad 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -96,6 +96,12 @@ For example: user = self.request.user return user.accounts.all() +--- + +**Note:** If the serializer_class used in the generic view spans orm relations, leading to an n+1 problem, you could optimize your queryset in this method using `select_related` and `prefetch_related`. To get more information about n+1 problem and use cases of the mentioned methods refer to related section in [django documentation][django-docs-select-related]. + +--- + #### `get_object(self)` Returns an object instance that should be used for detail views. Defaults to using the `lookup_field` parameter to filter the base queryset. @@ -389,3 +395,4 @@ The following third party packages provide additional generic view implementatio [UpdateModelMixin]: #updatemodelmixin [DestroyModelMixin]: #destroymodelmixin [django-rest-multiple-models]: https://github.com/MattBroach/DjangoRestMultipleModels +[django-docs-select-related]: https://docs.djangoproject.com/en/3.1/ref/models/querysets/#django.db.models.query.QuerySet.select_related \ No newline at end of file diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index f444125cff..4547253b0a 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -17,6 +17,37 @@ Relational fields are used to represent model relationships. They can be applie --- +--- + +**Note:** REST Framework does not attempt to automatically optimize querysets passed to serializers in terms of `select_related` and `prefetch_related` since it would be too much magic. A serializer with a field spanning an orm relation through its source attribute could require an additional database hit to fetch related object from the database. It is the programmer's responsibility to optimize queries to avoid additional database hits which could occur while using such a serializer. + +For example, the following serializer would lead to a database hit each time evaluating the tracks field if it is not prefetched: + + class AlbumSerializer(serializers.ModelSerializer): + tracks = serializers.SlugRelatedField( + many=True, + read_only=True, + slug_field='title' + ) + + class Meta: + model = Album + fields = ['album_name', 'artist', 'tracks'] + + # For each album object, tracks should be fetched from database + qs = Album.objects.all() + print(AlbumSerializer(qs, many=True).data) + +If `AlbumSerializer` is used to serialize a fairly large queryset with `many=True` then it could be a serious performance problem. Optimizing the queryset passed to `AlbumSerializer` with: + + qs = Album.objects.prefetch_related('tracks') + # No additional database hits required + print(AlbumSerializer(qs, many=True).data) + +would solve the issue. + +--- + #### Inspecting relationships. When using the `ModelSerializer` class, serializer fields and relationships will be automatically generated for you. Inspecting these automatically generated fields can be a useful tool for determining how to customize the relationship style. From 53a0585dacea328ce74083f0da0dea10c4df03e5 Mon Sep 17 00:00:00 2001 From: Edmund <2623895+edmundlam@users.noreply.github.com> Date: Thu, 7 Oct 2021 04:09:00 -0400 Subject: [PATCH 0816/1262] Update permissions.md to fix garden path sentences (#8206) --- docs/api-guide/permissions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 6912c375c2..19bc0e66ae 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -24,9 +24,9 @@ A slightly less strict style of permission would be to allow full access to auth Permissions in REST framework are always defined as a list of permission classes. Before running the main body of the view each permission in the list is checked. -If any permission check fails an `exceptions.PermissionDenied` or `exceptions.NotAuthenticated` exception will be raised, and the main body of the view will not run. +If any permission check fails, an `exceptions.PermissionDenied` or `exceptions.NotAuthenticated` exception will be raised, and the main body of the view will not run. -When the permissions checks fail either a "403 Forbidden" or a "401 Unauthorized" response will be returned, according to the following rules: +When the permission checks fail, either a "403 Forbidden" or a "401 Unauthorized" response will be returned, according to the following rules: * The request was successfully authenticated, but permission was denied. *— An HTTP 403 Forbidden response will be returned.* * The request was not successfully authenticated, and the highest priority authentication class *does not* use `WWW-Authenticate` headers. *— An HTTP 403 Forbidden response will be returned.* From ddc5cd7e4b5f4bf250afd412b314af6728ea1726 Mon Sep 17 00:00:00 2001 From: Uzair Ali <72073401+uzair-ali10@users.noreply.github.com> Date: Thu, 7 Oct 2021 20:22:44 +0530 Subject: [PATCH 0817/1262] Imported Response (#8207) --- docs/api-guide/views.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 2224c1f3a5..878a291b22 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -145,6 +145,7 @@ REST framework also allows you to work with regular function based views. It pr The core of this functionality is the `api_view` decorator, which takes a list of HTTP methods that your view should respond to. For example, this is how you would write a very simple view that just manually returns some data: from rest_framework.decorators import api_view + from rest_framework.response import Response @api_view() def hello_world(request): From 00cd4ef864a8bf6d6c90819a983017070f9f08a5 Mon Sep 17 00:00:00 2001 From: rgermain Date: Fri, 15 Oct 2021 16:13:20 +0200 Subject: [PATCH 0818/1262] add third packages nested-multipart-parser (#8208) --- 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 933244a6a9..e25421f503 100644 --- a/docs/community/third-party-packages.md +++ b/docs/community/third-party-packages.md @@ -106,6 +106,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque * [djangorestframework-msgpack][djangorestframework-msgpack] - Provides MessagePack renderer and parser 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. * [djangorestframework-camel-case][djangorestframework-camel-case] - Provides camel case JSON renderers and parsers. +* [nested-multipart-parser][nested-multipart-parser] - Provides nested parser for http multipart request ### Renderers @@ -183,6 +184,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [wq.db.rest]: https://wq.io/docs/about-rest [djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack [djangorestframework-camel-case]: https://github.com/vbabiy/djangorestframework-camel-case +[nested-multipart-parser]: https://github.com/remigermain/nested-multipart-parser [djangorestframework-csv]: https://github.com/mjumbewu/django-rest-framework-csv [drf_ujson2]: https://github.com/Amertz08/drf_ujson2 [rest-pandas]: https://github.com/wq/django-rest-pandas From 781890b7df88086d9cba07904e53db346ec4a715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lavoie?= Date: Mon, 8 Nov 2021 03:59:32 -0600 Subject: [PATCH 0819/1262] docs(api-guide-testing): Fix typo 'CRSF' and plural of word (#8238) --- docs/api-guide/testing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md index 73de68a76b..62eb8dd1a5 100644 --- a/docs/api-guide/testing.md +++ b/docs/api-guide/testing.md @@ -234,7 +234,7 @@ If you're using `SessionAuthentication` then you'll need to include a CSRF token for any `POST`, `PUT`, `PATCH` or `DELETE` requests. You can do so by following the same flow that a JavaScript based client would use. -First make a `GET` request in order to obtain a CRSF token, then present that +First, make a `GET` request in order to obtain a CSRF token, then present that token in the following request. For example... @@ -259,7 +259,7 @@ With careful usage both the `RequestsClient` and the `CoreAPIClient` provide the ability to write test cases that can run either in development, or be run directly against your staging server or production environment. -Using this style to create basic tests of a few core piece of functionality is +Using this style to create basic tests of a few core pieces of functionality is a powerful way to validate your live service. Doing so may require some careful attention to setup and teardown to ensure that the tests run in a way that they do not directly affect customer data. From 060a3b632f6f6ff2f84235d1be5da55020c40ff3 Mon Sep 17 00:00:00 2001 From: Anton Burnashev Date: Wed, 10 Nov 2021 17:31:15 +0100 Subject: [PATCH 0820/1262] Docs: fix broken link (#8245) --- docs/api-guide/pagination.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 632af6a823..379c1975ad 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -312,7 +312,7 @@ The [`drf-proxy-pagination` package][drf-proxy-pagination] includes a `ProxyPagi ## link-header-pagination -The [`django-rest-framework-link-header-pagination` package][drf-link-header-pagination] includes a `LinkHeaderPagination` class which provides pagination via an HTTP `Link` header as described in [Github's developer documentation](github-link-pagination). +The [`django-rest-framework-link-header-pagination` package][drf-link-header-pagination] includes a `LinkHeaderPagination` class which provides pagination via an HTTP `Link` header as described in [GitHub REST API documentation][github-traversing-with-pagination]. [cite]: https://docs.djangoproject.com/en/stable/topics/pagination/ [link-header]: ../img/link-header-pagination.png @@ -322,3 +322,4 @@ The [`django-rest-framework-link-header-pagination` package][drf-link-header-pag [drf-link-header-pagination]: https://github.com/tbeadle/django-rest-framework-link-header-pagination [disqus-cursor-api]: https://cra.mr/2011/03/08/building-cursors-for-the-disqus-api [float_cursor_pagination_example]: https://gist.github.com/keturn/8bc88525a183fd41c73ffb729b8865be#file-fpcursorpagination-py +[github-traversing-with-pagination]: https://docs.github.com/en/rest/guides/traversing-with-pagination From 0d5250cffada2ac250e24407953d4862d04d3dae Mon Sep 17 00:00:00 2001 From: Dmytro Litvinov Date: Mon, 15 Nov 2021 10:54:19 +0200 Subject: [PATCH 0821/1262] Fix link to installation of httpie (#8257) Right now httpie moved to "httpie" organization (https://github.com/httpie/httpie) and they don't have "installation" at their GitHub. Instead of that, they have "Getting started" section with link to "Installation instructions". --- docs/tutorial/quickstart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md index ee839790f1..f4dcc5606c 100644 --- a/docs/tutorial/quickstart.md +++ b/docs/tutorial/quickstart.md @@ -225,4 +225,4 @@ If you want to get a more in depth understanding of how REST framework fits toge [image]: ../img/quickstart.png [tutorial]: 1-serialization.md [guide]: ../api-guide/requests.md -[httpie]: https://github.com/jakubroztocil/httpie#installation +[httpie]: https://httpie.io/docs#installation From 580bf45ccfd5c423a938729907d813f4862dca38 Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Mon, 22 Nov 2021 16:48:58 +0600 Subject: [PATCH 0822/1262] test v4 beta 1 (#8222) * test v4 beta 1 * django 4 rc1 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 6f49d373fc..1ab5051953 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ deps = django22: Django>=2.2,<3.0 django31: Django>=3.1,<3.2 django32: Django>=3.2,<4.0 - django40: Django>=4.0a1,<5.0 + django40: Django>=4.0rc1,<5.0 djangomain: https://github.com/django/django/archive/main.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From 380ac8e79dd85e6798eb00a730b7d4c4c4a86ebd Mon Sep 17 00:00:00 2001 From: Yecine Megdiche Date: Mon, 6 Dec 2021 16:32:33 +0100 Subject: [PATCH 0823/1262] Remove old-style `super` calls (#8226) --- docs/api-guide/filtering.md | 2 +- docs/api-guide/serializers.md | 2 +- docs/tutorial/4-authentication-and-permissions.md | 2 +- rest_framework/fields.py | 2 ++ rest_framework/schemas/coreapi.py | 8 ++++---- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 3541388ca2..512acafbd9 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -241,7 +241,7 @@ To dynamically change search fields based on request content, it's possible to s def get_search_fields(self, view, request): if request.query_params.get('title_only'): return ['title'] - return super(CustomSearchFilter, self).get_search_fields(view, request) + return super().get_search_fields(view, request) For more details, see the [Django documentation][search-django-admin]. diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 377f732acd..4d032bd9ec 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -1095,7 +1095,7 @@ For example, if you wanted to be able to set which fields should be used by a se fields = kwargs.pop('fields', None) # Instantiate the superclass normally - super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if fields is not None: # Drop any fields that are not specified in the `fields` argument. diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md index 79ce355c93..cb0321ea21 100644 --- a/docs/tutorial/4-authentication-and-permissions.md +++ b/docs/tutorial/4-authentication-and-permissions.md @@ -38,7 +38,7 @@ And now we can add a `.save()` method to our model class: formatter = HtmlFormatter(style=self.style, linenos=linenos, full=True, **options) self.highlighted = highlight(self.code, lexer, formatter) - super(Snippet, self).save(*args, **kwargs) + super().save(*args, **kwargs) When that's all done we'll need to update our database tables. Normally we'd create a database migration in order to do that, but for the purposes of this tutorial, let's just delete the database and start again. diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 5cafed5556..d7e7816cee 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1491,6 +1491,8 @@ def to_internal_value(self, data): self.fail('empty') return { + # Arguments for super() are needed because of scoping inside + # comprehensions. super(MultipleChoiceField, self).to_internal_value(item) for item in data } diff --git a/rest_framework/schemas/coreapi.py b/rest_framework/schemas/coreapi.py index 75ed5671af..179f0fa3c8 100644 --- a/rest_framework/schemas/coreapi.py +++ b/rest_framework/schemas/coreapi.py @@ -58,7 +58,7 @@ class LinkNode(OrderedDict): def __init__(self): self.links = [] self.methods_counter = Counter() - super(LinkNode, self).__init__() + super().__init__() def get_available_key(self, preferred_key): if preferred_key not in self: @@ -120,7 +120,7 @@ def __init__(self, title=None, url=None, description=None, patterns=None, urlcon assert coreapi, '`coreapi` must be installed for schema support.' assert coreschema, '`coreschema` must be installed for schema support.' - super(SchemaGenerator, self).__init__(title, url, description, patterns, urlconf) + super().__init__(title, url, description, patterns, urlconf) self.coerce_method_names = api_settings.SCHEMA_COERCE_METHOD_NAMES def get_links(self, request=None): @@ -346,7 +346,7 @@ def __init__(self, manual_fields=None): * `manual_fields`: list of `coreapi.Field` instances that will be added to auto-generated fields, overwriting on `Field.name` """ - super(AutoSchema, self).__init__() + super().__init__() if manual_fields is None: manual_fields = [] self._manual_fields = manual_fields @@ -587,7 +587,7 @@ def __init__(self, fields, description='', encoding=None): * `fields`: list of `coreapi.Field` instances. * `description`: String description for view. Optional. """ - super(ManualSchema, self).__init__() + super().__init__() assert all(isinstance(f, coreapi.Field) for f in fields), "`fields` must be a list of coreapi.Field instances" self._fields = fields self._description = description From dabf2216c33a365f80354d962177d72914e8936f Mon Sep 17 00:00:00 2001 From: Jaap Roes Date: Wed, 8 Dec 2021 15:30:34 +0100 Subject: [PATCH 0824/1262] Update django-cors-headers links (#8176) --- docs/topics/ajax-csrf-cors.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/topics/ajax-csrf-cors.md b/docs/topics/ajax-csrf-cors.md index 646f3f5638..a65e3fdf8d 100644 --- a/docs/topics/ajax-csrf-cors.md +++ b/docs/topics/ajax-csrf-cors.md @@ -31,11 +31,11 @@ In order to make AJAX requests, you need to include CSRF token in the HTTP heade The best way to deal with CORS in REST framework is to add the required response headers in middleware. This ensures that CORS is supported transparently, without having to change any behavior in your views. -[Otto Yiu][ottoyiu] maintains the [django-cors-headers] package, which is known to work correctly with REST framework APIs. +[Adam Johnson][adamchainz] maintains the [django-cors-headers] package, which is known to work correctly with REST framework APIs. [cite]: https://blog.codinghorror.com/preventing-csrf-and-xsrf-attacks/ [csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) [csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax [cors]: https://www.w3.org/TR/cors/ -[ottoyiu]: https://github.com/ottoyiu/ -[django-cors-headers]: https://github.com/ottoyiu/django-cors-headers/ +[adamchainz]: https://github.com/adamchainz +[django-cors-headers]: https://github.com/adamchainz/django-cors-headers From 37b73ef46e8cf4cc746709542d7d26f6b152a26d Mon Sep 17 00:00:00 2001 From: Jeremy Langley Date: Wed, 8 Dec 2021 06:33:41 -0800 Subject: [PATCH 0825/1262] IsAdmin permissions changed to IsAdminUser (#8227) Documentation change to keep up with the code permission changes. Co-authored-by: Jeremy Langley --- docs/api-guide/viewsets.md | 2 +- docs/community/3.9-announcement.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index d4ab5a7317..4179725078 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -125,7 +125,7 @@ You may inspect these attributes to adjust behaviour based on the current action if self.action == 'list': permission_classes = [IsAuthenticated] else: - permission_classes = [IsAdmin] + permission_classes = [IsAdminUser] return [permission() for permission in permission_classes] ## Marking extra actions for routing diff --git a/docs/community/3.9-announcement.md b/docs/community/3.9-announcement.md index fee6e69096..d673fdd183 100644 --- a/docs/community/3.9-announcement.md +++ b/docs/community/3.9-announcement.md @@ -110,7 +110,7 @@ You can now compose permission classes using the and/or operators, `&` and `|`. For example... ```python -permission_classes = [IsAuthenticated & (ReadOnly | IsAdmin)] +permission_classes = [IsAuthenticated & (ReadOnly | IsAdminUser)] ``` If you're using custom permission classes then make sure that you are subclassing From 3a762d9aac526f26ea2e9798140cb99a1d3ebc18 Mon Sep 17 00:00:00 2001 From: Matthew Pull Date: Wed, 8 Dec 2021 14:35:06 +0000 Subject: [PATCH 0826/1262] Update permissions.md (#8260) I might just be misunderstanding something (always a strong possibility!), but it seems to me that the table on the Permissions page is slightly inaccurate. For `permission_classes`, wouldn't it have global-level permissions for list actions (rather than no permission control, as is currently listed)? --- docs/api-guide/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 19bc0e66ae..5d6462b45d 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -286,7 +286,7 @@ The following table lists the access restriction methods and the level of contro | | `queryset` | `permission_classes` | `serializer_class` | |------------------------------------|------------|----------------------|--------------------| -| Action: list | global | no | object-level* | +| Action: list | global | global | object-level* | | Action: create | no | global | object-level | | Action: retrieve | global | object-level | object-level | | Action: update | global | object-level | object-level | From b0d407fd6344e6be9a0f1374cf53cf7e5286b67f Mon Sep 17 00:00:00 2001 From: Alexander Klimenko Date: Wed, 8 Dec 2021 17:37:32 +0300 Subject: [PATCH 0827/1262] Made api_setting.UNICODE_JSON/ensure_ascii affecting json schema (#7991) --- rest_framework/renderers.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 5b7ba8a8c8..b0ddca2b59 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -1035,13 +1035,16 @@ class CoreAPIJSONOpenAPIRenderer(_BaseOpenAPIRenderer): media_type = 'application/vnd.oai.openapi+json' charset = None format = 'openapi-json' + ensure_ascii = not api_settings.UNICODE_JSON def __init__(self): assert coreapi, 'Using CoreAPIJSONOpenAPIRenderer, but `coreapi` is not installed.' def render(self, data, media_type=None, renderer_context=None): structure = self.get_structure(data) - return json.dumps(structure, indent=4).encode('utf-8') + return json.dumps( + structure, indent=4, + ensure_ascii=self.ensure_ascii).encode('utf-8') class OpenAPIRenderer(BaseRenderer): @@ -1065,6 +1068,9 @@ class JSONOpenAPIRenderer(BaseRenderer): charset = None encoder_class = encoders.JSONEncoder format = 'openapi-json' + ensure_ascii = not api_settings.UNICODE_JSON def render(self, data, media_type=None, renderer_context=None): - return json.dumps(data, cls=self.encoder_class, indent=2).encode('utf-8') + return json.dumps( + data, cls=self.encoder_class, indent=2, + ensure_ascii=self.ensure_ascii).encode('utf-8') From 47ee3fc9a999440e721424aa5100f9eb216f0096 Mon Sep 17 00:00:00 2001 From: Chen Wen Kang <23054115+cwkang1998@users.noreply.github.com> Date: Wed, 8 Dec 2021 22:38:42 +0800 Subject: [PATCH 0828/1262] Update docs related to coreapi to include deprecation notice (#8186) * Update docs related to coreapi to include deprecation notice * Update docs to use reference to version 3.10 release notes instead of 3.9 --- docs/coreapi/7-schemas-and-client-libraries.md | 11 +++++++++++ docs/coreapi/from-documenting-your-api.md | 11 +++++++++++ docs/coreapi/index.md | 4 ++-- docs/coreapi/schemas.md | 8 ++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/docs/coreapi/7-schemas-and-client-libraries.md b/docs/coreapi/7-schemas-and-client-libraries.md index 203d81ea5d..d95019dab6 100644 --- a/docs/coreapi/7-schemas-and-client-libraries.md +++ b/docs/coreapi/7-schemas-and-client-libraries.md @@ -1,5 +1,16 @@ # Tutorial 7: Schemas & client libraries +---- + +**DEPRECATION NOTICE:** Use of CoreAPI-based schemas were deprecated with the introduction of native OpenAPI-based schema generation as of Django REST Framework v3.10. See the [Version 3.10 Release Announcement](../community/3.10-announcement.md) for more details. + +If you are looking for information regarding schemas, you might want to look at these updated resources: + +1. [Schema](../api-guide/schemas.md) +2. [Documenting your API](../topics/documenting-your-api.md) + +---- + A schema is a machine-readable document that describes the available API endpoints, their URLS, and what operations they support. diff --git a/docs/coreapi/from-documenting-your-api.md b/docs/coreapi/from-documenting-your-api.md index 604dfa6686..65ad71c7a7 100644 --- a/docs/coreapi/from-documenting-your-api.md +++ b/docs/coreapi/from-documenting-your-api.md @@ -1,6 +1,17 @@ ## Built-in API documentation +---- + +**DEPRECATION NOTICE:** Use of CoreAPI-based schemas were deprecated with the introduction of native OpenAPI-based schema generation as of Django REST Framework v3.10. See the [Version 3.10 Release Announcement](../community/3.10-announcement.md) for more details. + +If you are looking for information regarding schemas, you might want to look at these updated resources: + +1. [Schema](../api-guide/schemas.md) +2. [Documenting your API](../topics/documenting-your-api.md) + +---- + The built-in API documentation includes: * Documentation of API endpoints. diff --git a/docs/coreapi/index.md b/docs/coreapi/index.md index 9195eb33e4..dbcb115840 100644 --- a/docs/coreapi/index.md +++ b/docs/coreapi/index.md @@ -1,8 +1,8 @@ # Legacy CoreAPI Schemas Docs -Use of CoreAPI-based schemas were deprecated with the introduction of native OpenAPI-based schema generation in Django REST Framework v3.10. +Use of CoreAPI-based schemas were deprecated with the introduction of native OpenAPI-based schema generation as of Django REST Framework v3.10. -See the [Version 3.10 Release Announcement](/community/3.10-announcement.md) for more details. +See the [Version 3.10 Release Announcement](../community/3.10-announcement.md) for more details. ---- diff --git a/docs/coreapi/schemas.md b/docs/coreapi/schemas.md index 653105a7a1..9f1482d2d8 100644 --- a/docs/coreapi/schemas.md +++ b/docs/coreapi/schemas.md @@ -2,6 +2,14 @@ source: schemas.py # Schemas +---- + +**DEPRECATION NOTICE:** Use of CoreAPI-based schemas were deprecated with the introduction of native OpenAPI-based schema generation as of Django REST Framework v3.10. See the [Version 3.10 Release Announcement](../community/3.10-announcement.md) for more details. + +You are probably looking for [this page](../api-guide/schemas.md) if you want latest information regarding schemas. + +---- + > A machine-readable [schema] describes what resources are available via the API, what their URLs are, how they are represented and what operations they support. > > — Heroku, [JSON Schema for the Heroku Platform API][cite] From 6e0cb8a7aa2db1694b46fa3eff5a5271fd7d828e Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 8 Dec 2021 14:53:06 +0000 Subject: [PATCH 0829/1262] Add CryptAPI sponsorship (#8283) --- README.md | 5 ++++- docs/img/premium/cryptapi-readme.png | Bin 0 -> 17864 bytes 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 docs/img/premium/cryptapi-readme.png diff --git a/README.md b/README.md index 8ba0a5e1d3..4b089469f6 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,9 @@ The initial aim is to provide a single full-time position on REST framework. [![][retool-img]][retool-url] [![][bitio-img]][bitio-url] [![][posthog-img]][posthog-url] +[![][cryptapi-img]][cryptapi-url] -Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Rollbar][rollbar-url], [ESG][esg-url], [Retool][retool-url], [bit.io][bitio-url], and [PostHog][posthog-url]. +Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Rollbar][rollbar-url], [ESG][esg-url], [Retool][retool-url], [bit.io][bitio-url], [PostHog][posthog-url], and [CryptAPI][cryptapi-url]. --- @@ -197,6 +198,7 @@ Please see the [security policy][security-policy]. [retool-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/retool-readme.png [bitio-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/bitio-readme.png [posthog-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/posthog-readme.png +[posthog-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/cryptapi-readme.png [sentry-url]: https://getsentry.com/welcome/ [stream-url]: https://getstream.io/?utm_source=drf&utm_medium=sponsorship&utm_content=developer @@ -205,6 +207,7 @@ Please see the [security policy][security-policy]. [retool-url]: https://retool.com/?utm_source=djangorest&utm_medium=sponsorship [bitio-url]: https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship [posthog-url]: https://posthog.com?utm_source=drf&utm_medium=sponsorship&utm_campaign=open-source-sponsorship +[cryptapi-url]: https://cryptapi.io [oauth1-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth [oauth2-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit diff --git a/docs/img/premium/cryptapi-readme.png b/docs/img/premium/cryptapi-readme.png new file mode 100644 index 0000000000000000000000000000000000000000..163f6a9ea20f17909e6e38537a627f19e5766a66 GIT binary patch literal 17864 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 G_5WXyfz+@7 literal 0 HcmV?d00001 From d1bab643ab249f2c18cc0e6047033ca726c51605 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 8 Dec 2021 14:53:45 +0000 Subject: [PATCH 0830/1262] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b089469f6..8875d650ef 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,7 @@ Please see the [security policy][security-policy]. [retool-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/retool-readme.png [bitio-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/bitio-readme.png [posthog-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/posthog-readme.png -[posthog-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/cryptapi-readme.png +[cryptapi-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/cryptapi-readme.png [sentry-url]: https://getsentry.com/welcome/ [stream-url]: https://getstream.io/?utm_source=drf&utm_medium=sponsorship&utm_content=developer From c05998f5ddedec7c8c012a9be08aa41130a46b75 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 8 Dec 2021 15:11:55 +0000 Subject: [PATCH 0831/1262] Add CryptAPI to docs homepage --- docs/index.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index 54b7881072..a86349df13 100644 --- a/docs/index.md +++ b/docs/index.md @@ -72,11 +72,12 @@ continued development by **[signing up for a paid plan][funding]**.

  • Rollbar
  • Retool
  • bit.io
  • -
  • PostHog
  • +
  • PostHog
  • +
  • CryptAPI
  • -*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=sponsorship&utm_content=developer), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), and [bit.io](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship).* +*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=sponsorship&utm_content=developer), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [PostHog](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), and [CryptAPI](https://cryptapi.io).* --- From 1cb3fa2e81ae33d52f4dcfc8b873f7b8093acd50 Mon Sep 17 00:00:00 2001 From: Jet Li Date: Fri, 10 Dec 2021 17:31:05 +0800 Subject: [PATCH 0832/1262] Test Django 4.0 (#8280) * Test Django 4.0 Django 4.0 released today. * Test Django 4.0 * Test Django 4.0 * Test Django 4.0 --- README.md | 2 +- docs/index.md | 2 +- setup.py | 1 + tox.ini | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8875d650ef..7a899cdb41 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ There is a live example API for testing purposes, [available here][sandbox]. # Requirements * Python (3.5, 3.6, 3.7, 3.8, 3.9) -* Django (2.2, 3.0, 3.1, 3.2) +* Django (2.2, 3.0, 3.1, 3.2, 4.0) 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 a86349df13..294e1e6d37 100644 --- a/docs/index.md +++ b/docs/index.md @@ -86,7 +86,7 @@ continued development by **[signing up for a paid plan][funding]**. REST framework requires the following: * Python (3.5, 3.6, 3.7, 3.8, 3.9) -* Django (2.2, 3.0, 3.1, 3.2) +* Django (2.2, 3.0, 3.1, 3.2, 4.0) 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 b8e220cb43..394845e148 100755 --- a/setup.py +++ b/setup.py @@ -93,6 +93,7 @@ def get_version(package): 'Framework :: Django :: 3.0', 'Framework :: Django :: 3.1', 'Framework :: Django :: 3.2', + 'Framework :: Django :: 4.0', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', diff --git a/tox.ini b/tox.ini index 1ab5051953..f2ae6cd6bd 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ deps = django22: Django>=2.2,<3.0 django31: Django>=3.1,<3.2 django32: Django>=3.2,<4.0 - django40: Django>=4.0rc1,<5.0 + django40: Django>=4.0,<5.0 djangomain: https://github.com/django/django/archive/main.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From 16ca0c24d3d6fdf5663f761cadc2d4b1baf8acc8 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 10 Dec 2021 11:53:48 +0000 Subject: [PATCH 0833/1262] Add 3.10 to tox.ini and setup.py --- setup.py | 2 +- tox.ini | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 394845e148..c18975ef29 100755 --- a/setup.py +++ b/setup.py @@ -99,11 +99,11 @@ def get_version(package): 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3 :: Only', 'Topic :: Internet :: WWW/HTTP', ], diff --git a/tox.ini b/tox.ini index f2ae6cd6bd..b7d62e0814 100644 --- a/tox.ini +++ b/tox.ini @@ -2,8 +2,8 @@ envlist = {py35,py36,py37}-django22, {py36,py37,py38,py39}-django31, - {py36,py37,py38,py39}-django32, - {py38,py39}-{django40,djangomain}, + {py36,py37,py38,py39,py310}-django32, + {py38,py39,py310}-{django40,djangomain}, base,dist,docs, [travis:env] @@ -54,3 +54,6 @@ ignore_outcome = true [testenv:py39-djangomain] ignore_outcome = true + +[testenv:py310-djangomain] +ignore_outcome = true From 217b0bf3af0b9332023113aab40283ea6929842d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 10 Dec 2021 12:04:27 +0000 Subject: [PATCH 0834/1262] Add Python 3.10 to test matrix (#8287) * Add Python 3.10 to test matrix * Update README, docs homepage to properly reflect Python versions that we test against --- .github/workflows/main.yml | 1 + README.md | 2 +- docs/index.md | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fc166c434d..42fee2a124 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,6 +18,7 @@ jobs: - '3.7' - '3.8' - '3.9' + - '3.10' steps: - uses: actions/checkout@v2 diff --git a/README.md b/README.md index 7a899cdb41..18d1364c69 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ There is a live example API for testing purposes, [available here][sandbox]. # Requirements -* Python (3.5, 3.6, 3.7, 3.8, 3.9) +* Python (3.6, 3.7, 3.8, 3.9, 3.10) * Django (2.2, 3.0, 3.1, 3.2, 4.0) We **highly recommend** and only officially support the latest patch release of diff --git a/docs/index.md b/docs/index.md index 294e1e6d37..2954f793ac 100644 --- a/docs/index.md +++ b/docs/index.md @@ -85,7 +85,7 @@ continued development by **[signing up for a paid plan][funding]**. REST framework requires the following: -* Python (3.5, 3.6, 3.7, 3.8, 3.9) +* Python (3.6, 3.7, 3.8, 3.9, 3.10) * Django (2.2, 3.0, 3.1, 3.2, 4.0) We **highly recommend** and only officially support the latest patch release of From 773f479719755193af8b0b7cb9915893738df152 Mon Sep 17 00:00:00 2001 From: Paolo Melchiorre Date: Fri, 10 Dec 2021 16:31:01 +0100 Subject: [PATCH 0835/1262] Python/Django compatibility updates (#8288) * Update python and django versions in tox.ini * Update python requires in setup.py * Update tox.ini Co-authored-by: Tom Christie --- setup.py | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index c18975ef29..210cc9ed0f 100755 --- a/setup.py +++ b/setup.py @@ -83,7 +83,7 @@ def get_version(package): packages=find_packages(exclude=['tests*']), include_package_data=True, install_requires=["django>=2.2", "pytz"], - python_requires=">=3.5", + python_requires=">=3.6", zip_safe=False, classifiers=[ 'Development Status :: 5 - Production/Stable', diff --git a/tox.ini b/tox.ini index b7d62e0814..a41176d72f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - {py35,py36,py37}-django22, + {py36,py37,py38,py39}-django22, {py36,py37,py38,py39}-django31, {py36,py37,py38,py39,py310}-django32, {py38,py39,py310}-{django40,djangomain}, From ba25869045f203c62a0a9ddf5c54b7f882d8308c Mon Sep 17 00:00:00 2001 From: Alexander Clausen Date: Mon, 13 Dec 2021 09:57:55 +0100 Subject: [PATCH 0836/1262] Fix `REQUIRED_PYTHON` in setup.py (#8292) Just a left-over from #8288 to sync the "Unsupported Python version" message with `python_requires`. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 210cc9ed0f..3c3761c866 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ from setuptools import find_packages, setup CURRENT_PYTHON = sys.version_info[:2] -REQUIRED_PYTHON = (3, 5) +REQUIRED_PYTHON = (3, 6) # This check and everything above must remain compatible with Python 2.7. if CURRENT_PYTHON < REQUIRED_PYTHON: From d0bb4d877f95ea85446b8fc66d247f01337897d2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 13 Dec 2021 09:33:03 +0000 Subject: [PATCH 0837/1262] Tweak test_description (#8293) --- tests/test_description.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_description.py b/tests/test_description.py index 3b7d95e0a1..363ad6513e 100644 --- a/tests/test_description.py +++ b/tests/test_description.py @@ -23,7 +23,7 @@ # hash style header # -``` json +```json [{ "alpha": 1, "beta": "this is a string" @@ -107,7 +107,7 @@ class MockView(APIView): # hash style header # - ``` json + ```json [{ "alpha": 1, "beta": "this is a string" From 9c97946531b85858fcee5df56240de6d29571da2 Mon Sep 17 00:00:00 2001 From: tim-mccurrach <34194722+tim-mccurrach@users.noreply.github.com> Date: Mon, 13 Dec 2021 13:08:40 +0000 Subject: [PATCH 0838/1262] Make api_view respect standard wrapper assignments (#8291) --- rest_framework/decorators.py | 20 ++++---------------- tests/test_decorators.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index 30b9d84d4e..7ba43d37c8 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -7,6 +7,7 @@ methods on viewsets that should be included by routers. """ import types +from functools import update_wrapper from django.forms.utils import pretty_name @@ -22,18 +23,8 @@ def api_view(http_method_names=None): def decorator(func): - WrappedAPIView = type( - 'WrappedAPIView', - (APIView,), - {'__doc__': func.__doc__} - ) - - # Note, the above allows us to set the docstring. - # It is the equivalent of: - # - # class WrappedAPIView(APIView): - # pass - # WrappedAPIView.__doc__ = func.doc <--- Not possible to do this + class WrappedAPIView(APIView): + pass # api_view applied without (method_names) assert not(isinstance(http_method_names, types.FunctionType)), \ @@ -52,9 +43,6 @@ def handler(self, *args, **kwargs): for method in http_method_names: setattr(WrappedAPIView, method.lower(), handler) - WrappedAPIView.__name__ = func.__name__ - WrappedAPIView.__module__ = func.__module__ - WrappedAPIView.renderer_classes = getattr(func, 'renderer_classes', APIView.renderer_classes) @@ -73,7 +61,7 @@ def handler(self, *args, **kwargs): WrappedAPIView.schema = getattr(func, 'schema', APIView.schema) - return WrappedAPIView.as_view() + return update_wrapper(WrappedAPIView.as_view(), func) return decorator diff --git a/tests/test_decorators.py b/tests/test_decorators.py index 99ba13e60c..116d6f1be4 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -162,6 +162,16 @@ def view(request): assert isinstance(view.cls.schema, CustomSchema) + def test_wrapper_assignments(self): + @api_view(["GET"]) + def test_view(request): + """example docstring""" + pass + + assert test_view.__name__ == "test_view" + assert test_view.__doc__ == "example docstring" + assert test_view.__qualname__ == "DecoratorTestCase.test_wrapper_assignments..test_view" + class ActionDecoratorTestCase(TestCase): From 7a84dc749cbc0db106c1ad40d7776ec307d08559 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 13 Dec 2021 13:10:17 +0000 Subject: [PATCH 0839/1262] Version 3.13 (#8285) * Version 3.12.5 * Version 3.13 * Version 3.13 --- docs/community/3.13-announcement.md | 55 +++++++++++++++++++++++++++++ docs/community/release-notes.md | 16 +++++++++ mkdocs.yml | 1 + rest_framework/__init__.py | 2 +- 4 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 docs/community/3.13-announcement.md diff --git a/docs/community/3.13-announcement.md b/docs/community/3.13-announcement.md new file mode 100644 index 0000000000..e2c1fefa64 --- /dev/null +++ b/docs/community/3.13-announcement.md @@ -0,0 +1,55 @@ + + +# Django REST framework 3.13 + +## Django 4.0 support + +The latest release now fully supports Django 4.0. + +Our requirements are now: + +* Python 3.6+ +* Django 4.0, 3.2, 3.1, 2.2 (LTS) + +## Fields arguments are now keyword-only + +When instantiating fields on serializers, you should always use keyword arguments, +such as `serializers.CharField(max_length=200)`. This has always been the case, +and all the examples that we have in the documentation use keyword arguments, +rather than positional arguments. + +From REST framework 3.13 onwards, this is now *explicitly enforced*. + +The most feasible cases where users might be accidentally omitting the keyword arguments +are likely in the composite fields, `ListField` and `DictField`. For instance... + +```python +aliases = serializers.ListField(serializers.CharField()) +``` + +They must now use the more explicit keyword argument style... + +```python +aliases = serializers.ListField(child=serializers.CharField()) +``` + +This change has been made because using positional arguments here *does not* result in the expected behaviour. + +See Pull Request [#7632](https://github.com/encode/django-rest-framework/pull/7632) for more details. diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md index baeeaf8741..d3e9dd7cc2 100644 --- a/docs/community/release-notes.md +++ b/docs/community/release-notes.md @@ -34,6 +34,22 @@ You can determine your currently installed version using `pip show`: --- +## 3.13.x series + +### 3.13.0 + +Date: 13th December 2021 + +* Django 4.0 compatability. [#8178] +* Add `max_length` and `min_length` options to `ListSerializer`. [#8165] +* Add `get_request_serializer` and `get_response_serializer` hooks to `AutoSchema`. [#7424] +* Fix OpenAPI representation of null-able read only fields. [#8116] +* Respect `UNICODE_JSON` setting in API schema outputs. [#7991] +* Fix for `RemoteUserAuthentication`. [#7158] +* Make Field constructors keyword-only. [#7632] + +--- + ## 3.12.x series ### 3.12.4 diff --git a/mkdocs.yml b/mkdocs.yml index 573898bca0..439245a8d2 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.13 Announcement': 'community/3.13-announcement.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' diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 0c75d3617e..88d86c03e5 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -10,7 +10,7 @@ import django __title__ = 'Django REST framework' -__version__ = '3.12.4' +__version__ = '3.13.0' __author__ = 'Tom Christie' __license__ = 'BSD 3-Clause' __copyright__ = 'Copyright 2011-2019 Encode OSS Ltd' From b3beb15b00ce8b251205aa5a344d6e6ddfac74a8 Mon Sep 17 00:00:00 2001 From: Jameel Al-Aziz <247849+jalaziz@users.noreply.github.com> Date: Mon, 13 Dec 2021 06:03:09 -0800 Subject: [PATCH 0840/1262] Fix CursorPagination parameter schema type (#7708) The CursorPagination's cursor query parameter expects a string and not an integer. Fixes #7691 --- rest_framework/pagination.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index dc120d8e86..e815d8d5cf 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -961,7 +961,7 @@ def get_schema_operation_parameters(self, view): 'in': 'query', 'description': force_str(self.cursor_query_description), 'schema': { - 'type': 'integer', + 'type': 'string', }, } ] From f3bb5b9cdc7cb53c27535e4817112e6d2eba08a0 Mon Sep 17 00:00:00 2001 From: Abhineet Date: Mon, 13 Dec 2021 19:34:04 +0530 Subject: [PATCH 0841/1262] Add missing commas in pagination response samples (#8185) --- docs/api-guide/pagination.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 379c1975ad..aadc1bbc7f 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -78,7 +78,7 @@ This pagination style accepts a single number page number in the request query p HTTP 200 OK { - "count": 1023 + "count": 1023, "next": "https://api.example.org/accounts/?page=5", "previous": "https://api.example.org/accounts/?page=3", "results": [ @@ -126,7 +126,7 @@ This pagination style mirrors the syntax used when looking up multiple database HTTP 200 OK { - "count": 1023 + "count": 1023, "next": "https://api.example.org/accounts/?limit=100&offset=500", "previous": "https://api.example.org/accounts/?limit=100&offset=300", "results": [ From 2d52c9e8bca06d5427596ea7f73b8294aa984036 Mon Sep 17 00:00:00 2001 From: juliangeissler <81534590+juliangeissler@users.noreply.github.com> Date: Mon, 13 Dec 2021 15:08:55 +0100 Subject: [PATCH 0842/1262] Update Tutorial - quickstart (#7943) * Tutorial - Adjust quickstart Add asgi.py file Also add paragraph for the second user, which is later displayed * Tutorial - Adjust quickstart It seems that there is no CLI command to easily create a user Remove the second user from the Markdown Image next * Tutorial - quickstart - Update browsable API image Only show the admin user New Image has similar width and is compressed --- docs/img/quickstart.png | Bin 39050 -> 27279 bytes docs/tutorial/quickstart.md | 13 +------------ 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/docs/img/quickstart.png b/docs/img/quickstart.png index 5006d60fe0c3f89723524ae5e8d45a115cd8e6d8..e3581f308b75b53443bab679e6ceb5a38b3776fa 100644 GIT binary patch literal 27279 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≥1YOF3HI=x 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 aw|wZ70#vl|AAy6>q{S7)%0%D1`#%6AGMOC! diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md index f4dcc5606c..4fa2fbbe52 100644 --- a/docs/tutorial/quickstart.md +++ b/docs/tutorial/quickstart.md @@ -42,6 +42,7 @@ The project layout should look like: ./tutorial/quickstart/models.py ./tutorial/quickstart/tests.py ./tutorial/quickstart/views.py + ./tutorial/asgi.py ./tutorial/settings.py ./tutorial/urls.py ./tutorial/wsgi.py @@ -176,12 +177,6 @@ We can now access our API, both from the command-line, using tools like `curl`.. "url": "http://127.0.0.1:8000/users/1/", "username": "admin" }, - { - "email": "tom@example.com", - "groups": [], - "url": "http://127.0.0.1:8000/users/2/", - "username": "tom" - } ] } @@ -202,12 +197,6 @@ Or using the [httpie][httpie], command line tool... "url": "http://localhost:8000/users/1/", "username": "paul" }, - { - "email": "tom@example.com", - "groups": [], - "url": "http://127.0.0.1:8000/users/2/", - "username": "tom" - } ] } From a780e80debac0d12bb62fbb0ed98635e601e76de Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 15 Dec 2021 15:16:38 +0000 Subject: [PATCH 0843/1262] Revert "Make api_view respect standard wrapper assignments (#8291)" (#8297) This reverts commit 9c97946531b85858fcee5df56240de6d29571da2. --- rest_framework/decorators.py | 20 ++++++++++++++++---- tests/test_decorators.py | 10 ---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index 7ba43d37c8..30b9d84d4e 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -7,7 +7,6 @@ methods on viewsets that should be included by routers. """ import types -from functools import update_wrapper from django.forms.utils import pretty_name @@ -23,8 +22,18 @@ def api_view(http_method_names=None): def decorator(func): - class WrappedAPIView(APIView): - pass + WrappedAPIView = type( + 'WrappedAPIView', + (APIView,), + {'__doc__': func.__doc__} + ) + + # Note, the above allows us to set the docstring. + # It is the equivalent of: + # + # class WrappedAPIView(APIView): + # pass + # WrappedAPIView.__doc__ = func.doc <--- Not possible to do this # api_view applied without (method_names) assert not(isinstance(http_method_names, types.FunctionType)), \ @@ -43,6 +52,9 @@ def handler(self, *args, **kwargs): for method in http_method_names: setattr(WrappedAPIView, method.lower(), handler) + WrappedAPIView.__name__ = func.__name__ + WrappedAPIView.__module__ = func.__module__ + WrappedAPIView.renderer_classes = getattr(func, 'renderer_classes', APIView.renderer_classes) @@ -61,7 +73,7 @@ def handler(self, *args, **kwargs): WrappedAPIView.schema = getattr(func, 'schema', APIView.schema) - return update_wrapper(WrappedAPIView.as_view(), func) + return WrappedAPIView.as_view() return decorator diff --git a/tests/test_decorators.py b/tests/test_decorators.py index 116d6f1be4..99ba13e60c 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -162,16 +162,6 @@ def view(request): assert isinstance(view.cls.schema, CustomSchema) - def test_wrapper_assignments(self): - @api_view(["GET"]) - def test_view(request): - """example docstring""" - pass - - assert test_view.__name__ == "test_view" - assert test_view.__doc__ == "example docstring" - assert test_view.__qualname__ == "DecoratorTestCase.test_wrapper_assignments..test_view" - class ActionDecoratorTestCase(TestCase): From f4cf0260bf3c9323e798325702be690ca25949ca Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 15 Dec 2021 15:18:24 +0000 Subject: [PATCH 0844/1262] Version 3.13.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 d3e9dd7cc2..8629e4fee7 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.13.x series +### 3.13.1 + +Date: 15th December 2021 + +* Revert schema naming changes with function based `@api_view`. [#8297] + ### 3.13.0 Date: 13th December 2021 diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 88d86c03e5..8b0679ef95 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -10,7 +10,7 @@ import django __title__ = 'Django REST framework' -__version__ = '3.13.0' +__version__ = '3.13.1' __author__ = 'Tom Christie' __license__ = 'BSD 3-Clause' __copyright__ = 'Copyright 2011-2019 Encode OSS Ltd' From 45082b39368729caa70534dde11b0788ef186a37 Mon Sep 17 00:00:00 2001 From: Guilouf Date: Fri, 17 Dec 2021 16:20:14 +0100 Subject: [PATCH 0845/1262] Fixed missing "fields" meta argument in docs (#8243) * Fixed missing "fields" meta argument in docs, leading to an assertion error * Update docs/api-guide/serializers.md Co-authored-by: Tom Christie Co-authored-by: Tom Christie --- docs/api-guide/serializers.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 4d032bd9ec..da57ac0a49 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -524,6 +524,7 @@ You can add extra fields to a `ModelSerializer` or override the default fields b class Meta: model = Account + fields = ['url', 'groups'] Extra fields can correspond to any property or callable on the model. From bce9df9b5e0f54a6076519835393fea59accb40c Mon Sep 17 00:00:00 2001 From: Luke Plant Date: Wed, 22 Dec 2021 15:08:58 +0000 Subject: [PATCH 0846/1262] Make ReturnDict support dict union operators on Python 3.9 and later (#8302) Fixes issue #8301 --- rest_framework/utils/serializer_helpers.py | 17 +++++++++++++++++ tests/test_serializer.py | 22 ++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index 4cd2ada314..54068f5fb0 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -1,3 +1,4 @@ +import sys from collections import OrderedDict from collections.abc import Mapping, MutableMapping @@ -28,6 +29,22 @@ def __reduce__(self): # but preserve the raw data. return (dict, (dict(self),)) + if sys.version_info >= (3, 9): + # These are basically copied from OrderedDict, with `serializer` added. + def __or__(self, other): + if not isinstance(other, dict): + return NotImplemented + new = self.__class__(self, serializer=self.serializer) + new.update(other) + return new + + def __ror__(self, other): + if not isinstance(other, dict): + return NotImplemented + new = self.__class__(other, serializer=self.serializer) + new.update(self) + return new + class ReturnList(list): """ diff --git a/tests/test_serializer.py b/tests/test_serializer.py index afefd70e1c..c4c29ba4ad 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -740,3 +740,25 @@ class TestSerializer(A, B): 'f4': serializers.CharField, 'f5': serializers.CharField, } + + +class Test8301Regression: + @pytest.mark.skipif( + sys.version_info < (3, 9), + reason="dictionary union operator requires Python 3.9 or higher", + ) + def test_ReturnDict_merging(self): + # Serializer.data returns ReturnDict, this is essentially a test for that. + + class TestSerializer(serializers.Serializer): + char = serializers.CharField() + + s = TestSerializer(data={'char': 'x'}) + assert s.is_valid() + assert s.data | {} == {'char': 'x'} + assert s.data | {'other': 'y'} == {'char': 'x', 'other': 'y'} + assert {} | s.data == {'char': 'x'} + assert {'other': 'y'} | s.data == {'char': 'x', 'other': 'y'} + + assert (s.data | {}).__class__ == s.data.__class__ + assert ({} | s.data).__class__ == s.data.__class__ From 5b2abbed25ef41c4cc1b3806037c6bd7631db327 Mon Sep 17 00:00:00 2001 From: kaushik kothiya <46051127+kaushikk25@users.noreply.github.com> Date: Wed, 22 Dec 2021 20:47:57 +0530 Subject: [PATCH 0847/1262] Correct variable name (#8306) urlpatterns name variable name in space remove. --- docs/api-guide/format-suffixes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/format-suffixes.md b/docs/api-guide/format-suffixes.md index dfdf24953d..da4a1af78b 100644 --- a/docs/api-guide/format-suffixes.md +++ b/docs/api-guide/format-suffixes.md @@ -62,7 +62,7 @@ Also note that `format_suffix_patterns` does not support descending into `includ If using the `i18n_patterns` function provided by Django, as well as `format_suffix_patterns` you should make sure that the `i18n_patterns` function is applied as the final, or outermost function. For example: - url patterns = [ + urlpatterns = [ … ] From f9ccbad4d910d19e4298300ee90467051fc08f47 Mon Sep 17 00:00:00 2001 From: Suntae Kim Date: Thu, 6 Jan 2022 22:55:44 +0900 Subject: [PATCH 0848/1262] minor update on tutorial serialization (#8323) --- docs/tutorial/1-serialization.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index 908b7474a0..67746c517e 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -45,7 +45,7 @@ We'll need to add our new `snippets` app and the `rest_framework` app to `INSTAL INSTALLED_APPS = [ ... 'rest_framework', - 'snippets.apps.SnippetsConfig', + 'snippets', ] Okay, we're ready to roll. @@ -77,7 +77,7 @@ For the purposes of this tutorial we're going to start by creating a simple `Sni We'll also need to create an initial migration for our snippet model, and sync the database for the first time. python manage.py makemigrations snippets - python manage.py migrate + python manage.py migrate snippets ## Creating a Serializer class @@ -307,8 +307,8 @@ Quit out of the shell... Validating models... 0 errors found - Django version 1.11, using settings 'tutorial.settings' - Development server is running at http://127.0.0.1:8000/ + Django version 4.0,1 using settings 'tutorial.settings' + Starting Development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C. In another terminal window, we can test the server. From c5be86a6dbf3d21b00a296af5994fa075826bf0b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 11 Jan 2022 13:01:25 +0000 Subject: [PATCH 0849/1262] Update sponsor URL (#8328) * Update sponsor URL * Update index.md --- README.md | 2 +- docs/index.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 18d1364c69..d8fd83d672 100644 --- a/README.md +++ b/README.md @@ -201,7 +201,7 @@ Please see the [security policy][security-policy]. [cryptapi-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/cryptapi-readme.png [sentry-url]: https://getsentry.com/welcome/ -[stream-url]: https://getstream.io/?utm_source=drf&utm_medium=sponsorship&utm_content=developer +[stream-url]: https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage [rollbar-url]: https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial [esg-url]: https://software.esg-usa.com/ [retool-url]: https://retool.com/?utm_source=djangorest&utm_medium=sponsorship diff --git a/docs/index.md b/docs/index.md index 2954f793ac..4871ba4587 100644 --- a/docs/index.md +++ b/docs/index.md @@ -67,7 +67,7 @@ continued development by **[signing up for a paid plan][funding]**.

    -*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=sponsorship&utm_content=developer), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [PostHog](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), and [CryptAPI](https://cryptapi.io).* +*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [PostHog](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), and [CryptAPI](https://cryptapi.io).* --- From 94eb804abe5d414d59b3a84332e93fbb58811e9c Mon Sep 17 00:00:00 2001 From: Shivendra Pratap Kushwaha <33164379+kushshiv@users.noreply.github.com> Date: Mon, 17 Jan 2022 15:11:23 +0530 Subject: [PATCH 0850/1262] Update filtering.md (#8331) --- docs/api-guide/filtering.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 512acafbd9..9a4ae27457 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -224,7 +224,7 @@ The search behavior may be restricted by prepending various characters to the `s * '^' Starts-with search. * '=' Exact matches. -* '@' Full-text search. (Currently only supported Django's [PostgreSQL backend](https://docs.djangoproject.com/en/dev/ref/contrib/postgres/search/).) +* '@' Full-text search. (Currently only supported Django's [PostgreSQL backend][postgres-search].) * '$' Regex search. For example: @@ -374,3 +374,4 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-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 +[postgres-search]: https://docs.djangoproject.com/en/stable/ref/contrib/postgres/search/ From 02eeb6fa003b5cbe3851ac18392f129d31a1a6bd Mon Sep 17 00:00:00 2001 From: denniskloyn <97915409+denniskloyn@users.noreply.github.com> Date: Tue, 18 Jan 2022 08:52:11 +0000 Subject: [PATCH 0851/1262] Change `MIDDLEWARE_CLASSES` to `MIDDLEWARE` (#8333) The `MIDDLEWARE_CLASSES` setting got changed to `MIDDLEWARE` in [Django 1.10](https://docs.djangoproject.com/en/1.10/topics/http/middleware/). --- docs/topics/internationalization.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/topics/internationalization.md b/docs/topics/internationalization.md index c20cf9e339..267ccdb377 100644 --- a/docs/topics/internationalization.md +++ b/docs/topics/internationalization.md @@ -17,9 +17,9 @@ You can change the default language by using the standard Django `LANGUAGE_CODE` LANGUAGE_CODE = "es-es" -You can turn on per-request language requests by adding `LocalMiddleware` to your `MIDDLEWARE_CLASSES` setting: +You can turn on per-request language requests by adding `LocalMiddleware` to your `MIDDLEWARE` setting: - MIDDLEWARE_CLASSES = [ + MIDDLEWARE = [ ... 'django.middleware.locale.LocaleMiddleware' ] @@ -90,7 +90,7 @@ If you're only translating custom error messages that exist inside your project ## How the language is determined -If you want to allow per-request language preferences you'll need to include `django.middleware.locale.LocaleMiddleware` in your `MIDDLEWARE_CLASSES` setting. +If you want to allow per-request language preferences you'll need to include `django.middleware.locale.LocaleMiddleware` in your `MIDDLEWARE` setting. You can find more information on how the language preference is determined in the [Django documentation][django-language-preference]. For reference, the method is: From f378f98a401f28df4ef9bdaf3ee56a1017c195ab Mon Sep 17 00:00:00 2001 From: Sevdimali Date: Thu, 27 Jan 2022 19:02:20 +0400 Subject: [PATCH 0852/1262] if else optimization (#8340) Removed redundant parentheses --- rest_framework/decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index 30b9d84d4e..3b572c09ef 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -142,7 +142,7 @@ def action(methods=None, detail=None, url_path=None, url_name=None, **kwargs): how the `@renderer_classes` etc. decorators work for function- based API views. """ - methods = ['get'] if (methods is None) else methods + methods = ['get'] if methods is None else methods methods = [method.lower() for method in methods] assert detail is not None, ( From a5d741aba473d5099d59ff622dbf9702df842441 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 1 Feb 2022 09:42:43 +0000 Subject: [PATCH 0853/1262] Update renderers.py --- rest_framework/renderers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index b0ddca2b59..8824fa6601 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -105,7 +105,7 @@ def render(self, data, accepted_media_type=None, renderer_context=None): # We always fully escape \u2028 and \u2029 to ensure we output JSON # that is a strict javascript subset. - # See: http://timelessrepo.com/json-isnt-a-javascript-subset + # See: https://gist.github.com/damncabbage/623b879af56f850a6ddc ret = ret.replace('\u2028', '\\u2028').replace('\u2029', '\\u2029') return ret.encode() From c26ec5a3b8080d528112557f86423aee317ed8a4 Mon Sep 17 00:00:00 2001 From: Bhuwan Panta <67514385+Bhuwan-web@users.noreply.github.com> Date: Tue, 1 Feb 2022 19:33:22 +0545 Subject: [PATCH 0854/1262] Update 6-viewsets-and-routers.md (#8349) # Basename key missing in the tutorial ```diff +router.register(r'snippets', views.SnippetViewSet,basename="snippets") +router.register(r'users', views.UserViewSet,basename="users") -router.register(r'snippets', views.SnippetViewSet) -router.register(r'users', views.UserViewSet) ``` --- docs/tutorial/6-viewsets-and-routers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md index f012677d30..18066f0563 100644 --- a/docs/tutorial/6-viewsets-and-routers.md +++ b/docs/tutorial/6-viewsets-and-routers.md @@ -112,8 +112,8 @@ Here's our re-wired `snippets/urls.py` file. # Create a router and register our viewsets with it. router = DefaultRouter() - router.register(r'snippets', views.SnippetViewSet) - router.register(r'users', views.UserViewSet) + router.register(r'snippets', views.SnippetViewSet,basename="snippets") + router.register(r'users', views.UserViewSet,basename="users") # The API URLs are now determined automatically by the router. urlpatterns = [ From 5bea22f32179276596b4c8152616d54a735a5341 Mon Sep 17 00:00:00 2001 From: Partho Kumar Rajvor Date: Thu, 3 Feb 2022 17:57:47 +0600 Subject: [PATCH 0855/1262] Added http 102, 103, 421, and 425 status codes (#8350) --- rest_framework/status.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rest_framework/status.py b/rest_framework/status.py index 2561d7689e..7df26b388c 100644 --- a/rest_framework/status.py +++ b/rest_framework/status.py @@ -29,6 +29,8 @@ def is_server_error(code): HTTP_100_CONTINUE = 100 HTTP_101_SWITCHING_PROTOCOLS = 101 +HTTP_102_PROCESSING = 102 +HTTP_103_EARLY_HINTS = 103 HTTP_200_OK = 200 HTTP_201_CREATED = 201 HTTP_202_ACCEPTED = 202 @@ -67,9 +69,11 @@ def is_server_error(code): HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416 HTTP_417_EXPECTATION_FAILED = 417 HTTP_418_IM_A_TEAPOT = 418 +HTTP_421_MISDIRECTED_REQUEST = 421 HTTP_422_UNPROCESSABLE_ENTITY = 422 HTTP_423_LOCKED = 423 HTTP_424_FAILED_DEPENDENCY = 424 +HTTP_425_TOO_EARLY = 425 HTTP_426_UPGRADE_REQUIRED = 426 HTTP_428_PRECONDITION_REQUIRED = 428 HTTP_429_TOO_MANY_REQUESTS = 429 From efc7c1d664e5909f5f1f4d07a7bb70daef1c396e Mon Sep 17 00:00:00 2001 From: Josh Date: Sat, 12 Feb 2022 10:21:59 -0600 Subject: [PATCH 0856/1262] Update `accepted_media_type` argument in Renderer docs (#8364) --- docs/api-guide/renderers.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index f13b7ba946..67dff441c2 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -257,7 +257,7 @@ This renderer is used for rendering HTML multipart form data. **It is not suita # Custom renderers -To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, media_type=None, renderer_context=None)` method. +To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, accepted_media_type=None, renderer_context=None)` method. The method should return a bytestring, which will be used as the body of the HTTP response. @@ -267,7 +267,7 @@ The arguments passed to the `.render()` method are: The request data, as set by the `Response()` instantiation. -### `media_type=None` +### `accepted_media_type=None` Optional. If provided, this is the accepted media type, as determined by the content negotiation stage. @@ -291,7 +291,7 @@ The following is an example plaintext renderer that will return a response with media_type = 'text/plain' format = 'txt' - def render(self, data, media_type=None, renderer_context=None): + def render(self, data, accepted_media_type=None, renderer_context=None): return smart_text(data, encoding=self.charset) ## Setting the character set @@ -303,7 +303,7 @@ By default renderer classes are assumed to be using the `UTF-8` encoding. To us format = 'txt' charset = 'iso-8859-1' - def render(self, data, media_type=None, renderer_context=None): + def render(self, data, accepted_media_type=None, renderer_context=None): return data.encode(self.charset) Note that if a renderer class returns a unicode string, then the response content will be coerced into a bytestring by the `Response` class, with the `charset` attribute set on the renderer used to determine the encoding. @@ -318,7 +318,7 @@ In some cases you may also want to set the `render_style` attribute to `'binary' charset = None render_style = 'binary' - def render(self, data, media_type=None, renderer_context=None): + def render(self, data, accepted_media_type=None, renderer_context=None): return data --- From a53e523f939332189b4ba8db7f99758b7d63e59b Mon Sep 17 00:00:00 2001 From: Timothy Allen Date: Tue, 8 Mar 2022 05:39:16 -0500 Subject: [PATCH 0857/1262] We've renamed the drf-renderer-xlsx package to drf-excel. (#8396) --- docs/api-guide/renderers.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 67dff441c2..685a98f5e0 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -470,15 +470,15 @@ Modify your REST framework settings. [MessagePack][messagepack] is a fast, efficient binary serialization format. [Juan Riaza][juanriaza] maintains the [djangorestframework-msgpack][djangorestframework-msgpack] package which provides MessagePack renderer and parser support for REST framework. -## XLSX (Binary Spreadsheet Endpoints) +## Microsoft Excel: XLSX (Binary Spreadsheet Endpoints) -XLSX is the world's most popular binary spreadsheet format. [Tim Allen][flipperpa] of [The Wharton School][wharton] maintains [drf-renderer-xlsx][drf-renderer-xlsx], which renders an endpoint as an XLSX spreadsheet using OpenPyXL, and allows the client to download it. Spreadsheets can be styled on a per-view basis. +XLSX is the world's most popular binary spreadsheet format. [Tim Allen][flipperpa] of [The Wharton School][wharton] maintains [drf-excel][drf-excel], which renders an endpoint as an XLSX spreadsheet using OpenPyXL, and allows the client to download it. Spreadsheets can be styled on a per-view basis. #### Installation & configuration Install using pip. - $ pip install drf-renderer-xlsx + $ pip install drf-excel Modify your REST framework settings. @@ -488,15 +488,15 @@ Modify your REST framework settings. 'DEFAULT_RENDERER_CLASSES': [ 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', - 'drf_renderer_xlsx.renderers.XLSXRenderer', + 'drf_excel.renderers.XLSXRenderer', ], } To avoid having a file streamed without a filename (which the browser will often default to the filename "download", with no extension), we need to use a mixin to override the `Content-Disposition` header. If no filename is provided, it will default to `export.xlsx`. For example: from rest_framework.viewsets import ReadOnlyModelViewSet - from drf_renderer_xlsx.mixins import XLSXFileMixin - from drf_renderer_xlsx.renderers import XLSXRenderer + from drf_excel.mixins import XLSXFileMixin + from drf_excel.renderers import XLSXRenderer from .models import MyExampleModel from .serializers import MyExampleSerializer @@ -549,7 +549,7 @@ Comma-separated values are a plain-text tabular data format, that can be easily [mjumbewu]: https://github.com/mjumbewu [flipperpa]: https://github.com/flipperpa [wharton]: https://github.com/wharton -[drf-renderer-xlsx]: https://github.com/wharton/drf-renderer-xlsx +[drf-excel]: https://github.com/wharton/drf-excel [vbabiy]: https://github.com/vbabiy [rest-framework-yaml]: https://jpadilla.github.io/django-rest-framework-yaml/ [rest-framework-xml]: https://jpadilla.github.io/django-rest-framework-xml/ From 0e3bc2b1e0189ecc143868ca31dd3393a8aa76f4 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 11 Mar 2022 10:58:59 +0000 Subject: [PATCH 0858/1262] Throttling disclaimer (#8403) * Throttling disclaimer * Expand throttling disclaimer. --- docs/api-guide/throttling.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index a3e42cacf9..6fab79a2b5 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -19,6 +19,8 @@ Multiple throttles can also be used if you want to impose both burst throttling Throttles do not necessarily only refer to rate-limiting requests. For example a storage service might also need to throttle against bandwidth, and a paid data service might want to throttle against a certain number of a records being accessed. +**The application-level throttling that REST framework provides should not be considered a security measure or protection against brute forcing or denial-of-service attacks. Deliberately malicious actors will always be able to spoof IP origins, and application-level throttling is intended for implementing policies such as different business tiers and basic protections against service over-use.** + ## How throttling is determined As with permissions and authentication, throttling in REST framework is always defined as a list of classes. @@ -79,7 +81,7 @@ Throttle classes set in this way will override any viewset level class settings. } return Response(content) -## How clients are identified +## How clients are identified The `X-Forwarded-For` HTTP header and `REMOTE_ADDR` WSGI variable are used to uniquely identify client IP addresses for throttling. If the `X-Forwarded-For` header is present then it will be used, otherwise the value of the `REMOTE_ADDR` variable from the WSGI environment will be used. From b3083d83ae197e92591116d1cd4231ae9565dbfa Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 16 Mar 2022 11:35:04 +0000 Subject: [PATCH 0859/1262] Update index.md (#8411) Update security email. --- docs/index.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index 4871ba4587..e58f24df87 100644 --- a/docs/index.md +++ b/docs/index.md @@ -194,9 +194,11 @@ For priority support please sign up for a [professional or premium sponsorship p ## Security -If you believe you’ve found something in Django REST framework which has security implications, please **do not raise the issue in a public forum**. +Security issues are handled under the supervision of the [Django security team](https://www.djangoproject.com/foundation/teams/#security-team). -Send a description of the issue via email to [rest-framework-security@googlegroups.com][security-mail]. The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure. +**Please report security issues by emailing security@djangoproject.com**. + +The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure. ## License From b521160c92783b9d2a76d6bc3fa30df10d7251ac Mon Sep 17 00:00:00 2001 From: baseplate-admin <61817579+baseplate-admin@users.noreply.github.com> Date: Wed, 16 Mar 2022 17:35:24 +0600 Subject: [PATCH 0860/1262] Fix code block in `README.md` (#8408) Hi there, The code block below show imply `Python` as it lives in `settings.py` ``` INSTALLED_APPS = [ ... 'rest_framework', ] ``` This pull request essentially fixes that. --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d8fd83d672..bf40167512 100644 --- a/README.md +++ b/README.md @@ -67,11 +67,12 @@ Install using `pip`... pip install djangorestframework Add `'rest_framework'` to your `INSTALLED_APPS` setting. - - INSTALLED_APPS = [ - ... - 'rest_framework', - ] +```python +INSTALLED_APPS = [ + ... + 'rest_framework', +] +``` # Example From 070c32f4a62ef0544f58de404c87d86db36fd825 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 16 Mar 2022 12:12:25 +0000 Subject: [PATCH 0861/1262] Update SECURITY.md (#8412) --- SECURITY.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index d3faefa3cb..a92a1b0cf1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,8 +2,8 @@ ## Reporting a Vulnerability -If you believe you've found something in Django REST framework which has security implications, please **do not raise the issue in a public forum**. +Security issues are handled under the supervision of the [Django security team](https://www.djangoproject.com/foundation/teams/#security-team). -Send a description of the issue via email to [rest-framework-security@googlegroups.com][security-mail]. The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure. + **Please report security issues by emailing security@djangoproject.com**. -[security-mail]: mailto:rest-framework-security@googlegroups.com + The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure. From 0b88583a101d6eb95bf9bfb5dd9df47014a45a71 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Wed, 23 Mar 2022 12:28:46 +0100 Subject: [PATCH 0862/1262] Bumped versions in Github actions configuration to v3. (#8414) --- .github/workflows/main.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 42fee2a124..c88dc55cd9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,18 +21,13 @@ jobs: - '3.10' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - - - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('requirements/*.txt') }} - restore-keys: | - ${{ runner.os }}-pip- + cache: 'pip' + cache-dependency-path: 'requirements/*.txt' - name: Upgrade packaging tools run: python -m pip install --upgrade pip setuptools virtualenv wheel From 75f19981233c112326b13e4b2abb4b71effd9fbe Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 23 Mar 2022 11:52:26 +0000 Subject: [PATCH 0863/1262] Update throttling docs (#8424) --- docs/api-guide/throttling.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 6fab79a2b5..719378247d 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -19,7 +19,9 @@ Multiple throttles can also be used if you want to impose both burst throttling Throttles do not necessarily only refer to rate-limiting requests. For example a storage service might also need to throttle against bandwidth, and a paid data service might want to throttle against a certain number of a records being accessed. -**The application-level throttling that REST framework provides should not be considered a security measure or protection against brute forcing or denial-of-service attacks. Deliberately malicious actors will always be able to spoof IP origins, and application-level throttling is intended for implementing policies such as different business tiers and basic protections against service over-use.** +**The application-level throttling that REST framework provides should not be considered a security measure or protection against brute forcing or denial-of-service attacks. Deliberately malicious actors will always be able to spoof IP origins. In addition to this, the built-in throttling implementations are implemented using Django's cache framework, and use non-atomic operations to determine the request rate, which may sometimes result in some fuzziness. + +The application-level throttling provided by REST framework is intended for implementing policies such as different business tiers and basic protections against service over-use.** ## How throttling is determined From a4334a81260b15df07550f7a9e96c1753beaa269 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 23 Mar 2022 11:52:45 +0000 Subject: [PATCH 0864/1262] Update contribution guidelines (#8422) --- CONTRIBUTING.md | 6 +++++- docs/community/contributing.md | 15 ++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a7f17b1a35..d567d45a87 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,7 @@ # Contributing to REST framework -See the [Contributing guide in the documentation](https://www.django-rest-framework.org/community/contributing/). +At this point in it's lifespan we consider Django REST framework to be essentially feature-complete. We may accept pull requests that track the continued development of Django versions, but would prefer not to accept new features or code formatting changes. + +Apart from minor documentation changes, the [GitHub discussions page](https://github.com/encode/django-rest-framework/discussions) should generally be your starting point. Please only raise an issue or pull request if you've been recommended to do so after discussion. + +The [Contributing guide in the documentation](https://www.django-rest-framework.org/community/contributing/) gives some more information on our process and code of conduct. diff --git a/docs/community/contributing.md b/docs/community/contributing.md index de1f8db0fb..2232bd10b9 100644 --- a/docs/community/contributing.md +++ b/docs/community/contributing.md @@ -6,6 +6,12 @@ There are many ways you can contribute to Django REST framework. We'd like it to be a community-led project, so please get involved and help shape the future of the project. +--- + +**Note**: At this point in it's lifespan we consider Django REST framework to be essentially feature-complete. We may accept pull requests that track the continued development of Django versions, but would prefer not to accept new features or code formatting changes. + +--- + ## Community The most important thing you can do to help push the REST framework project forward is to be actively involved wherever possible. Code contributions are often overvalued as being the primary way to get involved in a project, we don't believe that needs to be the case. @@ -26,14 +32,13 @@ The [Django code of conduct][code-of-conduct] gives a fuller set of guidelines f # Issues -It's really helpful if you can make sure to address issues on the correct channel. Usage questions should be directed to the [discussion group][google-group]. Feature requests, bug reports and other issues should be raised on the GitHub [issue tracker][issues]. +Our contribution process is that the [GitHub discussions page](https://github.com/encode/django-rest-framework/discussions) should generally be your starting point. Please only raise an issue or pull request if you've been recommended to do so after discussion. -Some tips on good issue reporting: +Some tips on good potential issue reporting: * When describing issues try to phrase your ticket in terms of the *behavior* you think needs changing rather than the *code* you think need changing. -* Search the issue list first for related items, and make sure you're running the latest version of REST framework before reporting an issue. -* If reporting a bug, then try to include a pull request with a failing test case. This will help us quickly identify if there is a valid issue, and make sure that it gets fixed more quickly if there is one. -* Feature requests will often be closed with a recommendation that they be implemented outside of the core REST framework library. Keeping new feature requests implemented as third party libraries allows us to keep down the maintenance overhead of REST framework, so that the focus can be on continued stability, bugfixes, and great documentation. +* Search the GitHub project page for related items, and make sure you're running the latest version of REST framework before reporting an issue. +* Feature requests will often be closed with a recommendation that they be implemented outside of the core REST framework library. Keeping new feature requests implemented as third party libraries allows us to keep down the maintenance overhead of REST framework, so that the focus can be on continued stability, bugfixes, and great documentation. At this point in it's lifespan we consider Django REST framework to be essentially feature-complete. * Closing an issue doesn't necessarily mean the end of a discussion. If you believe your issue has been closed incorrectly, explain why and we'll consider if it needs to be reopened. ## Triaging issues From 4464ce7270e6242fec212a4257aa1277d023ff1b Mon Sep 17 00:00:00 2001 From: Shivendra Pratap Kushwaha <33164379+kushshiv@users.noreply.github.com> Date: Wed, 23 Mar 2022 17:37:44 +0530 Subject: [PATCH 0865/1262] Link added for Test Case Classes and corrected stable link for Refresh from DB (#8381) Link added for Test Case Classes and corrected stable link for Refresh from DB. --- docs/api-guide/testing.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md index 62eb8dd1a5..261df80f27 100644 --- a/docs/api-guide/testing.md +++ b/docs/api-guide/testing.md @@ -299,7 +299,7 @@ similar way as with `RequestsClient`. # API Test cases -REST framework includes the following test case classes, that mirror the existing Django test case classes, but use `APIClient` instead of Django's default `Client`. +REST framework includes the following test case classes, that mirror the existing [Django's test case classes][provided_test_case_classes], but use `APIClient` instead of Django's default `Client`. * `APISimpleTestCase` * `APITransactionTestCase` @@ -413,5 +413,6 @@ For example, to add support for using `format='html'` in test requests, you migh [client]: https://docs.djangoproject.com/en/stable/topics/testing/tools/#the-test-client [requestfactory]: https://docs.djangoproject.com/en/stable/topics/testing/advanced/#django.test.client.RequestFactory [configuration]: #configuration -[refresh_from_db_docs]: https://docs.djangoproject.com/en/1.11/ref/models/instances/#django.db.models.Model.refresh_from_db +[refresh_from_db_docs]: https://docs.djangoproject.com/en/stable/ref/models/instances/#django.db.models.Model.refresh_from_db [session_objects]: https://requests.readthedocs.io/en/master/user/advanced/#session-objects +[provided_test_case_classes]: https://docs.djangoproject.com/en/stable/topics/testing/tools/#provided-test-case-classes From 7e4e6d207070d50736827a281b5cb70eb161b782 Mon Sep 17 00:00:00 2001 From: Wagner de Lima Date: Wed, 23 Mar 2022 13:09:05 +0100 Subject: [PATCH 0866/1262] docs: include drf-social-oauth2 to docs. (#8310) django-rest-framework-social-oauth2 is not a maintained library anymore. --- docs/api-guide/authentication.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 57bbaeb679..2f23e17185 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -420,9 +420,9 @@ There are currently two forks of this project. * [Django-rest-auth][django-rest-auth] is the original project, [but is not currently receiving updates](https://github.com/Tivix/django-rest-auth/issues/568). * [Dj-rest-auth][dj-rest-auth] is a newer fork of the project. -## django-rest-framework-social-oauth2 +## drf-social-oauth2 -[Django-rest-framework-social-oauth2][django-rest-framework-social-oauth2] library provides an easy way to integrate social plugins (facebook, twitter, google, etc.) to your authentication system and an easy oauth2 setup. With this library, you will be able to authenticate users based on external tokens (e.g. facebook access token), convert these tokens to "in-house" oauth2 tokens and use and generate oauth2 tokens to authenticate your users. +[Drf-social-oauth2][drf-social-oauth2] is a framework that helps you authenticate with major social oauth2 vendors, such as Facebook, Google, Twitter, Orcid, etc. It generates tokens in a JWTed way with an easy setup. ## django-rest-knox @@ -473,7 +473,7 @@ More information can be found in the [Documentation](https://django-rest-durin.r [djoser]: https://github.com/sunscrapers/djoser [django-rest-auth]: https://github.com/Tivix/django-rest-auth [dj-rest-auth]: https://github.com/jazzband/dj-rest-auth -[django-rest-framework-social-oauth2]: https://github.com/PhilipGarnero/django-rest-framework-social-oauth2 +[drf-social-oauth2]: https://github.com/wagnerdelima/drf-social-oauth2 [django-rest-knox]: https://github.com/James1345/django-rest-knox [drfpasswordless]: https://github.com/aaronn/django-rest-framework-passwordless [django-rest-authemail]: https://github.com/celiao/django-rest-authemail From df4d16d2f17bd53626266eb829fed40cf8a73e48 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 24 Mar 2022 09:23:16 +0000 Subject: [PATCH 0867/1262] Add StaleBot (#8423) --- .github/stale.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000000..f9ebbced4a --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,22 @@ +# Documentation: https://github.com/probot/stale + +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 + +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 + +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 1 + +# Label to use when marking as stale +staleLabel: stale From df92e57ad6c8394ca54654dfc7a2722f822ed8c8 Mon Sep 17 00:00:00 2001 From: hashlash Date: Thu, 24 Mar 2022 16:57:42 +0700 Subject: [PATCH 0868/1262] Added test client support for HTTP 307 and 308 redirects (#8419) * Add retain test data on follow=True * Simplify TestAPITestClient.test_follow_redirect Inspired from Django's ClientTest.test_follow_307_and_308_redirect * Add 307 308 follow redirect test --- rest_framework/test.py | 12 +++---- tests/test_testing.py | 72 +++++++++++++++++++++--------------------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/rest_framework/test.py b/rest_framework/test.py index 0212348ee0..07df743c8e 100644 --- a/rest_framework/test.py +++ b/rest_framework/test.py @@ -288,7 +288,7 @@ def request(self, **kwargs): def get(self, path, data=None, follow=False, **extra): response = super().get(path, data=data, **extra) if follow: - response = self._handle_redirects(response, **extra) + response = self._handle_redirects(response, data=data, **extra) return response def post(self, path, data=None, format=None, content_type=None, @@ -296,7 +296,7 @@ def post(self, path, data=None, format=None, content_type=None, response = super().post( path, data=data, format=format, content_type=content_type, **extra) if follow: - response = self._handle_redirects(response, **extra) + response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra) return response def put(self, path, data=None, format=None, content_type=None, @@ -304,7 +304,7 @@ def put(self, path, data=None, format=None, content_type=None, response = super().put( path, data=data, format=format, content_type=content_type, **extra) if follow: - response = self._handle_redirects(response, **extra) + response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra) return response def patch(self, path, data=None, format=None, content_type=None, @@ -312,7 +312,7 @@ def patch(self, path, data=None, format=None, content_type=None, response = super().patch( path, data=data, format=format, content_type=content_type, **extra) if follow: - response = self._handle_redirects(response, **extra) + response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra) return response def delete(self, path, data=None, format=None, content_type=None, @@ -320,7 +320,7 @@ def delete(self, path, data=None, format=None, content_type=None, response = super().delete( path, data=data, format=format, content_type=content_type, **extra) if follow: - response = self._handle_redirects(response, **extra) + response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra) return response def options(self, path, data=None, format=None, content_type=None, @@ -328,7 +328,7 @@ def options(self, path, data=None, format=None, content_type=None, response = super().options( path, data=data, format=format, content_type=content_type, **extra) if follow: - response = self._handle_redirects(response, **extra) + response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra) return response def logout(self): diff --git a/tests/test_testing.py b/tests/test_testing.py index 5066ee142e..b6579e3690 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -1,7 +1,10 @@ +import itertools from io import BytesIO +from unittest.mock import patch import django from django.contrib.auth.models import User +from django.http import HttpResponseRedirect from django.shortcuts import redirect from django.test import TestCase, override_settings from django.urls import path @@ -14,7 +17,7 @@ ) -@api_view(['GET', 'POST']) +@api_view(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']) def view(request): return Response({ 'auth': request.META.get('HTTP_AUTHORIZATION', b''), @@ -36,6 +39,11 @@ def redirect_view(request): return redirect('/view/') +@api_view(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']) +def redirect_307_308_view(request, code): + return HttpResponseRedirect('/view/', status=code) + + class BasicSerializer(serializers.Serializer): flag = fields.BooleanField(default=lambda: True) @@ -51,6 +59,7 @@ def post_view(request): path('view/', view), path('session-view/', session_view), path('redirect-view/', redirect_view), + path('redirect-view//', redirect_307_308_view), path('post-view/', post_view) ] @@ -146,41 +155,32 @@ def test_follow_redirect(self): """ Follow redirect by setting follow argument. """ - response = self.client.get('/redirect-view/') - assert response.status_code == 302 - response = self.client.get('/redirect-view/', follow=True) - assert response.redirect_chain is not None - assert response.status_code == 200 - - response = self.client.post('/redirect-view/') - assert response.status_code == 302 - response = self.client.post('/redirect-view/', follow=True) - assert response.redirect_chain is not None - assert response.status_code == 200 - - response = self.client.put('/redirect-view/') - assert response.status_code == 302 - response = self.client.put('/redirect-view/', follow=True) - assert response.redirect_chain is not None - assert response.status_code == 200 - - response = self.client.patch('/redirect-view/') - assert response.status_code == 302 - response = self.client.patch('/redirect-view/', follow=True) - assert response.redirect_chain is not None - assert response.status_code == 200 - - response = self.client.delete('/redirect-view/') - assert response.status_code == 302 - response = self.client.delete('/redirect-view/', follow=True) - assert response.redirect_chain is not None - assert response.status_code == 200 - - response = self.client.options('/redirect-view/') - assert response.status_code == 302 - response = self.client.options('/redirect-view/', follow=True) - assert response.redirect_chain is not None - assert response.status_code == 200 + for method in ('get', 'post', 'put', 'patch', 'delete', 'options'): + with self.subTest(method=method): + req_method = getattr(self.client, method) + response = req_method('/redirect-view/') + assert response.status_code == 302 + response = req_method('/redirect-view/', follow=True) + assert response.redirect_chain is not None + assert response.status_code == 200 + + def test_follow_307_308_preserve_kwargs(self, *mocked_methods): + """ + Follow redirect by setting follow argument, and make sure the following + method called with appropriate kwargs. + """ + methods = ('get', 'post', 'put', 'patch', 'delete', 'options') + codes = (307, 308) + for method, code in itertools.product(methods, codes): + subtest_ctx = self.subTest(method=method, code=code) + patch_ctx = patch.object(self.client, method, side_effect=getattr(self.client, method)) + with subtest_ctx, patch_ctx as req_method: + kwargs = {'data': {'example': 'test'}, 'format': 'json'} + response = req_method('/redirect-view/%s/' % code, follow=True, **kwargs) + assert response.redirect_chain is not None + assert response.status_code == 200 + for _, call_args, call_kwargs in req_method.mock_calls: + assert all(call_kwargs[k] == kwargs[k] for k in kwargs if k in call_kwargs) def test_invalid_multipart_data(self): """ From 86673a337a4fe8861c090b4532379b97e3921fef Mon Sep 17 00:00:00 2001 From: Kojo Idrissa Date: Thu, 14 Apr 2022 15:00:38 -0500 Subject: [PATCH 0869/1262] corrected grammar to improve clarity (#8466) --- docs/api-guide/relations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index 4547253b0a..9c8295b853 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -19,7 +19,7 @@ Relational fields are used to represent model relationships. They can be applie --- -**Note:** REST Framework does not attempt to automatically optimize querysets passed to serializers in terms of `select_related` and `prefetch_related` since it would be too much magic. A serializer with a field spanning an orm relation through its source attribute could require an additional database hit to fetch related object from the database. It is the programmer's responsibility to optimize queries to avoid additional database hits which could occur while using such a serializer. +**Note:** REST Framework does not attempt to automatically optimize querysets passed to serializers in terms of `select_related` and `prefetch_related` since it would be too much magic. A serializer with a field spanning an orm relation through its source attribute could require an additional database hit to fetch related objects from the database. It is the programmer's responsibility to optimize queries to avoid additional database hits which could occur while using such a serializer. For example, the following serializer would lead to a database hit each time evaluating the tracks field if it is not prefetched: From b1004a47334a0dd1929e6d50b8f7ff6badc959f4 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 25 Apr 2022 01:16:18 +0300 Subject: [PATCH 0870/1262] docs: Add a note on concurrency and races (#6950) Refs #5181 Co-authored-by: Adam Johnson --- docs/api-guide/throttling.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 719378247d..b875221978 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -106,6 +106,12 @@ If you need to use a cache other than `'default'`, you can do so by creating a c You'll need to remember to also set your custom throttle class in the `'DEFAULT_THROTTLE_CLASSES'` settings key, or using the `throttle_classes` view attribute. +## A note on concurrency + +The built-in throttle implementations are open to [race conditions][race], so under high concurrency they may allow a few extra requests through. + +If your project relies on guaranteeing the number of requests during concurrent requests, you will need to implement your own throttle class. See [issue #5181][gh5181] for more details. + --- # API Reference @@ -214,3 +220,5 @@ The following is an example of a rate throttle, that will randomly throttle 1 in [identifying-clients]: http://oxpedia.org/wiki/index.php?title=AppSuite:Grizzly#Multiple_Proxies_in_front_of_the_cluster [cache-setting]: https://docs.djangoproject.com/en/stable/ref/settings/#caches [cache-docs]: https://docs.djangoproject.com/en/stable/topics/cache/#setting-up-the-cache +[gh5181]: https://github.com/encode/django-rest-framework/issues/5181 +[race]: https://en.wikipedia.org/wiki/Race_condition#Data_race From cdc956a96caafddcf4ecaf6218e340ebb3ce6d72 Mon Sep 17 00:00:00 2001 From: Q_back Date: Tue, 3 May 2022 12:10:37 +0200 Subject: [PATCH 0871/1262] Update description of docs/api-guide/fields.md -> required (#8476) fix: Documentation did not point out that default value of `required` `Field` parameter is `False` depending on the Django's `Model.field`. --- 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 5b9688dcab..e9ef5c6b64 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -42,7 +42,7 @@ Set to false if this field is not required to be present during deserialization. Setting this to `False` also allows the object attribute or dictionary key to be omitted from output when serializing the instance. If the key is not present it will simply not be included in the output representation. -Defaults to `True`. +Defaults to `True`. If you're using [Model Serializer](https://www.django-rest-framework.org/api-guide/serializers/#modelserializer) default value will be `False` if you have specified `blank=True` or `default` or `null=True` at your field in your `Model`. ### `default` From 33b86a8e5383ae7c6bba03cf09b12b8f7cadd20f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 18 May 2022 13:10:38 +0100 Subject: [PATCH 0872/1262] Recommend Django REST Knox (#8490) * Recommend Django REST Knox * Pin jinja2 to fix docs builds --- docs/api-guide/authentication.md | 26 +++++++++++++-------- requirements/requirements-documentation.txt | 1 + 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 2f23e17185..fca9374d0a 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -120,6 +120,14 @@ Unauthenticated responses that are denied permission will result in an `HTTP 401 ## TokenAuthentication +--- + +**Note:** The token authentication provided by Django REST framework is a fairly simple implementation. + +For an implementation which allows more than one token per user, has some tighter security implementation details, and supports token expiry, please see the [Django REST Knox][django-rest-knox] third party package. + +--- + This authentication scheme uses a simple token-based HTTP Authentication scheme. Token authentication is appropriate for client-server setups, such as native desktop and mobile clients. To use the `TokenAuthentication` scheme you'll need to [configure the authentication classes](#setting-the-authentication-scheme) to include `TokenAuthentication`, and additionally include `rest_framework.authtoken` in your `INSTALLED_APPS` setting: @@ -129,11 +137,9 @@ To use the `TokenAuthentication` scheme you'll need to [configure the authentica 'rest_framework.authtoken' ] ---- - -**Note:** Make sure to run `manage.py migrate` after changing your settings. The `rest_framework.authtoken` app provides Django database migrations. +Make sure to run `manage.py migrate` after changing your settings. ---- +The `rest_framework.authtoken` app provides Django database migrations. You'll also need to create tokens for your users. @@ -146,7 +152,7 @@ For clients to authenticate, the token key should be included in the `Authorizat Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b -**Note:** If you want to use a different keyword in the header, such as `Bearer`, simply subclass `TokenAuthentication` and set the `keyword` class variable. +*If you want to use a different keyword in the header, such as `Bearer`, simply subclass `TokenAuthentication` and set the `keyword` class variable.* If successfully authenticated, `TokenAuthentication` provides the following credentials. @@ -355,6 +361,10 @@ The following example will authenticate any incoming request as the user given b The following third-party packages are also available. +## django-rest-knox + +[Django-rest-knox][django-rest-knox] library provides models and views to handle token-based authentication in a more secure and extensible way than the built-in TokenAuthentication scheme - with Single Page Applications and Mobile clients in mind. It provides per-client tokens, and views to generate them when provided some other authentication (usually basic authentication), to delete the token (providing a server enforced logout) and to delete all tokens (logs out all clients that a user is logged into). + ## Django OAuth Toolkit The [Django OAuth Toolkit][django-oauth-toolkit] package provides OAuth 2.0 support and works with Python 3.4+. The package is maintained by [jazzband][jazzband] and uses the excellent [OAuthLib][oauthlib]. The package is well documented, and well supported and is currently our **recommended package for OAuth 2.0 support**. @@ -422,11 +432,7 @@ There are currently two forks of this project. ## drf-social-oauth2 -[Drf-social-oauth2][drf-social-oauth2] is a framework that helps you authenticate with major social oauth2 vendors, such as Facebook, Google, Twitter, Orcid, etc. It generates tokens in a JWTed way with an easy setup. - -## django-rest-knox - -[Django-rest-knox][django-rest-knox] library provides models and views to handle token-based authentication in a more secure and extensible way than the built-in TokenAuthentication scheme - with Single Page Applications and Mobile clients in mind. It provides per-client tokens, and views to generate them when provided some other authentication (usually basic authentication), to delete the token (providing a server enforced logout) and to delete all tokens (logs out all clients that a user is logged into). +[Drf-social-oauth2][drf-social-oauth2] is a framework that helps you authenticate with major social oauth2 vendors, such as Facebook, Google, Twitter, Orcid, etc. It generates tokens in a JWTed way with an easy setup. ## drfpasswordless diff --git a/requirements/requirements-documentation.txt b/requirements/requirements-documentation.txt index ad49287304..cf2dc26e88 100644 --- a/requirements/requirements-documentation.txt +++ b/requirements/requirements-documentation.txt @@ -1,2 +1,3 @@ # MkDocs to build our documentation. mkdocs>=1.1.2,<1.2 +jinja2>=2.10,<3.1.0 # contextfilter has been renamed From ed00e11cc5f338062ff2e90b552643804fc67dba Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 26 May 2022 10:40:46 +0100 Subject: [PATCH 0873/1262] Add FEZTO --- README.md | 11 ++++------- docs/img/premium/fezto-readme.png | Bin 0 -> 22592 bytes 2 files changed, 4 insertions(+), 7 deletions(-) create mode 100644 docs/img/premium/fezto-readme.png diff --git a/README.md b/README.md index bf40167512..9476e92d2d 100644 --- a/README.md +++ b/README.md @@ -21,14 +21,13 @@ The initial aim is to provide a single full-time position on REST framework. [![][sentry-img]][sentry-url] [![][stream-img]][stream-url] -[![][rollbar-img]][rollbar-url] -[![][esg-img]][esg-url] [![][retool-img]][retool-url] [![][bitio-img]][bitio-url] [![][posthog-img]][posthog-url] [![][cryptapi-img]][cryptapi-url] +[![][fezto-img]][fezto-url] -Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Rollbar][rollbar-url], [ESG][esg-url], [Retool][retool-url], [bit.io][bitio-url], [PostHog][posthog-url], and [CryptAPI][cryptapi-url]. +Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Retool][retool-url], [bit.io][bitio-url], [PostHog][posthog-url], [CryptAPI][cryptapi-url], and [FEZTO][fezto-url]. --- @@ -194,21 +193,19 @@ Please see the [security policy][security-policy]. [sentry-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/sentry-readme.png [stream-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/stream-readme.png -[rollbar-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/rollbar-readme.png -[esg-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/esg-readme.png [retool-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/retool-readme.png [bitio-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/bitio-readme.png [posthog-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/posthog-readme.png [cryptapi-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/cryptapi-readme.png +[fezto-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/fezto-readme.png [sentry-url]: https://getsentry.com/welcome/ [stream-url]: https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage -[rollbar-url]: https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial -[esg-url]: https://software.esg-usa.com/ [retool-url]: https://retool.com/?utm_source=djangorest&utm_medium=sponsorship [bitio-url]: https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship [posthog-url]: https://posthog.com?utm_source=drf&utm_medium=sponsorship&utm_campaign=open-source-sponsorship [cryptapi-url]: https://cryptapi.io +[fezto-url]: https://www.fezto.xyz/?utm_source=DjangoRESTFramework [oauth1-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth [oauth2-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit diff --git a/docs/img/premium/fezto-readme.png b/docs/img/premium/fezto-readme.png new file mode 100644 index 0000000000000000000000000000000000000000..7cc3be6e6c18de05af1c8c2601f4edc627bac0b6 GIT binary patch literal 22592 zcmeFYg;QP6(k_fka0u@18ax|!cXu|nad&r@jfUXvZow^hfMCI$;1b-qfPg@ekrr2lfPh4Ommd+}-v5=qmy^6J z#6V(V$}(bNWXjGC79d-52nf0)ps_KgG!xy3iHWiC$PY&P56&K{k&&^g#sNdUV`QVf zKk^29b29YxH}TLnq1F2#3RU`AZIENh9vfIjsuGb}dHgi4U#z~Uy%_wuvxO}2{@9xe z*3CjLdV_G1brw-UgoXw}C06SjszR8yLM%~82undOeG)!#Mesq1%!V-`g{VYCbcSoF zg;;_b#gC#FzQrQbqeYYf5J#ajKrxa?D1;f;-5)ZEh_7mI1 zDB0kgL69MXnKAFGPNH@Sf(OJ7PueyokP_jm&u3`pgxbv7Jq&t0!?Z|A3P zZ*K-ey}iCML0i2%5ai-U@m3}~ZSPq{bd=U{fq=lF{(C^ms8U@(K)}#})U{o;73BGW z4t6XiW)7z2ES`3b@753y08jpR(azk}gv`^<*4~BRQ-I=M7X0t>Uo|TQ*}qI&Z3HN^ z6_m-u9GuO`xL7z@*eC=)kdcuAoXsrwRmCO$3;up4Kw;(T>d4Q^>fzzR;_->a!P%0P zosW-?m5qaygM<0qg4xB(-qpmD+1`cn-RFY4xaZ&{>PI4M2b1sIykF2 zngGrJy7n)~KUDu|{VyJ!fADbqUp)UP`3KSr$nWB2YHbd5{YQ(vx7)w^znZxd>p#>0 z*8kBO@ZKBv6&%b!7GB~euI7RqY#bc?Y+U>t++^%*e+51+0PBC){KJF4C1TFzCaw<7 z>JARJg8vG`?(ZqtCl)>yzJFu?Y4KlR0PEke^pCOi?}7U-?fb|T{P2$a-&X;_52WDd zJqQS42pMq^bx+7s1NcCV@84cq>zT%9e9pTlp*}FsDb(LBUSe{kWmtXWQc$U<@`@Uj z^J3kjCb;5^GWaN=45Nacxc-@m*gYuB+~oMF`^%^O9G~0uu9LH_l{4g30lUepFAl$b z_jBLgENSwKigs%$oC*C$@0W}*>JYSH#zgbbagAgkoIV;4gC%v8muzq#V*+u5L@}kJ z?qb(k-%XgHlF+4NKlT~^`@|oH$>|=We)!!^So~cU4;_*GThlXw_Vxv(X#z78PEh2# z9Ep$=_m|@yqi%3ZYLGW1EYVA` zDbX5;2+eJ{RJYM*lzKL7jBEdC@~ou~NGbp`mM zvNi+>nZ$wuOg=vbBURO8>T*MHN}zCN6Nst0qbX7H2cc|8EGKBDBB|+cRUR|>`9 zCim-CR(Q1E(wYl0OPvpSwrk$fsHA-4mck#umlYOlo6<8eqzw+j$S5j;LqlO?MEy8z za#*tYxs(01o%P1%O=t(iW+yWdh3B07G-w8bjI%jLY$08%O_Bwb5_W{QpA54k!Uo6gr| z+fh!)e(3V3f)<6a#qwYv9G0A=TLP&TYBy+Y7EY30o)Y8?4ZC+kn4*O@D>Ad%MVrg` zw`aAby!C?H`$@Lvq<}cWt%PD3v#&HvPx6mRFvEL?fpVord>D;h{fUr+*Pb=?cC`}ll=*jTeztmW|+?v3WS;LEQ@Q_CdY5vUy>wZ3AzFoEvL{p<` z+KrVWs{7-$ipId;awKN`gLdTh--BE;5$(S_a%T`myAjO5>tq^kmNl9D+BEuedIUwq z8N1eV3z$;Kl{*uopBlUu7#=R0zc&ONLykr`&r%~@!8#MD)LVPSy9NQi!0A}W+y}Wp z&LC!88SV>iZ&Jt<;k}$oq9;XUZOGC10aLE=7x==9YXxM~c&M=gq+G;cn!9P+}u~&+H*Wd@~9MSQ%zDVuOr}rfCHU1hKU7ZDk z!Bq$Y#=G{!olEi``7RQE!)w4>lO~}!wcY)JgF|yFYX`#Y`x`ERoGX ze5TpxvrF@&y^wc6^YdQ7?@_^w9(ofh8X72EiV^hQ89vwA1H4CA9@d_3F|53^)8dnj zk);IF}zkxb9Gm+nv(|i4T67DX?vRcf*oiNw3RO23^?B!#nEqiB z5~|wDOb=!0ot;q4<^qj&RD|Z0`+N*`N`7r#7l2&(L2gxuusdqytk;XDbm`CcA?i|f zgDNs}cQs=vce&nr?zOAbGFE`2VhQL!m=PctTcXDZxIlsV2w0an8)cb$ZOco@_XYEA|J)zQRUX zO^*%6z**+lT^7u!qon#|_C`;W;h*=9M~v~1W*2;XA6UfqjV@0Z58q?IG<0Vz9kZL4!qh zILGrvQgQ>@P2=ZJdJPvrOK_jgTZkj5aVLGLAa!jqla(eoVo*{6r%dA&jne6WRWhllXg--+ zV0k1j3fipq`%5u9QS%H#IgmGG$aDLEUI$5Hqd=$9{4-ISbNpUKtv*ySSxS=7Jl?k* zyQ;}<%&0960atleuM0~a>0}%fFR5zOuiPi;hY3C8Upf+cg@Sj_HUm}Z0gl0##35cz z_)_!O2>h^ju7P|$M9(JS*|QQ!;z*m|u`?^afy3D_=gmzgoz|^D--mf_oqFp8BHolj z{khch@L79x5)I;dQ~9%dw$g$+DTnzI1%eT*S(@=(r!KGhv0&=>529VqMsmA_mK#nA zRW&tGhxOgflN1-l`$#eg)8Rip72Peaa~zeZ%s@WE$P(5gT+6TZe3JX!(=3nE-6M;7 zE>uE=J|A}}IPDkZ8ZuX#K=&PVPM*D&WVBAjyu$NmU8VB&SFiQugwi_rW%{!UB*XQH z#9p1hyz-4X>V@fyt${Maa8;~2K6^uld={thB>qnc4hET%GfKc`sRn2BKQq}J4zDyi zZJuAVL~+&k)udE!wstjs#nJ9W_S?%5EKe7r=`-=@vo9t)+j=lNsY7gFlf~&M^;BPf zSZDnP3bc-SoR8*EXX)N7SGGpU1V~SI>;XVRdDhL_5ym1JS5viSeG}NuyS?l(;jSqV zVR>LL4M9XG_qM5t&-<52=Z4Uq$C_{*(lieQ=UeKWmsq?Wr5r{uVyR_lK#4Od+^kM@ zZTxjedCUsh)esovy+u7gSYD0|vqsO6I2423l$uOd$J?*{~w%xS9`fh;G?t*cJm*T+Tz3)H`N8pHJVYTvY}Di_Y& zT*>-0N!6l-zX+39*{DwnUfmaS+&%9-M8Avo9H(#amD0lbXalPJ9yN)9No~I8lw+t3wf@ za)Y~%np-tTHiXHfTom)4OyPR@utT1@2KydOTdASJr!iCd)ce`9eEnDq9!r?RI@=x) zg%n0T=hUNo(gu(p^N&awr^JY)V}J@};|IKLjrxxR1nx1}Pt<+OX+#Z@xkD|AS&0!qD z{7caxf{H>tDvc(@ZNT}~Te8^KJ2B@Ao0;tBUAShcfM{|px4U@^=&o(4g1;4>(&y?lg!Ml&%=PH^@(Rt-T zqT#QJ5%A|!zus$jbw-23#RzI{2J;29W`y}362^u(d5;9{4~m_C#8Fmod`$(+3i*|cAG>uD-sCRh zb5#fXennyL`Bi($vrhhea|)98eZJg`F)uQhWpVqCZVw(FV{M@um5B@alQN(3B`$T> z32)K13UareU}h?olph@4fC)1DqU5kiPCF?RMte3Ol*6FLQp=wU?;yy~Ap}$Mj9E8` znex?~K|>KBoIN1njaEB6VUEReP(0M8e!H~(#O&nx^Wvm2=5h(?d^|pkkbT%v7F`FX zpbr_&D55(ZIb}SjTN2anL6R=9EKn#VkkEof%f+k0+L3`;S)7fZDsG`` zdF0~(CzwyM7&X-C7jklkzc;KelI=hHQi?IO42J`Um<@IR>7#BLDXa;}+gL z!l#wSg)s&?%|d)8r%>VK(7Ka=eYw~2Pmq3Lr5v7eh#zgsGg3j9SihtxGkT6x@!%6i zJN%u)E{Qkg_Laj|5^mGDiUmi2rzBom`M-A2KRRn*EY$g2;z^6qi~22K8Nrp!<{55c zxGJ%H;pp{fz9lJvydwotxtx4^%kNgtX*rIbI3LzZ{c=Jng){WJSVt#kTdE?h- z^WkTdgM!}c4=62&6-tLhDS0CC-+Ve<@z>V$Yt;0>!>wcsvm&vqq=Y4RF&5n?04+3L zJDpakoPY}rK0M&1b}NNiPBF#xv=cQba%VOKd;?RG#eI}G zg6vfApTlQ>qX!u)CW8*#4FKN)$V`b>2&0PG?1xV5h*k=kmHJ?*Sh7Cfa4ug7Z5WhS zvG=hvxSK@{M68f|9+|VOSLhFpu0CoYa>||6|1`XRk-3KZkQfoYOnEg_3Qd$XukXfhukZ$`{26hnK})Nts;F*oUjVfjTjH7cpA&1={D zJPUw$+Rn4KP&Infd(A~(=n(3B)DtlraG+h>&fGmz?u&3w5h79+x+;F@3A2kq9acUA z0z5j*3*>)FVSFSf%@l;<5}8_aMUKe59ZYrpIp`mRIf7VG7xv3~O)X>NU;g?h5RSC*c}Am%`D>JEg$J-%HRr}ok>>Evkc z=e(Hlb|VGIT`tDf3rxKQ?yB&G(No9mm(TWcMx}Tk5KQH@jnnat=Y-?D9zyNU;Ia2)c78DX<5_P4UITiBC* zCp>sKdu$o%(s3eiS&uysmwg)}dyvixaK|>X!=%uB%)YkaGFvu_!0UnEKCQRUN9GBZ zlq`hfI4!T-3+AOk*J1L2Ec#ix15*_)$KK&> zE}BgfkI7ks-IGOa^Lmdc*-9g$#w3N#VrJJ*MQ;q;K7RX2&s4~@@FOxrW3qn>>+D%v zn#4oU|4U|ebPSK$VO}-UGQ8yJ6{mkDVDq88ih1h=K)@xF44=PC6mUq!^_9Tfwsh?? zKZ=0F`aq!+Ir6A{|j#$kne)kuce|0G7TJt&|(QvDjD;nKvZ2=)$-8)Zxodq7Q zZ_u2~Vd^SK18uSB@&>*A$iw4uQEy8>iI3@}Dr5^QPg6A2b$960wK8vx!do|lfes-)=8@HcPRPl4jpDjZp0@K!hxgYq(6Z}eXN4dJwS zA=*ZFxFSDM1z_II9+N7e!`N<1*}izm<1=2dOVR4=;XY)xl+FjWcpeq-@Eo~yLB3RO zm!xFh(rEYv0%v&BLKO)5*Y;k5Owl9YQvLpOa-(eXhK1E7n4E4!hx3aO zrEo6f`84G4qga0Tpe%FZX3bO?P-saG;803Jx?sZ2fFu5*%Fft^Vi^HJozP)om#?m} zJfO~Hz(dZ}5>}$Ct4DDFX#Z|}CeK6ZWi36fashEW zuc$ulJ+piyG6O%4uwXi0M#)fFkck=Uglc#P*y5rFfzPoygIO&rBM9=HsB1V3@fg+Hr+a zP^LkSG|tt%W-iF6<=Ps|t66zr+9InqhzQ+S{HSbgx;FCxeI)5o5FO0nfLI>j0U;Yz zF7(c3&g*rExt^ZrhQrh&hO`5^7sGf~vXkuJ{D{mfLhQ3FX7q#MQVZ#)rz0ri)ux@4 z*(<^h0*eap2yZVQ6=w6b zH2$Qe6Cn}M4Nc1~kShfOO@AdB1!B>tW&R0isAgXGIq?U@7*l{Vn~5(y{qh`~_+u$XL@dLOk-!@JF9CuK z@Dmk^cZAPy8af2G*m*6&did$kbrL)4I>U`#csj2vuU26@k9LpAOu>dw0Jl0xkc{Ld z+X7C)E=CG}WEK)lG9aff@fq4Y!#d=w5&lEa>S}P{9MK0XHdk-zYxm}G-uv$xFfzc# zh8$%Kf~=d~R=IQf89bz!`x`se92LesW3i-N4QCikwf%3pFQYGf_P!lzd@wXX zX9IV=l!$T)GqlZ|BAK0&7Tf%0*b?0pyFY^UD zs+;$moGd?iF!uG(k!uYu(g^SDW4e6C@}ZzZ%dhC@k5)xEu2R5p^8|w z-(f76HF-RGt8>nI{_Se`;Gwj}rW`Ao!rE9tB$d78X&`OoBTw?wbI}f&KVJ)Dk1iR8ntt9<<+Le7U>d!>-#mdYC#pP-LUA4=bzig zqnwPCHU%+{5*42w8E$k+jD@ej&uHU^h2Mgb-C3;>blOlk2^o%B4E#_;6Ug5hY z9n9qUy4~KjS;2-fVtM=a1tCK8}6sVd*K_lBmMAfZw@m{t!_RLkK%mb5z=J=wP;2aHQD2Aw(lmomV?}yAjCg|CFSOkc$G!+K`fbeq3<&PRLf~)2C;m9u-eD~91%&u4oejbTjs7CAiP1S& zR>Sz61PHs$`&e?)(kmdaUd>Du_6?~80%s~l^vH2@Cmg)m+kMJVykU$zhrHdZ+IK?jFam$xkg<6 zgwR!lP=;eXnf_z*oIG^}-AiHL4akxta(6?F~EUBQ>82m+!#Rz0+(qzLj@rmNBM zi~)^fjRq|R?gzmmTqmrmwCIM5>jIWaDhpc)u4`h*_P^2lx zz~TGlGyCl>^Ct)Gxn*Q%`jR=xTd$oqTQmp!+;EdCT^SU@qdS708P=l%brJ_s&d=i( zbn>N)YIeSK@=KKg54m!?k(W3qH?qKODDjE}jN#;hrXz#0($JbDtDLahp);cZx{j10 zNaqVTrMbdI!HadwkKUmzfj4G!)F|45ruh7Z(^9&(=XBI~vbBRm$@sH|Nwm?vNN-or zyrDWsa!kklU%9d0JKXft0F$d-&C`JaN{-qt2JMI0+Is1`r^a8R%hFI(CHWPm`h%r7o2f zO<8>Eh(T|JsnA5KChZtNy_|}Hg*2hA>td#$bGQzxJ_~IFw9tqc4>W>l#i9743MI)c zhg8hrb)ktQfu32C{AkWaG(@a2gyJH0X*Z>@93@J5Ol-kyxbMe_pIJq7VPx16?01l< zM$fQuogS|bTd90(_HZsximdM@;ew29kQnI2U)YE>iC+LI{jR!ege#Y?QpdY{T0Q;z z^;^XVs8&0n(jM#l6{>2mkH^Kih6`!*Q`#^*U>lHV#?}7a}&Ys7f>^&JVFb+|h5Wz9Z92c!VUd|D|KgND6ncbq&)LWbV_WL;2$d}4SB)Gh(AkVjWgW7$z zX=vwcWh$8^J5!?T1Gt1zTtI`iZ{Y1-lq2AkC0?2T6ETL|VanW#Et0zgyy;Cl@pcc6WHGF5UPH~+4zM-f+Jh<7QxQ6#){^|&zxeq(LoOc zm9ViF(^4*?kC-)@DKd}o+U6z^qYWkkILa@)FGyn)dP@&TV&}rj2^Af^f;n?wLB5=p z+2961RvFT9+9wfSzs`d29Yw~)Iyd0NaJ^>bXHmYYdV(c>_K1-)>J=)|V`q1fLgd3$&4q!J z<`0z9ScQUmP1aOK?+@az2ZqkSxo_P0?0zhjc^B-~vwqU!@Gu{Lz`b$LDrI;+$XL&a zS?bL3dKLS^XA=kJ47S+aPtGnGzSTnLlH7@XynI%wO*C9%QdE`?`UaqFJC zm(}F~b#&@Sdd>b#!|qlX{m|2zPpi`i@asomeJyXUw@(@x)Mb2(?x(%Bn`Kj5lpPPi z`N$ppCUOQ*p=Zx(Mi3kDw+4q^PM*}K{jK}Mrcn-m+o_D-LhXZ)Eh{`PD5E-chA=j% z2b(y|Wn?LwnQ??57Uz6Y!%^d)-ruYuIIO>L?xmI!TSs{wnwDtlBj3Yc#DF zrt0yvrBLFM%L1bja<-krSEQoqaQ%Ywx860}u^+!sk@O>f8mdQ89-kxaZsz{(cOc|P zNlTDLz6(=?DqD^RiF>rMVWv3(GA;pkU3qGTYzp9%Rd zSBG%(0#09XJ?1*X+<&J;;iUMw{;=nWtvj1q zo4`_4&epX@_e*l1L#}qiA8R4`$m#4dM0tIuWuRSl@{EbE6O>DV<`>||r?I3`|6Gs+ z_`p_@ckHFPuVPrmh}AG6?pL|{ov~71 zAJ_b`@qBz$iRV!|#3MhuzMStt>RkMg%f8z^6!5(EN28=N>VuPGd)LdSb+8)xk7h$L zBtinBE72{Yj4uEEw3*|(4X}EZxvCyC?e!t@tkWb*;_%Q*G@Y9S{|p?`?>SZvX_Ag~ zoy)TL$FrZM)meN7bd~An>NxD_tkOq{;RhzNmy3vgQc_J*_NiG3jU0RGYwxRIp>FyKx7lHP(a~z4hM|=q4p+jT*BVm*?Aj7FA0iCh8H)R zxS6M_ae;-H@uCG-cn(hs&>V!_m>R@%AONh)PO=vvIcWM^W^aa*RLOnQ1ZBpU&%V=w zmntr%n8YnwgNQ+ah}NPuZT9#RHRLB?7!tMdW62J@%&~(Z9*b~ z6;!524efZU^(pHxi4kc4I@ChY8s%d$Q z6~R*7@G@dDH0yX8tw!|QyzD-4S%zI|!9$LoSCi57EMRUABMQ*PQRTQu((o0J^toLA zRbWCid4jTe$!=dgbIe<`{rFV=RoyPehmwq}j=jnH8T_+F>rvP%m?=G3a4FV2{CQPk zGOglQJocsEd0*cKuRZqE65*uoFPHM6lx$=QM}C6TbBjzawVbwlr3bTkaOl^<&usxV9f8omZOv3f1UtKNoSZkHmD@SIsf31 z{Kj9>+c8#4$nQASblsV2k)mpab6F2}QR zXRQ|9DWx}`=59yBn#;?<)H>Hl`+3p9!G8}y#(FtVJ;jq(Bzt%e|CfwVruCHMWuWKt z-UiOd39bcH2ghKFCq(UsG0&~g*O_i$qp?B6{R&vHfWwaYo0~X4>4qBFGtK zuq1CW9EtQkOT25>-tQ!i)BH?I5FQhQIMKrjn#^mw7+JjXEwBNOi5~BC*=mODDywaE z0p3gA`y#H`q76ZJDd_&w*N?iI-^XI&y&Tt(G$=)^G%!@OGUm&bTH`-+xN|7(;#BV zkiQoiZQu(b@-fX#M=OgT-{j=<6p-U%iQy%gb)V3+^G|p6R@jnd*4GcCJNu1QyBIqy z&MZTy^AYhuMjjgElS2S*M3>5wWhe1?EoYV`B3S~ZZpK_jiLzS{4u^Og{s{~L(Zf=e z0;;$O>qv^Z&8;=fNZv_d`VXf`Jj7FilLO~M&~k*KG&o9gyi3K+=Fws#Ss_$J&Y>YY zx(A`xupdhrO)B>K#kk+6Axttt3W2|HAY-A=YrV;E*~j`Dq6zDY%p^j{<6y52F$mUW zREww15PjwCyZ`7v7^0+dBBSon#GBH2OxiA267XUnzB+HiAW-8QErk~XKPN@f3)?M$ z0W7dy*Iz}mBS=;XQbb;%E-K-b52;$vcmE`5WISR-FFV1tgV%goG#64KEP0zI&q)1PNykVF~$3;V>K{W|2apiCB9=P!rSA9U}^2ZnS}l0NNqcD9IbnSW+ey zX&?I=QLXoliLySHFUo;7)K!(!?GxCU-j$UV>b+b<5kA)Y$ z-TL=v$>E1|Y~(<~j9wNO7c4VnPS6rW;T*gZ>oDN4d}uzCYF3&Yk4ots#6-A-mHeAf zF6)`wp&dS!ydPX9_8o3bX`I%Hqw`<2-u%Bs6{5RD_bb9MiH7vS&z+;(T`EgI(tz3~I9AJWQs&q&mJBjYL#SdK^UC081Fz{)|>G+EY#ZX~}ztE5=dUuY{wHZ3Cx3NNWpHZ<4yBQ637(heB29h3l8I-j`i$9)L`{U`r4;WHVG?7D z3;q2*YI*`iCvW*Ok!c;0RB^Au!S+K~pp73Dx-FS1it=1Nh^VB4`(wY5#o5JE%(?t*tt@?F1Um?0c%S?2{N;;&|DGaGkI?dJrh56X z>Pt$S1PYb2A!LMGSNe(n5`wY`KlVaeP+iMGkQ3xt9@Q{h6#+ZjZ!K`iGHk$;{<_vI zx-M;f!_PS1dGyewql7~Hox#ugymfCGOvB=H7OL4ZDsF2(l|lxcR!;XngC!pfyBw!# zYp(Xj{SY(R{7f~b(P5#(C|`Qo$J8oww3oOmYLO#=3q%mCqsd&S>1C(J7$*xy z=}o|lN|Ws$YQP;6eqTCb%)6q4l&O7jET$@c6hMXMAjH-#)jKcPNdo!HD~ zfgHCw5hQpxl~up?V~OlO2C|28|MMcXNG&HR^o~Wvm%*_}BG*y@HUx3sbjb3Rp&ts9 zX-)aVSgG;(t_0Iasx$YK0=V>O8XKq0w{`lM@Y#1*U(G#*VG2&VH*?b#>Xa}(K- z;}jf?dRmJ~lQ?2hE>;RngW;)>u}4tW-J|^&9l^)w&=+a2=-B?*k!cXSgYfE8T37)NkThPrxm zUY$0>Euo@ibKl-K?~e+C30LFT!~-o9Rr5l$kKNoS$Gkt(%k)XxqnHLZdkXF=2%y8r4ykjJ?TX1l<^sYAu5mELegOw zu$G0g?5wEjnNXQmMLP{zbsqL|z7yD?s>ksTg~P6i4SByyna;Y(Oh{w(MGA6j!kr2o zaT;FhT`rt}O{?1M#H?U$YsXSNLDGZ-n&EDZ=d*s99SwBLLvVHoNQ=38RSC&=(s!R2epx ze=9s;f16eaqVgMV9UAM><`Tpo_gmOgKAWL#b)Xr|a0n55ik;|VCamKhI+=#`X)LZo z#$9Viwkm2M7H!>KgEvY7_D1ed;en5fN7yGGF5svV{3_ie6ezG8U5u`tZ#dW{7IDtw zIG@;Glw?R)CsfDy&)qs|F%~;bNK?KNof5}Jf>zX>Th2$$ybgPt6;VkiR0dTdJzM=; zVtt>^kg%Q7;|*FTQ@-+IlD8BaFF`knhjIPk&ZzX`T%?HfFV#FL)039S=Y%Iy>4bLX zZK0I|^L~r@U|W#Eg)%&ROPtI@8oUb`MWwF{hHqyUD?v?y+Q}jL$VV2Ye3{qpO481J z{=P`d_%NO7`T;1A4n|2% zF=?4Vrw%?CYI@PP**GTCCgc%C10EttudBA2 z(vIz?Qu-M)N`}bF+UJCk>VuqBlw(9($LLby-96`2zG5Fj8T(oF%U04*5gr)hITs>c zhEg1d)S+LobXv=9%$G@VPv-AVNqLY;-cfyblpHa|8GxrD9^0?<_iuE?Ti`PzI+uC$ z(nrdMmjTX4RBq;wRPb{#m)j$`k+8C=@48a2&K@@`Y{aOQz09%=)EIPQv34$eD(59& zFpwhCYmd1jU6paW6D@Y?yBAE9p4k>H-C_-hY+WZUzILL}w3O>86Z+lDR%9^ zwK#O51>v#1zlU`4`S}JQ=MO_LxoX{i-%03osf6mbS5M4&_ATVY(pm|ri|<6+ud%K2 zOpmRn+JZuC#pBP1Q|329lquZ5@7^j17jPH>vb_NEF4WwN*P%8HDprEqA7BctH=<|x z! zqg5Io?|D<_v-xscaU6lS74dexW`#gI97Chgt;nEL%yoPF0SjJk?xMnl`U?_k`Hi3~ zk0OVR3}lv>zmQ(^w(;f)`8~+j^X-4NbCzvUa9n} z0jU8bg&ANN!jYUIrE5rmp#`K%KTzd2{`wSF32B$?TfV#u{Z z4&l9EN{-qZ)y=d`zzfpAncT;t^7m%$NU3?+8&1Q$a6$5njm^wkLYjOJy~F5mu=&E> zncfUeL3!x+`zMX`!iCok4%RDo|#9|`v9p4Z@B=;F@iD~3n)2jHk|yn~4~T2Tc7vG`34@*f_p z2{A>Y>E_Zq(gc3Dz{%$-ThAX1Hor&YciDFG2R16Hx z?WNJ9Gq2k^o4D4>lVxEtu`Ua~h=I=dD~b|M2tb9_Tz%f; zG)-4~x7Qt{bDfhAmc0(if3aKnv0-(aDJi5;C&1fPPXGzGzsPkv%#sUM^71+aIAODM z!5%WUN1|I4b{&3)itq4 z4MS$|2lbpH_&>MJHxaHpq$0O7HYC{srbaCHU3^*~Ih3jq)wuk;%XVVzRw%HzeIEd$ zlM8rMGO+GEN^mLvA4uzgRC%{bG9Frg9GA^E6;PDDm+dL2r(IRD zJ3A>||2%iKY?jmCoQ(TWJ?wFIeiYN5K zXB+^;u^^j2VCE}n-`nq1ew&!JcV2aYBl}s^-g{vYwfM^~8p$^~Ho5h+$UW|EG%jwc zML!K$8k`qr$F9N$c^*JFsz-{<`TSkG#gm3&Fm6(OxVa!epl!=;DVygCaACC6nkp9C zp0X)hwFIta8yp~@;+0UefF%XW5!!D@Zrl7rT?^Qa?Wtm~g+B05lgHx`_ zv{9=t7>I=yu|tJnGD6$anWR^2{_nF>5hU}N`~@9m%imC8$&y#UI0SUm+!nv2pm6yl$m`y^sn12vuZUDK3e%D;xXyG`q+6=*k<7g z#?Yd$HpjU)kO)=2cXRIbME}2h&y<)K$6OEVT$K8C1!%eW7Vf4?yslf&>>G?tjrX?E z3BDfu?ZoHwY(%H)5#{lMJA!{(UjiA(_82Cv;gXe#n)%f?@tjqsKeKpy%9Oe3G=>k; z*4thSsH`%V2+|{)x|K#Z^x|WO58>j1>ov`%WA%3FX2*oe*8Llk$xm*+{TPzwCCX{q z3^V;x7W{|mNd(UW$L`Xy_cZ!vgV#N_7ZhOd^)wIWvf71;Up5`j9YuCoSW&hcv?J26 zg-D!8IWfDLOQXcsUF^`j%D0i&BX6bEtya!coX0ByKHaBUVvzbQ>c@qpu3ZJ^p;rg< zI7x4Q7L)8C0M}k|-!8BKX^an}Y+g-DZ(n_;Q_2dZCc_S2f#N8JU^2Ok+NBIdN%UiE z_`l}MTcW@#~89uqz4uvd;q zFMpmCluoasu2NogO+RX_J=pr}BjPLb$qvXe&Tx6}x?OqOh`Cq++0~Fn6;;fg)^U2b zV&!!_kM0<$;`F0@WVgr(ZS$|y5wr+N{^Fos9TSdT6<-J8(Req@8L_cgm9%(I_9oP) z_OY2z5fKxoJXZ=r!xn9u)k(@$P^aK<{!aN;ywVKr(G7wRtF!cnIV@>6zmHb(Kk#SE(F++h{D)G7 zedmzbeDwutco9>Hs37sL7+}_fSxvs7HfH{OSED-3QDJXP>+A#K(YjBDbQ@(4o zxg#5Obfg3Jn55H?aK2v3ZQC*0&!@;C$ShW=h(^wNnp9O1{2ZxGPl~tNZFS{r)T080 zV$6A2)PD@(AK68RTbXO7jWt`TJvl2eN1LYc94pzOyR$Ay)&@QV-z2U)Ea!l6J1N;@ zS8!_qcExx0|KnVo!lpreH|%|t-hkWok0-xoXsE$JcruRL6H`ffAC8MgJr{46S&za$ zCV(V*!>GEn`Tc61=nF9~gjn#1HDDybyx;KgUVY*CnSDAjDZPly%Tj%+u5xB8ynCRJ zFI-HTqMY3yja@mKHrIG}_jADzK?CWi{Pz3-n(}59zuh`3iUwK~)uu|7EeTW&SBH2g zh^&R6jCq&+LIR&JujT#lS=#{Gq;jytn&&I!QUn@4k>iJ?ANK;Id-2RayOX@~AH9iP z6uHJv1qCz>6H^_{)QY=WU{r2DJsCN(mVRw!Jzy*=|4lib3MjPDJS6%}Fo}RL%Sa?6 zlu^=yJAU7#Fi@f58Q)DX%=bA>jXw-jwEQCMQ*)J5qS921UB|KXhe!<(GS6kP%-o~w z)9_v_n&(~K>-rCuj9#{V_B;@bY3^l4t7@+V z#5!6y38K9>VB0z?}#7-s3G}X@2p`{>P(bLCIU1} z!rv^r@-Z2;4qj(BnkStxp-XxCE;Xo`VOp0x4_^3NXQ_*#O^c-ov#c`}UfrcYxkYG* z;-ETFc#!!STO%FAssF5xTc7t}O*|8P=I^+r^~UFp-c~=+)8QdO+@@w@0A}uVJ`N2` ztF@ZP-O+BKD5PDoCX3r&l{94sPsSBCp$XcZ$G~G2?ZmMwz9;Kw z600n9J>u4>iGL=#@#^K+iA7H^Wtp(7gv^o1hDl>ilF<2*Pm50)37(VK)~sSz*>;H3 zJhith%>1<_iFk0MHjZF@eL{3fM;f@Ds68ZJI^pZ3a8G2mLCXP( ztLznB;P)b{twMMM#)0voyJgvK2Q~X{Q4#2(QYf(`w1J%QH`$a;Xs$LO66#hlw*=rQ zgxh@4m8+q?QH*kjL;Nrak3hV;(#estk&Rh`p$ROkw1B5qZrF)ixl9U;-rkjrJFmZ_Y6a!|5QH%?JKwc6dz00=a8GnBK z{!!4KSCJ`kjxq1Yi{LGthTEY^n21n2oK#pLdo~6O-=aZKOPzl|TG?>eMm;d|&e?qY zhFF5^MCp-8XZuIYPrfn(o1q3OwL-i>?*&!2-7{ZLm;|#4i3{1u6D^_SkEwh=6PY&< zC_O0Ey3I?37V>85zbfbUXU6_pToj5lpS`p1F)%84WKX617-HDu@ftHh)I{!Wr66{; z%DgH^D($GSK$2^RvMz=)rpB%)u?=}+fiDb7fc)cv$sL4J#fU>lGw2YClz*gr1EMSE!kSTVh46Tv*J;I|E2c6q-RSH2L;1v5mM4m)07Q}MC4cfI2&;iS)Fs(`W2W(i_)=X>vvw-bgl;0sm~JPl zg5dr5jy*pb<8x{@6CqH_c3i3RHUO?T3yXY)lME9nND@XCVc@v!yj{`)K^y1AWvr&$ zJQN|0PC#s9S4*YXvO2$|xU1%Cr>TYp;G}yA0fa#V;C?Xz({`KY<1*TX1H=XZu0H|& zx_JiopD1TLn24CPlsWU$hbqjj(dRvpYODD5&VFPB+=MPzl#PFs{k^lsTgoF*OwZg7 z1Fd`M0DZuvyb$X?;QqPpO_O8EPB-tq^suRCypfqc97=lMP*x5FdP=N+#4}<;mEuqAXbC{@uoOZ3@Xl-D8Gng~l@vtF`MKhb(W#YIcJ0z@`0^VhZYNWcT}h znSs`n>VSIWY_K};sm1(P+ECl+zxO#QkS_MJ$Q8}y$c{`%8r{~A-QdwPmrNuXNSwuZ zyaYzQ)gxj+la8qTp$`-$+wD(c+CJ^`P5WK_Co2&wr8d-N6>dbXL<1>su3EQKII*_L zGZ?`Gk9jB?Z<6#>PBLY5kaI9~=9zH4|1w`fi+i6^iBKlARi#`mW$Js(-H7s7{yWNO>U&`)Q014c8E$UOJr&O}*J-SZpp)=kxP;1e z6BfW3`3KPx8>w+J{Z~>0;HfuKmqrV5{KwqOaGb5-$MFoi7fKa5nrp_%i~U3NX^FX+ z(L$sY?uAH$8i9Nj){KAEpJhl0#K|LB?}h(=?Ej;;4U-~TMt!?)@I9W Date: Thu, 26 May 2022 10:48:08 +0100 Subject: [PATCH 0874/1262] Resize sponsor images --- docs/img/premium/bitio-readme.png | Bin 18526 -> 18479 bytes docs/img/premium/cryptapi-readme.png | Bin 17864 -> 17824 bytes docs/img/premium/posthog-readme.png | Bin 2402 -> 4881 bytes docs/img/premium/retool-readme.png | Bin 8962 -> 8921 bytes docs/img/premium/sentry-readme.png | Bin 24504 -> 24506 bytes docs/img/premium/stream-readme.png | Bin 19213 -> 19170 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/img/premium/bitio-readme.png b/docs/img/premium/bitio-readme.png index c47118cc6956b8a27a1be8edf3b84f8fa119e54c..d5d6259e61946a911024aaf9835d885539323c31 100644 GIT binary patch delta 17556 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 a@`qhkO^(>-(f~YWgu&C*&t;ucLK6TrZLfp? delta 17605 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 QcrFQpr>mdKI;Vst0HO}u@&Et; diff --git a/docs/img/premium/cryptapi-readme.png b/docs/img/premium/cryptapi-readme.png index 163f6a9ea20f17909e6e38537a627f19e5766a66..10839b13b4835ea1abf18dbb89b0b8e9c5b6e0fa 100644 GIT binary patch delta 16101 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 Pe1VXcR*|ZfF#Y*I_$DB3 delta 16128 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 ME$q!}&R@OzA3!)iFaQ7m literal 2402 zcmai$dpy(oAIIm?kqEUVMI1|COS+h2jFQXTx1B9IU5@L*+;d$jLMd%T+LDdA=Y9!U zO`&8o_mayIDdv_-A-UVxJbwN6dpyqL@p*i{ulM)+e7#=Z@At0{8Hcslxnuth5D2u> z$`W-31QG!Y^y+po!Cqx!8!Whp6OA#(AW(UN)Vj+y!7LJV#=-^wfTCV04r1+CN^`RT6k>eVYF zBcruOb2^VqMINXu5{N;Nm zqHJ#!fI!<5tx(2xq5U(1Q-n$wWLu}W2EJU6rPH6guvvy==MQlDuww4toQBkVvx=gB zS56+3F_shmk9B{jb$qEsJ>}{Re&V>w8v9)6amureTuaCH72!MtnmxS`rtIbfQ8e9$UWfTA8?9)t?!XzL_}Y0;vq!}N8n+<^&H^8AcCFY4@fNOCpmAEU**@Zl%mYXyJ-UgseWGAX^U|40*v+ zfXPrB@bc8vNIp9*MDikKcSEHwu|0PLVtGV@`6R2u#yu$wc8g+LOl>S2ad zNlq75jnbj!Li+}4Hk8eTq?md-io$%ik8Vif4I@P%8K6VEX@g_9_%%>-ohogqEmNrl zWWBQA=;}w`oukPTxMpbNgHp2WFZqNl@k?fK$Cs8z-!G%x@j@t#e39C>$l zgZtJ{NLKmfDJJjzpM)p-zow=T7c!IAXmXAAa3Y5p5EM;Z>c zOqrTzK1V;PP3h$!@uGlamb4CXM%Fvf9sS;PpLbw?dN;3?t`-CD<~==K$ffI8?KJBV z$O0~1mn?g`EfXfhy#hsnfXpWyk2r#9m7z44^L5I$0d_@TD)J6|dUee^N(9NF1QIdW z7c8JDBqEOj8U4gaCAneZ3DCwRAn``nY$?~Xx0?q80yo_Ds5fs2k_s70qc`RT8gvp` zTmuBwT)-mjhWgODYlM1lVq4QO!B6S!<=I0&irx##s3-*T9Gs{@SVZkJf7v0pGL<_l zXb8u|uZMka*Uc{xCpT@S)B;2g$(-4-#?`YOSJi-6!ACCYAY)CZ^w9FXyr)y%fk_sq zKox&aL8E}ZYC|6VH1U2bs(rYDQ1M*QV@F-69=a(k=n+(2Uo7kG{ah1=LObq9E{v5P z@wfuEW_yVEt^(zEYC-g_v**Icv2Uw@^!n2qc0lw;GgSyEajkc()wgzg zMwx4?mmd*ZSzv-sS*PrXmPwoHCu{J#mEU6f3zKHcfstl%v;N|g@enS*%Ae54Yj}uj z%lRoIKV@-jL<`+`t5!Mnl8aSt*vsldhPXT0zMwmyK)w{wBavcnpw(X>_Rr*HGi{_F z`~2dwy1Ho@*a$7Y4t*SX^aNdgA^0qU)M;vy`e}AuIy{*?sC@UQs*0i<6PuK90L3BJ z#nu2OGmkAxPU;{s;ymw-l>?7T5Yauir!94LW*z^DztyXG!f=i;5#G|L`JzA-TT^-W zHSb#W!vG3FzjIEZh*9}Hxs6!gW;WIx?N=MJ7)t$m?ZjAUQpgDMNjqCLrYR@e8Ul8~ z+Psy>pX(WZ4Gf-eKGYK;%Zx|s>8`Sae1=jgch4VabK)gvvD*?Qv2i2$B^`H8g(+00 z8erfHDDIDivGiM~LzTQwW+tc7($y{!-|=5XBhem8GYv~9_Zy_fot1a<^}{Qx3@~0n zS>`OXmrvV6GeX5woY=vOc{V)GTp#|EC$#lWGRBhLZsws0(C2FIb2}u9JexkenOO|F z=vsfrkhza|EXMqeXfR2?fdF^x_TMO+4y!qL_>}X}ju(jP#?Fv{$BUg`IpaUv(gW(Z zokCrLI|RF?YB?TgRZY3pmX*-GG86H1Km1GAOX<@~Si73-#j?8`_8RP`)TGykPIkqn zX{gu7UC#Qv&Yk?s{{C}%Q7(!+Rdy)#y!uRZldTj(uRUy_tCLN2kOmNxK6b8dBUqK0 z3rQnZ0wp{|!dKhD!%!+SW46E;%T$M|%&@3Z6X%=% E0-^VOCIA2c diff --git a/docs/img/premium/retool-readme.png b/docs/img/premium/retool-readme.png index 56adba04d3acc812f5499e19ecd9c72d3632eb32..9715634274d3a17d50099384201259e6e2d4c172 100644 GIT binary patch literal 8921 zcmeHNhgVbE(x*vLLJ{eqD_`R=;!FZeENh0K{fzcVv?&&+O#)YsDjon<>qL_`F-rmb;{h=`a5 zppPiYf&cpW=bXS7nWL(%DiKj-{J9gGGr%*IyS536i0B-{&kr%tlMEI>hTqB17;UVp zqhN=0h1l35Z4nTxt2-b~M5KgO07zE^+J+nJ>f(k{z$)|pl28EXpKK^E_b(B&vof!- zu0FRq(i6cg4UvL~@xsn>b8{D?p+5@85^qmxLfa9iig# z^72qI38;jGC?Fw<@^(YpU`5?fe1E*;U+>XCpzJ)I+|f=*H}0SBwXsEdp_O@ge=7R- z-ye3OogDt7$qn_VTY!VmpDj>vh#2(mU(zUjcAWC%{vipLW5-|5Wfh@2~PE zf2ZJg=5K(XU9X_;ghjX*Yd8TN`pXLmS-_9KZTTxw9qEGfG;p`EL;SSt7vwkAU!{NP zG5Jl8^ndC3t>rhQy`2Ke%l00^4*lDB0i*US`qgY4p}(<}pnof^1jK@ZF4Eq~!CS)y zjetprNk}M&Nh?UmaEpumBqZgPpnsA1&4QmT>YfN2G}6-$iFASea>MoKBex_(9`Yyl zcZom2O3wA{(7fS&blkB%z@M3R?OmGuyzYlJ zmwgR?@5fCX>ylAU9p5MCo@_$OdAUja9<~W8Lmc*s#A%N#$TU>Q%t_AEq~OXlRmk$l zE`o1rY;%+F&YXYfUZ;T;CE=yJ#7)5QKBl0jBTfkn?YxxdOivdiNKB=5;gJ$9kg^Vd zKl< zOz#dr%y}D_i0N^FJ`MWb5K3I&4V4bU`Jq>oIF-b}P`)T_&VNk&rx*W;#b46U-Et;tVv>Ohh&l~B*gLn%b893%NoY2>qeCrC4KeVOCr+o)Ce zyFg;ne%SGvT{jrj@6exO1!O%rWz6uL?bN*oF4NvinGVZ?xtexv+>@ymNv+dfqZSU` ztd3G45e*d#aL~hEE+Yk|{GN@~=Fx&qgEhAotgjfGs4in*k z5W&^&nC*9)QVVfU`n%khqWmYwmcwq}@83qH@kMum7+ELXKDQ}s^8vwmhRSI$u1*&iT=9p!#coE!7Il_Ce)x$MlhHOUs*vUS9dEpU9)mCh3jN(R} zLpZC0Sf5CB%*u3YYntbq%Vtq`f{r+ezqCXM;`%@*`QMoHyny-7Syl>vgDlknSY~_E5cc~e7z`F~}^Jwa`2bqS3Z0(RS=;-$^ z$}}nrUe`?IO%+7aqZPt(vTvhx``7aM*lw}IbUt;y+PNomcKH0*4oy5$e) z{j@c6pOu0E+h|c4wemR5(fiYWj#Ga77CXZ?pp1o&=p%&oPW+G8YtyAi&8v-D)|s@& zbkdHx;vX1!e@RxFchC~;AeQBu;9M*wC@hhE({+*0p_!)L&lZT+fBr`Ar>c(BA_nK0_n`$eXjvl@?P&46XwtGYuQGCdsyprXA zv~vB5jHO?+3J#dXG@j8;EuGQY)nS2zgFD*!dBWvAc?X4nS+r!nLFzN0#Z1RA!iS@u zxuP|Mpj%rv2EMws@7AlOhj}m4(|+y4{VDIlZKqM@_9{=w0jZJQG%AG$4q?sgEsPL0 z9)>cU7GIpr`GuoycI1T{4QsVITI!!)Jk?cPe~CX#1){c|;JSljdmwvIvlr?@xWDI7 zu^r4uU4qTce3XmDu9KP_kN0&ri(bn<06aErekW<;j9mE=>YMgnt7%zU-o2OIVYRjSul^qQ!hLx+6MV>+d@)(#+|* z*&fCsCJ=&aA782qm~gF4qu|8mXlHl(zvhXMGGrP%SC(e!Pg zfVF6cYP+NXoH*2BSJ`Pl(oD%B+i!1tHnOj`6R2>ttLtv_*A`^5JfCf}p8BJPG?^LU zj=n>uM`ec2!UA@+W*=RMfBB-yLe?9zbV2UQ;uDcOR?&Gr_ zu7JqrppKkwKWF_=U_0%5`Ae zUE|~zQv(+HZ3$lvGC7|0aNN(RKl)N~PBpLJ)N7uU@N~&@IXB`-JnX25P*f4J{+&{+ z!#ILt*PJEK?_{e7G%=(Xe?E?UWm{_2sk>3Haa?(C%x*CO4x@tg(o@!~Hp0vLAdN4u z3d6?5S<961-wiz~VmA^E^qf{ejz%8G{(M%u=+uPBg?45bI=(KoUuhP{$7UL7D{ni7h>qF9Gqh{zuH1V5fq-R7$eC|`^U9W@KP@2F5vPwC{vgpa)Jr}85MxB!ws;!G zY$;Zli7_3TDEh)SLYZ1x8|NmCA~Dpkk`t0^^|&1lau6}$1yfQ9xUCSx(%#G$K$TW3 z{0|o#dM~Aj+_uzawwWv69;!RIV&l}+;`@gwz{1v82{mpm2#ra#Mc{gpW@_1iG{?I-mGU0yQh))0rfY!`$l=tgO+Y*(Hu2M zW!HH=5yUMjCv3|bJ-z~)uT^~z{EL=}oU!8yAcvSKXjZbSc?q|%9gA{iPjv{)A5#u&^J&Km!PO6@|m9k9B9rdH0I)`T=AuLtVBrty&hLdT^trTbO2 zY}qAnv#LA~h;3v*cG?`>=_+OD!bS?$?jnk?P881l=G^*xOlg69e@&}W_yW4|P~EiU z%{954Mx(D;g zsW7TM>j~wJagTRV*hAQ94^7ufASn%HR5*M1=+(7~PMUUk#;%aanX!y$cBIAV z`M3t{8gWjxGqL3-gsk$)#DScSTWNiaWxmp=qRk`L zPcGvWIARxpn#J`z!fvE3PWq#9eyUaFKHH&#TxUiMluOEZDkyl_G~&u)sxL_lm;cfx zAuk$oFGOlrmt|4H7xR#*xlyl~=9YR=cvJ<_JC87N7)MM>o?2S-saVcnb}_g%_qZ!! z69;;rmb&Tuo}Rg{sYTAoT}sC1`htAa)w#XHxf3ekYhCLK+iin^-mwlGdy>RQKy1cc zNStQjZ8_QQbJ!bqg!(U?P^qUrnt^RHV{EG9Xbt zl;q84ew3Y^n+{_#eVxt=iZ=1JLKNO_(=(BQNv{?SKK-gNYyUX0bW!K*M?wV`&$jZ) zO01a39G#_bT*|G=?O?&WnAd#p2di|Tt}aK41yy)VXGE!7@>^DN&nK+m2rbs+b-~k* z8>c{S>ehuHIFT8i@9s5VKylLXg&vh&VrtyzVo7z|3uRnw8eg?LDUCXt6KVGX$w}#ij7{U1fJQ%+RuF4Sjd> zQL5IAQs+%A4#pSnT@0%x>L#(vOp-jc12PjaQe~L$GALb5$EtEjPdDWLXXM;7@q(<2 z3m}V@gIgdM>U2a~ubmS${2fYk^1JsVbhtUg>`_>g!oy<%& z3o((v5L$C#=(+{eWa0*8OD^ZO!s3e(*D3KsamQ@MFGKFJ&pj=ySS@R0f-6mjrs=W; zWH?&e-FjWwKu8zJ2_A7KaL`SzJVyjpmPYTLHajn(f#d~1i)skH|mm1rS0y$!q zgWRXRfVCsGE1z+Hvy+w?L4Q88ilek^`d+GOD5(+XazIJ=W%E=b_IPp-o78a4k|$-u z+kUvaZF1=sSdTNEI^*NB?-cDzx)0qlbl!XHg4<_j%s*(9vDYLy`{*>8j|uqCXYYDj z1j3DrheMd5gsl%wT|ot(wuLXQcyM>LT)VYq9Y><*+i?p)F{_MT9t>YEiYnQdzDG~< zK6%o4y-d){u?5=GvsgBWu}_DF^SRI|EM=h9O6gnDBy+b8*^<|7tvq`#@r6Ebu)eFI zDbNh;ohE?I80R9eV-%`+Ql0x^Jlt;NY?%7)7~2K|O^1ZN-M&C<&CQ50UjJMI@oZa5 zOq!40m}vw@hC*3Y2q$&$r=n`i!QLY}3QR?1Op5URjt{)xBB2C~&e)-0Ed#5w))T`S z{Y!3IK1LqH6($H9=elyjL`5Rijh7kAXpTJ9Fj^`Y=<6rva%pCBchx~!k6VjPsxfv8 zI(3ivV{vwSVD+WgMR1_=0lpN zAo4=KFW3T63oL1FC!blspUbgfHCwmQ5@DF2Hc;C!9%~=Z*N?AsF;o0uH8C#;hwAZc z2j9UAf1d!K!7H+uPAYt+;Y7!q9M1SL<(|FTUt`&~wJzBUfHEmm#yxaN z34Y9A+}t3eS*3|)TjdV=V3V-;(F`f-U-;;g+`+TTO`E#Ty|1@r)aV5m+MC2uGc&px zzkM`ghPJnp*My<+lBVLmu3H@loSwW*qdkGt%>?7KVcbZiai=2ifR_1qB&z~jeA{*+ zw3U*I-Mt^>4p}~0Er*8goNoO1YUNBJvT~-h>Wxr%{*G8BrsAlB%65-17_Nqh8lX-} zHgl^#y)@W&TB0jg(P-7`uNjVQ7p7}iSTScdXuy|d`4#n%%W`Jj&WTLmxVP@Bj*-L7OXb#<28=Vv zF2VL^*qSY-_EqDFb3HQ$ND~T_#^_jTJSWKeHg2%-@iM-l@Jg$zop4ix3Jia?Ptc7@ z5gJ;KobW;@Vo~10m`$Smj0kGh^CC#uNsM{Cj=VS{e-u`t_Ztmy*0ZMCAcMyuJV!zz z$%EY3A!_&(up{#%o2z@KwWarNSR}S*Y0a-;+@-SnQBCvLbLw{adB7RRUXt>whciiY z7p;8hXof`~8Qs=zaq8Ay7g{O?s{*khGv0Ub;)~+JOoYfHJz@ILg%P_!g-3M zjH?$={qo%RD#MwgBX*bl2y_n(wO})NMfl24sE>r3f|QH!yw{j2c&uIjF0c=w-m^DX z{Nyc4xGOMe8JGU%4VkS`d61g~kadn89Fv>*@vsK0DhNJ4mM{T&CmpQHMh(B~lACKF zFTxPFd)@e38;0!Z7XzBrAK6#qh$MV@XiIVQHgwi|&J|DlH}fC)Cc5T6;=;}Z;X{3j zIr=YG3WW*EIBShRQ#iFc25V(@<7e|8i_18AmK0BbqYEERM+N}rcy3OJ93lT>G|gcy zkId2gyvog%{!ZE}P1~=P;Ci>!UdJEv+RCJCIYPXJQWs>#Am;8Bd@4;@o#L*w~W1#{iSH^B9QFM{KeHv|Dq1MrtG;aN| zcokRyRzAkV&Tr#2JS8y|SE3lfGRO5LHazLeY5I=7BYM(WSqCApB|}@=>;A?j)(j>% zj=n@GrDV!+I{Z_?y|FhD13GaXAqdm(8%^yKcbwQ4Gz+xkiuV*Mc2~@Nz7+&bN|~l^ zI=I(8q$rl)+Ky|+o!J^*&1)}g^6hb?Sh6%e?4Y@gQ^RmG^&g5PfF9-L1&LcoleN{1>(N>hiN$k?m#X zx2&C9$0}^_o@V1y3P0X?a->H#tQ)#;cpHFuXZRk*x*tfCKLBoWO6l)v$Q|v1wVaJF zTnBD@l7OSGq@0dum2luzh?S|KN`&M&Ks_g8v?PAN z4#2|0%0!}7ejbGhsfIBJQ3F>(5+Z1X3E3@xxXQ3plz!j3c@$#E0%BKMJ^t>UK{`Et^piZ?r$(sL|_D`?< c|BJN)sMuD6Q}=U$U%xiFrm3eVQG_VVZYW0C)?qXzSL^mFw=xO#hmF4lE`dIumBI5{r_ z{rB^So(Na?Uy{81|1=9QQ1oI(R9r+%^q*j`AlLr~b}{q+!kircYn4xcpT|#|oE$}A z9xzXs7s4OFiT|TGz_5Qilt@AmV=t_?7l| ze#?In@GJ2bz=iAO3|)g@9u{h@fJ1*;At8MWEc%Z#e@Ci%dwBbq_&7MiE;RcI`Gxd% z?w@ijf01(w@Yi2*ewq0N>EtNq9{_cOIU;`9FJRYxo_-YvXVG8CV9|f%1_Nh7PS4xP z6&|eSfPg_H#3aPz#AM{eZh^#aUJPWVz@mTS`9*_^8C5@+1H#+S)Z5zw^3x2@i%XEC zi0mK8U+4dXf<-Ul=$E+qBWQn8fvAO$0m%Pk00@~df#oC~9tT2OP1!UEe=84$;(mQK zmK6x(z`*I>djfndog94mXNt?Z7K!zZnp=gaVp>Cm6I)wbXTR;WFYfPu-pn3yTFGrI ze?QR+&BLIia5iH}Zz(R~sv-TOW_P$cn@>Pk>(O3GW3cqJPAhuSnEml^SC zQNXm2#4Zy)EeR(`oV2U>9k3=N{=xM*xoOM{;niAHmBMbOejfe7f*KzGT6{#Ei{PyTx*|2J?n znmXdK=TmkOJqa80K^m-{4H4&4ia^NCDi^?@yJM=2QX`eO+w^Gp5{)RV^gG$#?hGz} zY26-yKXWbd7%NbIeSaeqh-F6lcsV?sij@)h?ID-P4OUtUDv5;3M@OsAtsiN>2Qt71 zP8u`adnY@6xNoD1qdi6DUE&?r0zMekd%r!EI{uvF4OJ_ZQBxv(1_Jn)Zj}1m#zOF0BO~o?@Jz z4?B1EGCMv3>Cn)X^!2d^&Un0h$U9JyAF|=`Ioo*zp5xi*@ngNz+d5OS-{rE`ji~vE za|QX0vckGi$nnte6J?kExzKHox!~2uZgFUCfstOW3(f~) zK5d%TXWyDuYvqRU={qJXAoshe6`h!D5Jt$aoy4mK6(ZtB@I7%LokEbR;TQ)WMk5v} z4+|uqg7fy<^A1sqzEca)z1hI|?3(#|$5qQx19&+~AH1l-Z`p1k^0eynjuNvO6Hg#N z)(UYC==J;U0M+gdt>q+bwtX8JoSwjb;@H`&c?-|+?g*YUbCFqqsEJZ@}X|DX+`Qu5BRYCFi!Vq)FoN6)O`o#n+A>8x@$3ckJ>gStQU* zW_>5j56*sU*64}5_7phuNqi!vPZHq0S)A?HvR;xSQBrCp-fz-IY?=!|6mpzWwG-yT zcO!^QGcDoS9y>iu*IsOR@kOFhw#aeO<(?7{Uf3`Z4+cU5obq|aG1~mAO@>)@AUiBYQ_EV z^i=m9XL16Y1U)GXIzj*JqIOA((dMbSsGf)o2`H>E6n^jG=5N5B}`pgmb zr=&K8UcRq;)!f48g^#p8CapR0;ugXWW&;JEC$!T_Noq{!%X`+a5F$bm7S>BSmW5D> zvc!T9%WF_yyZNwh!(k!W-$4yw@i{DQfa65wsc#Mn)qiN`!F%E8NxD74G052AoJT4N zk*RjrAG2C??dhgv@vye9qSQ=Cg)AHCYDzvCRe;;k$9eTwJ;v3?giB6$rXIXx*M;hW zchE;2uPmMOZq3uZzbzlucx!{nOt-m{4HE>Iu>b7#9Z?y$(&t?N5s$A6o$b)b>;dVX zD<-BKqwh{t3}3Om?8G+(QcMKI5gkH(=V&(IizGwpa7^xK>+fzA*!5Pij=+*AYT1=1 zcR|d{%F_!t|3lz-ZegOSAz7u#cx)kSF9K%CJ?`OZ`*tNC+J$btZgcPZE+!zCNv-A< z%L4EJRy!b2;gn_o2^P2LKIe+1Ry^@R&W9ght=ZS0lXcOjW`fnt(0gjGLh@Hflpd28 zCXmKNu@HDA+)6X-&aXawR{GU{R3TF_8hK+x>~1DflJl^{mPdz`oG8&&DA?nWb}7 zCA!&;BI3lxP3SA za1L-UrnZmcc-um=Y7-hZdK&|z7D(&oXYPI}OZm4p`iY^p$yZU`RD+PS!{wI3E~-Wm z&_FzlLf&O@to?W;&0wRk&<3A}H>xPuZRf)>4&#MPRy+&Z2Qua>D=7iPk>d8@ z!M{w;{!KUh+)=wjlj4X@dQl{f?drr9puQrt&oYg&Ru{(Pc2F`bEgJIZa5|s_O zugvV@Nn+pkxte#wk_OP!b#La41!7;UZ`)3O7Xi<=qS}tTE{-aTVv|Zm#elCcGL6Y& zHE*;-#s-W?cS+|oe^BLj$0ooGGIo2e;w~5RUG5H>)suMVrsgt7-r3ah4mV9Rolfi# zP}+y|6;Jot%#1%)mO#aH7TEOsdYSoiJ2v_E!usYwj6LGv30WW!oLUJFI#T#jQOBnC zc7kAae}!=sUJ6;?`CXl!eQesEHg(EE1iA=JrJ~OFxbE#B7qCMB@0nGJ$o@gTE$39hnS+Qz25M6ceV2K<4kGti%1BmXm2p;GEKIdOb1`3`eG@%4A*d} z`?l=bK`f~P2-k8{XJR-;TW0&pSgXZ)t5Hb??CeF4@LQ-xJPeRPc$g%5Rrz6^q*cp#F=R1A#`I+FSi1X8Wbw*3>?Gca|OvJ|# z@9B4G?Bi5~qJ@ef54X@R7bW5JYJc~P|7hM@#=Ab9ZSvjWf{8N7vDBC3Z-9z={L-yr zzQ69%wx*E%u78?KfLM=NO}ha}tBuUui6Z!b2Ei9=c}8i>M7h&3zWR6B?yW}Plu%UQuSzCb15-ZeUZ5nqB)tdmQ%Fr+ z?Y`0XDPfc@&D_w_DA)9gfYVOQ^BMvMy#gAOcSkoU$RsW|tl5UF^LVST$Pp%d4vJr1 zAHfKc7-1ab6*p-oj8bfa7GqMZR75Co7XHJxzU^FHy)!CrbGvD^a*HtF@>)u-d@`?M&?Yo(vaTDD!c+d=t4pq7)y+s>mW{uhjT`(t0)2 zW#G-?mC)xPUj+j6GX^4t%>Z=I(|S>Tf$I!U7|nc#3g?=`7pJ|uDHwe-*@FwLJ7u)X zy6+KLJ`dR_PZ7b&w`A6|?cSE^WAk5`ub+?ok_;mb^{Q0itk zt{0_cAyV&=PiUyQn0;-P-796X4RY{yGnIlTo$cyTYtK--8 zdi9|XNxN3yZUc==qc@R-Nt5(BOyR=Uc2z3+-w4PvPdy9Y$JmT51QO-bs*MXKl0HvF z5^8d+->2mfF)m8rh2TGMin`3$KNSmNG)o%Ll`hP9LPLD&BhbXdYqoAN|1lvk0V{>G z^nGwRL0LhE+7+08(o8yN#cX$Cx&-~b=RDjMjk<({5pQRK2Y059EuqGu@7k zg~&_0e4gnAW9{|~{Krr?S%HT&FF&+YXK!mKpP>C3AVQkz-vXK{ze3JmZQ8h}Y`)sP z(oI#NetSk;J}lTJ?m4=M{V7**RHPxw65Jb_PGb0OM0Pdz87mzNF@N&qE5kgLR-(0b zW{pe}HLDR7slLMNVoI1tj%>i%`u30AU$ZxW!jr&$)OqY3Vq0G)vb5#-HT9#vi~vsp zx!Z3Y6yipWnwc~0&wGL7>B)#-yz_=<0~_RGEV9*QzsC=py4w5tO9 zCXsAvhfQ%d;>vxi2dHq}RMk7AX^Gt-&Wv%6qG3qTO?CN78LO=tFZ~MACjNU?SAbhv z>occ4%7Hgdq@P zHF}sRsg5&xS$sbN<3Ox)M*CD_hASDII!~?>pMbnc7xBpbWdiAjyTWxSbA^ls_d960 zax~H7hN4#H1i17vJB}O9(lP2wSZtkKk zdev-oWMe9>oNgI2wb38LrC9+o#r3WFQ3xhK^hjIx=`3TP{nAaMoYzB~s z-FFEPrlFX8JTz1PZCVy*FO5uRZ$QvsLh|ann?_<}P|-a;Y>~6Bz@4Oq}V<3R22 z-#39Hw z`{YrtcMnA)g<8qW>u5*_E3Kr>Kl!E*dY7biOobe)=%_sBf^Qfs+l$u; zQ!fvAg!fX%y&$OhiqrzLw8OOzl+rU#64xWb&(EhY_&QcP71b?nz2cX|e34?C-6L+3 zq(*U-p$Wr?`V6~!N9TNFo#!tTNrTsc@|UkowRl;#3Ebo8TcSO?W}9pw^>lghhBnV) zsYc~(C5Phk;X_h`Ht6-@-$uM=#}sz-q)kXk3}Qa_AcREOsxD7K`FkxJXj7H~?JSlI z?mm{mtrAKg`|0e0zj0JSM|57mqJq-%1}|Nz1LJ7eGs;_U4+$6x@OQzm(RI2rpS<6; zXVSZR0S~`N%4Q_^zL_@fsq}Nb(Nr96GppBCW3vugbG<4QlXbzy2^1PTglX>7`1vil zp`BlhN+iQ$n03my+2*?GLn^8ouXf5nCU%3GoS$WpVf{y*^U4kO`4(-Rgijgb-#C%S zp;TdsBXph6+?7Nos7i~vrgWUhwKzzc?NKPx95OQbu+E0-mi$= zAk1mAUcx+LZ%87d=E!Yw6O#XZOL1w|C2-@mr~rP8Em*u<%Zy4{S(iDQU#{0??d7hm z`%og;l0c)GWqi&6T8M}*_5JHIKiwU=4wk-*OU$4vuVw6|36^Zl)}c(ZH?34FcgnFc zw^OS5(mTxauhvFO!Usy*KGv0goY&E{(rX^xIoO_8tZA4WcOQE3)wti1-8x?ct{WX? zOc+z@BMFomwku%*=E8l?YR^$(xk)Lk0P0z$HsSSt-x4B8BTx3aax&e zT3OnyG!H3Z4Tj18{@oHfE;8<^8%Hn%f|+gJ=ari1!Oh>0fA$rq_Tk#T`+F5fYo5lu^ zghs<1YPq$7u(1wHdFDc>6vYdQ*M%-76=&Xpufu3d8c19&f z!IZ;Vrp4I&C39v0=XL{W0h-lJ4qr5FdLQ}M2QZ@8lc&^)aAh{dr~%jA6E75NoH9tL z_Y0}M@~Rjw+*)OS!G08YLzh1~#4_*^0jowfDNAprv&2EmEtoeSvbdrqBJ1#)2rQ&Sdn6qFicdWSZu@E%f!#f z2SS;v9W;u<;CTgXD7xcBk{uQrT1`0~SMW}x){BmwjBt4L6$^=TU}YegoWa&B+EWYH z028pOoEo*S!y*eq-fk>&?fXZ!*Cz83X|w9q{g+z3f4fvAEWnmdZlpqCS)*d>(%vwh z!V9%@dg!S;C@+sHW>NN32SK~7?%fClsjOFrK`oYo+!fUmft+KR zq219A$G~ zAv)PcHof!aJj$Vaw_MTLy)v+b$`bFRXKRcj)?g=IDO+hAY8?Aw6nOjmn4W|o4I&O7 zml3hK0ko8WW)3%_b`KZ9L;5FK+_rd6!)fDzJ`!DG(GqPGnp8`dP84sTg9Ht9haRdM z18Xe-@4y~1;lb<(p+HLr%~>9#2J!{on4!rd>qH4^fp!x##T3l>L%D)^-bH9co#Q#Kwzx)xY~wEem7=Y#uU4&MfB*jg;5kKS diff --git a/docs/img/premium/sentry-readme.png b/docs/img/premium/sentry-readme.png index 420e8ee8770b07df2af147c9b95293331158db18..3c8858aca7093410b568e373f0a6ad78469f6c62 100644 GIT binary patch delta 23629 zcmcG0Q*a<{&~9wo&L+DtHrB@4IN8{?olI=owrx8bXJgy8^XEHtF3-ie`ERDCYHIrF zerKlfbidQm4cgWXN*XFBBZdfv2L}QIf+!&_tN;Q6iu85<3BMt?+T%LhNHhm@jQ4Q%sbNV54C_z*N0WLOV1C3^VD~q zI{q>FIzLRD3IPQKeKZHb$)k*ar{1My7wenfQH1kndQQv!WcvJoJ$pP+UiY|YJga{8HvGBcT=Kck30f= zVZe=;P?)0XsKbat{zKt|A{$~NCZBnP0lpCa>XYab7|CPsqI}8k@%)l+z$cn_`5$z` z;1~4YJZkqZ-~iv}FZsis5yG3~U(nRyU(g96@^@bsV8CzxlmCnp+9mb{Er<05O&y+h z^mPFh>cHSj{4EojSM(Qj4(%7TJeJ7J|FsPG-!tw1%QE19&$R!CW%PN_97B?ylTL}R z@0yn)j|?0D0uJ_CNFFHU5Xd5xP9cBB_Z`v=!Z&Ds*q3OR&ey}e#72Wwm}AhGuq1HngwK;i;vr+3mA!BNt^7Imy~^QE zSA9x)-fqZ18V2<`@*hX1DxXNiu72vV+b)K5>o}u{$ZC=Nb65NAS<$M3{rZq-mh@nc z9Eom=LTXVlIC@iMKtk2Q_L5ia}on?^ex0yzy=Gg5doj~w#`;6hhzQxUkO3%WP>V~n)8B#sC_ z)z{!rvMoGd%YI|AEJ|9JGr7s*`+3ZF)=b5%9Sd! z?spfGy1Gmeo-j-mrmp{IT*i~$_mJ-ytq(c3vn&4tX2;F>p_j7v2>vO0yz?0y{sU3bZYG)HF9Cn~;nB=u51OMMW;yA@%<;>t2L z<#aL_tGQ0C-kVtEo5NuHml7^*zS?pxG;vekomtFRdMUomge`HEOB;H{V zq9Uy_k1)*ljIVf*`M6N8+Hn#QoCNH^zc5~Q6#KpuboI85#L~9WGk!UMU9j}&fBnAI z#Rrsb@2@6HUpJH)7CCcbG2Z|3lFy!F??o|_ke&J)c=KJK0Q;}>BrY_a_LI+#BpKPO z)k{552aU*p=7kKgq&xC^${DKr-;5}5Dm+x1v>zJH%#008P5!lmm7*ESLI(gW}6_ath%pSC#=*UGG3ykzb7s(LFOX zRlgn9CHo71TV&qX>VzLBJ?QyOaMudw4NJ;O4Ze#-av^1e?NAjS#HKq63y=eW`hjaTuYVoZ3lFpJ(rT=`{6j6QH+tfaMc1w- zqG7OhH)PqdHr$K^IzH@&jOhKgK^zAJz+`6rc4IL`C1(4Fk&>8!OE3{=YjEp%zBwS- zSen7Q;|a{2rrb^f_}%s&uRx>nR4ffl%g$-H``FXV=e&)k!@r4;Jj#RZ)0-!C1Bdea z>#3;rVWi0L>zM$*5Aq%SuIZLCqK8h8iXTVhx;zLmV^COkwCy`-X5RO7gsLUSBqhCO z1M*Z+nR1_Z{Iixdk1AMFme(KK^)H8?vCdJ%$GyITu(NDH??AOT5ZrZkVuqv&dvK)Q z{$Gq)$>c`1UzvzSY$!NMAKqJkT;+;|qp)&q2l&{xcs>mNT&{Y-a>(bqWaN-T@|||J zGi-OeE*S?d`;x?g^^V4s8b9~G683P(FnT<8$P0g}S^OteJ-zn%N>JKopNy)i+o-SQ zAc@NmfJICQm1?)1bUWSu{=Q}~-==B`;gI8W{+>VOD9ih*+W0;+_76sTYy#SVX%jR> zPxH2m%k7DqXtaH zxbL-BUGVwYx(3-k{k!pb#C_C!JK%fa{qqPoBYRvz97X7|5Rw*^Er|RbFAih(V3!@*(t+8Ml=>e#DG|P z6;1nPAG^cO$Z1>%$;sWm$NuAUqWvj;v-;yv^}sZkm~WG9M_EPguq^05&sudjoNXRc z@a6hpugWQOlYBCVww8^i^p#C~QPx#O{?qtJTEw8=PO1)Oy_51pNyp`4SX))8SMB8_ z5S0uUKN&77!)?>08OQ~jdb`X89I+R=4SBuJN4)ODD|V=+WNdEabua#G@~AOD05b?h z`=G^hDBtP*@}Oa6syrwSO#LQ8^(nFQi<`_~!xbvhWJc7FUZgWMyF~mvWZ5sJ2`v8* z)f1?o-9?Ry@s0@HlUJ_RnY4WZ3{lTsULK>TRZXR1r%@r|X>MiQB-L<%mOSmLP|&ir zpjiky{Gkb1fHMkauE_^prvA9}*ZnQ=WSM_Eej=$NJfZx7|F5a(`iowdv?DD(mY*RS z;coX4MKH#G>u+<|yR2Aviay4ZrYtXYp8qYw&34;PmT9egMQ{@u`rD8O)&5Xrr5qd7 zY-pblt4a6sO^WwNDFqc0ZV%;BeHy|VJdy3MK&AAN8I z!i?J8Wcj}o%Sg4*YyaV~Uz*#Xs~NjAu)6 z7AbQFgQufmx68Dz>IbX`l57Z zm7Xx4$z4HgfcGuOb~!r|XSgnZ4G5;n*J+(N!515s{V;fWDcqEWU*J||RU;4y%0mkUR`!<^!8B$YHh9W17O zyy}ua>3<}-1_NO-wInduV{%B_Q3cD$@pB~XLy#jU=r&x$ zcME*z$R4ZG#t0^;&noU3oP-A7oIUyepfsXQVY`*y;w@$r8d{nGSH`naB&N#cyKNox z%fd%%}isIAQ&I>U9#Cm++7Yd)L$aze}UT~A1|?jicfvZO`3Ld5A^#uA%j)h_YNQ-h3A5k#tq zmq!RKQTI%I?|%<*t3@M-z1Ey#tly8B%=#0?%~=Kd4C7$2OYc5htbvHod`=KbpYQNA zePxc^^mPj`MoM?b{6aQ;Hj$9x=A)vYv00eGewaL^dqhw z%r}ysT*zXm3Q19^c#!mJEf3n=pP{3l+#g!xa+M%8rW-bF9tW_ddlvDM(=j*9k?($LsJwZ`^`o2~bW3-d8b+_^bQtJ|ZLGhm zU_dFsUX}V1F2W*HBvvr%pz3rUbG?;Z^fY9p0alH^k8!u{^}h40Ly>a2K;7l3_}j~F z@mEpJF#5^J2j3nJ{2qp=!681wvOdq#pjjX6_BZCKDkMKGKEPa^a968QE(4=K7HOKX zekToIEF%3P=f`cS4@moubV6m>562N)Vhe%4{5Vuemw8Nm9kpOF}@pJKjnFdvS>kF zgIJk1@O7ILDu6gM@b7lVF-Wp`$^a$Dq4FdHQI&$n20=g*rQ9Tv>11@r?n{im+%j(f!+YP-+`Y4!C|}3b0`y=xVIY3 zxApb}IxqW`_D7PUo>#qQz1-Ce< zdva>_1QNaO0jVD1JZvUn>Si3%P8E=$(Lb^E;M?Z)R+EpPqz44t91&r5s&^j@C}n1v zUIz7ckHb{1%)Gu>U=j=;9#I>rUq$||Dh$n-J2L!A4AN>#q3(vWEL3@J>&^wIY>4U( zv)wSSONgwuUEI%~k9I>2HIfwwkBkMQa*&hHz_QRkE^balMzCJcP6?Ki-J+!jDQMlB z3B|fc@spAib+Dgb3nO3(e@Uq>xF*Tib^DjXMrRXs89TE)6x9`Dklux{0 z^^1>^&ToCEQ^DBNP8-h2i$!C{S`Hu_lkg<(AV|&Xtj#+AIk8&f;I|9XjWAqjpt2cf zG`;pSBx%`FeRT0s7CRKGd+=;@FMXC|Bkm(FwX(x0%=90y%Med)zg*@kjV_8^{)>vw zDPNB;Z60VSffu+jbJuBkF{FsvlQh!i+~v4hj)}zAP)U3LJ=Viim2 zi^op}Ec7jT+gpldLtJ`NTsV%x=Q-EvwmZD@m#Ik0fT*aitBRJ*;JJ zXp?z8Wc$jE5*w|`aQ(DotjyH^xaNqhF2nmrXVg_RlV;m<`$|p;hP(c)vnLkS{yOH3rNvai_rA@A^q30Na+rjjLMi1Tb^$&EMmAulcxs|771ixg z-S@K%yDj~BrSqtzj)JHLu+Sv*JFxMUr*X8I^vAy58nRJRjuC$$q^5Xk_8?Cdh5|7! z4OSV#`&(&3?I3a0HGw&HnU&Ya@sMrb27N9p3Bp8l@d1t>!`puwLB91C)jL@};`<>! zL>wI?Rmdo(uhqqn&=9OcN5LvMIx?$mrWhCqQ9=~r_Bkc{y5)5Fs}$kkQHM>e7KyCB zU?k5ri;V8F9-pMJMWA*bBIe$wWly2x$`|Dfk>ruu z*{0TuF<3j(UP?P!FB{V>X1MzO-~5}MG48J9Tk`ZR2C_YF!q~~N5q)&^Z&_riVZJc=YKCEh~r8^vh4<`>OCYMs)eFDr;Vvi z!#J!VkzTxU(FV}XTdK1g8xGZqzV#ybN6{xTp#9iinlm1GK!ki!U6RvITT^pC0A(o~ z9TL@SHF{0a>}Gdo-C`7<~Y@gqoK->R3z#J(=D8uzvdjK}=L8NDz*hsX96Itbg&9-Z81F zJ50r(kRWDdPt_=f3lWP7v+Reo`zKqm<$1X4}C%6cFU22D>WhM#WYeE*Uhd zL92&oW=Pbl(@&-Zn8Y4~g@16~kHzTavSpvFtKLz}+YhVD-e#A;ZcYR~1YX?RGzO`ZF%07<%Cmnt@3XhYf9U(=#Dc6LMk{iLkM0_Vgu< z+l9D6*xV5uE$+doQy1Kg@8ztxH70=Ws!m7DMOsyH&$1UGpH=!o~+2g0$oQ z;=%@4>5Wmc+S^ET9tA4G&#~aC3?1+fuZ5xDlr~ku+;}DBHz`~dEl?H2e31q9%78UW zJtA8oCOM3B+==tfJPQq|s)s%Klh@gzR4k{+{FA_9OO+92p}f!nBx2PFoM2=_EtF`% zOH#?ff_fX58A3dq!=FyyH;m>kMXVHi2~X_vl%mN6F{-LW(UuU15lkpSeo5+RSWKm{haU2fi-qkqqgU-9-U`|V^)$lyr+9RE>9aYUrXxAo-g>@9SIFNG4?F(W?7+?~FNRkZCSz=uR;mMV#f zvQm|zC=It+ck`_mUQ{&zE?%02!dpNTQWKi_tQZwA}dOeHJ?0<)A zQQ)RJ4pC|4O9K0@e%`D00kvrL;Z+n+et0lrN!pKl-{|fHkb^ZIX)P@2uY>*91sM;A?UVc^+2b$Qs8T^*w%rE|H#D z$^8m`ug?NDh!c`mu?#TBkZul4XkuTaWSToIq0PO3x0rbsfh31HaO+RJYd<#0gW*U{ z?Ozgbp2A`A$MEub|y=;z{S+*9KzHl+YfpxxM3WG&o%6VVSZqN&cf@qNZI6_5Q8aZ*?(fYNpa7j z3Esf8%$Zqc`Ku1QC(FuCoDeZ04;L9oxtbOZi1^#ORafFOHl2kq(O^+;BePFHTrR~p zX?t=})x|3VK5NMZgd08!qW@JlLvDf>yvbDMP!$ZHy*_s9l^QiaC+|HmmbT-?(-di{ zfo@%`)uH?GIUHg_<-S@2RSWx3C*mUrDfN@sUL0<_C1Vbm+6Cv{N{(f7@JeTQWY}Oz zoy|gd(@|yKVG1H~G`ibd%q1=yi zkoAgC*j-IDOue=0t5hUtc~l{6$jUfe`q@l?n*Et+Y)H*tj)z|{K_b{p#6z!;Dm_V>Q+44_|xFx=qe%$k7YYf zV%)&271}31u#!sjH&P~iwVAChW$e`~LIrTE%Mn@I=KkcR4!Pi(ON(kpek`vI!!2xw4TH1bciT5WaoGV2vM!Qoa0wbVgRqz6ha@|4cIMwWk`^POOHzfQDYKKydD--{K%b7w) z**DcbVn#=1iQN{c=vgnbTqm*{YTm&8e*pQA9UivSO(+&Zp0K26{{i{U=BGRu6QNei zVP(FEvv^xH2HJE%q!qVS(2q0soN54MGX!EGqqV>t84U&_Wtch&YgeerG@!Jp^*$kc zn5>uoJta8YFboZ}m63O~?X^U>u}Ln9c0icYEdl9%wI&8ZisH>Tk98Y;k*G$TB}M=I zwD|6{JIyfOz|rtpD{cF(gkfCv>($7f~Hg{OSlgjcNjvHQREbgVt zrYCHx^7&odPD`C$^~ma%TELG6{})rE+#`=H9hZOdr?=g!BXOg~Hro(q@e7cgG__Ha z5EU5zMqW8bp@(b~mI=icQ5ypV!Vbj!1P_2eEwdVpwEzuYJjL)*rG zo$jOVS8#?YbUFGY}S2_@)9l1!3IFw>JGrB4Jdh)z(F}wDVdJed1 z`gswaJUI#~Cc~>3HXQ8TV}$!d4^B6R9v0=fZ4Acv#`!GvoZG-T)EMLm!kf|jbQ!Qo zo=wlugsbs--;-KffG~R4j+H>U`fXM>P>{lD{Fmcbub+f`En03Ep0mHIy>;qo4qF2? zTH(|qFcX}_96G%zP>VY<10%|B!yoY;8{&>giR^};TGblgK(XHtWL2cS~6||4V6O%_eh8YGq;?%rlFxaSzHK5dN%h~!U<7xMvBwx z2_#Ho%FitTAaMpUBOrCIi7?%mW*lg1D&7tj3{%iae(W&%rmFl-;&DrvK4r)RJ2}n84?9}UjZ;?u`XfhUb$sXzbBAsdc zacA)~L2T)jod52{?;_clekWUP%?Vk8B%%ev<4^$hCUZkZ2FAKN4wx2rUzZqW#igcw zvTbC|XUvmfs5q{k5eQnnD>K~VKhQ44c5lMsw+eRqH%IDpva~^V0bPn!Nxck^1SG6^ zKhi5#*G$$Pml47T@sjhh-K-=rGOF0E+g&=}`Yv1T|26$OYoSziJmAHe{X=W1RE7Pr zWi17K?s-)4)iEFwLZXE~b({VIeHS++qoF0rF1ccx_w!a9VeT#bUkJ0b! z>6xPCzPbgRtW3#Z4w4S16IWpGBF&Tk5ucL-!>!%EWhD_Ebrnpv`N)QPUaG+w4Qxrf zaXhJIiR>F)GM+Etam_B$`Yz+~yoTSosDMQm0w&tW$YjOIc>b9%G|cz{%q_o@fN(nQ z*`drw&b*>37>po;9*_ueSk+yg_Y%kG3u0eZMFbF{-z33&CYb{nvum>i467ATpstA> zyHXIHrs&VE(*9-vamLGu>_kk!?qeFN!))ES159|hS!kO(ymWqmXPR4P<~Q!cI6#Tk zA7!eSha#gO$7g)0qf@a@^DJCjW*a&QuNN;Z0gsrjZo}Z4zk#Zp#bqhmF@tV`r#YBr z6k@1URZWj3IOQAYX~;F!KrO)r3kVI?{#i?jLxQMSVylwMsL5F)2OfvDkYe`pa<#3t z#-g@_gYRbHyoz!<>t#pv-RqV*{MstqQwTWGBHqhx_7VFSyU^cAwy+ zEa5Bsr@j)X&7vQO5pCJ`7x7psz&%I(?NInIL2K^eI9)p=Dp$7YU=6qnX}z(bdxMxL zect-hqOmw9=0GUjrQUK5e{Jokq&84NR^`7VZ(PxSk?ymoPZi>1J@Q7T4!~TZ4#YDq zG||&vb~LN=JYn`WYcf;*Y)Dg~*WTNke1b*zO5X zC(8)(#T40*W?Q{@+w+Jbx*&!k0%Kygkzs0HfWA!6X+?tXVjOD0Tm6qHF^1%hnMl(J zUV`SAr$K{tWrOSz_x`1y))$o?4|Pq!WuxUqFyTrg`XW`MoDx+83|;F~buPP`Top&jaD7(1x)URJIKAY-6AEp2;J6{IdQ z@Nl6_tgIPdrKN%W7+ckMOi@4!+9`As)o~R5fdR2Ln|oREgA|(jnA;ONSZ^F*qX}l4 z9sZ)%i=fxDS8MeUL_8y<-5TwuPSL=_n2Csx1LhrZSLQxQ_H^nA&XkNx`|E}bQ}Wuo zPz~{vuM?f8W<(S@@Rfxcqe=V-vs^7)uigBRg-t3wA=!EK@U5w3I`Oa1@86UYL+bv4 zHi{5Eh&Qbw_stOwVvTAMSs}uh}b#|l|ho2EOY1H?u-c;DL$5clK9kc z!$Ay$!*+B9)4C$8Au*`%w%3?ksa(u@EKl><0GM?KhEBK(V2@gp5CT?kz)-_v!R>Xw z0*6o#vn8CtolTzre)e<6MqinFfJN@^w4~+arIO^YfP$aGMa2c*L)2@q4uL-ZTXCd& ziCA5mC>=hYfh{?-?;l6EIOx7*N*3C}=yL;0Ht16Sk3V{24!wCznuGUu6vdW0)JT=C zKYq8jeaghx0n!EKQs$ql^wOPs3*I-M4zIVbxK|SU>gI+mB`%s-yOpQ^Y}b*=oEVd2 z$eX`k79Ocl2mJFLI>P=?zVrNIs!|Zt5tn35B2G7e6yT!VN*o$zZds}|pB#L$`O{pm z<7R9X$6;ewmcVLz_~B5w%v`;pmu8L;jB-1Zg(qqkNWfScZOo4eJPpx*&hX8_{tS1Q z$v5#5_E-zLRCE3-qUsS^eafm!_CVRFkUl`^_`6b<tQgYx$x6_#g;_nOF|twe zr=8wwh%B6?f%I4e%dN{sh;+Vi%<_8FTFR$o z`OuaS*Qq70(2+KUHSU`8RK;CJNoltPR@%?Y1Nh#T6VWwbxneAb6 zx`54j+piJ{)C3gIDex_|f9GTBA=PEQ8TOjG!Qr{P!CA^5-2Vh0(8hQlX8*B=cmI}G zJM=4riSuWwE%N1r1^uv_Vz{UPZXqH4TH+gT`jI>YE!6ZTeS%FSOznzt));ph(A+s4H z#ilf|T#l_%XJ8vWujS?iPC|8QIOVqNSK@*;N5+r7ocExs?R3t8@%jiP7|%Op{whgj zeQ#6E*q42Kih2D9h&U6-b1x<@YXB&KuaPrzKGJ#P{n;5zbvb%S$nWi=o%HEVd?KbS z>Bhje(lF%vebr6a0+Is1F--cAN3I^|hCz6@-XbbZj9l->5i}AMBzl~y0H-Uu>clWd4?#LLM`K?LrW7i8= zQ#HHT>3DKbWN{$u!5*xnffV+h$aXBH}gpf5w-@wbU3#l6+E7PY3kq5RuZxAkj8N@-+QUo zad*@$eS|*DHQH{M3;D|;m8?rwtu`Yy73+R&w{(_#Gj+jho#m;N>;M2Ys>1lAnU2c7 zaq!o$?C3c^!;e0qJBBMki+S#1kxoh6VSdh)G5YsXz#d{Vt~jKsupkArNec=+Vh}Cm zcGiK<0azrI=3QQzsjsP@DS5@w1`1Sb-tRBE66s~z_^c1qRCw)H7c{Y3-Cy#t>NH{P z-Nvb1_6=hH1Xo#t{}8yZBJmmUp{~oAu(r+mXoflr{yb{wqyjtNap3anA~(Qw*%(_k z`m^84N+?d`;El9RJ_+G&&w9Z38IX&7Rtp}y$=*yg@#xhfq8+KAnuofbVJqvfH34}* zF%l9A6@nG=ErcyZ)UUxIxASZ8W1;w3SgO=~`4$Tg5zn~~%ilJv4|6El*UjZndM zxm5l;Y$-Xr1y?oiRHm0rk{mbN^iIP4Z`kJ#WOM2eD?nT?Tx9h-Bi7sLPe0C_)*Mbf zN^aXb+8Kx$s1EH{9(A>v;qXJ_0S?ARrLzQFacI}acXaz(N<)!r!r)&NLfmOA%#2l^ z=Q_Ky^sMF?{BLoy5(mDE#&tpObF%s;v6uU-i6M8Z(;0``QP!jB7a^he)^yjxc^DV5 zkZnkHdw?Tx<6XEc4KK4WtdpM)O{CVMhUFoq$Ut3-uC0S8${YEFFFSS$x=8`RQ!@99 z^;ZMw<;y@zQchw`<3w+83z>~*1h^?Qnxtj_0pE46EGzSOf6;Dp>g~Eo24dp*VWdVT zy{x-s7{S1OAB8VIad_5&M|O575qFZywwyBu*Tfq`8H`hJ73X>&k@> zOnH5QPjWxR#E&wOArY`RMY}mbHdj;p)7OSTshC1lsYh=rV`^<01){;I2W;#j|x(mbQCi0jRS!>$G8-gX(a&MI&sD>~-9({5oX# z030Nj3Y{41Uy|51EXH1KZI*kA#F?(u9WC{DG6I? zpuTU{E{>+YL%l%_)3EUd;!zHwZMF>V-~VP(@O^3(r}W1mDuVKGI2%BXZ(OAb98S#7E3BM^Z@L91$ST|p0i=~hn=9}Y7WwhdI5<>`y4}(n z>^?+*O06)o#;&7Qt>aoZOpoPHSUgQV6DG!zLztJ_`mRZv&7eNy8=)qNUu$>o`#R+G z8iXhyTtg0rOQGfLckX@5q%tC}VDnjCh|svrvmMLJZO6L*ak`oQGQEKdq?mzgsjd)E z5~1%rM8gO-V@c%WXJsLe!R!;QlfN415>=%}dkPDq>x&UA6t0pd6ATJx<6`s2g>&X`dp?0pHs>SvF28*W&H| z1$oLOV^y0x9{Za1&M_<9qS+btlPuXunKMlKim5-?6Iw3NQaia;SuY zU+SFrhUOwtdIIuolU{coyE3G*_o`q&k&t5>?rW!%TD4YR7`c4`NtO)6?Uhysd1jn{p_C6FeC*}Zw$-2AoT`kudBxe?I@W*_6LEJ8{7!ESMk?H&8${Vq7H z)tZDCEE01gM&kLSy>O`uo)gr~RRv32b2?Mp>8@t~%nkim?CsSFMR5MW3f66~kup{? zPDji%+2lgSI?^53*;a2nJV$5DfsW(iNi#EDMtj|}nJh6hCwQp~mpo~q#)Up-Dy!l% z*0g(fCwE1fX&s1Ah*SygI7reM=l@_SJi*@-J&zGw8lfx&AHutciX=OI60&V`_bsb{ zRJf6U;@j-tHS^vg3WQk%8%S;4Ej?nA=XjMp5gb|(la zX=kUg3ZY>j@CxWP54|s|t@zRj!99IfhLtD=D_v}M7B}n(o`-W9z9qTXY3luZNbA8f z>K7n_0q;UhJb26C*6!W87PZ@-SdFOuke69xCqR*@S+}&#ci_*~^dho@TX*3(;JIT^ ze|$9bUl9d5Dt6ngLqch)>jTY#4sb+MXHsVs2-m2TZJNPt6h0&TRko7|zc;xB_gAFA zowf)*QIvvr&`oFqwXhPQ&}%-*TP1XR(N>{e4`W4TmB(x-4~L5ozpW|;$C8D;wx8^D z51lO`9X11&>;@5}g%_$R)UVcNfriVW%3MFca6q{Ds2Vu=QZ^zD8_fXf2J!l-h9hRQ2UoR ztpE1ae^eL(-BF=Fo!LAmo6zjua6V?OlQ*hq^V;4e;D!tyb5v-kls!=@)9JoX%O`9P z%ZL%wm(gz4c5s#_DR0#$#J|zk!lo?0p32DyH9YKsw`#rMtC zmR0JiDXQc3Z#cQVc>~b`{-*Q|k1qY|7njR{{5O=^t<>-&i#&CZ5d~{+AVLFLzhnz zLSbCllryZLmNO_fALKqS=x99x057Za%_wt`rSPnH9O)Q!*=DHXgBe>bd` z8Z!2gm2kU2LwQlM>xoVfjSvs_P1zBcN%QSvwa)nn!85iXH3nyLtn&^ll;D#vEKu}Kq3`=ch!#6V3@g`HtB0O;xvLi#ympL0 zel*ER=;e9BGl%hqEe`$K*3?gafLZW6>(GvqGf6lT`CLZ&;r2Y)*okLGi79Y)M~Y&% zqc*X<|6TGitK?pvqHiN2Y~+)Zvs7gG1x;g9L?PHFEYJ;an05as9~wib65W)gKbFh% zbi$~yYeTEq@>`%i<_`Q-1^-W zT`@qOh$&WQwz1oFO%sn9j>~%HLG(BF%>IptDM~~?wc)G=BBdim_I4MVuKDmNXQ|Z! zVTVeBC^7SoJe1Gk9HlUs!BMA8k92CIKuRuZ0+kbqJki)bhhGY>*U`_&BhKzio4;qiGUaQUBg>R9Gs0<($-8z6WdA{^T7X zTC<7<)rPo&j_ZDU$E+J|Ayd(aXHIV3t{usQSGKx=^Ku=t#SWKWtpn~ac zpym51e$=s5@_dh25LPDxM<ktQ>_YXpE^_@Ov-?2Z@JN+$3$rhfmA*imRLs|o_j*PTk7ADf-;1-DmS{nl2nyr7k3 zgEA%5EvGMSpXb)$AFrM3Lj*L|b0FHsNTi0r^fjaP;JK~Ble|wB^W642)4U5fgrpmcOX_@Harn&N*Uez(<;EU;LtEN=yc-34}=z87d1s59Dc0R z{_u72FoSxpC+63pf35#NF88}n?>^b2Sn%rFrDy(-+x~JtNljP^beK@O{-TrJsSq(u zh^r;mcb`dc-_2|iCq_!QshO7dyD3Z+iyD%a6|R3RH&B(ONLDqC4p>`Yfg2phzuTe{ zfi;ER$DCtJ1IzY=Q>VdTW$h0ed2GHeRil*vqIR{VcMWAg}To z_N*RjrF6F-F?2fzXtlxG^c)*P`(wqZ*|T zlRzugWXYuVPr|`sD)9YfIps#PTC-xrF@%$9+KK)BGnBd^@159hore?Tx*5GEwIDo+ ze=GdwP#2BOuLQ@IzxuKw)5NIXVSbRx5~%6Ce14P{BAqP<3v=~O;o9C}5#pl_0b0cS zbw#Aa)^PlIC>YgVRQeYV(H0m|55J#?9Q^P5J9fC}@Y7J=u*CJVgVhNB*e)%4YziNb zY{c2WhpTf`saoDTSN;1Lf~}gy=$vM8*S|bICwl0yNqdr73c3at00J(fTO!w-rQ@^? zvXfV{OpDo`9(-c-g!iQz8E)NAz@<^}j7}FtHQaR$NG`26g{d-%q>XLW#swqIXBVOpNiR!=i>y$TRHX^9kaWgbv zz98cL|n4WTVk&{~9$7XvgB8dC(<|>fi@NISBlAq5B*jkuP0*V(K-2*9ncnFJ?>?$&bT$Hy9*8a7Vfpx zm2$S0Hq6OEZACu~U5g|<^*|V2Oa5AOlxTAdqX5mGwu;!gk470^GKe+=6>WgWfN3zf z(M-t8LP#wOU0z;2i_e}61Q;z#mD*4g3p}l|mJ!1Qeh`xzH}8BjBV}B57FDXfSXGSk3C)cW2+;GrLm*g7?L@ zaN~nm*{mDoj4B88O9fT0W=lr-Kx`EHmtT{7LYYwh%!U>eayO>|Wx0^x$YK<;;(7f@ zXH8p{B8`?_)nu+uL;FoM96!o?k1%iECR4@Dacj~#UQOHGk5>Q55N*GMiV&nYS?FiK zM5;Gd-EuUA!wa6Y;iDEPlF^-nxV zO%Vhy8na#H$rc#^Zz~$;JF{AxuBtEE;trCz6_E7 zn#=lp!z^{_o%yDM_sSRI907g83KOTcJVx^*pLCl_KOaL9P84m-S@=OxB1yg z2lMQ|{i5&6W^yw!GJQ2jqW@i!RPe&Z1)~&Zl*$mC78SJBl~(eF2w@Y#ZYfL3%u)U> zeF%dLGb(lc+0mZEv13E9ux1=Sku2_NDX4RJIa?bL#9o+>+(EwOs_d2lLpT#77Vu|& z>20>$z%~rHMT`#{CcJ-;s=7Fi+Qxx0d?rjpCg|ZAvtLfxs?*SR6GDIujdNWEfdt_lXAZtaKnzg1UK<=uFZCI#Lq1=U`o z4%D5o;GQf%=T1BmqRY;@2~XN14b>82#eFlyu=_V$7z?JIC@JD(+Z>M9#59$4w)-AP zJouFGBgA5pN>~dV>Js^zs7mR_hD77zIY<3>NS|s_I)qub3%~Mo>qonEt2I{Y1)B=7 z<75HAj`m7GrtTn0;(dpqXj?*~TiwA~6C=Xk5ax{Q-%ZyPVG{BCxO=KJsTvZU8Oom@ zgkoeH2`hGmquk9=?dZGy_BF)iqZ0z!CF4}k4RFVhktGyh)e6&YnEOB3^|%v1z;{fy z2T;`gATKVw*Y{bLRdE!8o3SvQ_w+Xs3AHGJnZ|hmdw896NG7-|bwRr7C=d{QqW^wI zu*&PLO1hUdq|(YE?2{-t-f=SYJ*ALT@7Xbu%5&c&{3P)(MuAbP*L6cK`Gmir$EY+$ zczK);<8nd!jyAOVgJBd8@Apq$FX;>KO04rd*Tq?8-cJ%{XL&{>>L0mNrrgG!=iL-Q zjfay@!A3gl3kZ}RS*~SR_2oFum6V+Fyq9N=iIPeE_wf!jrbX!AX8Qw6M>$*Pne~?a zMFOF^)Ov`_c1%n>a&y5C84;+kdQ|slqxJmhMMvi2v#2EdQc@ zzBVqf)Y6^ODBZD0BOTI6gXEG@0#YBO!KIM~>1F`|5tNp0N$KvCW`X_j{Ri#`_w$)K zPv*SloH^I^-oF?9r1Ux-FQWy123qiTVD`;H;)S#j3dH3NWFr=>jSN%w-E6SCf=3CQ z!`pcx+^b&YJ;10OMaFAsZRsCxD4kqR!WKqDHu5!ejn6om+ngjx^%ge2-na8_|f5}Kjj<9H79H@rF&}6V!K=w%HQmz;^Pc}QU@sx zaW9gU*d?GeOZ31`YJBH1RElAVg9)gK|4YR9%ibvO=m=nJ7aAL2P+`EnO z4$(m$aaqZ-V+F3SM+jVG?jDeZGD3$F9>LxQ|H$YMTv$6L z>Vfy~`$7$J&42^!pxX~=2{VY{m*i)$qks7{W47%u9rai~m))bDulm{Wxk}%bb>rO1 zC6K!>i^9}uRCn=AEb+0s&CXd!-BOdPuG6=2A*%LdD~}|L4PmYa4SLojuMH(U$PA+R zX^+YcVqTqkD~_lmL^POY3~##o->bTJ3$c@457*oRkYSV?I$)a-z1PTylNF>+ka0F*K(*cbUL9;1=xL za$eZkcRX=f;L~?0wia6Z+V|tPXMm60nr;Hzjp~u~yVY`Z0` znmzGXHr>QzCRaOEchO07kd#=faz3#vz;bXiUIHFD;SqO;=+wcsh7L4&;HP|dFPa7A z2Vv-|XXmDQe{Gu^ODH2>(lGmWubB^ZEJrve~!2C3{WqOH^U)s!jgICr^2?;T3KBe+Ay(;-{bbR za6H%Svn>70}oB<{1>!9>m#5gdwv2gaL1(ps*eKfb?fJqt(nXbFhCkli2) zZgj4JQ}wQWjC$ot2SjV%t&1+t;BM)RA5IL8jKsZKxm}==5^kKZWqg?wn3lzUM15R2 z#_BazcP3Ns(8IEr#XU2TL6B7F*(s&6-#C-+AwqdUsl`&qX{ zzvYK|8%3L_Onl-vlw`Hr?>_crzN3v&oZv7MwvoCaGoiBc*CN*D%NJ|++fGLe*5?eT zxN8C9CFDUTOWRP)&BDUybzNy;w`W(HT2bm-BQ6+84?n2iJje0bK8O2=_z;;)+J7#` z{&sicm=@Ke47|WI)3@h9Ba-_P(U>eiB~V07m6-sSj1E6pkbCFlnz7l4R}Z~TwUl0{pntwJn58uveU*6GkQs)yEC(Bl zGLfFEsrgxqv+UDp3R8&|77iMI7`1q5Q$D9`RGoIA1Dv|}?NO0@PVo@CX~7csJ)hn? zYQ|QnCCUC^KQCS>z3yPyjxk7Q=v+qc@Ad3@O1*Jt)}?<>oh*Qo!@lKr^OFEZ->*y> z-+kpeY*(*^{P@BlO2(}Y;?%F^7aFWEK-qI1KL4lbk)t$@meMAwmEaFM%s^*3!2f66 z0mduzPC%!AIP?trBGZ!Arb1_AB=8B)lF4UXr}))6;KaQscxWL$2%w}(IZ`dS>d~=C zATtD-!X>hL7^aFebeSrB7U#y%)smirS!EMswafU^bihpN%c6$kD=ZK&zKUUx%mP_A z$b~RepLN@&w1XxvwA)vL%v$)8Rn>zK71G8AbkRs$C=MpjT&}f>ip2+6bbFBXv%^wm zttMIZw`&50tDT(qQ)09aoVbGM%`ahSJCe|~P3WO@yBK^y^!WF)(9WtxyvY^pYbB&9 z4!GJPu1vlb5B|-l@?!oo1~GW#CWjWf0&#-YU3?#M$L+0S0$Fv9;`UWK6N7bd@C|`B zK}^}H4Mju?)!#)a#&DSq673jCFbjgf@Pn}8uZBQfDL zAwM<;JQgokT}{dPP0M60SrP$87G+a+jrz+q&0!|%Zvc-UxXkv3#+ir*&2lRIWfO<+ z08LhWG$(w?Qk6(uwIgIwFxh%^BOW-`FGO>5-v-*W zV>tZxyyQ3dQe#&6vC9bI*I99}+p+DrX)k@OVoaJK`YJ^yH1Ld}&MWFDv8qIt-vOOu zXeU^Kx$B1xJ~5N~&&{8|X#lGQZ37WH`#%?BUfUf+y-J|P=s$wU)LILGk*4Ur+n-OU zahm$;4LAC1sMdw@Nh8gAr2Y^@*?R1G@sq;|+Z`c!kk4@Od~9_sx+u0h+6aL_CTQ#G zigv>cH~cRRg5BoPmkz^<364C`aXW*uF@MDB;o`C+Ki^bQSay>}N>zL!LOxlobZ*l{2i^Bs7!TFsO{wD zv|mDGB(vWtgRtEgfw6e++`B!lg$Kpb?_7pDT2;^t-lFK_yWeX+1f}+~Kr+GwDXK=w z@?rCFdUv&QOkD@_lboBZ8U2&MQe)}Mda8ofX6f5ou6-ISU|R{$;2+|;+bj-h!M;EI z##_)M3l8Y63jam=(S3v)+gAt_nyhp86(X=7aQ&Xdm`%A6rFm>USgKFgYdhp+uO+=b zT5B6YwU8TU#)L03H z3!C+O4Lf*#t6;_C3+#3GU+jz)%J$8Fps-j=&SeGDq0gFQWfoGYf8;PHLdQt^R`oNsQ>hrqS>os~G)hm<)})L4m$I(MY+)qlAJGR^NA-O1ez^ z@>&cnIaDcZG92&xbz6M_d@j5rgIO{dp2_`CHE>d(v}0%g)F7!HMAvN&rqy;(Ewg93 zzSkr!#sCMEV-@^zbK3s`b?o`u)oyYnN4nvF_D^hY)UI3>sj5)aE=XKX8JGCDB)NkI z>K!~6V|*FG=4<;{W80p5o#yJCvsSW)-1=6H=^qS@gN_x-j$0)dK;1KQOk6OXKF!D{ z03xaTIr;+-w&@&am!UHalRzam;(|As7GHVyo6I9Tp0#19g}3&6SKH7YM~C5&-){am zc|w=b^Wz%O8g&vW(ZzYpl8_!+nWuqC48K42?CG`iA5bf;`Xp3)rM;X5%)U)JL-VLI zCIjpVA~8Dt?5Qtu@;0w02PUy$;TA}u1Jqs?Xxv@QmUWvRy`KHjP7U6hP>re*Co%zi z;};OIw@=My#MVkH?ci&WJK51mVDf<+Km4t1Cl?db=7z9VQH!b%y^0*|wZii9sC5S4 z&`hDPwsNCV{`;AFK@9rcCi$Ql(dTS}y-N{xBkS1uA93lPXE%f!v%Bq`)~FZh0B#Ys z#qaQi-%j=(PS8QeA9-cHYqXJYN8P=k?Y8hrT|#wA3mH2K^lcX-=B z1*rcmL#KVLRO#oQPrfJz84QF+QxqrtNw&7Oq-i6lV#?qd#LL29__!HLxEjDk_-wvT zH^8w9!?5rJvRrj(hd z$HaJk4Op2h^A`&O)hI4h*v2H4d&Xwt!#%j)XBavrLI``3x(|s{Dk>x2CtRZRq1wd0 zq9jOS&6ZGBSnwMlE@R%xy5DB<9<{%jEw{=C@y{c*6*x&~0>JPY{@Q)9SODWbiY&x= zN3efl%|uhyi^+;#HC~oHqqdgBjp5t^4eu4}K;j4^mgO>ahjIGKzAA9tI?09EQM3^` z3nfY?SDslv7*-2c&e`7fo0!p-``blnGpBD=G>N+roe}!*0afJH@1dJ|@{i&HzoJcE zSP4XdUcZaU{1P(>Q6hR*;0qkB^P<5BtDc`#L45v-eAh>&W;C~A>!85!+3gTB&;3VE zduL?BUKXJ!Jm(`Ztl4@ujU9>fO-k4OW3{oJ;91YN_#HT_F!$ z(GW!sYgb3`Z>DcP!l_dKhDt6F^GVYu3Ckbop`GV=sEn)bUsEqa&Y!D3WqqoYxmc%cox%>bds`+XOd|^G1Kpq z_fm5t%2Z=s-VynZ{@Sf4Z#+z~BKXxQDGWN&LOa`lsHTuubUfw~eGXq|Kaq?M|1}3J zY(f^fHx*VBp7>{YlthBtV55^aqAfaK6VlNB4k-78%c9%R(j9_XXAdypxSo+A-8g=G2m+%0TP!k zo^=n{C7V&$fv_~2_OM%%i|N?7eu@A2#*Pk{TbhwGIU$KZ5mq+-G9cX=i$6W<_ zwzKE?^oC$n5f+5I;U7i+RJ!iIU6Ww`5fv126V~{yx7*7@&VWBl=L)5#GFIeage$|B zV_YTs7x)1!aIr#$SlW8Lq3e`D1Tczl zJaNi$m&xH_^wRs)@rF0*%f@s0gS$qG7gNd7pDLRbg)d@!*Qh+{X3{2o!IeFP3!iRL z!jn8z5GTl=>=BqD`tmqLxnEqtIJD)C2J}SLLgsI-;-M67@u~`UlUB2XYZ>tunxx2M zUT*#VVkll!uhR!PVNBhW6IuFsGeGyQ7vS8ra(5inoant<|Lt0Vf67tpXf_T;p){g&s%8AJ7*iVxe@=9Nd z(_Sf;79ipX7Ley-Ul7A%CR#D%x%QRDbEhxdj`;9V(~>#_Z1;BPoY?53vm5DdMo`?Z z169a1i_Z@B`@Tp1+m)`Q0?>xaXg{nSOBd21+~GG50POQ}A=O+@Y-5KI^l^rKgCQ#4 zR*~8&^pY0Ne&2)K>+hqZ6FqsD!u|Pp1sm2gVtt|!-;25EFYbS*yUO9^J9yl>60hyt(8Yc8AkF%s zK3Jb#o(rLCkEe6A2P?8|Haz{X(iEfW)8Vk2wtI{RG|J@W3jXEdB=6snph`hbtjUY9 z$|K@-V)y5<18Veu7zH}KcL#?olZBzv+fz>>kfYfKzoU=vt9G;as98{e=BmqgZ_3{C zk7wZR3QI0^bk21wuDV3tS0tPm$SyV4%aqV$ z*yDeB?(-LO`};3rsy-FUXk(d6(q|Y-z~=Co)SqXZ;MDBT!CxL>oq2NvOafgC`4Uo} zObMclWTSdj0Rlc>q!&z=IJ$rLhfXIX$~?>|l2}Q(%?||5kugPuS)Ru+FE6cznyrgc zbVFu#wQW&`CvK5bT(TYVsH*CE=%fuY6qOs~iQc|xO=8`;3wY^*OcKgn(7mNE#v{0VSGcH zJN%Pek*1UKKio6h6Xr&V4GN=<=5urZ4}(fE8=zG9#EJ(0M>+vh7b-I0nwtK{!s!uS znSe6mhJ`%Y1HwXj%M!8BC@B78t-6o+2+cpGw&m0A{5*a~madMT@TWzlrsvOrSdsrT z#v{^uKM(ZmS;VJwU{=_@y;tTPRkj-oOc5&p8>?@&-As*vh%hc}0_zhm%G%9clM^-z zYF8p3HRCBO1FZ$poBNZz41`#b8Po;}+c;Q(vnF6+DRn`^I?{sO0v;9`13|_8>V%xTpiEZ09Z~n9H+kLq&=cQLa)cR^y z)!w!Bb+>kbHg|y%g~`i`A;4n8f`EV^NQet7f`EXce7!$IfBSkufKDI95mADY)<`yj zfwClkf^Q+xbJDYO(z6gUF#q?$oNxusmGBEf3}9ycNeu$R4E)?u26_s^Le zlbk7w?y?L7l^fga5^p(roq7GN(2|u(F<2JYoO#BfK7D?=asq2SGh=>YI_8hbgBc~B zLK13}*-B_(+j8*v(ngPG;^T!1Pjc#y*L8>~N)Q!(kxsSm!=%&qI-O&oIBYg+&Gx@c ziGa#8>%Y#Si@9pV@WPntBACP6p1yy5e@f#=XGYN@=@a&LdtrvA{PRi)yAZ<+rPmAe z3r)q$`RT#l%OB|XOpnBn@Jx@g9`iM{8yfrv6N8}Ion4?GH2AGPp#k}=K6xv_7l!YT zZ^R}Bft5$wUt_)<^zrvgAM{B(@%8cpfuJPt!ejl=+S7esgW$(PzVOFFQjdK80}=h2 zI_yVlZphad%oM~g{8U7y>G%IYBEP0aA#oBx{7))+f-ihV0+Za=|1dEBn_BcJ96$O? zDm~sWcq861;m7|l=zG7Wj+{c{!TrB01OE4x_W!jq;D2vv|If-!iQQ8S)9-9MPrbS( zA9b8Q{Re#kXMq@6Y%NN7JMTA^m%dcuPUN*7;3^^e6JjxpZ>UT7v*U>2>yRTdA$&pN znM0Mf_@0VVXEa|&Ftkv;i6m!g!8ks1+OgZBIbsvtf$EJ*|p2O_>sQmftZ zb7L4>LWrs$mA2ODKBYl_w0@&mCfq4_R9Nzsu>~cT&9-^7OvE`l!#$ zEZ>bBNDQbP`tU19Dv!S?Qo4YmHF_0Suhm-&_l;x-S7l(@V@d zpHI5nEWCk@L1+w#4mE9@8!;*WPEv|)vblT2n{TuiYHZ}+4lXgyxOm%xxoV`{0fej% zmuwnPN93-DRhW%NlkV5HuCv9CAQ$f!hU#E7^-pN+F1)YbMpu!?l zBlj_w@4e`KPlI0Nap^bk;>&vmv|U(d^Oo4!)pchW*R)2gX#4zUug`~iaYVk8r3Ym0 zeXm8!+K=<4Mum;Yobk7yGT!pKa}@V7^ulRNxBAx3pZE+3^E|*|U?w`Vmj;?t(a|$w5(^9c|8BJ2t9r>Se_$~+mg%h4@>@lj zt#l84y84NqZx(5JnT>SVXcZQ;9tZwG-JG8A`!CS18>SXQTql|~S_)(h4MC>wjU6ihm{TFgv;j^5BT*mxC#YLK9hkPVy`iU;@m3!pK!!PF3V|TqF_q&Uc9iS1>Tqw z=JwXBmPrViD0r5u8oOR+iw3O)-l2MMvRrYYwQIAA(;p-ziV9A&fZ^U`4;pI5b$^QY zkhh`1`@+Oj0(=yeqZAqJNgi;Q%G|0iiq&21GwN{~{W=I>>A~s2&nu>E|CMSa3kAgA^`OR;WVHoDl#+fKxat!!$M#zAfyNrAMV!P=(6_m zbjZE2G=mI0kouWzDO|3z0sHTdpC_dkLTX5Q18hF`jraGTWs|KB2fvZDrQrvTVBNG0 z9m{U7C!?RmzDI`D#0C2ElI@U*nrYP_;#|`HbRqqr$L>e?Hx!BKEZnl_iKWR!@4B!r z8<$oS7!GS?ohqEB#gEmjt}syUsI`O$T>twkjd19xr+o*x*QtFtU8 zA#=H4Z`3d;WO_vm5Ue|OON$zW!fdLP|Cy;Eg$a=!WTD=GmbMBL@#SYjZl$G#?y#pL zPoGo}%w<7QVSAHKiQss|SPNi9bN6?Kd$;RWa}YQ#kG+ zzvD&rcK;0pA)fKKYcXxsrA@fui2@rAPfChQJ$PpHPJLh&I_g)vtSqs~{ae(Rt4b{c znc=6_U%@Cjv8U-QUu$+gr%^+yo2Inw2cDCaK31#dqzd$5S(BlT@H`fQ(|wQ2ebd^t zN3k(q_77&~U#wOd6koc{9EHFbWBXkuB{R>JI_N79QHD*BnwG6@&HfqZqGQ`(HTTl( z&63ic2GDB@>7TSgJw&$2aa&!7O?&VKDm&EbdL+w5FL=sv6H7t4#we{)08GzPhBdr) zwow}%$CObFv9iR`2L8Rq-mG8y9PU#$5hDiam^{z+eKqTCJifAY)oP))q{y8Jk>?)@ z?dq)!+(mG_q->*$V6KGH@0VxYX6`H;uIInkz~;$|%mW2bmd_w+R39TfFbJ*HiSebw zUd?PL9PEDb2DvF2#%$}nq*a|I;LDprRN4)Ske}Tb3ims8(ROeRlEbyfzAH#%_4hNg z(SY3~^u0cFHwq#+QpDgOgD8Go5v2`9|M5@h#c3{MgKR5e>blRP`yf4EVQ({5vRQXv zqo!ujXKPW36CJL=sUYcYZ>iYhW}BOXdWw3enq3vWPLk z|7$z&Y2f;-@09od)4GvuhabOtC3k2LN5`w1cS-hgH1TSgQv5wb=IHR}J&dXq;IP7p zGRkZ%lo0dIi#}v({5FWVaU`#%drQ$>*H#HxZSZnx#0qyD^}UVX{&PwU0OGBnzzmPp zn6{2KSNX-WupdjklRtPM^2?v!xJr{e9F-exIq0RKC-P-pI^r>({MO&ByFR=sKGzaZ z<__I;Qg09SA~DNa4|gGTv=NMH?C1{@rpN#If2mw~VrmNFcg0ps15s_4b-S}h#Dcgx zO4iN1!`7H#ou+!*#n8t&!0>NcjV^rKlhUe1c|7X@eL z*8^IA=2-80zMuaWUR~1P3L6$MtY*D9SKx33$MG`8%k~mFgl&X<xAv2

    #CGJW@%-Vi_DV!G=cC3Dxq)931F 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 Prr~5I6(!2W4gCKH;KJcb diff --git a/docs/img/premium/stream-readme.png b/docs/img/premium/stream-readme.png index 15da6ba71d07b65483d340b7dd519c6171126eba..a6a7317b7c2ef88c011e7cb8a98b649287ac0657 100644 GIT binary patch literal 19170 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 X{nT9-*dJL4I-tta)z4*}Q$iB}z?3lk From e5fb9af0eaffde683fa0af3987085f86cf0d2640 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 26 May 2022 11:19:09 +0100 Subject: [PATCH 0875/1262] Add FEZTO as a premium sponsor --- docs/index.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/index.md b/docs/index.md index e58f24df87..a96f06a30b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -68,16 +68,15 @@ continued development by **[signing up for a paid plan][funding]**.

    -*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage), [ESG](https://software.esg-usa.com/), [Rollbar](https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [PostHog](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), and [CryptAPI](https://cryptapi.io).* +*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [PostHog](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [CryptAPI](https://cryptapi.io), and [FEZTO](https://www.fezto.xyz/?utm_source=DjangoRESTFramework).* --- From ce21454a431e0feb057ff9069a8295e2b071d79e Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 6 Jun 2022 11:00:01 +0100 Subject: [PATCH 0876/1262] Update homepage sponsors --- docs/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index a96f06a30b..aa502a6218 100644 --- a/docs/index.md +++ b/docs/index.md @@ -68,6 +68,7 @@ continued development by **[signing up for a paid plan][funding]**.
    -*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage), [Cadre](https://cadre.com), [Kloudless](https://hubs.ly/H0f30Lf0), [Lights On Software](https://lightsonsoftware.com), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [PostHog](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [CryptAPI](https://cryptapi.io), and [FEZTO](https://www.fezto.xyz/?utm_source=DjangoRESTFramework).* +*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage), [Spacinov](https://www.spacinov.com/), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [PostHog](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [CryptAPI](https://cryptapi.io), and [FEZTO](https://www.fezto.xyz/?utm_source=DjangoRESTFramework).* --- From 7069083b0f3c67f030dad1b9f5c6c079f30a84d2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 6 Jun 2022 11:07:25 +0100 Subject: [PATCH 0877/1262] Promote Spacinov to premium sponsorship --- README.md | 5 ++++- docs/img/premium/spacinov-readme.png | Bin 0 -> 56997 bytes 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 docs/img/premium/spacinov-readme.png diff --git a/README.md b/README.md index 9476e92d2d..3fbd2f83a1 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,14 @@ The initial aim is to provide a single full-time position on REST framework. [![][sentry-img]][sentry-url] [![][stream-img]][stream-url] +[![][spacinov-img]][spacinov-url] [![][retool-img]][retool-url] [![][bitio-img]][bitio-url] [![][posthog-img]][posthog-url] [![][cryptapi-img]][cryptapi-url] [![][fezto-img]][fezto-url] -Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Retool][retool-url], [bit.io][bitio-url], [PostHog][posthog-url], [CryptAPI][cryptapi-url], and [FEZTO][fezto-url]. +Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Spacinov][spacinov-url], [Retool][retool-url], [bit.io][bitio-url], [PostHog][posthog-url], [CryptAPI][cryptapi-url], and [FEZTO][fezto-url]. --- @@ -193,6 +194,7 @@ Please see the [security policy][security-policy]. [sentry-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/sentry-readme.png [stream-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/stream-readme.png +[spacinov-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/spacinov-readme.png [retool-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/retool-readme.png [bitio-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/bitio-readme.png [posthog-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/posthog-readme.png @@ -201,6 +203,7 @@ Please see the [security policy][security-policy]. [sentry-url]: https://getsentry.com/welcome/ [stream-url]: https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage +[spacinov-url]: https://www.spacinov.com/ [retool-url]: https://retool.com/?utm_source=djangorest&utm_medium=sponsorship [bitio-url]: https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship [posthog-url]: https://posthog.com?utm_source=drf&utm_medium=sponsorship&utm_campaign=open-source-sponsorship diff --git a/docs/img/premium/spacinov-readme.png b/docs/img/premium/spacinov-readme.png new file mode 100644 index 0000000000000000000000000000000000000000..20e925211aecab7d5be832d9c4cd96478fb2e909 GIT binary patch literal 56997 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 z zeQ7pu-9z}Zl1u38fazVUFQPM2n8_A4Us$C9?!t$ZMH5Q1XXqUao;`nTOc-xarRQ~$ z1h%Uu?twvJvzclbF#h|bxxn4`f-u0jPOSPi^>Q!GR1{;+Sa;Fzl7o&$jF<#Okvx`I z;k2OvGfQWW9ve9X@YY_ZXba180?ug#g}MU~wa#V*Pt%CRwF1ZhP&p9>;zmA%n7P+- z9rS6a%3CSlWm55Aw)82~Q&=`FI>hwDmVI_l7Xx?N5#KvYNxkI@iz;1J!F6W5iS^+@ zg$!?Oe&z~BofS**J$8)2eX2VY^GbBc8-5m$4`Av3ysTgeufwcH+1W5Kl?m zk929a_awfs7fZZ6gvUWf$u@fNQitzU=$Sye{Ca#hK5`RWH95m#fjoLnr^>IV-XeupA#Mp1rLc}FnUfoblgouX5C3;{=htaMhu>=*=_d%L9X|_OdGNy|1s{M z$aH_wCsBv@{&ie-qsLJA8!Bo|4rXJC!cCIq+^FE zXvC!G;Tj$9hyyV9QP9||@$Se8#z#B3Vq$b(p{gP|y>32%5 zSt_GP!>S|L1_2sO&9Y<+2SV`;F7MkzX`k{cOGUTRvKZLc*tHnyv?ZGj$4HL(Ci10*lxVSr}J25kLI~*j{~w%~8-sr#-kt znp!8GG>j)x0&k)tOl(A>Bt&d2Xu=&az37*6b}W4Up6mlX=n07&slL=xRH@6`%b;kN(CKg`z8&21JjJu_sCUVzL2A0Ps{c zHkXu|osL&ho#*=oFf=PF=ej))1B!_oqt8JtZNJy)-Z*=F39vbcAgrFkY|YOkR#mYvu{n! zTn5**waGDNej9;!WJSj|!{S7%-a$5~D$B$%N;DWONuW=AMSq}X`#pVjDXU7Bcmbw0 zvO@=Y)aMl_c;DQyK^}zbs*z0SQ=Aa$y*gT3?Got^s%>GuRj*X~l+5-LAlJzpUyqmV znd$qy=LeDjeKWUwFX&7sQW87?CeE2hj%5lV<7-t(er~ftff=Nl+}F%?N-9m{k@qdt zcw(<-Gb(>!h&rohm|K4_=$9inVUM|W-S%NEhzU@-JQzzmM7e7#JIPYdkSLYc!eRFk z$f>Q2lDSkN>6bJL4_<5iI^@&N%^I6qqQy3H&xPKW%p4CNj7fwI?+k&8(;W3hMaW2DML(z|% zV%cmu;pM63sCl*lIMn)l-zh%{LbBnLWP*>xnBSjm2nF;YeEF_gp`PYOQBNUIq6LBy0Cng_EocqE6>KY^2J_Uo%GDUEx=?REWI5x2Xv z_ZG1xcSrdq;iJ0?jaKBqO=VyHuy8Gj){aC0Cu-gYjf@Vvci;3j3@2N7>JOu{8{}D1 z5~Gq0C$ZEgaYlG*-MLAFTIMt~%uOJBg~rBpH(@LM#2$@U9Y-6Yx=yfv99`C~ZNI2J z286%L@aA?yU>n?LY46YXKHv5Jr03d5YAs&1^zJ2e=j#+IIW`~U0d7pDAS&MFCFm?7-weu z8cAhGVrVPK-2GE!w=sdDVkZfDUyQH~u!3i-trX*C3NMadOBa#yiXPxm6*YU^;-nb5 z;5Y2G*t%jyRqJx99OG+CnV86a{RKrC9)=yMDb=UMsnvorH5-H~Ns7d5Sg6oJ%RMBG z<7nmv-;Nu+ZvyGtymSm8p4^vVxZ2wW&STF(vpE=Y&}Q^F$>rC_((hdM@6Y=vip{kiEh~$lEHMH^QtHO`f$W z+V%_`{sHR_OnDbXdQgW~sAUW1vF@#_MWiVy*}{+4;?4?-^4lQmYRdbY-SM+jM~V8f zjzfPy3GyGp-nUL|Zjh_*RaZSf085!jr8wCuhMl9t$+0;>`_qdVNkPuguDcr&po}Cs zy8${u5DOfh-&1IYmr~FP@#pNnnn`VA+bW zHn+9<*4bIS&$?apHZBdT6r@>tdSXpl*$Vr22M*}o)GD02hyhPO?cUm!loK$6rU6Tk_0Ic zyVD-dVNm8;Ne$kWU{YwEJX1XKKP;iPsIL;ZYX$fEu}vBJzfrnYvErh*%-rN({td@!jp225d@X3jsFBQCM}14N2drMGp#d z7<~jq@f^W7gttu*)?c30f>JNa!9&(~&Nu}JBcn*tvbji$SG#0x(!7|Y=G9r-X2>%p z-ByO@F1k8ID65eEe$}-wor1x7@Cjl;d(B%}yiG2l=O)IE%-S$rtdL)5<%%`= zPwmfF=CiBdXQD+L131`BU|ZZ)tWjL9kx!SAR(EWa=NGP=8Lj@tCf=L{9R z9aRQ2+s;JYRN4xMIU{y4-{!>ARvsF*6C+H&kNxxB)-F&})4bM&kImL0+lRB@ zP)--&Zs>ZA^gK-NjI%4Ct}wp|u{(wt6Du456=`BIM=h})N^q?ryOC(hgF0tc$YZ^G zvv8n_sZHV+Q1GZ!W6&yoOk1soH8)@P_66X`3cJL5KAQmjMa8da>9-qb~Wr7Jb{BTF%l2ThGK)Jrfp! zV&<@+tXpRfD$t@tm$At$FIp#eV2X!iSqtHeNVtxk<^D&0Q*|Ax4xH1wza~p_eaCN5 z$?s1!k}-VoERTVQ_eG3MsYsHotV+~Ma0(KOd;<5d{GWGS0&WLyPD5z@i&O`tKvczaK< zZq1*`b$0pz^*fRhZs5X7H^kHLt70Rs(&B_xGXZuV_j$b(@#-%W`X(281)TBdjdNN#zg%jcX{yseK8 z5PWgxN`!g7)vzIr67A9wMz@ynnnuk$CJ8YPl^(G1aIB@L9|JU4)&%`jX6#51-Osh*uzIo?I2bV*vp>p5F&^4v>Ly6`zda*!cpD$NiipkXr-Pq&^b>K*!nz$R z-7^N)iFKEA1(GG><9gNti47`XsBI?+1f?$bVvj6U!1F?dzfwyLvXZ29%*#uvSD*yM z2v^>p^uBQZDOmy<4V9fUI6#+6vk_xJ8ioa>8gn^o!7o<;Z?z1smZAauNj$I-Vz`P& zZ9#2qXP%gQ&9?)~&UiUxf@#ul=E7e&(d5FiY!fuPIlf!g_ch>edI3m_l3 zLI3m!kY!?Q2r6WKTm%8g||85ZTkUSHf(9dG0xB?jST@5hvBp!VFJ=b9KtM#d@w39 z+YJQY18f3_6&Dr3jy*Gwt7sEgP zUJukE@~HpbT1s1o>JUqQ8NGd5E7mQV8O@m?;gn6C3^`eiXh9)`rC#l^v9ay?sC(tP z0*i)H&FK*vm=melCe^&cDQ<0t`CFUW|w{eAV7XcU=;ajw8&476kw;Omx-S zyeEvY5J6|U$fKqNU2qn^1-s2;26cgW;rEoa|RvrX-ctc=V#R>I|hv|I$)3!XXl%&?ur{ z)3Y_iFQ-v7NCeMPWB^GmCMm80zA9;Zo(7gS@e*9Rkbq>=`s6q4azzkp$?n zG~3EpnTyIbiHzDqgL|v|Pl}|}ur`Uk3cjonw;iA3*EEJL$U;8asUX{^VEoRU zJ-i9+p2(vq$;nRoq-K#L7)AT`A|0INONZN^ha{#3!RhP2frbq|1-?!b7l&{M^Gwul zIcT!S?tEVE{5E`Xx7(D{8=mNa4>jTk8Sy1Uj8Q)kq9Ejb=85SO_ zo6x&pquaxYGfa!3JhKpDqA(_AqBN`T^&`H|$ey8F8 zNXStf9~rHd{j9JMO}cp&fkL>gse^B0+_z1-2V(3_0jbNSq}A*koNvNMgIR~Rd3nH~ z->K3W_giN{N?iqYUaiyc=IjJgY!tE%I%VVo{QkIKnN;8A6+UeL#Z_rE1v!uCdmY5n z+0ZMa71z)mz}$9%Ubi+Q?6xeBI~85&8bQVv$vM&n3%e>Z6$< zKsAJTZP01KLwvR69fKR~@@{fGojn#GQhI8-#I;H~J1R?G7*L&WY8NO~rX73tyi`Z1 z!p}ckP*;v4=@CK2KSr5Tk4#B`mw^8uHgN%5^gC*VzyQ`&}32dWD*O}Z9-k?CdtXT)RPDw(YNdiw2tDZ~{;h-!3=+`9xCSI~V4;KtN z7nZ;0Q-oikzTCd3wu3%Xc&lIPv65&PVY#r1mDkOwx&Q6d@v@`Jn+x8h#Gie2EP8@@ zGHO_4&jE=bC{E#;pwI|e`&b}#)85~U4^dMIHp404*ufeOb8Q5{p=ygfdz!C|xSFcF zW!s{P-~0I*DQbWrio;El9l=(=GhhnfR?xF82M(8yesX{>ry`dqN=NM{K!BIUGs4U~ z{Ze3oCp`o#fTt*jyN@fR6T{AhF80$@6b+>#I4qPNe&`igmIyM2_|LE(h=iCRY6U&%wkrq-xz9nJ#S^r#e3k;P4H+xXN6j3MU$j6Y9RmsKmbWZK~$~d9!;X;PtK259{+&B9P_F*pXvZHb8J$%si1ZP=nnuq(g{5V?U{F(umtSv%la3<9hq zv*CDbdMzPj(-w}8Z{zATZIk>Bi=NIT<+IO?|Mj0v)tZ#}rI-zO@O|k=fNEC7`~nTH zb%;+805dgy8%6;{x)~qQZb6>{#N)0U?tY1r+o0s@OqrP%Gg&k{-gnwOv0J3Q>I_TM zll$&_=l(*W_#7)u8E=b5V5W5xv?MOHg$^aRUn1TN+kr%I@s;;sNz<^fU^`Dpsbz<7 zF(syzMm~PZKxw=URs8VJ-hTTJIcC%1F4WIu(KvLD=(1(Y`dMkI+s7ptv#|~LVyPNg zjq!oHUoGk=VgsR8^@Sn|nYvXH7-~2!TDM=t46(6AbL&NN$ZZ9Pg$UYuRK4L7ZlONN zj%WC|Idm7*CFb&Q(ukgxDJA0zq;WjVMN~zuTI6d74YbaMbt9NS4%znMr(46HSz3h1 zxdd@8jFIM{OPjQoM^`#*qH1}#{JJ`_((HUdGX&zz@Ma5Bin*tn>Uu`@wVX!dI7s0R z!6rK4t%_S5(_uWwG9es>H+#VB5}_l$yzOZO9uzbmpN|=|G%FfOGR*1deJrwE&|!!L zOZ+RMue*VE!t@zR#Lp`m+bkwq+CM zV{diBu|3`u^C2)&#yie&f9-#i*1?-I=4Li9Wve@jIeFg~%{#0YBz>6l@7W$+6;C;2 z`zyZ?Fd00AJFwF<lwvkf)KY=l-0ih~?LtGz92sJH7&~YS5u?Y7GmWvtBP?&rqY6Q7aHo2?aEp@2 z?X(P6VXoq=TW}lZZ6yW>f@KY;i7fK{TFJ-nTAt-Dfra_KEEWV$v|*Dns?@uuk=`2 zO54Xagw%5ac~e4lE%G&2CojZqBcD>^zXc20N5Iya4~24)uuUO@OMv9zKETR0BG@)5 z7!|DKJ72G*2Db85SxeoxNjt|ERE=iQv3O)Mjly|(-7Fv%E`j3{2z2#mZRkojq=N%( z_!W5HN~6*NSdaRkdAAl+aA;7JEz6o`=ENe-IAFs`cmvTwkBA7FCs~;{Ua#4+I0A~Z zNBigr*J6{@SvgvA^Q~%|e$(=LuhKd&0Cg;b+O`l(F_h$llIE{1-=mMGshamTtj#IEQGt*3C+VKJnl+Hfij`wUY?Tmr#sPpEt zbX3NgSz#ZB_=Q=EhWQzY++%+E6D3owpEtWdpayYO7%jH?*vCFL#=4_3{k`SAFi!&k zvRw57i};U6CKlSu9HfYcdRA=V(l&4b7TKns$EbMqnaHI!r7M?UhAzVqkL>)KTR zd2A(rsjk^G+C}6fp}`g0n>mA1mnQAtO&O<<5tBIPBlCd5w4zt}E5bWW`Aza-Y+%BGv zPnqNohZM7@kn~e+cUjLC*{w-!!tW!og`fYQFMY6TUW^|%W9bhWEd2@HxThhg@3MH> zioVl5*|?+Xpv%(*)D5i-CQh0zzgfZaMX`hDT*wu_)|8sRXG)C^GWPLKD)=VD$H-%1 zU0DO~rmXQ8ef;sq4@HI2M`5Txfv0p{E_c@YrVQ`ET#hvwXZ7{i|dvwS+XDt!WL9;Q1CJu=o%P%3Dd2Tz+Lj= z)niiwPbWt4-W_I({8Eh66~6ziQy&Ki3O8r8%4d03%+%r_xmvy!s&z?7bbuUo3)&eU$}#LhgNZeJY1Sd!UV4JV zRIA*i9>0;7*=olOU{-y6f)R}Fpw6)vh1lonoo9> F<;<N&~UKB_J(W5^*P>MtknutzDcc-KKFlEQFDvYeJ=A3g@ z@tGBa%d5jIXj;Ir4tmXJ_l=mi=a1>B{3i%=jE2S(E#2$<7rvW-P#w(p5SUM6Hf}g| z&T%fBt_p(o7X1v*uk((-%bBMC8qlwp($cl&H#RS2$WJtm0&UO}Hs`?U zh}wb$B*95PJek7-0zC7&O3)09_Q%^~5iWS`Bfk=}m5;{?6Kv;q*)RkavstB>HtXA) zXC}6rH@s#+$B@j!A8wlA^rR_r!LuoRmF&Nd+-PJ%A#-$*5?w$Udgho3sUxhzJEtT> zS_zTqB0*EaSo;%elOvaGn_uL6{y&?E&m3lhGx$g=sZh`HFDIi!tI3%Cw} zm-qzJ*!O-qF~ikwkQdXm5$An|s}pSD0CyVTGnoqnca3V}TueltT4#NA&BQLJW~VJj z`bE)Q*dq)yJ;v6@)1cE;yRwFv7M0ZV3(VS4uVteogh}Oo4vPjfAn1ujk7-_`*4dUts0!rYBH?7#B$P zKx9toHrJBj3@4%IXwj`?7YLpjMG@v4$t7d!k+}K#oSH!kqAdsRz~-Ohoo_I4dDy)B zlLyVo@yE>&-Oa)wl;;gZI4R`6%PAJPUc@ z5fkOqR$E`%YYro4En{obI$5&Zh`W?!47@@&7e8(>;ndh(;5BlQfak}A6jpvy+$HU@TJ$&NW z_rO4X#6?-06xQ3@JFkPJ3qoWFFu-Wx4Ln&D`8rF|__Cb1vUdTEBw0ZIY-rWUsp(r+dR!Gw%J`H%rB;8wS z@RZZ?qc?Iy;omcBHvg*mjXzjp{>dLO8As8KNzLSU_A%zrCVr#I;t~ULltl?#avP^lbzz4ma|A#R)lyjLg)lku z;rTGZJHoPPs`lI;c(#JDrGeOo*2-k_Hoe&-Wiy-&Vh0Ya#nJc_ODD=9IM6;! z^sA;ixY~>k^>a{Aj`xUq8I5LI*syu5P%8a5vef}VoE2-A3RyN37kP)ZY|%VFDMTi4 zT0nJ(g!-r+$+D|;`ycV79*ou&ZG|syoNvLgAPjxyop)|Uc>Dt>!%+2&UYl4TSi-~hjunuHP~l*;$J6%)PLW14-5yy(y0|K z``EY)T|f?f8bB_AZcdfA8$7^VAVtx@4OXGT8HL;fQ`u|2_b2n#zYMu`wI6;sH9zzB z##xc}6s6b?k(tr5Q<|L9pz`^5@Xi2g*$Tgi=TfI*^)hqK*T!+LMEMaCTJyo_a#8wf z>i_kui92g<`Q<@#!+XxLRexRw2bJ}O%#Iy95-y0Is7_3X({)<98uErILc=w3SlEYv zB;$N?WB>TxGRgl^F>PfZr06t~_!c^7uc0!aKZ2w_-hU+yNTx05=)np*H;*z7_B6Nr zouW(#FqkCpj3;xu*ZbOmo@E8Zh|hA#1XLetQP6!MPP&ZAhgGbX>d^)71k6k2&a2?i zY)ZXeU&~mZ6mYe=K<0DEx#Jm4< z(_6gXEM0o9*`X80E?XTqI`hj6%QKvl@dXs`NANz4_&$~AFG&&m7g)&qTYO6_8`hFH zszvuES_9zf)Q0*rG#*D6g`i}RzVkB9x7>Kc4L59N|B9XAB>E+pc@-QSa8h7@${>TC zZ1%F-;HfCpqhqJ`lD42lL?c>?d;4~zmM<+aQIU>8E*6XV*%eDYP;AV+C%9YD+gujk z#|v?TZ4(ReXR_N;Z1>o-G-VjEekEqjLA;!!NQccXONw^fh4ZE6jx%GXH+~9D<2h>J zAXzb0$-OL{OVj70452zN)}D_t*fdlCo&L@My(u!YRbc#sYerME)oeIF$hgP6G@Q8< zSNmDGfb1|@$5ECi9Ou)c!+)#^92WdeLf~^e)WWeJO2G->vBBQXq55P55uUZ!YM+)- zbA}i!9p=M6FH11yRdB56=@}U8E#1I&v@M8>f)g2f7spL`o)R>u&8+h%&|N9^O-$HY zw_%+>g`PT%P@B2-LpY~#cp^GtDuO3ii)fa}f!H(J`M6npierOe<+} zdwYXJYRtP#t^O`!9@hBGPC(XjYW|nJHG^%~k+}_+>rc)LzfXg2=x? z^Y|n9<_8p!T?+^R`*>D@*__r8_zEoRbbIM7_AjvX+f3U;#~eTeDWX zX}$(Y=T&e-Lxm!1kmzG_IZU_P)bqTc*YO68BVxIETOB~ zPp?2XYiFJg+)i&utt}0C_%>M6764>6enph3$Y8Ek4;r3IqsKf5Gha?4;qj->A2&zV zJ&xM?4DYm$>`y^Mt!UMYOf~s>nyq_I4ZlmGT|hG?IknW?Af(E`u+BC$tm2Fq`!LM* znaN@EBI6!z{7pGu7Q=+W$x- z3~xo3g*=DjLSaRrR^uCe>1JNkmQajzG9E%nyqPZ=%WRn@v&L~m_&8)R0HMa}&1Sh) zt@bb-8$cn+mR&txGrdDUh7q(6-Y;&-kJQk%s<)6Tj?pC1sy<6)%W+aLHE8teDW5SyQ( zh{yaA=}+B9q{^DBu~9A}-dyt$zUV*KI!)<1r^!7;ezhsbJ505(*Sz!3{?IJxA2FZ0 zPp8H&xH*3O_^GvP*Pi9FzlX_;m87CSOOO&$I>K^Y>pdsn5Ho#Cb-Rw}CZe$y#<)G#$UPsqumHB=Jp=#OS zQc5@XxSwY)#pi$ZG>Yp{BJbxpLAj*GT}B}`_hQdW4991>sF{=GxN}v5r{#FPshnSJ zFT-BYoy_hHG|Qrs!{8feo*qCxYpg>HkEfad^t@$Ja0gaYgl^HMCL}~OjwI0mKg0Ou zMOm0v!O@7DgY>svNl8%)Ifa8#wtu{+JqvK-z)rSspgSi%ae?d*7dB0=#;+T!!SIi> zI`DbEHhxZ?9Mt~ws8=B(2ip@Ipo0hheAvoFU%Z(fiepVEAIpC^9rOdta_lsB^erbFAh6W_I*$HGLJGUvXK?lI6>PSnBKh1RTVe9UJtRRCHq<(h~eb0O9VT zO7eVcd+O@th?lM9Fy*zF0{dVa!Xi0D$9BSY#Kqzg1lT1Dk9iXu9HUlfHsdmN!$3vU z%S+hTb+0n@SAlvUl(sAtAx6oJj!s`5tspH=PQdIF2nzKNS&0W(!qX!2)XWwFVrxV% zFx{iFDNMF~G*HJZ3rFJBIGFc5=XG4EgJ^c&Uon6F9uA&~4pR#Uc$lU|``{u99$4-o zOsLSg*vdxum)A)5V4sVw>fnH&G~H;Lg^i|BTyEETTn5d959B0!xz4h#;|$Dk780-; z!5i_4jOQEc!!6r~WVXed@yExajnPf%p<;W}LZQ5Q^JWQ-OQQVrU-%gd)E23D95%>+J(SHs9NQ z<+=$Q-%OXK`HU%)Ux1oFqB1toW28%E&;3;8*!r85ZQs$?fvu3)x;-K1l$Xjyyy@a7 z$!=kJc*+?cnzD_}HGH1mKtt^{{ryZ=Q#+6U*X}&1`14@c=AL}+bDyif?QL)EqiG*w zidNbH3$>KE)5hg;YzEK^6E`OyjYNU^w-LOI32tZG_*q)|il>^ErFkabnVdX-e(Tm( zoZqu&&&7QtNPPM)ymFqUp|QtE&>ogz=pIX5(Ku^?B#i?#N+Z3Rs+_qzyYH8Z+p9*~!5Pix8UWK=XNs$s7JO8r`&Y(j%)>0<6fqWt zY0QJ`4Wa2Vn0FGl6KJz-|J@qu1!3fBab(Z&0sWGU@?*&WGj$sGSO*O@AV?VZsBt0G zgMH@iuV1n{KSTKfbDm`E;RtPDf=37NJE;I4TdwtX;NW1-kggB%$!G1swgP>|XkTe> zlGOUwty^a)aN)GNW6TS2?%uMcI6gkk{*-6~C2MQ^(lo>y*2wn$3rMiX9}VE!K|lEV*&FQMk?H3o=Vfr0%9&Ob4G$vZ+1F?H4HUW+sS^ znu#+n5bdvK6LmpwB#d`HxYvvwd)h2tevsX@t65?2e?!Bytp8YpFrFQcWOXoyQrfEN ztgqR`C!+G352LjnDdSHjx{u`EY?8v~OwagVvRLdn{CXD5RI80|^)wnkiF(UFPyRz_ z9;^>pnEPe>Hjj;+(Yv+p3sYLN zG;NK1mT{(_9VwUNzEa%J9^O7G)K+PGwaSNRu)lJ(8$!wuM}vj4C{G{#@R%=TauSMZ z-%6fZj*@~nsJ8a zPWFx%ShAv51XJp?>TGO5evHes!}j?3xZT{{BfGq;0GTfR96TJ{(VqSqRx`eb)!%=Y z0pFWUz2>iuP=T+6*_D_rPn zwuv-ddegxb!7@`9J0uqPZ&o&Ch|#PfK3NE8ok4%h#K;%NA_5g->kr=+UDU zh6BF_n|+Rzq$k@xNbn27q{ZG8%(AkFhX9Eg?M<=l6OwqoQ*?e^w*u=*$ zr5nqcRf_Y9q*bOezx~nkW`f)3(xIO)nnu^q4ni-1JJ(WgD*cr=5(qPzj*1D@+W~GR zk)_9Vl+5s%h--7#*Z<^p*8<3r554*6S@)d2WkclB-nwZIg~7Z zNM|;7NPo6=D3i^XN=%UYNwkH6{xbyO&zUV8eYZ*i^a`7geQ?zL@u7p9ef;lCv-YnT zM*1&|BmNJ{Sc9plycr&G)J);?5oYsHK3yJ5IMzZ#SV_c`=Aj{YgWx0`0= zO{T)yn*}h;NH&;J{86>h_$DRR9XW@&Qj$es;%73nmUIef6z&PU>Y>(aaV-u}ET7R3 zEWWi|eD`72c_o*uam*Ju6sIY~8^(|)Lnzf8~6wT#4 zzhmkPQcARs(V+=b=^cU5o}~-;B#rS&Ds(?}%3?9Z-gG9^`)us#(zsZ7;I}CPf&}yB zQThS5TVAR5xG-oMaTjac*kRXt{OX@yV|MP+?BBc^9W%(JJrn`=pz14C3}GR|tR8$^T+ z4k)tqGOTN$n?_+~Unw=t<|srDQn7a+ZvMW_P}9{)P4v2uX)hRYIsqYp*(4TlXKVX3 z9jUYli}8&_qxyH5X8n_9r12?ZZsKw!qvy&Q*JT=^0DsOv;eaXk?Po0Dm1s%71x$zL zsEgTQrlVsATJX*h-9}Sgsh+1b(}Yc_yqP<5C|n5b(QL4OY*@DheAqNW`Q)c} zhcv7`KweyH(7Y37eGlUcuV%~xK{|!oU0E;pZ(pT`>aLU2A3;$6DK+#(o-?>BwzgDb z;mI5UFoQiq(2U8<6xcP*4xqd;9boHk1LBG3LZ|Uv3_rci^!EI$`PDzaVZLWu9(w2@ z#zxZ@(c01M%m{LkH8zfUf#VXR$w+MEg@%XaSjnOZQ&B=RACFA>=?9^4RMhqZ6+)C* z+w&qgI0jyQ!lk@4V-;?IQ_{eFP(xWl4F!K@gT|c`saeWE3A03l{Roc>zmNv1{@;5} zQJGB5rcaR?Zcz_c{%P~%0&-4^GF!ZSyy@c17Ot9YJx*|#^tF6VnQe(*V^*yyU&#Y^ zvl@~^^!`tOcdwb4{4TQ%f5!9Qso^uoH5fe9ttuVdxjb`1_6c7o{pr%|jMojX6US*D z8Zbq0L~rNq?qG|=4UBorUON{3E?`(q+JyJ_nXxg=Vx8l2F~MMFOI{G)i;Nb3dp-*+ zWHCHTh3u3-NCca=OZ#YW{f$PcFspVJ66&z$MR1fF4ebGyrqJu?Eb4m`DkAuy%2c63 zm0*&zxw6riVCV5#1ko%_wLU_h?VF5%@3)k-UGH4$&NW>1eT(NKF^G#&m41RTc_nnN@! zt@pr$3ZJDtOSHCR+dN$o=mP3mwoW`tln!9<@>PTtBrNWNS>M9@?lAxG(_80rg{ia- zwtnk0^u2mu6T3hwYy5$4JWu8YhL~6WU@{>S0EFuWK#7q@#r|fq&zv}M(W{o{WsIXy zDrv*NPT}eEJ5+pe9n}Nvvv=s`U_D1vh*$FRYoB`RDXnU&cSD;3D>u(EI`R$R*Hf8L zM!7jQ$#A>%haFyqLfn=+xCD}v>k{Np2vBxPt6Th#&fIKu_c${%x`S~K_D>YJp>5j< zGGW|o{xX?ck#g8y`k)Q~I*c$oX_gfZG2Zb?%6bcB+hEHrf`}%wHJ$Y^+jujPV!AZj z;!w!8dd+!4`2`B~?+&KYp-h~f-h2h86F$OzqkgmMhZ^r-F!R!lv<^;2udzT2ts`O_ zB6>9(tw{rnZS+&hY)hRhy2Vs8$X>2J9wdnQbQjVd)~gN=YX%QlHFtB#8VBl5v6Lhw z`nqi0&7W>1>QB)>+DqtA0xae-qm^khTFZvH*?(bAB9-IfQ3(AE3H>dUWy}Ltcrr%- zc)P>fiqH{2B}|t@2G1NFaW4s;FksASRcZVd))4%P`JHdAv9m36YnFkw3t>0eQ91@2 z>KMXLk-?WK8kazs<5qC||Lna9xLrqe=Ue*>cho#sl4VP>JYdUszy=c=NJMuMGeeRV zuM;rHT2;Gh)!M73Rk3YPXHN?_Ae$-9)^x3T z=i{>)Yss^qYc^SJNz*s8HUDj{XT`!fj*N)D=vTh_LJ_m$9B5$!Ot^2 zemCv+9Xi!c>~KiVx39HsYbTBVDS$-KCcDYgf~3Wpfo6^XkcVrHlV9;N1}DEgqSJ-E z@0%H>y@R=)mqV@>xzS^+x@?_wf4c3q+o~w%czmp0f1C|rL!5`N81+T^vH}J6kPC~$ znK++eV4_>WXy>vV`iEZmPil2;v6s@V8JbxSRk*fs0isoZ>n#Ye&a-k^aaS%M;b4}KUqNz-cx{#o17c;KH>%r=Qt}bF71t!@w#3h$ z7ablP=1bEJZ2hjfk)d7gWO9ewy7M`=^&$-!tkm!k2zT3WsDpGjQ_`0!J zVkf?g3h{2i1fPW&Og%-YDJ(*wnelS}j^?e*Fptl^^RT<^UvL#e=}A}x(jUr!QjOE4AC~sErk9<2R32;g zxh|MxCgkA>_@h_$luSrqWyGCNPtT+5Vjh>%KAT*3vWhcQi`>@Voig@LMc_JxF&M`w z6by3&p>U8sA$1aFBDqGtF_FkYvcLdKc*ac3O}G&7KCFax^TF@@VQsYX+5ahJ*h zUb0`SB0?@JIDK9g9OpXXYs;1iE#WN=;3O9?RJjD4-OQa#uWXgKh<(H!O<1d+D0}MUeBEPuhSPl=vFUahw0tS zisu+MMLX-JHu@w4@K=cOe+VP~eKzUAOxSQ_Cv2%ieaW*YmNw8qq=(bl2F{OYYf2CK zw9BF?5OT}oqR>tLe0nQ30V&Lbtpq$=Fkblgu2y=l+qP~wOnuhJ28zdzkCM#;h?FO>}x5CysG4u zUNG?hY5E7KVP-}X-J zG(F)y@P#f~L#q%!N!gcDW@wG8BCIV!UCCdaMxs61_PW=tqX0|z%D;^QEi~q#I<>~~(udrhnR~y*3Pyn#)`#R) zBo`Q^Nq2AW*V)UFtXsG4vyGFzu_78v;VH-!>Y%n13MCVI^;jJi|h!$=-` zz$>+ctC+)*XirBFGtbqjNgzO}>ZbBoLE8XY5I>&S8n)nD!vrf6(KLC4LvgahW3bV0 zz#hh@gLcK#`GVI_ZOKLJ?y~>wN%zE^F!bLIz|*r9En1Xr+_(`*Eg7p-t4xr)K6r<9 z^w`LOB|r{@SRf(hq?{PfKti0w$58F|E!+?8(fjYe|Az)V%^wlstPB!I5k;si3`hm1 zE+b~|jlO2PIS!o&NjJllm5jkwqTZ|pZ*$VNzCV83ko)7e?I-SeYL5LNTpLLNeT}LT zRf9R(YCMD)Lb+Q^jP7U?k!w7|--NegLNc08oXW*^VNM}aPuIe6uXCfdi`=0jYuzo^ z_k`uzmcpMU@5fPye8Kk*yPg`F>D|MWS040{ zoMj_-W?BGSyzSG(!~&KHiHy_>QYo>55zf^_T+SxBrR@5J$ELQT^ zGO=`?vG|hvxR9pEvEo^!|MVNkm)~I39tILN_(ejQiXD5ikqdPln%di;6iw~v z>B>6z>F~GCX|(#iSPUv~W)FEbmBsXyycQz(Std%ixc>C@%y($F=}b)erZ>H5eB{WH zlYI9c=lKCo%@_o<#fk`|mZLXvi0c#56?myMg%N2FKbb9ecp3l^ zFxV04xniqAZTmBmTLmfCxzJUl;vapX@7~yZhVMn4yi#;y-RCxsx&HDYmdf`6yqo&o zOI7m)HO13vi5ZwkR4sPasR=hXs55&8*-VQrI3`Q1O=|VkuIoV0EH;Ow zm9DND9pV_<6pry8ZR$!vfqo+$#~>fWuP1kNgdh4DLdS&feI8^|_pBL5VZnkL7r!mT(vvW7#yrH)@Znw=Kr|!7*EhBF9zyYSI{tWi~DZbDm zSg~nvgs16?rLY)90rOujvG1%=fa~PeEjW5Q#m{{>TDnry`=?CIRsXZAj{mmnTYSAc z*nQC~h}EiLZhxrgy@MxQ?S(zAIQ~(WjQn4yCFJsJbek5~`5Ou1bhfCqBaGto&_4Mm zzDR7(JZ8|XA($2m`|IzWBy#LS>T$GQt}I~xcYJw5NI>sjLfN1r^Z;5f0$Q$ z81kuVFPCHo{dzi+<&~pENy8)&i&vJ-l7?gvrWg1wYrj)*#{GZB31cltS&`S{qnDYv zu{O8lxU{90OHS#7UANqF%jva(waZrA`S}g3s&LUz!7d(RV-5$gyn2j8%kqGTBpe%Aa+4S}Gu>ev(u7{yY`0xn>ZEsej%@8DSa9Xr}T8s)Ji9_=oEm2eU z&U`2kveVAj-I|v9bUIE*&l%_6(W|cnwU><_Q>bmYEXrl(ViLwI4;Uz*{-k9 zeec>;tLm@1@x~$UZn0ro$aZSh^PjJ~tKT@_#w+_>sjC~*QU<$*#9~YnjTM;SR@)$3 z13URAqMerzqYjk;V+xE|>eOmWl4|GXmDnd-_gJ6n|Mde-^>D732U%(8-8byc8-EPO z@jX{8A=Vd;Q(l-JTDR2vogOueReJ{*z2k(m;l)h~32xQhghp<`9rFrFvV3EB%*Rlv zP{CL@Ky_1;!{liFQdg}Fy6gVkHusMAvhX#%wph7xWqsqtmk!dzr>eEuzof5p(dqpM z-`;~(*Sm@H2ETdbSCPVx`EYxoI~_r>_;*<29ev<|2fin;>6%$f9ImSi&6%as5T>=Z zuX{?EtLCh{9>Pab_pC7|-OQexH!?nc-m+!OX3pR~_r6hdC!B;Zw^M76Gbg~;j`1Q? zn`Isys^pH3YMmre@;IH{8PQ~!OfAkR#HhfRN9AWz4^n6k_G)|^nBRE?yI8JqmmR&t z{p)+?MPH_}oxXvr?_=KMsso3)ua)EYTB_wqlRdP{Q663_QbUW~;%y5yp+HI`6B5S` zpV^J52x8!q{^SZ0olWordIo|dm|Vz+V1r8zZ*;p3PrDuNyz|aF=fQ^A)1lqaCzzMe zZrs2nL<8oFR+Bv;GL%tCU_doJA!ypxtQiN(rV^#X9EWN#nAkAp4R06YLJKwOyy1!< zaK>)B!nO5ca_zvtzzkMbA|`Adgd02g_pUPd=XC!2d0-%#m{Y}1=Mm2ME}iTM!aK!r z^_<%(hs=aBC{tEkw9B;i0o?x{M)@9B8@twNC+;jnQ=i`){_58D|Ne_dUDp6QJ?fvL zocBP@-(@O+)i3ht^mL3ML8Q4!?TDMYi##fOEoB;v<=+k`@fH{C=xLuP`&R7MK2}=l zQg3-xz&>S*&YiN0`Xmbl8frg7=6h&%`{?=B7Z1Tjh)Ei_qjVwi@IDV?aKtl2Nt0@I ztWd3LliG~TtQiM$9PCf2BKcUctq{5Dw`y(FbgqoC@^e9XC+(o6qVsvj^A|0e`3|=X z=Z0P5sJe$+OQ&P~DZb2HuaHu4#yJ2}Gt7kvcnqytZXO;LQ+PWX+X}7OeZFwqOIoj8 z?)F^1$^EN8KhGG5Z;+0u&s+pFvtS3Ck*b4irGE;B{xt2Pl^0gR+BHZdnn~<-QB5x- z0GW1xA*?mZC+P6oCQSxuag4tU%XpiM*~q-gbhl3blBbmOU^RyQ$LS9zd5kxR21Es| zPZkm*LV0ptX2y~V6V#?CC@bzX95WvRgiPMJd2?T>RO&^zJh=O8dy&Rrkv0cd~)^S+s|| zimdqx+k8i0pjP6}_#3>jGmOQACOQqe(;#UdZ46^d)u01%+JJNkHOsxIg-fW?ad&dv zPIvug=N`Fd;!v%9j`JI;c6snG#@uT@fWo4&T`bx1l`agkDzOpT2s=!vND+u=C0K23 z3siAiffS^);W3ctmxua{N#xBuf(ib%X)Q9qv`J@WRyb4DNlH5~QC}}Xe}%ex{0ka# zOxfIg^G|eDD#cACDDjO(k&sp&DYuGlG~wBW{N6;~5&|NOd6S`9z4jHp_Q$^Ut#3US zQ%uL6wZx$f^_5CROKN%r^7gDF79vz_^iIL{H~>_qUcelw^q4N?6t2zyOdnlSj+C)~ z`LOFQNLMCXZPO>P*;$&Pu1z(1x~B=*x$m*2V8x}trB8`l@9La$sg1wFjV!&uz3~$a z|7Jd`KF2Sgx{u#D>PDG+D!QjBFOr9LnRb#qCd)n2PtMLcC#K9twz>sPSi9f}hy)Z& zgpYo9B7&rcb4F~tPhQ3roaI^66dlMKtf8jI`5GW82YC~6jTGB(H3b^>Ru~B^rKDKl z106PDO|cL;uvs&XZM$|IAFVNK?A52i#Bk;6FxI*cj**eMG>2y8^1)~$bm#F|yZ}8x zFYoK?x`MIZtR*D(@ta27(Z}|oyW@YjLiytiU3M`*JVD+}6y+*C)^^_3H(CtlN9v)> zb#7r#QMQ}b-^*+M7uTJ>)vaI6t?YNrY^fWRb8lekpLuAXd$xK965$<4M_=S)^esQ< z0YT{NEozNl{Y>qG^P%adnqYC!2YqaSIg~-wNcZf8@pZ7OtH6vN&$Yyi4n-A0YnsA+w znKk1eSKT^Qr8FG z?c}e!{^E7+mOIzE4}4{Ilh0PBx%k;ycXzXKZ>0DFo$RZW^=o{kzYUFO^#v0tql4WV zoE0e-k#ja^uO%>J1}))+Phl9rK1z75XF;shS!JK>x>d@=N}o}t(! zu2kis@e$2IP01`@zMMq}w~tMqhc$U6nzCQfLT2(|o=Dok&`(p;6OJ-g8Of4P(=ltt z5%R0kz&Zka%HGHVV-0pHk&kZ=6+*G#LVPKcjf}@G9y@+~*{8N{h4|;loWH-uF2O>elj}&a4M$Gq0bYj?i%DnIs{pa zIIGC@r_O%o$w0-qS3Gzj7 zkc-*rw$%0Yae33^LQQ)#Z9;W=^R~AhaXme|7;t@w=O1An(pFVvXq0b@;8y2VE3}Pl zm%xX`mW&NJ9a|hBrfTlJQ!W=B`H5$GOEgK91z^@teQS za0SNU9^*lkUZj;ac56^4*va5J#*w>BjL#e)U_!VD&%mO?!)*Z3W>@T5%JR5EPHULo ztN+$-{niO~ZJnU)hIw*|f_x(zX$Bg}NG8MFfFZ69;~3+p$RzzWOW4-IZ13z%Uth{e zc)OqBloz9@i&&8=Z>x!ME#Z^Ypf0uUWvFsBOHaGs|Ni&0!!R7nZox4l?}4GJTU_4j zDpfX5rGEkQ*ud9oGq|+!ps#bYnK9p@GA4P+=1hXmw|Cp9$pd4>N4o{u2N!ui6wq*2 zNUG&en8~l+vB#b4`X=j!oR}-A= zOl$k);X1`l2oH#}EJEH9*97^Wf^D5a{S?jj6TCg|yF zFp)*lVe{9u)k1m#7&pB{k3nm+v5ZnJ7HTxdM(4G8tPayo#1l zqi}v=LJG>7HA$Pu4ne*xN!t!#r!+IjN2o+Wos31<4~)aHSAyHgQYMBAsg?|Pxc=eq zbBmmmJ)Z-NgHkN{>8IVkN1k>4hb0kRWbKhA4aPxW)*i8H^?Iu3cGb|-c5PtV#ZL%} zh)LfXmoJapk%=_mxb74Au9$3OWpB`(TzCYiDKQR*f?tk!=pG^ZE+05Eo?*!8n_XpV zgc*qWHGyXf7z|TNqo4fb$JE9%GIPW@P}Y}lyO%b)uq{(1QTyqcX#7&Fk86bpsoDgC zzYh-k7WTHUMy=Y{KKjv*9{=!%KRm5XG!x6x5^@ZwX=#ZY$;a5a2xpfjr^LMn8+CBD zD&Z4|0Ms@!HL+8RuqT*V;*R(xEuSJM07!S{O;Fx7%ttS9qh0&ld;k28-NMBK?voFp zwQH-jYeauW?X99ID(&C=-9Iu_jwZ3f&(SVFM;X@gpcr_ zG6K`Ecd#F;KB|y~ndBfgldN^V!!tK?GC3-lhk^{VE;|ycCzQxnkbYxT&A6!SS>gJr zH4KGTA$2U^?g^~~%o=S|dF<#nt1V;6ex#1ane&j+W3HG?S#1&RdiurRnO2(+o+Xb_ zkjgmOL)k=A@ljo-90PaBS|%f}!F{zWcdcZ@*kZSZE&n>}a#jsJu2?%nn{1<`J1GkD z9Y{&)C1xU(1=}ctpO)YW{zYN@N|=JB@r}gH6>I1Df#60$t%f1&ocA)Y?XW-19%n<^c^2yILFZ;sIykK5lqeKzh&Csp=Q-fh9|VK!T8-DXv3=? z94nP(H4L3A#-UH~5Hqz}Sy73tnq|+Mn$>T00k3N?FiiIE0($A9(`#yrIyC`{B+vttYwTF7JvQ1)ltSYO{ zMZE6{lRWAP^F8(Rkvvwo&Fj17@^wl*-vQrOU=l2cId;j0=4kzz zpH07N-G;qkNY#m(NupAZn!#BlRBqJ418 z3|sLTZi6e)9M>ZKyw>%1No&}oX1rLe6iTH57{?$A!A!XDqcsU1fhG+DZv0s@^P=J! zOL>4KpGCmK`tJy)w?r^k4-{;gIDgm;eEDv#KC-Wos+?LWXQt zvD$Ji1izk}e5m)sy>83anQHHzL77^TNCEOE-!z8wxr3M|dEeu_w2p{R^5%Ks%cv^r z|B(+g*)7>NfQg9cCcr2l;BRFr?O=5qT#~G074LjEUQm5cYWg)isf~}PX@!lSyl-r- zT&frp?Tko$?Ti!(=`gxXy)Tz;I?g$fIGAgw(0}*9IQBDK83!rjr+m$NcMOT^D$zf& zz^yU(Gtna{pd`KzX1jqqBi}VLGW5oY>E}3P7?F6SxXo2+f5c|pU!`~60rPl)X9miE zXs_%uaF*Q0T-xN_P7+bY*Xmp}RJ(uy^nYTf>F=`W@;=vf(a$1zoIU2DCLUS$n5*{R z3$6V%^BuZ&@?ewH5Ib&18ytC36OF}3_}ixI6i(U+@Ok{XjLaF}3oNT9g&Row)2@dL z#7?8DW3*JN)Lc=ke%!Yr86*6*w5cCWE7nUQOsNW*^$Yg`2zH?gV^-T4=7e#mj$s_* zOp=a4j^mu5X^26pq$!zP#kPabd!q9+I1DW0VYqVe<_~q7=LuF8%TbaWk_YE; zihVVA)ANViLx0+K)#GQbyt0V2-3`B42}ViVy7^E!&@5)jWsm%lNzA0uFPfZUe&kQs zf&J);6)R??7;7%S(z+%AE{LL$f;&Zr_9f2MrGJAt`$Yqiu2D!9lka&{hC1&&o2^`I z&G>q8*KuaTnUG5%SgE>W#f5csnn?oVi_8SV3&hAf!zj}wwDU^deI29H3tjEldG31` zs{ZG%wL8`-bk;q0|=)V3TGt#xt^H$&yhd^w_YSl1&=k zv4E4N>JUi*hv4{+Q7hYVPu_U>257Ft)=JI%8JKs#n$j+wi;zq{gx=E|I}~f~gU@^7 z6ia7^U7`D<$Qi#uC;9@N=s|{)I)EfLf94;%4C-|G!q=t*m&Zf(;myYWaSlMbC7gRR zjq`3a-Tx=o$AP(9?(RL^7=7*=1B_cyM!mRu(2bY>KUeGe1fnWeesrNb9Wiia_EhEOtYc zj{TBzS}A)lYGbvU7SB&(G6=Db5q8}UbxZH8xXUaJV&~kMK2!)+%H{7Y>ZWb1$uwZ*$4or6Ydypw(?ghA(6u-pD*K}VZZBJ&{5*pwryLwdiCmB zPfwW@5)etU@Q8#A(z9Q=%OU&K3ctW1nE_IYWnmq?(?f zkZe8Y#q33{&wTO3Z8Fj~xoMd`yCs9vB zI84;hNLsEvr{+D>7^iteY57O*>Zwnzc1APT`T)Tw(Jq$OxVdcfS~tRW>g!GVYig`^ z2<9wIQebhU!q8v`^}mC1^L6bs@1$boo}9B|dE%QGZ1BXerbGtN2x;;9hn6hpXUfg%vYg;TK!XblO!oz&*a!G#d7(^O`F#A-*CgS`5&@m z1%p9;^@C$>xceX7(d5s$8u$}5>}Plrb~mDnm1%Hm#C(T58*Y_1j@KvH@ns^}SR?T<2e-k`$RF9`6+qMAH4GhgSAsH2?)3SW^>Z_SQy6E7+B1insu+fciBhN~&!X8>~2lk`1 zBWGQ7*)?@SrOdqv#A1jWq~h~x@lVPCK%n?3n-<*a8Ju6mV!4(Ju5)7-6fVx6BrgUi7SPGOmeh(bX0c7o9o zasj#3)qC8+lMM64-J6XV1WLOTzC}%O`PRtVo zjA9~6*^@kir9d4`C&tk>Ts57;)W9tUzLdBNndN#phtamXrHcn$?@y1r{b%SBs0Thh z=3cSA#x(@zQ60yj!po=^jw(>HKxdxlfiYiB(9qx`k32%e?95zC9I-Ux)Wac=bKj*(KZ(iJ_asmgqgv7# z6QRZmNdclwE@r9fd{$T%YrbP*;&~3ajeAiKA0Bn((qY!z_fiGlhiP!JvgwfET!q?I zO*_lSI5hM(*Z9%I)XN znk4Kd<JrGOTekY!NDC`LbCY|++4#_ z-$s7Vz&!5ZL%REkC!Uy-x}TRXK($;RW|iY9Dqxot-b+rZ!6_Qma8}E0(sYieLWyKz zB`Iv83~y&5Y2Bwj^{EA0wrrVwM{QG*(+APBhaiGemE#oai+ln9nhth16QgfR_Op5v>ulYG6BDI*b(WTlAo6zu5D@@W^I50g7*^4_Mr zLYhX#AyFQeMM@vaBG7N|);ON}y-YC``bx{b2ZI8v|jSlBT=ZWArp< zPp78SH1`^lIVTisGT>>%ohlZ4o?=dh#`1@8rjfP{XsqxyQ5MvQr^4ista2r?={t(W zbluXWOZ!>II!la$(rCry%m3z>`?L3a(T$IBk`u;JaNj~&(1a=7xoxi_;UwYe==Mww zW+;qdOomH1Cc)RK9IYtxaEepcOcNLf5?1P@U-Rv*mmAx++%;cxXPNpR81_fre$v(Y z2dHZ;&+bHoMN^nBmggFQZX)?K#RXI`&Jr3xK#!G&&)`D32%m)qhoy-y!ET08ooQKi zkt>Z%s3(RR`Zkte9z|<1QBQEt-*AV=rZ%*ilI}+8+Fq>|=TtxMgo3ra(u}-pQmLWg zY`9P;AEOquqf%!%z@?!?JCoI1yU3Y^C2ZSG(PN^^Qc|U z4%3_5_>r63=Fj>`y}7E_PIyL;_W0t$q@SipbTeozl3TR%0qw%_Y4Wh`!n5?TKp`Lt z$B}oNC&C81z%Z}KGukc4Cez>FUqFuNg_fy5eI$d#L3@aS#K+cBQdg-LkT{CTc95L= z5{IsZWKxpbrcxM3vIpJ|<7hP#>ZBE5cK#H`fnt4z2R}(pZ(?nK`RowRQR=(??U1|c zeUGx@@(pO_4#p4CDu&)n+bl*V`QA5)dy=RLC?*87N!%tVOO`nTkbU8B=hHNKB3$-5 zI>dDWzL)X7ufe{_^&ik2$o!in3#HIlyNRDh4)wZ8EmzSWPq3Qo=*h=*O49@z6Bw|^ zJ*9L3-ox^(t@pAQrq}gz7g>v0!E#|`7+u3P<~A4xd111_JYX1c+mIB9*Xk9wJ4wnv z8jK^pYZ=Yw=r?+Txdy2eJoAR7D|WK3rDI;o;`OUO^O?`Q`i?u!qW*|{4Gav7u|@p_ z#Ahwn-b%;%TL_$aq4Y>|kIeT@b=LThswHYt|74M;!_%6!6MP=7EetzM(>mJ3P=xII zHoCWbZ0)?OA>4<8=`J+56>X@(PF@`^mx-MmJLk+i`7pU7Pz+a)nGleLxBA<_O~BPD zO-$Rc3+1xLD1F?3)(cxm*n2>AHYANGaloMYSf{%y_3`7FQ=D-L@7=&P8`r!pRnkgB zrNlkPc-}z3qC!bBDc@Kv& zpzCiv=q_8h)73`bKtoF{0pfhZy{@L;W8&^tt43d!Q!f7Lz{QLvGx_KUn-6Q3*5W4s zX`jNC5cLX_KwrTZv(FuV;wZ`IuPtu7zUn^qot3zVPN zP!DTjAOm=Jpyp%X_S-)9o(X18f+kG3l-~RefxS9FDng zb|KaM46ONIBEH?g(d0|d$SK;Xl>CwRSKaoLoTyFyfq9Srf;jzATIGL`^Z^>4jXRp{ z!e@_uOgN1JQ?yBwNh(HZQ}gTmr59! z;LPHcCg6$=jW;AZv@u*1U9Q$@OBkvw;9@4kCu#L!c*0~Tdo>v0KrY;!K=4k2@9(PB zj!5jM988i{c%dVF^^fx^I!W5PCf)G`LiJjF-rX_5%VZ+#6?>pfB>XvP^JnQbL{%0v0TjC~?iLJ+DQHvs{={}wF?i>^8*78)o z@#&C%XH4y4bZgwDl0;phw$Ke72ortnK<6M#Cud8&x)Vp6?>HTbe^L{AvRR#|FTljk z-WDvvgv_FMGFW5ANE?YH4(*?8Hf$T!Ly_HvhPO)D8YJ_6HVpbzOGx#TT!V zzS8OAn$Hx~ot$dZ}U8SF8*z(Wa&;CBgoaWJ_Lu|DyJi}!DZqGP!y;xs= z^@O|>VE$O17T7#oD@evjNE6`{7Ewfa*)cw|Ls+3uM~^Bs$o{z&Q|apJLTS~iYOS)I z+0Oz?tu!xkhkaX+IMVtUzk~RnDRp;0SYEQE&B8ygPPea8x%^j$(F8^e@40Jlv;yumfV8ubwyE0?fMP=HtM2rzZS zrX8?>1{cysv6!U&^e2{N!%KK*65wD7Rw<$FV{Ah>{JGD4ZdTgNn=;RN=2hZ|`4lRZ zXL!*sv*qn+YF9T=pR?$Vj-z>saN*F_f(Y& zkOUwRBodqvT`YSD%%lYT1|QxlriqCar(R|-f)yM;ehWrOl(*CfHBM7k2i>wB$!$%h zSgUn07+J-$jFpw7#+ch(XqIWYb+N z&mN`zbXKE_ik!G&kV`WkPHQt})M=T1w6U|Z)NzWozMcuE;+i#Uc9Q43RPv18MM{x1 zC$x3z)9ya%Yx!yp?{OKFZdNnJwr(YERw+1!9bRlgKD?lrXior$0UZE31jkg(b}KOc z)}ZDD@|9rX=L3kWJ+{bw?;%a(N#Z(vN~NBT>8tS7=spR*VB}M?^Q}rvtoDHQ}(#PxHgTA+4kFiYpe9z8hV-;2O7i>wL{QRmto?F>yzk=c;px znhljYAn6~x)Dv|WJG5-`hcNSpdd%L+0&{Tk%P0*`#A(t?&Trx1GNUvB-se~6W}?Gx zl&8Cx<8U4trqEMO3+w9DbTt#71+An=`zW?%IXKIfKE({l-4&RJPcc8gXM}Mu45;%| zIqJ^V>hu7^7JXIDn(>SpHUBgM!zU)`k_#ZBm2Ta-#Tw3>wSrBS@!jt~<-U9fUFP}x2fZb8GvxI$2$9DnVyk29l`H{e=T>l!V>Pxn>U(FT#@7T zg-YnEp<_?w9V1DEle2r?CP1`}~$x8f+HwXC~1$~6g*yJT2R;z+r;$uUP{ zCpaP_*YMT!Zk(i_9cM0tb5QeV=B<(16}hKWdXx0^0ikE`zcuB(+l?5fF!U<@3ZOTK~RFfu{1h+Ja=L*dP5 z8a6?)2)Hb-tf13%F!@`1tt*Vb(rsKbKf~vvBMj>5FpgSd6|eLPaB>MkrcMN%yeI@@=G#Cr^xz@Bh<3{Zp2m=g-VfBXuY*oRCd8 zg+oXj#~Gi|lH5Y&`F@8K^A$|aG_)iS8je*8CTy87r86DA;DQTQ!mYK06%_8wR~crA zS4x63_p#;918(f(7~4E{tNer=f(DoJT3lJ67YmY;UiDlzsrqoTgkmmqV}ojxxi`z# zQz6IdMVLlC)J`^4q1>ibPoaob&7{o`O$&*C$B08dTTNlyQ7u;7NO#3u`B3Wa@?eT( z<6;+ms0+rXJserzwQqpqypwbL(?b1(Rx-0x^W?}fpC|lHXutjFqlZxKK5)T#=Y5}! z`VdUyS^%U@?+lwfZJL`6Nba=|??#lPEN0yM1Ufwac;(8KBWHbUdi$Dzpx%iny5*qz zrPqJTjn)>qO5tI;-_;CTeg&fH8@WP)MxYN9fD&Z!O#q6; zQ~cKQ3Np%3RGuHKmP&h1 zX&&(mm@~>aviB(#f50ifKV|&LU7tqPS=3~XNaK^i%sfu*<`yGy+{E){zC0h~*|KTV zrgM1CBfRZ>?;CY{ix0ZdnAuuuc@2tX@-RS%T7-z}b zAj4~q)m1(&L}c-Dv@<5(&}QiYGMq5fVD6>NHqpz8TCW|Wb{<80dFr*3?A!P!Fg^-% zd76*2mSN}JoE64VDwR&rDUY!Jd=wJZJ)iTkyKM%w$Fzs-FIhnO*70maJiL74#*Hgx zfa}aA(Rmj)G|X)Le)PRQPv%mN%#s`@LMKyCcxFpES#A;o=(DvhR~eoAJwD5OQJYm{ zF0`Ok92r@ZM_em(W-JTQk9tP*XQj5-9D|4v3YW>n8*U%k_zm$B5tEd8s9X5jzs!^5t${7EE^d+C5% zVH$tPfc-F;vj;Cq5qbN}6l==4=w6obV!x|&arAxL+}8CX=HUzF1+;B%lr-`Z1&b+s z=wTt)VklAakhmd%M-0T6P>7HfffUzx*y4nmg(P{7sqYDgL2DP?i${92+w=^Yv%)yy`%xEPtJKmz!@rlOX0ck&<4z^4T-Ewy}i$X11u(((%C#yKEz8r>tZ~;|?N0Nr55PAKc4*4Z0XrG4{7!*0FidS#TY()ehpH|FbHyLhs$*HEHyH-@{Jw3e~kGO&blnt8W;2LRd{Cg1pLzi56 z<&MwXdFQ<7-B2mpnX}3`xIFhL*B9&w!xc^NWHZ8f63V;Vy~I3*ndCw9u0tw%b*+|a z()2xrTJ4p0-+JpgK55EQURRx^z3O((K75;R)K?KYo}I_Knmy{2Nx_62lXZ4@-ld~N zb7yMZl_LAIS988{IZVQIEQopdC+otNVHU3buD=K;ci;gQu8tr2e&#U0exhl0hXh%0 z=C8R@aW6VbcDUk#jB#wMQ7&t=9e4iKp4sWho?$0Ts^6+pPkv$U5-Ap;(!@=fPxrGb+I2boy z=PKr=4y2y&i@&+Y)oPD05d0cMc5X8c4OffF2!jdcWaie?$EqyF7FNJCP+-ls9|?p1 zOrlUnFv$bkx^iR|c=B+P7(62ZPJoefSfZR?&UU7K8ipw_&D8C8T}*5r7|P~2Hf-2X zO4G`P?D4yh(M&JPu$&duR?*}b#P)3;QVz z%p<+bh|_kL>SnWZ#?TDxVc_a22CT>2SP98kTAO1Lx9)e-oK*=a|&iJ_v>4f0}iiWc4 z7+UbTOe&az2M=~FUA*`b%D$e7QKo2gfqC)-?#HUh_+i)8lS%0@&6%*zNavjFMG&*9 zLt|q`B0Fqc%ag(K;k2p^x?J>hJ7V%ujvzhBH|MyZUxs(flmmWTRcgmsF(-g>wixpq1ZT2IFohfPOs*}r=0S1fX(qN7rJGT``cL)dv|ZSeAV4|-nsTG zpZLT)?0B5~{iYb3x~*IJSaCU4ntTUB-ZRB?X9`TBIUfzwEmW&a;5ItWtP8+0`npd) zzO=VcTTZ861|L|Y-6G-)Vg?$97=w`C2U~ayQ~v7r;xq~~50)ekb)E=kQ38wra(D7K z$8o4F*@ddWr#T(mv@)V=OG~9n56t4?v|8Ut1HxF-y(sW`fVEoUM&{>dWjFR7mX!KrY zxnIq#^p_rAv0}+rzVelGmw7mD^-9wRc>CQG-so%wNZp+#ZtHHBJ9&6+rOh>ODb_2c z74@`y9@4{77{x;9%CiY$2A%;OGwN(>gjc>#M3s@~=^x@*n$qd*4&9RNlsNt#+P@EY2-cDY1lyvfeZ~E8=a3 z%FyJ&5MSJ{xpLutX1KI$JZEO*{*&EhH~xxpQhNn`7Hw&o6xD{Vz#PN?==`?)Dw}Zo zqxH-UH_>oKM!!m@$BP>|$wR4>mMA?QI(Wbv0^lP6D(hwbGV<}>X$ml#KU_wiC!{R>)bI8q`pPnt`8C6HSD{54Ig_hV@1+Hg3x0V zpK=H05&42iA9y4|7_8g@UN5LVAEg#AWi&R50Y)4!;Su~K<~fl4kj3Y{?%Dcb_sNH{ z{To~^-BYWMy{=lX+`x|4C)iE<9mewiR4f*sWQfxKV>(Gja~*ZgF%E_-^`l3J_oQj* zdFp;2o%9fm!)So+$=kgHu!7>w?;R#nt4tC97(Hwc!xV;3P!2PK3`MfKYUT`}O%;`bL zE4OazVou;?weDmc3T~hwF%M3aVqyb`_@ykZMks9~4BXrKEfJ?j2vhe*IH4ECf~GM{7vaNk4JAfu4p?gLE)No0$Zp zHKmf|=dGIKr-0B9_*^NTRQPGnN10$9M0yayT>RCqe)Y%~zxc(`x4-@E!$2xUMBJR+ ztb9loqTd@AmEA(RM6$HoRff-Z#o8JUVsPyunzsA~uvz6Yv&2?O;|x!j)2Q#J9J}3E z&vv)?;)8D6uV-TV%y2iSI<{t{w4hRCw`D<0V*!kuYZE<;+F9gdl}}?uGKqbo`!rngBEhjsDTwfdpE?tV4j8Ab4+t&k`_n%3f$DTf?@A+ zE=^I&k!rE@97B{B&V?aLe2a6Hao7!TaNZ*a4jdRk55fPSh1M5Kh5tYY6!Qq>jxEyC zI#7jlsmVw!t&F$w4I9#CC^Cn^H(Fci;}fC+B}9_*grgKbFY`0+!SyH0DK{4^*Z%`C zoO-O^RW6sGyZ7FEzl3hnkxMVVbZ)m+Uw>=at?OCj(t(A1$u4qf*GhDBT*ujut5EQ> ziON7tV@kYz7~{*FLEE-fKTjEW-0v~>u;1<6yWf4~*Tghp1K^y!=^ZRDDE1eBu8=0{ z)3mxkoI(RrMb7+_c_Ns>S{rAO5k8Nn$ql3#yT%|rLaBz_f~!Z|)@=A% zfZi!qdk&`kd(ary8Mt3# z7|@H~7@sk55~%&HJ!9e-*pY%G5wzk{e8#q7WImDdZ1LmO{sEnH`FXx>LomQM@nw4s zWypWddq@pF+rejJU>fgQw!obnS%Ze`*E7+0AwvOdDS}ScazIKuU14Ccz_ZC~x@i*_ zLxN6r%M?NCpsFN+XpTbd-70w~y{c0*C&|Mk5CXMAV*-uhWh|aB z{ej3Nj|A;(Rrh69SogXAoH32$mMvQr9vK>1SgF-Cr82@%;cxJ<8MyoIyEBQzBR%`S z=RD(}iqiMJ?|q{m``E{layi+9Dgx#XI1uEuqth{bZ?J|8^+~1AiHSHTFp8OBXKIFF z0#`yHdTO^|1xac>n~6&Q@DKm+0&%0qjvW($)Ml(%y;#yNA$B7}mbz+_yvT=5bULnX zTF-T+Ms2P>Ggs9Fs=QHcsphmlgNub+U1u+t)3M#EyV6UJiN0fxuzJ+D zisSjKlC;X|QPK@_w$5**qGa*KefttefH43CC{jJ&|7Wm-HjLo$=}m34Bj#ePL7~FM zphfAu*Kk>cTF3S!CCA}JT&7jX4^@*RgM)o6A9A-b^Ln&l_t41D-`sa!(>8R&a}NCc zC@>DIrr-FD-x&SqM?ZQ$a|AoklCGw`oSyhDzTmprAU4rytCV!!1V{9%q0K~0nIDHy zR*1Jw5rGt@Z;6b;yemr@{8{lRHBej{oUQ?8C6dP#P|^S^M~~qDCQG;Xqj2^xi1bXu zY^}RL-8$s1=l!D_bE-5EtZK8P^c}x*h zS5j&>WP%<{m;R8R#g}z_5B4rs?j3T2hfZ>(<-BY_+k&*Qb0?%)LPc&RJ`0sCq4ix0%`8U7y`}bV6aG%>bkTC@=KQE4zlB=PG71dhduV8k=aQ^%jbj$Bt`T3Dy z98_5P;SYa!_z(Wz4gjQgTd;jrkU)$SLE*~E&6i#sRc8Yhqz&xaN zL-PnS9PV=4P(^pDhy4IdFOvtIcR#Plo(YC9XmkgPj-@+IB+9diO_x(i?-ZdaD~xkJ*Z`BE!{^c} zie58s?7i~=KLYNAd5<#l1M66&_<0)XW$fyEpTtobPVLXHk%70x`7K7HPlK>xCI~5;=B^N-@B;jy4efW%MxvO_evNqPxTs@$x>D~ z&ZpOK?nZlM*c_JD*J8tPQtg}Zg>U3up$Ea_QjWnhKa*-0Qr`>MF5J86mCu8hhvNcc zJl`4Ad(j_ZZ1ZTYymMPu-*DkAX)U>=TCaVW{>~vpE#X@~wwxtZXx>81K*s#o^2c!N z;sHi0D<+8;jCFlpE2NiAxUHDtVC?Df60S71Dih)rgQ5sNbjj^7pWCa2;tuzJzj`Op zGTOJX=){Q=Trpi+!H1*t+@3v8ZQHhO{+O)KcUHeYN)ktW(G6xB8@*p*tWJB+`6zDKX!E7bcD9>Qwf{}PGwqxtfaQT8_{+pwywuK##d() z1q@5R{N*p7fS{yiag-IA=_n0K+YSrf{Asjdp?EHJJCS|R1yMV}>I?U`){#iKB`hiK zp**@>jX46pX{u2Nn(Rz9XCWzrv>Zg_ONyKA;@k-$eIJX@KL&h_+vaR^&`3K!t~auZ zvU~60{&dNb_3X5~gq0GLn9!7E8H)3Hm%L)`vIz-+vDGC}c&Sta6c3}V=3*)e5S$(l z=Tk+05r z9A_mpjgn|9H%nD(y1PqnV86y2XstKWYJ9ADj6nFERN^o);>QzHOym8&YD^LlRp?)E z0k8ZfwP1N6MF7oGOeSF{y@C6Wx5p!J{D#vkj~^9sQ-TElhhTF+Fu`cyMJ8aLX*H@xw5^+?%?= z1za+DO8tZVODk&baDUyce4_5|n}6w|Q5IYbQEt5G7uOUE^HcNFFgP!yRBe&VXBaC>%WEU6AUdNGgxw z`O^54d%xs0e0dh7A}~~0z(=CU8(@Nvu^8_>&2IxIxd&!oRERsn zCaT0QlZCb5C0u(B#Qd~`tNDl3OagZyiCl?$`6x{BJKy=v(XW5~>qpsPIL=10V;sM( zUUbn#nxT(qTd=lzt?p8H+qpxabTX~@P<4`MrfSmUdiKMzlYhf_Wek;$MKqWvy*B%0 zRV_B}moj1-tAflO+d*v-jKW0^GO8hcG&CQgY6Y(`f)E{$7^HqIA9I8s!=(>^t}&=1 zi7_u%EFqb-k;0HLY7AA<9~GvdSIp`myzh^qmE3yktzv)|Gj6|_cK$isKQ98}?RwPs z_L{H}xooC`Fgxvtzab@f;?QMc0u-UhA=^Ll3PT_4Zc<JI zufqQQ`!g8B(v{;;$<|qA(x~I*$mqR4?GOeiik%u~>7Ap)eZ7WuheBz(8!& z3IOLoEDyg_%64+|`6?2N7qjqfulwA<4!njlYEdzNF$@0*fq{goRk#kwDQgAQ7)p}M zL7>0L8tx-*^|Iq`V6Q#`FA6iI4)vq(rkti_|Gztq^fAP|$K%ZYX^ngXkM?(m^344 zWIGhSpRM^1aeV`;9qGe-!H=<|cY-+R_?%)MC0D6jzZWCQp+S}Jkuv0Z@2b_!;xvDdwjOUiJst6GchHt5Cd~1>gXpBXGM2Ffug9*4p zbw-@VR7fs(NJalJiR2*rKE7D0Ri7UI;2fVzQc=&M>DZypVwq-}^(l5_YT)}#8hQkg z{wJZP^N}SlVF*ieL%J9woZhrS=sSHz!41fXt{iGnVKYkM ziM=4W1Bes}p}kw%@RJ^t*=k?NHQEb6hdlVuLk}JM?svZ{#wsRqg8CUE?j+|TjxAic zaGcFYOr6YTvNe=5XG#Cal~-O_L?O*`q={b2fo_SUpUIoG`}XYVN0U_-?g~=J6DkWs ztwHq8Y6&vFbq-sz^L4jBu_h^%SHm!Fpf%YDSksPNOQ`D6w_dG~YB82BtVttvXc=w$ z&bFRM#-WsQXF2;*fRa2SBUAgDRN=XYEz^r}i+>1n2sSi?A>|bwWZCM8YIS5!TI<#U zwHKA~wfUlYCI2!IZWpuEY)?<#%NJ!6FM2brx<-^B8Y%F3(#vzrff0uh6A}5j4^e@A z&lRo*cuS1K{QAU&FrovDIWl-0MsNlv=^)^d-ws&$h%DUW4LAgY)6&H9D!p)8I`5V< zNFHV@iPAvRRW9}(1b!F(J%sL-o6dTG&dTzsdlso=2u3pa%2&Q}dcy?eF^Qp5m5&@b z(lb6hd`Y!dTgI7-R{+ixjD;I0!y?M|EdKqte~&U8*|1^57;b`3Z~ocs&s@j4&nw^p z+OP34y{|fPoPV&PMhH_SQJ9XO${htKyI5N`rHMjN3b9zsPy}&eoUW^h)tO99HA70Si{cXN(JL-kPaR_WJ zBIgZsq7@K>j^{T7g_tU{MJ_Vd4ZV|Jg=Xa492H^(+8mK#5+z!+kI%vlt3~8o9yA8T zkx~ftl-~wd11F{6vj=hOfZG~!>?2?`{+GdIG(mR~_c0^^HmDX(JoeaQBJNY9sVE*B zJvg;7zOr0RSQ1m=>YHNxu;MiXA1LU9-h*A{Vy!M1gocK_oc`6#@OoCz$u#f8Nmb5` zr0F5@eHspTKX6BSdwU1hu3c*y-VEa;KR177+cGwnY zih6OG7cJum2gP*I>)UJ?D?yz5a9=?4o`)^fdFoIidW^mmo&@}O7q|EN=-7Lk14CRh zh&s4W>TyPQ3~fr3@8$W39D~U~a$y3^!ect?G317AaS$>-2_||^JzCbK7@-^Crn3tM z8c$i$PR4NSj0^|U+yTPI2|G?Fsla_WD93(imP(Z-XF!>jfm@1=T0%SjvJ_bD+FS-C zch7J()#q8o&52KUJN`$KLH+vs0Etd;xIlb`)T$N4}Yf7czks=It#2M@? zT=51i&;T23%dL$YaAN|7LmrGV1z=-X#AUVsgeNco!m27Rc7-rIxr?7OVnyw#5HGaV z0OGChurzFCgAvG<)S(ZM`YYAR{u7u9ab?1bL>?mi1omIze-`jZm;^nHb~ovgozg19 zo9^7XniDmv>(x|S%HIP1wG?D6OpT&pD2EEB^4K2{=C?=+uimeJ#bbMkLGIuW@Y+fn z;r#H$#}gWcBB}^O>}x+1000^ENkl4Bc;Fm%fGCzn;3}3(g#LHPF(|Wj22z#GQzdbrxz;J3LRg_m|A2Z zlLdJ1RDP8-CJSz}M7c5{li=e+8C`ASj66l4o~elXHO4mdO4lf`q0W9H&X&9sABz@X z?Yl>x%WkeT&m-h0T2hqz!i^-B^e6eC{B(vRFy>UbA}qe)_2CiD2-4Dfgo;&KgMw%qm1!-#8c|cy2D&oRZaQ9<%>d}QxjMxJ(;%n` zQB466PJwc_!_|~vq9>w=NyQXmnS$Fe2WFAQO~lj)39wOeF-D^f1BBce!@(c3RpQp7 zC@dH)C}^0Yt?zpF!8EobWqhBFd3)BZSu^z|UwZJCyPhbI3>{fmDE3@XFVruiL|4G>*e;i^3cG*+7i1MSwXml zOxN-JG?l|)DR&{nZ_>Lull@_!i0i(6thG4 zX>n^<5z~^MqMJVpi*X@O3vYF+P#7yGw&pauFuOeqUuPQ(gt}R8p2+%5VzQ;MK#yWwJoCdex3>G*uxx7P2EIz2blwmjl zHm0F;q=_BS!aOG7)jEqNPwQx88|AGI0=EnQHYAOQn1nuZ(M1>K?+XbjC$YAsg|}Y6 zzb7qLm#~(zfzEkpAuVj;V1GY4LagFcZY#UQjo^+h<0&*u1T;8cRBnaE_b{nKegah` z#!v!2f-OMsSsPoR{~>G~WJ5&mMHJmtDb=4m|KPyD_O0(!UA$z>OPz5vHT14sy9Sx< z|89T(sqc>uvvH?TSX3-jE=~&F*KpqHD&_<>A+ugVf6`4W zP>I7n34{z1a|fa}C;}q=ig2?q97g2o+_GD_qX0r>shFD5ASRo`&?1&1V_jCw zPaPQ)FQK%=hbayK1*v%b;^seW=#rDs5KGj>M_L>oc<$g0$DZ5z(ioml zEt&Z-rV`^QH3g-9$(9HiDk(6IuWoKg26-vf=4hGhHY;B@W{$c;NF3Ssv-zUPrx~b z*lN6&iLgB*J+(tSZnR6S275F`#yu96fCHg@J*$IS>j2kWtf z-zm#OGd<2$ z^4+-E9y30~PEnf@P*zhl*WdbZ`P2)0*Rz(f3jIs}Hcd(wvDftq#EUM}@JJU4WdfU( z)0bI&TJNMde`SIE1XA}_6-1dNVaa^$iEo%uT7dk%j)vqR$poDlCS9mLq{c`UBk2vK zqx=qo{qwbY;i*csaDTN@IIE40?&0A2W_g8lFxh!q8T1J(561`LYhOfedB8 z-2Cpha%-=GM0dvFt{cK+2!kY|8B@Eer^XjN$_|~v z!e{{>XAXn$;lZDSwf72m=(rQ`sYk^IZHNOz&4t z@PdHQLvW5=H2EL#33zJQop|Qhcwung{_98GOK?s@^_Dk3mN@dYaT+%rMKr7NzQ+1` zbWA<@!WX_Ei9|XwbWiLlt~Xo-=`Ci9If^-LN2*tzo2K1#Nct!H5tti+P|NDPgxN|B zILs&$84V@8!*^`&Y5XeIV$=~l9^XR(BWOT+7w{7N=?|t!Omh_~x6o)AQFDufl;;%h zunYe{+SNtJNspC}9^K0!jM2P?F_b3(GIjIbdw2Fcf4IN&%%OoL5yY*a*!C91tGx(4u zx_wgO$=DuFqcUG{1ou7y_cO_-Q=G?{+jx%W1Sg31Q1;c-pTYd}ke(kp~01yd8 za~Fyc!<8)wLMi;^(v3L`%Y!H%PdY!p0XE4R$v=_$pP1*k|D@}>{|Cy7!<6eV_Ac&fI@!gA-t|&x2!?SOrMaUszJk(9;(xY$8y-|l`qII|dBtkif;6e4 zbfA_=#&NkdivE#+NIIxb+ zypd$mMGue`@E~5`3P$~oFDx|gq*r4U9MUnWgM4BVqs&bVz!*+;b$1WLEUKs|sc}C` z9lQSU!w=V2uU?&UA&@P%l1AU~IWY@Qb*(AXlgpXtd`DWWY^2koOo`2Og`^9cmC`zo ze;o}!^d9Qv9Iv(f^+Ga=gB=p(0~8M9Q4GY}%ouFx_Wy71e17A)syP0B&3iNBAF+ee z#KjGE(lmu4DzN|rEMmcevQKwiR6<2!&)#cRNc;nl*flIGq_z^RSWqMsKRtFaKesUdwK~)VS?)U6r*zlNUBlg`04y(s zYl|gL2jycI$4gz7IbDEcgm{#D9x{g0DzZ$H2z!vXs6&i$M7Yt4_;Y7#6piINpp?>I z<^eOn89PGTqg6yne{ zbDO2Tj3=2e1cs(yTdX<@4O&i6L4o~Ix?y}%Ve@>Wx|tb8ez#R1IMrAtrbc6_1j8b- z(TXkuqe7myjFj_FO8-vzTR6O0dFxltteAn^+ifY}faA8L+4Bmyw)AoRCzpU8zBYXH z3_i;TyB4{_bU5)^(bvOVV)fKhPgO`W@PxXESd>T^T1h_g$7KQ?TIF9j7JECNv;n zS=~wpw#Hzq z5{BBTnBAf^%2u(8dZx^ai`laG=gG-q{j<;e+KzVNOHjZ8$Cn`59lI>N8+*%({_OX^ zZT@mEy?8I~8Sgmyo-|m%yW06zK0Da*;w0)6M`1j6h+1=1QI4}!GXr}b2*c_aY^>YE zoL7<3Q(W6j&niA`Zdhotk}8JxNa`vR(H$j>9fCB!W<0aKss_9aNDvDY%o@zLGUKxG zq&94faIXZbTwMBpmp1E^zs*={%>7(Y#;dI-W^UkR_+BpInepA*O$s>RxJip$OC3Iz zw~8mmgYt3^1#JZ9g$35m3x*GN?L!{qKyc-|?|IQk>`nPzet_MPAH$GSXcd13=dyL| z&B3~8X=^5Ln0=UUCI=&bF5CnZ)ov$>_+EgbLY6v=29PLALZHxqhNKTw>hPSENNigJ zMxYW0?^bEN!cm&n!^;1(N_wx3bfS$j^T*r)F6g-%LC?m8-KKyfjkcqeWE~t@fbDhGZISmaC8D!Qi#uKC5lpJqs^!xomava1pbU<}s9hL!4wjWqUTzB;zBY5#9}(?rXkt@6%+!^N zgba%6*ye^=m71D5VI#>X{M=HeA%HSoe3T68Y;qv%0^L{Hje48P^|bQeK`zIZx_xIcyS0 zFvi)N(E*G)=&nnXws8Xv6lfmleGlXY*g*mQnos`j{AjOVw2~R` zpr5C4lt&zZ@?wkwjdPbP4#IdW^vjt4M0N_26_W>-_|Oy2MBf zK`?^e07Yhr{}YaU0~#=Wo8zi75rC}`95@+U80J)~J+Qj%>si2{Cyj=GKy_l6r@cvU zFVGl5fF=^>tA1OVO{&7C?AeRD4OjbtjtyiC?KNLTN$~-Tlr@yI7uYgc3!~(J+>l%Q z_u`?&bHDbWj}|vBJO~Qxe5?=RGu)Up1!R9>&}mHuY4B`W_|q`)Px*g-kpK3$F|4-W z{T{DPj)-rPXF20-aFVhIcjcf=4?f6I0_35Ozmuv=kzU~auq&AW(2-%($O3~$b55UD zWt%hI6CB6H2vNn$pMdEnh;fZsjSB$8`bJV9O@A}f?XgPCe|}Q{m;%3PZhO2AVZqmB2Mj90d7wiQeic6i@GRn-)sJu)ucWi?9!NtFX#o)?P zZ)xT1^N*gJ#cd?pcS-x?_uaG})d|6ZFL}{3KX`AfEc!{9ClgUCNn*d`-bRIARlR~E zF#Ta9k0UVGG*?mNbi(0B?{)Lin?u5wg-ega(Z>OS<8b2%5QAleA0s)i(%`KI z2394y2nTGGu)UCiN}i^e*_4yk!0vt+(x$FgtN`lN?a7Ja%P%hCF{pMvWTx-Gx_E|`;%ydQ z@1VOlPx~vv%+jGwJ9@m`@$zmJg`F}gy4ar@OSCzqUhloFsQ*pq6tY8Y#W_<1l3NFd1tY~@<3@`I=5xgWE4x7029zec)q#) zPFCeIN61ZK>20bH*tOX}UOJE5vBAav|CEupbUD0wzSWPiGxN@9PEF>08M*D)efe0# zjb=xnSbOk9yqxDnD;$a5@XKzi^wvl7DvY{O7h4yDB&fn^&Mb_v<8l~r zqYeK~0tuKc^iYWUW4uOSZk@sCa8Qc!00k=6$+T)9BkmXK@XJf@0t_3PrHg=rU%66C zfdTH2`Is4$6|o9y-&LRf*eLaTl%d%5*MW}f%wY+D>7(DgM(;k#ndRIo`(dxx8U#0r zFg~yu4ZLErz1$laBw6;+lUpZF)6<20O#%BCvahG;CU;v3z`Za0@k+M{(|9n!rf*qd zt}vZJmf+At zMpQKilqyIOzKjD2EHDlf$eC429%H`MmH-1uViU;*w-S_v;c}5xSFn@bV|Vy+l$QNV z{m!*Br!dWDf$_Fq%~kAg3h3{{g|9LN%nHEsp8Lax@o2sjcr#Ci=?2JfbSl_N*Mjlt zN*E0e1Yr^cF~$pTU=K_OM+*FrrBND;2OYniN1faoL*l5^R!9TvDPe7k>GT zby6-k3OEWl3OEWl3OEWl3OEWl3OEWl3OEWl3OEWl3OEWl3OEWl3OEWl3OEWl3OEWl f3OEYf6$Sne-T)PxgNG(p00000NkvXXu0mjfilCYh literal 0 HcmV?d00001 From 333f1ffb943370816f2e0b0137f0e7cb786a1d51 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Mon, 6 Jun 2022 13:39:06 +0200 Subject: [PATCH 0878/1262] Confirmed support for Django 4.1. (#8498) --- README.md | 2 +- docs/index.md | 2 +- rest_framework/fields.py | 3 +-- setup.py | 1 + tests/test_encoders.py | 5 +++-- tests/test_fields.py | 4 +++- tests/test_model_serializer.py | 10 +++++++--- tox.ini | 6 ++++-- 8 files changed, 21 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 3fbd2f83a1..e6e43047e9 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ There is a live example API for testing purposes, [available here][sandbox]. # Requirements * Python (3.6, 3.7, 3.8, 3.9, 3.10) -* Django (2.2, 3.0, 3.1, 3.2, 4.0) +* Django (2.2, 3.0, 3.1, 3.2, 4.0, 4.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 aa502a6218..2f44fae9a0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -86,7 +86,7 @@ continued development by **[signing up for a paid plan][funding]**. REST framework requires the following: * Python (3.6, 3.7, 3.8, 3.9, 3.10) -* Django (2.2, 3.0, 3.1, 3.2, 4.0) +* Django (2.2, 3.0, 3.1, 3.2, 4.0, 4.1) We **highly recommend** and only officially support the latest patch release of each Python and Django series. diff --git a/rest_framework/fields.py b/rest_framework/fields.py index d7e7816cee..8d02b3206e 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -27,7 +27,6 @@ from django.utils.encoding import is_protected_type, smart_str from django.utils.formats import localize_input, sanitize_separators from django.utils.ipv6 import clean_ipv6_address -from django.utils.timezone import utc from django.utils.translation import gettext_lazy as _ from pytz.exceptions import InvalidTimeError @@ -1190,7 +1189,7 @@ def enforce_timezone(self, value): except InvalidTimeError: self.fail('make_aware', timezone=field_timezone) elif (field_timezone is None) and timezone.is_aware(value): - return timezone.make_naive(value, utc) + return timezone.make_naive(value, datetime.timezone.utc) return value def default_timezone(self): diff --git a/setup.py b/setup.py index 3c3761c866..cb6708c6e9 100755 --- a/setup.py +++ b/setup.py @@ -94,6 +94,7 @@ def get_version(package): 'Framework :: Django :: 3.1', 'Framework :: Django :: 3.2', 'Framework :: Django :: 4.0', + 'Framework :: Django :: 4.1', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', diff --git a/tests/test_encoders.py b/tests/test_encoders.py index c104dd5a5d..953e5564bb 100644 --- a/tests/test_encoders.py +++ b/tests/test_encoders.py @@ -1,15 +1,16 @@ -from datetime import date, datetime, timedelta +from datetime import date, datetime, timedelta, timezone from decimal import Decimal from uuid import uuid4 import pytest from django.test import TestCase -from django.utils.timezone import utc from rest_framework.compat import coreapi from rest_framework.utils.encoders import JSONEncoder from rest_framework.utils.serializer_helpers import ReturnList +utc = timezone.utc + class MockList: def tolist(self): diff --git a/tests/test_fields.py b/tests/test_fields.py index 7a5304a82a..ec121c822c 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -9,7 +9,7 @@ from django.core.exceptions import ValidationError as DjangoValidationError from django.http import QueryDict from django.test import TestCase, override_settings -from django.utils.timezone import activate, deactivate, override, utc +from django.utils.timezone import activate, deactivate, override import rest_framework from rest_framework import exceptions, serializers @@ -17,6 +17,8 @@ BuiltinSignatureError, DjangoImageField, is_simple_callable ) +utc = datetime.timezone.utc + # Tests for helper functions. # --------------------------- diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 7da1b41ae5..abb4830d14 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -12,6 +12,7 @@ import tempfile from collections import OrderedDict +import django import pytest from django.core.exceptions import ImproperlyConfigured from django.core.serializers.json import DjangoJSONEncoder @@ -452,11 +453,14 @@ class Meta: model = ArrayFieldModel fields = ['array_field', 'array_field_with_blank'] + validators = "" + if django.VERSION < (4, 1): + validators = ", validators=[]" expected = dedent(""" TestSerializer(): - array_field = ListField(allow_empty=False, child=CharField(label='Array field', validators=[])) - array_field_with_blank = ListField(child=CharField(label='Array field with blank', validators=[]), required=False) - """) + array_field = ListField(allow_empty=False, child=CharField(label='Array field'%s)) + array_field_with_blank = ListField(child=CharField(label='Array field with blank'%s), required=False) + """ % (validators, validators)) self.assertEqual(repr(TestSerializer()), expected) @pytest.mark.skipif(hasattr(models, 'JSONField'), reason='has models.JSONField') diff --git a/tox.ini b/tox.ini index a41176d72f..c275a0abef 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ envlist = {py36,py37,py38,py39}-django22, {py36,py37,py38,py39}-django31, {py36,py37,py38,py39,py310}-django32, - {py38,py39,py310}-{django40,djangomain}, + {py38,py39,py310}-{django40,django41,djangomain}, base,dist,docs, [travis:env] @@ -12,6 +12,7 @@ DJANGO = 3.1: django31 3.2: django32 4.0: django40 + 4.1: django41 main: djangomain [testenv] @@ -24,7 +25,8 @@ deps = django22: Django>=2.2,<3.0 django31: Django>=3.1,<3.2 django32: Django>=3.2,<4.0 - django40: Django>=4.0,<5.0 + django40: Django>=4.0,<4.1 + django41: Django>=4.1a1,<4.2 djangomain: https://github.com/django/django/archive/main.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From 5471f8a1d9f54e298b3826a4d910d56ad3c6206c Mon Sep 17 00:00:00 2001 From: Leonardo Gregianin Date: Mon, 6 Jun 2022 07:39:36 -0400 Subject: [PATCH 0879/1262] Added django-requestlogs in third party packages (#8497) --- 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 e25421f503..d4e590952d 100644 --- a/docs/community/third-party-packages.md +++ b/docs/community/third-party-packages.md @@ -148,6 +148,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque * [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.. * [fast-drf] - A model based library for making API development faster and easier. +* [django-requestlogs] - Providing middleware and other helpers for audit logging for REST framework. [cite]: http://www.software-ecosystems.com/Software_Ecosystems/Ecosystems.html [cookiecutter]: https://github.com/jpadilla/cookiecutter-django-rest-framework @@ -237,3 +238,4 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [graphwrap]: https://github.com/PaulGilmartin/graph_wrap [rest-framework-actions]: https://github.com/AlexisMunera98/rest-framework-actions [fast-drf]: https://github.com/iashraful/fast-drf +[django-requestlogs]: https://github.com/Raekkeri/django-requestlogs From 292ead1fe0f779254590a47a352d545d57132266 Mon Sep 17 00:00:00 2001 From: ghazi-git Date: Mon, 6 Jun 2022 12:46:11 +0100 Subject: [PATCH 0880/1262] add drf-standardized-errors to third party packages (#8487) Co-authored-by: Tom Christie --- docs/api-guide/exceptions.md | 9 +++++++++ docs/community/third-party-packages.md | 2 ++ 2 files changed, 11 insertions(+) diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index e62a7e4f9d..347541d56c 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -260,6 +260,15 @@ Set as `handler400`: handler400 = 'rest_framework.exceptions.bad_request' +# Third party packages + +The following third-party packages are also available. + +## DRF Standardized Errors + +The [drf-standardized-errors][drf-standardized-errors] package provides an exception handler that generates the same format for all 4xx and 5xx responses. It is a drop-in replacement for the default exception handler and allows customizing the error response format without rewriting the whole exception handler. The standardized error response format is easier to document and easier to handle by API consumers. + [cite]: https://doughellmann.com/blog/2009/06/19/python-exception-handling-techniques/ [authentication]: authentication.md [django-custom-error-views]: https://docs.djangoproject.com/en/dev/topics/http/views/#customizing-error-views +[drf-standardized-errors]: https://github.com/ghazi-git/drf-standardized-errors diff --git a/docs/community/third-party-packages.md b/docs/community/third-party-packages.md index d4e590952d..9513b13d1a 100644 --- a/docs/community/third-party-packages.md +++ b/docs/community/third-party-packages.md @@ -149,6 +149,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque * [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.. * [fast-drf] - A model based library for making API development faster and easier. * [django-requestlogs] - Providing middleware and other helpers for audit logging for REST framework. +* [drf-standardized-errors][drf-standardized-errors] - DRF exception handler to standardize error responses for all API endpoints. [cite]: http://www.software-ecosystems.com/Software_Ecosystems/Ecosystems.html [cookiecutter]: https://github.com/jpadilla/cookiecutter-django-rest-framework @@ -239,3 +240,4 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [rest-framework-actions]: https://github.com/AlexisMunera98/rest-framework-actions [fast-drf]: https://github.com/iashraful/fast-drf [django-requestlogs]: https://github.com/Raekkeri/django-requestlogs +[drf-standardized-errors]: https://github.com/ghazi-git/drf-standardized-errors From 281fc074ba255ed9c5724cc971fa86c78d4dca38 Mon Sep 17 00:00:00 2001 From: Krukov D Date: Mon, 6 Jun 2022 14:54:57 +0300 Subject: [PATCH 0881/1262] improve performance for noncallble attributes (#8502) Co-authored-by: Dima Kryukov --- rest_framework/fields.py | 3 +++ tests/test_fields.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 8d02b3206e..31e5b994cc 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -62,6 +62,9 @@ def is_simple_callable(obj): """ True if the object is a callable that takes no arguments. """ + if not callable(obj): + return False + # Bail early since we cannot inspect built-in function signatures. if inspect.isbuiltin(obj): raise BuiltinSignatureError( diff --git a/tests/test_fields.py b/tests/test_fields.py index ec121c822c..19f7345138 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -75,6 +75,10 @@ def invalid(param, param2='value'): assert is_simple_callable(valid_vargs_kwargs) assert not is_simple_callable(invalid) + @pytest.mark.parametrize('obj', (True, None, "str", b'bytes', 123, 1.23)) + def test_not_callable(self, obj): + assert not is_simple_callable(obj) + def test_4602_regression(self): from django.db import models From bb7dcef19bceae1dd43c19d5a9d9dddc75868c83 Mon Sep 17 00:00:00 2001 From: Daniel Gilge <33256939+dgilge@users.noreply.github.com> Date: Mon, 6 Jun 2022 14:31:00 +0200 Subject: [PATCH 0882/1262] Update get_schema in docs (#7402) --- docs/api-guide/schemas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index b9de6745fe..5bdf5ee542 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -165,7 +165,7 @@ In order to customize the top-level schema, subclass as an argument to the `generateschema` command or `get_schema_view()` helper function. -### get_schema(self, request) +### get_schema(self, request=None, public=False) Returns a dictionary that represents the OpenAPI schema: From 563a20a04098937db1e2c353b2ee6a3059e92ebf Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Mon, 6 Jun 2022 08:44:02 -0400 Subject: [PATCH 0883/1262] make get_reference public (#7515) --- docs/api-guide/schemas.md | 5 +++++ rest_framework/schemas/openapi.py | 14 +++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 5bdf5ee542..004e2d3ce9 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -313,6 +313,11 @@ Computes the component's name from the serializer. You may see warnings if your API has duplicate component names. If so you can override `get_component_name()` or pass the `component_name` `__init__()` kwarg (see below) to provide different names. +#### `get_reference()` + +Returns a reference to the serializer component. This may be useful if you override `get_schema()`. + + #### `map_serializer()` Maps serializers to their OpenAPI representations. diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 5e9d59f8bf..122846376f 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -636,7 +636,7 @@ def get_response_serializer(self, path, method): """ return self.get_serializer(path, method) - def _get_reference(self, serializer): + def get_reference(self, serializer): return {'$ref': '#/components/schemas/{}'.format(self.get_component_name(serializer))} def get_request_body(self, path, method): @@ -650,7 +650,7 @@ def get_request_body(self, path, method): if not isinstance(serializer, serializers.Serializer): item_schema = {} else: - item_schema = self._get_reference(serializer) + item_schema = self.get_reference(serializer) return { 'content': { @@ -674,7 +674,7 @@ def get_responses(self, path, method): if not isinstance(serializer, serializers.Serializer): item_schema = {} else: - item_schema = self._get_reference(serializer) + item_schema = self.get_reference(serializer) if is_list_view(path, method, self.view): response_schema = { @@ -808,3 +808,11 @@ def _allows_filters(self, path, method): RemovedInDRF314Warning, stacklevel=2 ) return self.allows_filters(path, method) + + def _get_reference(self, serializer): + warnings.warn( + "Method `_get_reference()` has been renamed to `get_reference()`. " + "The old name will be removed in DRF v3.14.", + RemovedInDRF314Warning, stacklevel=2 + ) + return self.get_reference(serializer) From 1396f6886a39acb7fe52729c7b99fe2d7d245dac Mon Sep 17 00:00:00 2001 From: Grigory Date: Mon, 6 Jun 2022 15:53:42 +0300 Subject: [PATCH 0884/1262] Respect model error_messages for relation (#7599) --- rest_framework/utils/field_mapping.py | 22 ++++++++++++++-------- tests/test_validators.py | 13 +++++++++++++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index 4f8a4f1926..673821b5e1 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -217,15 +217,9 @@ def get_field_kwargs(field_name, model_field): ] if getattr(model_field, 'unique', False): - unique_error_message = model_field.error_messages.get('unique', None) - if unique_error_message: - unique_error_message = unique_error_message % { - 'model_name': model_field.model._meta.verbose_name, - 'field_label': model_field.verbose_name - } validator = UniqueValidator( queryset=model_field.model._default_manager, - message=unique_error_message) + message=get_unique_error_message(model_field)) validator_kwarg.append(validator) if validator_kwarg: @@ -281,7 +275,9 @@ def get_relation_kwargs(field_name, relation_info): if model_field.validators: kwargs['validators'] = model_field.validators if getattr(model_field, 'unique', False): - validator = UniqueValidator(queryset=model_field.model._default_manager) + validator = UniqueValidator( + queryset=model_field.model._default_manager, + message=get_unique_error_message(model_field)) kwargs['validators'] = kwargs.get('validators', []) + [validator] if to_many and not model_field.blank: kwargs['allow_empty'] = False @@ -300,3 +296,13 @@ def get_url_kwargs(model_field): return { 'view_name': get_detail_view_name(model_field) } + + +def get_unique_error_message(model_field): + unique_error_message = model_field.error_messages.get('unique', None) + if unique_error_message: + unique_error_message = unique_error_message % { + 'model_name': model_field.model._meta.verbose_name, + 'field_label': model_field.verbose_name + } + return unique_error_message diff --git a/tests/test_validators.py b/tests/test_validators.py index bccbe1514b..39490ac863 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -42,6 +42,12 @@ class Meta: fields = ('username', 'email') +class RelatedModelUserSerializer(serializers.ModelSerializer): + class Meta: + model = RelatedModel + fields = ('user',) + + class AnotherUniquenessModel(models.Model): code = models.IntegerField(unique=True) @@ -83,6 +89,13 @@ def test_is_not_unique(self): assert not serializer.is_valid() assert serializer.errors == {'username': ['uniqueness model with this username already exists.']} + def test_relation_is_not_unique(self): + RelatedModel.objects.create(user=self.instance) + data = {'user': self.instance.pk} + serializer = RelatedModelUserSerializer(data=data) + assert not serializer.is_valid() + assert serializer.errors == {'user': ['related model with this user already exists.']} + def test_is_unique(self): data = {'username': 'other'} serializer = UniquenessSerializer(data=data) From f8a03b096be27b4caac25dc119ccda81ad678d4f Mon Sep 17 00:00:00 2001 From: Patrick Daley Date: Tue, 7 Jun 2022 23:35:01 +1200 Subject: [PATCH 0885/1262] Remove pluralisation from basenames (#8517) HyperlinkIdentityFields in serializers reference 'snippet-highlight' and 'snippet-detail', router basenames updated to match. --- docs/tutorial/6-viewsets-and-routers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md index 18066f0563..e12becbd06 100644 --- a/docs/tutorial/6-viewsets-and-routers.md +++ b/docs/tutorial/6-viewsets-and-routers.md @@ -112,8 +112,8 @@ Here's our re-wired `snippets/urls.py` file. # Create a router and register our viewsets with it. router = DefaultRouter() - router.register(r'snippets', views.SnippetViewSet,basename="snippets") - router.register(r'users', views.UserViewSet,basename="users") + router.register(r'snippets', views.SnippetViewSet,basename="snippet") + router.register(r'users', views.UserViewSet,basename="user") # The API URLs are now determined automatically by the router. urlpatterns = [ From e7af8d662bf837e4fee844b28606cda63c0d30a9 Mon Sep 17 00:00:00 2001 From: itsdkey Date: Wed, 8 Jun 2022 14:41:26 +0200 Subject: [PATCH 0886/1262] tests for #5127 (#7715) * tests for #5127 * Resolves #5127 --- tests/browsable_api/no_auth_urls.py | 3 ++- tests/browsable_api/serializers.py | 8 +++++++ tests/browsable_api/test_browsable_api.py | 27 +++++++++++++++++++++++ tests/browsable_api/views.py | 22 ++++++++++++++++++ tests/models.py | 5 +++++ 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 tests/browsable_api/serializers.py diff --git a/tests/browsable_api/no_auth_urls.py b/tests/browsable_api/no_auth_urls.py index 65701c0653..33491ad926 100644 --- a/tests/browsable_api/no_auth_urls.py +++ b/tests/browsable_api/no_auth_urls.py @@ -1,7 +1,8 @@ from django.urls import path -from .views import MockView +from .views import BasicModelWithUsersViewSet, MockView urlpatterns = [ path('', MockView.as_view()), + path('basicviewset', BasicModelWithUsersViewSet.as_view({'get': 'list'})), ] diff --git a/tests/browsable_api/serializers.py b/tests/browsable_api/serializers.py new file mode 100644 index 0000000000..e8a1cdef85 --- /dev/null +++ b/tests/browsable_api/serializers.py @@ -0,0 +1,8 @@ +from rest_framework.serializers import ModelSerializer +from tests.models import BasicModelWithUsers + + +class BasicSerializer(ModelSerializer): + class Meta: + model = BasicModelWithUsers + fields = '__all__' diff --git a/tests/browsable_api/test_browsable_api.py b/tests/browsable_api/test_browsable_api.py index 17644c2ac7..a76d11fe35 100644 --- a/tests/browsable_api/test_browsable_api.py +++ b/tests/browsable_api/test_browsable_api.py @@ -1,8 +1,35 @@ from django.contrib.auth.models import User from django.test import TestCase, override_settings +from rest_framework.permissions import IsAuthenticated from rest_framework.test import APIClient +from .views import BasicModelWithUsersViewSet, OrganizationPermissions + + +@override_settings(ROOT_URLCONF='tests.browsable_api.no_auth_urls') +class AnonymousUserTests(TestCase): + """Tests correct handling of anonymous user request on endpoints with IsAuthenticated permission class.""" + + def setUp(self): + self.client = APIClient(enforce_csrf_checks=True) + + def tearDown(self): + self.client.logout() + + def test_get_raises_typeerror_when_anonymous_user_in_queryset_filter(self): + with self.assertRaises(TypeError): + self.client.get('/basicviewset') + + def test_get_returns_http_forbidden_when_anonymous_user(self): + old_permissions = BasicModelWithUsersViewSet.permission_classes + BasicModelWithUsersViewSet.permission_classes = [IsAuthenticated, OrganizationPermissions] + + response = self.client.get('/basicviewset') + + BasicModelWithUsersViewSet.permission_classes = old_permissions + self.assertEqual(response.status_code, 403) + @override_settings(ROOT_URLCONF='tests.browsable_api.auth_urls') class DropdownWithAuthTests(TestCase): diff --git a/tests/browsable_api/views.py b/tests/browsable_api/views.py index e1cf13a1ec..e73967bf8c 100644 --- a/tests/browsable_api/views.py +++ b/tests/browsable_api/views.py @@ -1,6 +1,16 @@ from rest_framework import authentication, renderers +from rest_framework.permissions import BasePermission from rest_framework.response import Response from rest_framework.views import APIView +from rest_framework.viewsets import ModelViewSet + +from ..models import BasicModelWithUsers +from .serializers import BasicSerializer + + +class OrganizationPermissions(BasePermission): + def has_object_permission(self, request, view, obj): + return request.user.is_staff or (request.user == obj.owner.organization_user.user) class MockView(APIView): @@ -9,3 +19,15 @@ class MockView(APIView): def get(self, request): return Response({'a': 1, 'b': 2, 'c': 3}) + + +class BasicModelWithUsersViewSet(ModelViewSet): + queryset = BasicModelWithUsers.objects.all() + serializer_class = BasicSerializer + permission_classes = [OrganizationPermissions] + # permission_classes = [IsAuthenticated, OrganizationPermissions] + renderer_classes = (renderers.BrowsableAPIRenderer, renderers.JSONRenderer) + + def get_queryset(self): + qs = super().get_queryset().filter(users=self.request.user) + return qs diff --git a/tests/models.py b/tests/models.py index afe649760a..666e9f0031 100644 --- a/tests/models.py +++ b/tests/models.py @@ -1,5 +1,6 @@ import uuid +from django.contrib.auth.models import User from django.db import models from django.utils.translation import gettext_lazy as _ @@ -33,6 +34,10 @@ class ManyToManySource(RESTFrameworkModel): targets = models.ManyToManyField(ManyToManyTarget, related_name='sources') +class BasicModelWithUsers(RESTFrameworkModel): + users = models.ManyToManyField(User) + + # ForeignKey class ForeignKeyTarget(RESTFrameworkModel): name = models.CharField(max_length=100) From 26830c3d2d45a60385d1166c37a031e2e75cf858 Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Wed, 8 Jun 2022 16:37:46 +0300 Subject: [PATCH 0887/1262] Fix QueryDict type error in test (#8475) QueryDict takes a `str` argument. Discovered while working on djangorestframework-stubs. --- tests/test_fields.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_fields.py b/tests/test_fields.py index 19f7345138..cbec79119a 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1865,9 +1865,9 @@ class TestMultipleChoiceField(FieldValues): def test_against_partial_and_full_updates(self): field = serializers.MultipleChoiceField(choices=(('a', 'a'), ('b', 'b'))) field.partial = False - assert field.get_value(QueryDict({})) == [] + assert field.get_value(QueryDict('')) == [] field.partial = True - assert field.get_value(QueryDict({})) == rest_framework.fields.empty + assert field.get_value(QueryDict('')) == rest_framework.fields.empty class TestEmptyMultipleChoiceField(FieldValues): From 5185cc934862a5ab13316c85402c12a6d744f94c Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 8 Jun 2022 15:46:19 +0200 Subject: [PATCH 0888/1262] Handle unset fields with 'many=True' (#7574) * Handle unset fields with 'many=True' The docs note: When serializing fields with dotted notation, it may be necessary to provide a `default` value if any object is not present or is empty during attribute traversal. However, this doesn't work for fields with 'many=True'. When using these, the default is simply ignored. The solution is simple: do in 'ManyRelatedField' what we were already doing for 'Field', namely, catch possible 'AttributeError' and 'KeyError' exceptions and return the default if there is one set. Signed-off-by: Stephen Finucane Closes: #7550 * Add test cases for #7550 Signed-off-by: Stephen Finucane --- rest_framework/relations.py | 27 +++++++++++++- tests/test_model_serializer.py | 67 ++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/rest_framework/relations.py b/rest_framework/relations.py index c987007842..bdedd43b86 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -10,7 +10,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework.fields import ( - Field, empty, get_attribute, is_simple_callable, iter_options + Field, SkipField, empty, get_attribute, is_simple_callable, iter_options ) from rest_framework.reverse import reverse from rest_framework.settings import api_settings @@ -535,7 +535,30 @@ def get_attribute(self, instance): if hasattr(instance, 'pk') and instance.pk is None: return [] - relationship = get_attribute(instance, self.source_attrs) + try: + relationship = get_attribute(instance, self.source_attrs) + except (KeyError, AttributeError) as exc: + if self.default is not empty: + return self.get_default() + if self.allow_null: + return None + if not self.required: + raise SkipField() + msg = ( + 'Got {exc_type} when attempting to get a value for field ' + '`{field}` on serializer `{serializer}`.\nThe serializer ' + 'field might be named incorrectly and not match ' + 'any attribute or key on the `{instance}` instance.\n' + 'Original exception text was: {exc}.'.format( + exc_type=type(exc).__name__, + field=self.field_name, + serializer=self.parent.__class__.__name__, + instance=instance.__class__.__name__, + exc=exc + ) + ) + raise type(exc)(msg) + return relationship.all() if hasattr(relationship, 'all') else relationship def to_representation(self, iterable): diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index abb4830d14..419eae632b 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -1025,6 +1025,73 @@ class Meta: assert serializer.data == expected +class Issue7550FooModel(models.Model): + text = models.CharField(max_length=100) + bar = models.ForeignKey( + 'Issue7550BarModel', null=True, blank=True, on_delete=models.SET_NULL, + related_name='foos', related_query_name='foo') + + +class Issue7550BarModel(models.Model): + pass + + +class Issue7550TestCase(TestCase): + + def test_dotted_source(self): + + class _FooSerializer(serializers.ModelSerializer): + class Meta: + model = Issue7550FooModel + fields = ('id', 'text') + + class FooSerializer(serializers.ModelSerializer): + other_foos = _FooSerializer(source='bar.foos', many=True) + + class Meta: + model = Issue7550BarModel + fields = ('id', 'other_foos') + + bar = Issue7550BarModel.objects.create() + foo_a = Issue7550FooModel.objects.create(bar=bar, text='abc') + foo_b = Issue7550FooModel.objects.create(bar=bar, text='123') + + assert FooSerializer(foo_a).data == { + 'id': foo_a.id, + 'other_foos': [ + { + 'id': foo_a.id, + 'text': foo_a.text, + }, + { + 'id': foo_b.id, + 'text': foo_b.text, + }, + ], + } + + def test_dotted_source_with_default(self): + + class _FooSerializer(serializers.ModelSerializer): + class Meta: + model = Issue7550FooModel + fields = ('id', 'text') + + class FooSerializer(serializers.ModelSerializer): + other_foos = _FooSerializer(source='bar.foos', default=[], many=True) + + class Meta: + model = Issue7550FooModel + fields = ('id', 'other_foos') + + foo = Issue7550FooModel.objects.create(bar=None, text='abc') + + assert FooSerializer(foo).data == { + 'id': foo.id, + 'other_foos': [], + } + + class DecimalFieldModel(models.Model): decimal_field = models.DecimalField( max_digits=3, From 82475c232b531e075fe7666d808f9e68d052d35b Mon Sep 17 00:00:00 2001 From: Alessandro <9991341+alessandrosp@users.noreply.github.com> Date: Wed, 8 Jun 2022 23:03:00 +0900 Subject: [PATCH 0889/1262] Made relative URLs clickable as well. (#8464) --- rest_framework/templatetags/rest_framework.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index db0e9c95c3..ccd9430b4e 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -218,7 +218,7 @@ def format_value(value): return template.render(context) elif isinstance(value, str): if ( - (value.startswith('http:') or value.startswith('https:')) and not + (value.startswith('http:') or value.startswith('https:') or value.startswith('/')) and not re.search(r'\s', value) ): return mark_safe('{value}'.format(value=escape(value))) From 2506d0b4f2ac8bdbf35d33b3dd8a56f3e8d0da75 Mon Sep 17 00:00:00 2001 From: Burak Kadir Er Date: Thu, 9 Jun 2022 17:30:47 +0300 Subject: [PATCH 0890/1262] Update include and namespace URLs (#8520) --- docs/api-guide/routers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md index 8d8594eeea..70c05fdde7 100644 --- a/docs/api-guide/routers.md +++ b/docs/api-guide/routers.md @@ -338,5 +338,5 @@ The [`DRF-extensions` package][drf-extensions] provides [routers][drf-extensions [drf-extensions-nested-viewsets]: https://chibisov.github.io/drf-extensions/docs/#nested-routes [drf-extensions-collection-level-controllers]: https://chibisov.github.io/drf-extensions/docs/#collection-level-controllers [drf-extensions-customizable-endpoint-names]: https://chibisov.github.io/drf-extensions/docs/#controller-endpoint-name -[url-namespace-docs]: https://docs.djangoproject.com/en/1.11/topics/http/urls/#url-namespaces -[include-api-reference]: https://docs.djangoproject.com/en/2.0/ref/urls/#include +[url-namespace-docs]: https://docs.djangoproject.com/en/4.0/topics/http/urls/#url-namespaces +[include-api-reference]: https://docs.djangoproject.com/en/4.0/ref/urls/#include From fa9d516ee24b2447a351af92e139d386b4f0d2b4 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 20 Jun 2022 10:44:27 +0100 Subject: [PATCH 0891/1262] Update docstring test for more recent pygments version (#8530) * Update docstring test for more recent pygments version * Drop unused import --- requirements/requirements-optionals.txt | 5 ++-- tests/test_description.py | 31 ++++--------------------- 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt index 75b9ab4d60..f3bb9b709d 100644 --- a/requirements/requirements-optionals.txt +++ b/requirements/requirements-optionals.txt @@ -3,8 +3,7 @@ coreapi==2.3.1 coreschema==0.0.4 django-filter>=2.4.0,<3.0 django-guardian>=2.4.0,<2.5 -markdown==3.3;python_version>="3.6" -markdown==3.2.2;python_version=="3.5" +markdown==3.3 psycopg2-binary>=2.8.5,<2.9 -pygments>=2.7.1,<2.8 +pygments==2.12 pyyaml>=5.3.1,<5.4 diff --git a/tests/test_description.py b/tests/test_description.py index 363ad6513e..ecc6b9776d 100644 --- a/tests/test_description.py +++ b/tests/test_description.py @@ -1,5 +1,3 @@ -import sys - import pytest from django.test import TestCase @@ -33,7 +31,7 @@ # 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

    +MARKDOWN_DOCSTRING = """

    an example docstring

    ","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),v.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
    ",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(v.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return B(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=_e(v.pixelPosition,function(e,t){if(t)return t=Be(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return B(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0{{ error_title }} "csrfToken": "{{ csrf_token }}" } - + diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index 533db13780..53c964f231 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -293,7 +293,7 @@

    {{ name }}

    "csrfToken": "{% if request %}{{ csrf_token }}{% endif %}" } - + diff --git a/rest_framework/templates/rest_framework/docs/error.html b/rest_framework/templates/rest_framework/docs/error.html index 694f88a150..8e67238b9e 100644 --- a/rest_framework/templates/rest_framework/docs/error.html +++ b/rest_framework/templates/rest_framework/docs/error.html @@ -66,6 +66,6 @@

    Overriding this template

    - + diff --git a/rest_framework/templates/rest_framework/docs/index.html b/rest_framework/templates/rest_framework/docs/index.html index dfd363772a..8f8536fbeb 100644 --- a/rest_framework/templates/rest_framework/docs/index.html +++ b/rest_framework/templates/rest_framework/docs/index.html @@ -38,7 +38,7 @@ {% include "rest_framework/docs/auth/basic.html" %} {% include "rest_framework/docs/auth/session.html" %} - + From 59430111bdecf9e2c7488d599c18a91d5ac89240 Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Tue, 28 Mar 2023 16:08:27 +0600 Subject: [PATCH 1009/1262] Update tox with django 4.2rc1 (#8920) --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 7027612e04..10b52063ed 100644 --- a/tox.ini +++ b/tox.ini @@ -21,7 +21,7 @@ deps = django32: Django>=3.2,<4.0 django40: Django>=4.0,<4.1 django41: Django>=4.1,<4.2 - django42: Django>=4.2b1,<5.0 + django42: Django>=4.2rc1,<5.0 djangomain: https://github.com/django/django/archive/main.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From 6b73acc1735631df1e666cc87eee0d14de6ae018 Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Tue, 28 Mar 2023 16:35:44 +0600 Subject: [PATCH 1010/1262] Update requirements-packaging.txt (#8921) --- requirements/requirements-packaging.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/requirements-packaging.txt b/requirements/requirements-packaging.txt index a9733185bc..81f22a35a1 100644 --- a/requirements/requirements-packaging.txt +++ b/requirements/requirements-packaging.txt @@ -1,8 +1,8 @@ # Wheel for PyPI installs. -wheel>=0.35.1,<0.36 +wheel>=0.36.2,<0.40.0 # Twine for secured PyPI uploads. -twine>=3.2.0,<3.3 +twine>=3.4.2,<4.0.2 # Transifex client for managing translation resources. transifex-client From b60fbf3374efcf0abfccb10597736329f891d4fc Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Mon, 3 Apr 2023 22:35:11 +0600 Subject: [PATCH 1011/1262] test django 4.2 stable release (#8932) --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 10b52063ed..2b8733d7df 100644 --- a/tox.ini +++ b/tox.ini @@ -21,7 +21,7 @@ deps = django32: Django>=3.2,<4.0 django40: Django>=4.0,<4.1 django41: Django>=4.1,<4.2 - django42: Django>=4.2rc1,<5.0 + django42: Django>=4.2,<5.0 djangomain: https://github.com/django/django/archive/main.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From 3428cec194060f9896be90f00f2d316557acd00f Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Tue, 4 Apr 2023 09:38:23 +0200 Subject: [PATCH 1012/1262] Use consistent spelling for "authorization" (#8929) Apart from a few exceptions, django-rest-framework uses the American English spelling "authorization"/"authorized". $ git grep -oi authorised | wc -l 2 $ git grep -oi authorized | wc -l 30 Replace the few occurences of the British English spelling with the American English one. --- docs/api-guide/permissions.md | 2 +- rest_framework/templates/rest_framework/docs/error.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 27f7c5adb4..e70cc63be5 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -165,7 +165,7 @@ This permission is suitable if you want your API to only be accessible to a subs ## IsAuthenticatedOrReadOnly -The `IsAuthenticatedOrReadOnly` will allow authenticated users to perform any request. Requests for unauthorised users will only be permitted if the request method is one of the "safe" methods; `GET`, `HEAD` or `OPTIONS`. +The `IsAuthenticatedOrReadOnly` will allow authenticated users to perform any request. Requests for unauthorized users will only be permitted if the request method is one of the "safe" methods; `GET`, `HEAD` or `OPTIONS`. This permission is suitable if you want to your API to allow read permissions to anonymous users, and only allow write permissions to authenticated users. diff --git a/rest_framework/templates/rest_framework/docs/error.html b/rest_framework/templates/rest_framework/docs/error.html index 8e67238b9e..0c369e9e8e 100644 --- a/rest_framework/templates/rest_framework/docs/error.html +++ b/rest_framework/templates/rest_framework/docs/error.html @@ -30,7 +30,7 @@

    Additional Information

    Your response status code is: {{ response.status_code }}

    -

    401 Unauthorised.

    +

    401 Unauthorized.

    • Do you have SessionAuthentication enabled?
    • Are you logged in?
    • From ea03e95174f46003e7e917b623c5316247b8b316 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Tue, 4 Apr 2023 10:44:59 +0200 Subject: [PATCH 1013/1262] docs: Fix authentication / authorization mixup (#8930) `IsAuthenticatedOrReadOnly` authorizes users that are not authenticated for read-only access to resources. Therefore, they are unauthenticated users, not unauthorized users. --- docs/api-guide/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index e70cc63be5..5e0b6a153d 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -165,7 +165,7 @@ This permission is suitable if you want your API to only be accessible to a subs ## IsAuthenticatedOrReadOnly -The `IsAuthenticatedOrReadOnly` will allow authenticated users to perform any request. Requests for unauthorized users will only be permitted if the request method is one of the "safe" methods; `GET`, `HEAD` or `OPTIONS`. +The `IsAuthenticatedOrReadOnly` will allow authenticated users to perform any request. Requests for unauthenticated users will only be permitted if the request method is one of the "safe" methods; `GET`, `HEAD` or `OPTIONS`. This permission is suitable if you want to your API to allow read permissions to anonymous users, and only allow write permissions to authenticated users. From 959085c1455a7075a8c237a0283c2a6e35dfcd76 Mon Sep 17 00:00:00 2001 From: Arnab Kumar Shil Date: Sat, 8 Apr 2023 08:27:14 +0200 Subject: [PATCH 1014/1262] Handle Nested Relation in SlugRelatedField when many=False (#8922) * Update relations.py Currently if you define the slug field as a nested relationship in a `SlugRelatedField` while many=False, it will cause an attribute error. For example: For this code: ``` class SomeSerializer(serializers.ModelSerializer): some_field= serializers.SlugRelatedField(queryset=SomeClass.objects.all(), slug_field="foo__bar") ``` The POST request (or save operation) should work just fine, but if you use GET, then it will fail with Attribute error: > AttributeError: 'SomeClass' object has no attribute 'foo__bar' Thus I am handling nested relation here. Reference: https://stackoverflow.com/questions/75878103/drf-attributeerror-when-trying-to-creating-a-instance-with-slugrelatedfield-and/75882424#75882424 * Fixed test cases * code comment changes related to slugrelatedfield * changes based on pre-commit and removed comma which was added accidentally * fixed primary keys of the mock object * added more test cases based on review --------- Co-authored-by: Arnab Shil --- rest_framework/relations.py | 8 ++- tests/test_relations.py | 136 ++++++++++++++++++++++++++++++++++++ tests/utils.py | 5 +- 3 files changed, 147 insertions(+), 2 deletions(-) diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 62da685fb6..53ea2113b4 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -1,6 +1,7 @@ import contextlib import sys from collections import OrderedDict +from operator import attrgetter from urllib import parse from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist @@ -71,6 +72,7 @@ class PKOnlyObject: instance, but still want to return an object with a .pk attribute, in order to keep the same interface as a regular model instance. """ + def __init__(self, pk): self.pk = pk @@ -464,7 +466,11 @@ def to_internal_value(self, data): self.fail('invalid') def to_representation(self, obj): - return getattr(obj, self.slug_field) + slug = self.slug_field + if "__" in slug: + # handling nested relationship if defined + slug = slug.replace('__', '.') + return attrgetter(slug)(obj) class ManyRelatedField(Field): diff --git a/tests/test_relations.py b/tests/test_relations.py index 7a4db1c487..b9ab157896 100644 --- a/tests/test_relations.py +++ b/tests/test_relations.py @@ -342,6 +342,142 @@ def get_queryset(self): field.to_internal_value(self.instance.name) +class TestNestedSlugRelatedField(APISimpleTestCase): + def setUp(self): + self.queryset = MockQueryset([ + MockObject( + pk=1, name='foo', nested=MockObject( + pk=2, name='bar', nested=MockObject( + pk=7, name="foobar" + ) + ) + ), + MockObject( + pk=3, name='hello', nested=MockObject( + pk=4, name='world', nested=MockObject( + pk=8, name="helloworld" + ) + ) + ), + MockObject( + pk=5, name='harry', nested=MockObject( + pk=6, name='potter', nested=MockObject( + pk=9, name="harrypotter" + ) + ) + ) + ]) + self.instance = self.queryset.items[2] + self.field = serializers.SlugRelatedField( + slug_field='name', queryset=self.queryset + ) + self.nested_field = serializers.SlugRelatedField( + slug_field='nested__name', queryset=self.queryset + ) + + self.nested_nested_field = serializers.SlugRelatedField( + slug_field='nested__nested__name', queryset=self.queryset + ) + + # testing nested inside nested relations + def test_slug_related_nested_nested_lookup_exists(self): + instance = self.nested_nested_field.to_internal_value( + self.instance.nested.nested.name + ) + assert instance is self.instance + + def test_slug_related_nested_nested_lookup_does_not_exist(self): + with pytest.raises(serializers.ValidationError) as excinfo: + self.nested_nested_field.to_internal_value('doesnotexist') + msg = excinfo.value.detail[0] + assert msg == \ + 'Object with nested__nested__name=doesnotexist does not exist.' + + def test_slug_related_nested_nested_lookup_invalid_type(self): + with pytest.raises(serializers.ValidationError) as excinfo: + self.nested_nested_field.to_internal_value(BadType()) + msg = excinfo.value.detail[0] + assert msg == 'Invalid value.' + + def test_nested_nested_representation(self): + representation =\ + self.nested_nested_field.to_representation(self.instance) + assert representation == self.instance.nested.nested.name + + def test_nested_nested_overriding_get_queryset(self): + qs = self.queryset + + class NoQuerySetSlugRelatedField(serializers.SlugRelatedField): + def get_queryset(self): + return qs + + field = NoQuerySetSlugRelatedField(slug_field='nested__nested__name') + field.to_internal_value(self.instance.nested.nested.name) + + # testing nested relations + def test_slug_related_nested_lookup_exists(self): + instance = \ + self.nested_field.to_internal_value(self.instance.nested.name) + assert instance is self.instance + + def test_slug_related_nested_lookup_does_not_exist(self): + with pytest.raises(serializers.ValidationError) as excinfo: + self.nested_field.to_internal_value('doesnotexist') + msg = excinfo.value.detail[0] + assert msg == 'Object with nested__name=doesnotexist does not exist.' + + def test_slug_related_nested_lookup_invalid_type(self): + with pytest.raises(serializers.ValidationError) as excinfo: + self.nested_field.to_internal_value(BadType()) + msg = excinfo.value.detail[0] + assert msg == 'Invalid value.' + + def test_nested_representation(self): + representation = self.nested_field.to_representation(self.instance) + assert representation == self.instance.nested.name + + def test_nested_overriding_get_queryset(self): + qs = self.queryset + + class NoQuerySetSlugRelatedField(serializers.SlugRelatedField): + def get_queryset(self): + return qs + + field = NoQuerySetSlugRelatedField(slug_field='nested__name') + field.to_internal_value(self.instance.nested.name) + + # testing non-nested relations + def test_slug_related_lookup_exists(self): + instance = self.field.to_internal_value(self.instance.name) + assert instance is self.instance + + def test_slug_related_lookup_does_not_exist(self): + with pytest.raises(serializers.ValidationError) as excinfo: + self.field.to_internal_value('doesnotexist') + msg = excinfo.value.detail[0] + assert msg == 'Object with name=doesnotexist does not exist.' + + def test_slug_related_lookup_invalid_type(self): + with pytest.raises(serializers.ValidationError) as excinfo: + self.field.to_internal_value(BadType()) + msg = excinfo.value.detail[0] + assert msg == 'Invalid value.' + + def test_representation(self): + representation = self.field.to_representation(self.instance) + assert representation == self.instance.name + + def test_overriding_get_queryset(self): + qs = self.queryset + + class NoQuerySetSlugRelatedField(serializers.SlugRelatedField): + def get_queryset(self): + return qs + + field = NoQuerySetSlugRelatedField(slug_field='name') + field.to_internal_value(self.instance.name) + + class TestManyRelatedField(APISimpleTestCase): def setUp(self): self.instance = MockObject(pk=1, name='foo') diff --git a/tests/utils.py b/tests/utils.py index 06e5b9abe6..4ceb353099 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,3 +1,5 @@ +from operator import attrgetter + from django.core.exceptions import ObjectDoesNotExist from django.urls import NoReverseMatch @@ -26,7 +28,7 @@ def __getitem__(self, val): def get(self, **lookup): for item in self.items: if all([ - getattr(item, key, None) == value + attrgetter(key.replace('__', '.'))(item) == value for key, value in lookup.items() ]): return item @@ -39,6 +41,7 @@ class BadType: will raise a `TypeError`, as occurs in Django when making queryset lookups with an incorrect type for the lookup value. """ + def __eq__(self): raise TypeError() From 4842ad1b6ae9a2d08cb479c5d254aa3276ea2352 Mon Sep 17 00:00:00 2001 From: Nikita Reznikov <63803175+rnv812@users.noreply.github.com> Date: Sat, 8 Apr 2023 11:56:49 +0300 Subject: [PATCH 1015/1262] Add username search field for TokenAdmin (#8927) (#8934) * Add username search field for TokenAdmin (#8927) * Sort imports in a proper order (#8927) --- rest_framework/authtoken/admin.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rest_framework/authtoken/admin.py b/rest_framework/authtoken/admin.py index e41eb00023..163328eb07 100644 --- a/rest_framework/authtoken/admin.py +++ b/rest_framework/authtoken/admin.py @@ -4,6 +4,7 @@ from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError from django.urls import reverse +from django.utils.translation import gettext_lazy as _ from rest_framework.authtoken.models import Token, TokenProxy @@ -23,6 +24,8 @@ def url_for_result(self, result): class TokenAdmin(admin.ModelAdmin): list_display = ('key', 'user', 'created') fields = ('user',) + search_fields = ('user__username',) + search_help_text = _('Username') ordering = ('-created',) actions = None # Actions not compatible with mapped IDs. autocomplete_fields = ("user",) From 62abf6ac1f20b48809c55f92d086d5f06d4c6c55 Mon Sep 17 00:00:00 2001 From: Maxwell Muoto Date: Sat, 8 Apr 2023 04:16:00 -0500 Subject: [PATCH 1016/1262] Use ZoneInfo as primary source of timezone data (#8924) * Use ZoneInfo as primary source of timezone data * Update tests/test_fields.py --------- Co-authored-by: Asif Saif Uddin --- rest_framework/fields.py | 8 ++++- rest_framework/utils/timezone.py | 25 ++++++++++++++++ setup.py | 2 +- tests/test_fields.py | 51 ++++++++++++++++++++++++-------- 4 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 rest_framework/utils/timezone.py diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 613bd325a6..e41b56fb01 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -35,6 +35,7 @@ from rest_framework.settings import api_settings from rest_framework.utils import html, humanize_datetime, json, representation from rest_framework.utils.formatting import lazy_format +from rest_framework.utils.timezone import valid_datetime from rest_framework.validators import ProhibitSurrogateCharactersValidator @@ -1154,7 +1155,12 @@ def enforce_timezone(self, value): except OverflowError: self.fail('overflow') try: - return timezone.make_aware(value, field_timezone) + dt = timezone.make_aware(value, field_timezone) + # When the resulting datetime is a ZoneInfo instance, it won't necessarily + # throw given an invalid datetime, so we need to specifically check. + if not valid_datetime(dt): + self.fail('make_aware', timezone=field_timezone) + return dt except InvalidTimeError: self.fail('make_aware', timezone=field_timezone) elif (field_timezone is None) and timezone.is_aware(value): diff --git a/rest_framework/utils/timezone.py b/rest_framework/utils/timezone.py new file mode 100644 index 0000000000..3257c8e274 --- /dev/null +++ b/rest_framework/utils/timezone.py @@ -0,0 +1,25 @@ +from datetime import datetime, timezone, tzinfo + + +def datetime_exists(dt): + """Check if a datetime exists. Taken from: https://pytz-deprecation-shim.readthedocs.io/en/latest/migration.html""" + # There are no non-existent times in UTC, and comparisons between + # aware time zones always compare absolute times; if a datetime is + # not equal to the same datetime represented in UTC, it is imaginary. + return dt.astimezone(timezone.utc) == dt + + +def datetime_ambiguous(dt: datetime): + """Check whether a datetime is ambiguous. Taken from: https://pytz-deprecation-shim.readthedocs.io/en/latest/migration.html""" + # If a datetime exists and its UTC offset changes in response to + # changing `fold`, it is ambiguous in the zone specified. + return datetime_exists(dt) and ( + dt.replace(fold=not dt.fold).utcoffset() != dt.utcoffset() + ) + + +def valid_datetime(dt): + """Returns True if the datetime is not ambiguous or imaginary, False otherwise.""" + if isinstance(dt.tzinfo, tzinfo) and not datetime_ambiguous(dt): + return True + return False diff --git a/setup.py b/setup.py index 9a5b272f3b..d9002fdb91 100755 --- a/setup.py +++ b/setup.py @@ -82,7 +82,7 @@ def get_version(package): author_email='tom@tomchristie.com', # SEE NOTE BELOW (*) packages=find_packages(exclude=['tests*']), include_package_data=True, - install_requires=["django>=3.0", "pytz"], + install_requires=["django>=3.0", "pytz", 'backports.zoneinfo;python_version<"3.9"'], python_requires=">=3.6", zip_safe=False, classifiers=[ diff --git a/tests/test_fields.py b/tests/test_fields.py index 56e2a45bad..5804d7b3b3 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -5,6 +5,7 @@ import sys import uuid from decimal import ROUND_DOWN, ROUND_UP, Decimal +from unittest.mock import patch import pytest import pytz @@ -21,6 +22,11 @@ ) from tests.models import UUIDForeignKeyTarget +if sys.version_info >= (3, 9): + from zoneinfo import ZoneInfo +else: + from backports.zoneinfo import ZoneInfo + utc = datetime.timezone.utc # Tests for helper functions. @@ -651,7 +657,7 @@ class FieldValues: """ Base class for testing valid and invalid input values. """ - def test_valid_inputs(self): + def test_valid_inputs(self, *args): """ Ensure that valid values return the expected validated data. """ @@ -659,7 +665,7 @@ def test_valid_inputs(self): assert self.field.run_validation(input_value) == expected_output, \ 'input value: {}'.format(repr(input_value)) - def test_invalid_inputs(self): + def test_invalid_inputs(self, *args): """ Ensure that invalid values raise the expected validation error. """ @@ -669,7 +675,7 @@ def test_invalid_inputs(self): assert exc_info.value.detail == expected_failure, \ 'input value: {}'.format(repr(input_value)) - def test_outputs(self): + def test_outputs(self, *args): for output_value, expected_output in get_items(self.outputs): assert self.field.to_representation(output_value) == expected_output, \ 'output value: {}'.format(repr(output_value)) @@ -1505,12 +1511,12 @@ class TestTZWithDateTimeField(FieldValues): @classmethod def setup_class(cls): # use class setup method, as class-level attribute will still be evaluated even if test is skipped - kolkata = pytz.timezone('Asia/Kolkata') + kolkata = ZoneInfo('Asia/Kolkata') cls.valid_inputs = { - '2016-12-19T10:00:00': kolkata.localize(datetime.datetime(2016, 12, 19, 10)), - '2016-12-19T10:00:00+05:30': kolkata.localize(datetime.datetime(2016, 12, 19, 10)), - datetime.datetime(2016, 12, 19, 10): kolkata.localize(datetime.datetime(2016, 12, 19, 10)), + '2016-12-19T10:00:00': datetime.datetime(2016, 12, 19, 10, tzinfo=kolkata), + '2016-12-19T10:00:00+05:30': datetime.datetime(2016, 12, 19, 10, tzinfo=kolkata), + datetime.datetime(2016, 12, 19, 10): datetime.datetime(2016, 12, 19, 10, tzinfo=kolkata), } cls.invalid_inputs = {} cls.outputs = { @@ -1529,7 +1535,7 @@ class TestDefaultTZDateTimeField(TestCase): @classmethod def setup_class(cls): cls.field = serializers.DateTimeField() - cls.kolkata = pytz.timezone('Asia/Kolkata') + cls.kolkata = ZoneInfo('Asia/Kolkata') def assertUTC(self, tzinfo): """ @@ -1551,18 +1557,17 @@ def test_current_timezone(self): self.assertUTC(self.field.default_timezone()) -@pytest.mark.skipif(pytz is None, reason='pytz not installed') @override_settings(TIME_ZONE='UTC', USE_TZ=True) class TestCustomTimezoneForDateTimeField(TestCase): @classmethod def setup_class(cls): - cls.kolkata = pytz.timezone('Asia/Kolkata') + cls.kolkata = ZoneInfo('Asia/Kolkata') cls.date_format = '%d/%m/%Y %H:%M' def test_should_render_date_time_in_default_timezone(self): field = serializers.DateTimeField(default_timezone=self.kolkata, format=self.date_format) - dt = datetime.datetime(2018, 2, 8, 14, 15, 16, tzinfo=pytz.utc) + dt = datetime.datetime(2018, 2, 8, 14, 15, 16, tzinfo=ZoneInfo("UTC")) with override(self.kolkata): rendered_date = field.to_representation(dt) @@ -1572,7 +1577,8 @@ def test_should_render_date_time_in_default_timezone(self): assert rendered_date == rendered_date_in_timezone -class TestNaiveDayLightSavingTimeTimeZoneDateTimeField(FieldValues): +@pytest.mark.skipif(pytz is None, reason="As Django 4.0 has deprecated pytz, this test should eventually be able to get removed.") +class TestPytzNaiveDayLightSavingTimeTimeZoneDateTimeField(FieldValues): """ Invalid values for `DateTimeField` with datetime in DST shift (non-existing or ambiguous) and timezone with DST. Timezone America/New_York has DST shift from 2017-03-12T02:00:00 to 2017-03-12T03:00:00 and @@ -1596,6 +1602,27 @@ def __str__(self): field = serializers.DateTimeField(default_timezone=MockTimezone()) +@patch('rest_framework.utils.timezone.datetime_ambiguous', return_value=True) +class TestNaiveDayLightSavingTimeTimeZoneDateTimeField(FieldValues): + """ + Invalid values for `DateTimeField` with datetime in DST shift (non-existing or ambiguous) and timezone with DST. + Timezone America/New_York has DST shift from 2017-03-12T02:00:00 to 2017-03-12T03:00:00 and + from 2017-11-05T02:00:00 to 2017-11-05T01:00:00 in 2017. + """ + valid_inputs = {} + invalid_inputs = { + '2017-03-12T02:30:00': ['Invalid datetime for the timezone "America/New_York".'], + '2017-11-05T01:30:00': ['Invalid datetime for the timezone "America/New_York".'] + } + outputs = {} + + class MockZoneInfoTimezone(datetime.tzinfo): + def __str__(self): + return 'America/New_York' + + field = serializers.DateTimeField(default_timezone=MockZoneInfoTimezone()) + + class TestTimeField(FieldValues): """ Valid and invalid values for `TimeField`. From b1cec517ff33d633d3ebcf5794a5f0f0583fabe6 Mon Sep 17 00:00:00 2001 From: ddelange <14880945+ddelange@users.noreply.github.com> Date: Sat, 8 Apr 2023 12:42:28 +0200 Subject: [PATCH 1017/1262] Ensure CursorPagination respects nulls in the ordering field (#8912) * Ensure CursorPagination respects nulls in the ordering field * Lint * Fix pagination tests * Add test_ascending with nulls * Push tests for nulls * Test pass * Add comment * Fix test for django30 --- rest_framework/pagination.py | 16 +++-- tests/test_pagination.py | 134 ++++++++++++++++++++++++++++++++++- 2 files changed, 142 insertions(+), 8 deletions(-) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index f5c5b913b5..34d71f828c 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -10,6 +10,7 @@ from django.core.paginator import InvalidPage from django.core.paginator import Paginator as DjangoPaginator +from django.db.models import Q from django.template import loader from django.utils.encoding import force_str from django.utils.translation import gettext_lazy as _ @@ -620,7 +621,7 @@ def paginate_queryset(self, queryset, request, view=None): queryset = queryset.order_by(*self.ordering) # If we have a cursor with a fixed position then filter by that. - if current_position is not None: + if str(current_position) != 'None': order = self.ordering[0] is_reversed = order.startswith('-') order_attr = order.lstrip('-') @@ -631,7 +632,12 @@ def paginate_queryset(self, queryset, request, view=None): else: kwargs = {order_attr + '__gt': current_position} - queryset = queryset.filter(**kwargs) + filter_query = Q(**kwargs) + # If some records contain a null for the ordering field, don't lose them. + # When reverse ordering, nulls will come last and need to be included. + if (reverse and not is_reversed) or is_reversed: + filter_query |= Q(**{order_attr + '__isnull': True}) + queryset = queryset.filter(filter_query) # If we have an offset cursor then offset the entire page by that amount. # We also always fetch an extra item in order to determine if there is a @@ -704,7 +710,7 @@ def get_next_link(self): # The item in this position and the item following it # have different positions. We can use this position as # our marker. - has_item_with_unique_position = True + has_item_with_unique_position = position is not None break # The item in this position has the same position as the item @@ -757,7 +763,7 @@ def get_previous_link(self): # The item in this position and the item following it # have different positions. We can use this position as # our marker. - has_item_with_unique_position = True + has_item_with_unique_position = position is not None break # The item in this position has the same position as the item @@ -883,7 +889,7 @@ def _get_position_from_instance(self, instance, ordering): attr = instance[field_name] else: attr = getattr(instance, field_name) - return str(attr) + return None if attr is None else str(attr) def get_paginated_response(self, data): return Response(OrderedDict([ diff --git a/tests/test_pagination.py b/tests/test_pagination.py index 2812c4489e..8f9b20a0d4 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -951,17 +951,24 @@ class MockQuerySet: def __init__(self, items): self.items = items - def filter(self, created__gt=None, created__lt=None): + def filter(self, q): + q_args = dict(q.deconstruct()[1]) + if not q_args: + # django 3.0.x artifact + q_args = dict(q.deconstruct()[2]) + created__gt = q_args.get('created__gt') + created__lt = q_args.get('created__lt') + if created__gt is not None: return MockQuerySet([ item for item in self.items - if item.created > int(created__gt) + if item.created is None or item.created > int(created__gt) ]) assert created__lt is not None return MockQuerySet([ item for item in self.items - if item.created < int(created__lt) + if item.created is None or item.created < int(created__lt) ]) def order_by(self, *ordering): @@ -1080,6 +1087,127 @@ def get_pages(self, url): return (previous, current, next, previous_url, next_url) +class NullableCursorPaginationModel(models.Model): + created = models.IntegerField(null=True) + + +class TestCursorPaginationWithNulls(TestCase): + """ + Unit tests for `pagination.CursorPagination` with ordering on a nullable field. + """ + + def setUp(self): + class ExamplePagination(pagination.CursorPagination): + page_size = 1 + ordering = 'created' + + self.pagination = ExamplePagination() + data = [ + None, None, 3, 4 + ] + for idx in data: + NullableCursorPaginationModel.objects.create(created=idx) + + self.queryset = NullableCursorPaginationModel.objects.all() + + get_pages = TestCursorPagination.get_pages + + def test_ascending(self): + """Test paginating one row at a time, current should go 1, 2, 3, 4, 3, 2, 1.""" + (previous, current, next, previous_url, next_url) = self.get_pages('/') + + assert previous is None + assert current == [None] + assert next == [None] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [None] + assert current == [None] + assert next == [3] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [3] # [None] paging artifact documented at https://github.com/ddelange/django-rest-framework/blob/3.14.0/rest_framework/pagination.py#L789 + assert current == [3] + assert next == [4] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [3] + assert current == [4] + assert next is None + assert next_url is None + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous == [None] + assert current == [3] + assert next == [4] + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous == [None] + assert current == [None] + assert next == [None] # [3] paging artifact documented at https://github.com/ddelange/django-rest-framework/blob/3.14.0/rest_framework/pagination.py#L731 + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous is None + assert current == [None] + assert next == [None] + + def test_descending(self): + """Test paginating one row at a time, current should go 4, 3, 2, 1, 2, 3, 4.""" + self.pagination.ordering = ('-created',) + (previous, current, next, previous_url, next_url) = self.get_pages('/') + + assert previous is None + assert current == [4] + assert next == [3] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [None] # [4] paging artifact + assert current == [3] + assert next == [None] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [None] # [3] paging artifact + assert current == [None] + assert next == [None] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [None] + assert current == [None] + assert next is None + assert next_url is None + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous == [3] + assert current == [None] + assert next == [None] + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous == [None] + assert current == [3] + assert next == [3] # [4] paging artifact documented at https://github.com/ddelange/django-rest-framework/blob/3.14.0/rest_framework/pagination.py#L731 + + # skip back artifact + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous is None + assert current == [4] + assert next == [3] + + def test_get_displayed_page_numbers(): """ Test our contextual page display function. From 0d6ef034d2eed788f4fe6f9721148bf3874802ec Mon Sep 17 00:00:00 2001 From: Maxwell Muoto Date: Sun, 9 Apr 2023 03:53:47 -0500 Subject: [PATCH 1018/1262] Implement `__eq__` for validators (#8925) * Implement equality operator and add test coverage * Add documentation on implementation --- docs/api-guide/validators.md | 2 +- rest_framework/validators.py | 37 ++++++++++++++++++++++++++++++++++++ tests/test_validators.py | 11 +++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index bb8466a2c8..dac937d9bd 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -53,7 +53,7 @@ If we open up the Django shell using `manage.py shell` we can now The interesting bit here is the `reference` field. We can see that the uniqueness constraint is being explicitly enforced by a validator on the serializer field. -Because of this more explicit style REST framework includes a few validator classes that are not available in core Django. These classes are detailed below. +Because of this more explicit style REST framework includes a few validator classes that are not available in core Django. These classes are detailed below. REST framework validators, like their Django counterparts, implement the `__eq__` method, allowing you to compare instances for equality. --- diff --git a/rest_framework/validators.py b/rest_framework/validators.py index a5cb75a84a..07ad11b47b 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -79,6 +79,15 @@ def __repr__(self): smart_repr(self.queryset) ) + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return (self.message == other.message + and self.requires_context == other.requires_context + and self.queryset == other.queryset + and self.lookup == other.lookup + ) + class UniqueTogetherValidator: """ @@ -166,6 +175,16 @@ def __repr__(self): smart_repr(self.fields) ) + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return (self.message == other.message + and self.requires_context == other.requires_context + and self.missing_message == other.missing_message + and self.queryset == other.queryset + and self.fields == other.fields + ) + class ProhibitSurrogateCharactersValidator: message = _('Surrogate characters are not allowed: U+{code_point:X}.') @@ -177,6 +196,13 @@ def __call__(self, value): message = self.message.format(code_point=ord(surrogate_character)) raise ValidationError(message, code=self.code) + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return (self.message == other.message + and self.code == other.code + ) + class BaseUniqueForValidator: message = None @@ -230,6 +256,17 @@ def __call__(self, attrs, serializer): self.field: message }, code='unique') + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return (self.message == other.message + and self.missing_message == other.missing_message + and self.requires_context == other.requires_context + and self.queryset == other.queryset + and self.field == other.field + and self.date_field == other.date_field + ) + def __repr__(self): return '<%s(queryset=%s, field=%s, date_field=%s)>' % ( self.__class__.__name__, diff --git a/tests/test_validators.py b/tests/test_validators.py index 35fef6f26c..1cf42ed07e 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -1,4 +1,5 @@ import datetime +from unittest.mock import MagicMock import pytest from django.db import DataError, models @@ -787,3 +788,13 @@ def test_validator_raises_error_when_abstract_method_called(self): validator.filter_queryset( attrs=None, queryset=None, field_name='', date_field_name='' ) + + def test_equality_operator(self): + mock_queryset = MagicMock() + validator = BaseUniqueForValidator(queryset=mock_queryset, field='foo', + date_field='bar') + validator2 = BaseUniqueForValidator(queryset=mock_queryset, field='foo', + date_field='bar') + assert validator == validator2 + validator2.date_field = "bar2" + assert validator != validator2 From 684522807f370d6dca731d1d34bffeaa051fe505 Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Thu, 13 Apr 2023 21:48:45 +0600 Subject: [PATCH 1019/1262] test codecov gha (#8946) --- .github/workflows/main.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f245f69644..e3435208b0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,7 +9,7 @@ on: jobs: tests: name: Python ${{ matrix.python-version }} - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: @@ -34,7 +34,7 @@ jobs: run: python -m pip install --upgrade pip setuptools virtualenv wheel - name: Install dependencies - run: python -m pip install --upgrade codecov tox + run: python -m pip install --upgrade tox - name: Install tox-py if: ${{ matrix.python-version == '3.6' }} @@ -54,5 +54,8 @@ jobs: tox -e base,dist,docs - name: Upload coverage - run: | - codecov -e TOXENV,DJANGO + - uses: codecov/codecov-action@v3.1.0 + with: + flags: unittests # optional + fail_ci_if_error: true # optional (default = false) + verbose: true # optional (default = false) From 38a74b42da10576857d6bf8bd82a73b15d12a7ed Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Sat, 15 Apr 2023 12:11:35 +0600 Subject: [PATCH 1020/1262] Revert "test codecov gha (#8946)" (#8947) This reverts commit 684522807f370d6dca731d1d34bffeaa051fe505. --- .github/workflows/main.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e3435208b0..f245f69644 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,7 +9,7 @@ on: jobs: tests: name: Python ${{ matrix.python-version }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-20.04 strategy: matrix: @@ -34,7 +34,7 @@ jobs: run: python -m pip install --upgrade pip setuptools virtualenv wheel - name: Install dependencies - run: python -m pip install --upgrade tox + run: python -m pip install --upgrade codecov tox - name: Install tox-py if: ${{ matrix.python-version == '3.6' }} @@ -54,8 +54,5 @@ jobs: tox -e base,dist,docs - name: Upload coverage - - uses: codecov/codecov-action@v3.1.0 - with: - flags: unittests # optional - fail_ci_if_error: true # optional (default = false) - verbose: true # optional (default = false) + run: | + codecov -e TOXENV,DJANGO From 1ce0853ac51635526dc1a285e6b83c9848002f0e Mon Sep 17 00:00:00 2001 From: Mahdi Rahimi <31624047+mahdirahimi1999@users.noreply.github.com> Date: Thu, 27 Apr 2023 07:54:13 +0330 Subject: [PATCH 1021/1262] Refactor get_field_info method to include max_digits and decimal_places attributes in SimpleMetadata class (#8943) * Refactor get_field_info method to include max_digits and decimal_places attributes in SimpleMetadata class * Add new test to check decimal_field_info_type * Update rest_framework/metadata.py --------- Co-authored-by: Mahdi Co-authored-by: Asif Saif Uddin --- rest_framework/metadata.py | 3 ++- tests/test_metadata.py | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/rest_framework/metadata.py b/rest_framework/metadata.py index 015587897d..c400b1b79e 100644 --- a/rest_framework/metadata.py +++ b/rest_framework/metadata.py @@ -124,7 +124,8 @@ def get_field_info(self, field): attrs = [ 'read_only', 'label', 'help_text', 'min_length', 'max_length', - 'min_value', 'max_value' + 'min_value', 'max_value', + 'max_digits', 'decimal_places' ] for attr in attrs: diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 4abc0fc07e..1bdc8697c4 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -324,6 +324,13 @@ def test_related_field_choices(self): ) assert 'choices' not in field_info + def test_decimal_field_info_type(self): + options = metadata.SimpleMetadata() + field_info = options.get_field_info(serializers.DecimalField(max_digits=18, decimal_places=4)) + assert field_info['type'] == 'decimal' + assert field_info['max_digits'] == 18 + assert field_info['decimal_places'] == 4 + class TestModelSerializerMetadata(TestCase): def test_read_only_primary_key_related_field(self): From 54307a4394820173f7bfeaed53a675c00563bf18 Mon Sep 17 00:00:00 2001 From: suayip uzulmez <17948971+realsuayip@users.noreply.github.com> Date: Sun, 30 Apr 2023 12:20:02 +0300 Subject: [PATCH 1022/1262] Replaced `OrderedDict` with `dict` (#8964) --- rest_framework/fields.py | 9 ++--- rest_framework/metadata.py | 26 ++++++------ rest_framework/pagination.py | 36 ++++++++--------- rest_framework/relations.py | 11 ++--- rest_framework/renderers.py | 3 +- rest_framework/routers.py | 6 +-- rest_framework/schemas/coreapi.py | 12 +++--- rest_framework/schemas/openapi.py | 3 +- rest_framework/serializers.py | 40 +++++++++---------- rest_framework/templatetags/rest_framework.py | 9 ++--- rest_framework/utils/model_meta.py | 21 ++++------ rest_framework/utils/serializer_helpers.py | 5 +-- rest_framework/viewsets.py | 3 +- tests/test_model_serializer.py | 5 +-- tests/test_renderers.py | 7 ++-- tests/test_viewsets.py | 33 ++++++++------- 16 files changed, 105 insertions(+), 124 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index e41b56fb01..1d77ce4abc 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -6,7 +6,6 @@ import inspect import re import uuid -from collections import OrderedDict from collections.abc import Mapping from django.conf import settings @@ -143,7 +142,7 @@ def to_choices_dict(choices): # choices = [1, 2, 3] # choices = [(1, 'First'), (2, 'Second'), (3, 'Third')] # choices = [('Category', ((1, 'First'), (2, 'Second'))), (3, 'Third')] - ret = OrderedDict() + ret = {} for choice in choices: if not isinstance(choice, (list, tuple)): # single choice @@ -166,7 +165,7 @@ def flatten_choices_dict(choices): flatten_choices_dict({1: '1st', 2: '2nd'}) -> {1: '1st', 2: '2nd'} flatten_choices_dict({'Group': {1: '1st', 2: '2nd'}}) -> {1: '1st', 2: '2nd'} """ - ret = OrderedDict() + ret = {} for key, value in choices.items(): if isinstance(value, dict): # grouped choices (category, sub choices) @@ -1649,7 +1648,7 @@ def to_representation(self, data): def run_child_validation(self, data): result = [] - errors = OrderedDict() + errors = {} for idx, item in enumerate(data): try: @@ -1713,7 +1712,7 @@ def to_representation(self, value): def run_child_validation(self, data): result = {} - errors = OrderedDict() + errors = {} for key, value in data.items(): key = str(key) diff --git a/rest_framework/metadata.py b/rest_framework/metadata.py index c400b1b79e..364ca5b14d 100644 --- a/rest_framework/metadata.py +++ b/rest_framework/metadata.py @@ -6,8 +6,6 @@ Future implementations might use JSON schema or other definitions in order to return this information in a more standardized way. """ -from collections import OrderedDict - from django.core.exceptions import PermissionDenied from django.http import Http404 from django.utils.encoding import force_str @@ -59,11 +57,12 @@ class SimpleMetadata(BaseMetadata): }) def determine_metadata(self, request, view): - metadata = OrderedDict() - metadata['name'] = view.get_view_name() - metadata['description'] = view.get_view_description() - metadata['renders'] = [renderer.media_type for renderer in view.renderer_classes] - metadata['parses'] = [parser.media_type for parser in view.parser_classes] + metadata = { + "name": view.get_view_name(), + "description": view.get_view_description(), + "renders": [renderer.media_type for renderer in view.renderer_classes], + "parses": [parser.media_type for parser in view.parser_classes], + } if hasattr(view, 'get_serializer'): actions = self.determine_actions(request, view) if actions: @@ -106,20 +105,21 @@ def get_serializer_info(self, serializer): # If this is a `ListSerializer` then we want to examine the # underlying child serializer instance instead. serializer = serializer.child - return OrderedDict([ - (field_name, self.get_field_info(field)) + return { + field_name: self.get_field_info(field) for field_name, field in serializer.fields.items() if not isinstance(field, serializers.HiddenField) - ]) + } def get_field_info(self, field): """ Given an instance of a serializer field, return a dictionary of metadata about it. """ - field_info = OrderedDict() - field_info['type'] = self.label_lookup[field] - field_info['required'] = getattr(field, 'required', False) + field_info = { + "type": self.label_lookup[field], + "required": getattr(field, "required", False), + } attrs = [ 'read_only', 'label', 'help_text', diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 34d71f828c..68ab9c786b 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -5,7 +5,7 @@ import contextlib from base64 import b64decode, b64encode -from collections import OrderedDict, namedtuple +from collections import namedtuple from urllib import parse from django.core.paginator import InvalidPage @@ -225,12 +225,12 @@ def get_page_number(self, request, paginator): return page_number def get_paginated_response(self, data): - return Response(OrderedDict([ - ('count', self.page.paginator.count), - ('next', self.get_next_link()), - ('previous', self.get_previous_link()), - ('results', data) - ])) + return Response({ + 'count': self.page.paginator.count, + 'next': self.get_next_link(), + 'previous': self.get_previous_link(), + 'results': data, + }) def get_paginated_response_schema(self, schema): return { @@ -395,12 +395,12 @@ def paginate_queryset(self, queryset, request, view=None): return list(queryset[self.offset:self.offset + self.limit]) def get_paginated_response(self, data): - return Response(OrderedDict([ - ('count', self.count), - ('next', self.get_next_link()), - ('previous', self.get_previous_link()), - ('results', data) - ])) + return Response({ + 'count': self.count, + 'next': self.get_next_link(), + 'previous': self.get_previous_link(), + 'results': data + }) def get_paginated_response_schema(self, schema): return { @@ -892,11 +892,11 @@ def _get_position_from_instance(self, instance, ordering): return None if attr is None else str(attr) def get_paginated_response(self, data): - return Response(OrderedDict([ - ('next', self.get_next_link()), - ('previous', self.get_previous_link()), - ('results', data) - ])) + return Response({ + 'next': self.get_next_link(), + 'previous': self.get_previous_link(), + 'results': data, + }) def get_paginated_response_schema(self, schema): return { diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 53ea2113b4..4409bce77c 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -1,6 +1,5 @@ import contextlib import sys -from collections import OrderedDict from operator import attrgetter from urllib import parse @@ -199,13 +198,9 @@ def get_choices(self, cutoff=None): if cutoff is not None: queryset = queryset[:cutoff] - return OrderedDict([ - ( - self.to_representation(item), - self.display_value(item) - ) - for item in queryset - ]) + return { + self.to_representation(item): self.display_value(item) for item in queryset + } @property def choices(self): diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 8de0a77a1a..8e8c3a9b3c 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -9,7 +9,6 @@ import base64 import contextlib -from collections import OrderedDict from urllib import parse from django import forms @@ -653,7 +652,7 @@ def get_context(self, data, accepted_media_type, renderer_context): raw_data_patch_form = self.get_raw_data_form(data, view, 'PATCH', request) raw_data_put_or_patch_form = raw_data_put_form or raw_data_patch_form - response_headers = OrderedDict(sorted(response.items())) + response_headers = dict(sorted(response.items())) renderer_content_type = '' if renderer: renderer_content_type = '%s' % renderer.media_type diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 722fc50a6d..fa5d16922b 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -14,7 +14,7 @@ urlpatterns = router.urls """ import itertools -from collections import OrderedDict, namedtuple +from collections import namedtuple from django.core.exceptions import ImproperlyConfigured from django.urls import NoReverseMatch, path, re_path @@ -321,7 +321,7 @@ class APIRootView(views.APIView): def get(self, request, *args, **kwargs): # Return a plain {"name": "hyperlink"} response. - ret = OrderedDict() + ret = {} namespace = request.resolver_match.namespace for key, url_name in self.api_root_dict.items(): if namespace: @@ -365,7 +365,7 @@ def get_api_root_view(self, api_urls=None): """ Return a basic root view. """ - api_root_dict = OrderedDict() + api_root_dict = {} list_name = self.routes[0].name for prefix, viewset, basename in self.registry: api_root_dict[prefix] = list_name.format(basename=basename) diff --git a/rest_framework/schemas/coreapi.py b/rest_framework/schemas/coreapi.py index 908518e9d7..0713e0cb80 100644 --- a/rest_framework/schemas/coreapi.py +++ b/rest_framework/schemas/coreapi.py @@ -1,5 +1,5 @@ import warnings -from collections import Counter, OrderedDict +from collections import Counter from urllib import parse from django.db import models @@ -54,7 +54,7 @@ def distribute_links(obj): """ -class LinkNode(OrderedDict): +class LinkNode(dict): def __init__(self): self.links = [] self.methods_counter = Counter() @@ -268,11 +268,11 @@ def field_to_schema(field): ) elif isinstance(field, serializers.Serializer): return coreschema.Object( - properties=OrderedDict([ - (key, field_to_schema(value)) + properties={ + key: field_to_schema(value) for key, value in field.fields.items() - ]), + }, title=title, description=description ) @@ -549,7 +549,7 @@ def update_fields(fields, update_with): if not update_with: return fields - by_name = OrderedDict((f.name, f) for f in fields) + by_name = {f.name: f for f in fields} for f in update_with: by_name[f.name] = f fields = list(by_name.values()) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index ea220c631f..d8707e1e1f 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -1,6 +1,5 @@ import re import warnings -from collections import OrderedDict from decimal import Decimal from operator import attrgetter from urllib.parse import urljoin @@ -340,7 +339,7 @@ def get_pagination_parameters(self, path, method): return paginator.get_schema_operation_parameters(view) def map_choicefield(self, field): - choices = list(OrderedDict.fromkeys(field.choices)) # preserve order and remove duplicates + choices = list(dict.fromkeys(field.choices)) # preserve order and remove duplicates if all(isinstance(choice, bool) for choice in choices): type = 'boolean' elif all(isinstance(choice, int) for choice in choices): diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index e27f8a47c6..01bebf5fca 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -15,7 +15,7 @@ import copy import inspect import traceback -from collections import OrderedDict, defaultdict +from collections import defaultdict from collections.abc import Mapping from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured @@ -308,7 +308,7 @@ def visit(name): for name, f in base._declared_fields.items() if name not in known ] - return OrderedDict(base_fields + fields) + return dict(base_fields + fields) def __new__(cls, name, bases, attrs): attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs) @@ -393,20 +393,20 @@ def get_initial(self): if hasattr(self, 'initial_data'): # initial_data may not be a valid type if not isinstance(self.initial_data, Mapping): - return OrderedDict() + return {} - return OrderedDict([ - (field_name, field.get_value(self.initial_data)) + return { + field_name: field.get_value(self.initial_data) for field_name, field in self.fields.items() if (field.get_value(self.initial_data) is not empty) and not field.read_only - ]) + } - return OrderedDict([ - (field.field_name, field.get_initial()) + return { + field.field_name: field.get_initial() for field in self.fields.values() if not field.read_only - ]) + } def get_value(self, dictionary): # We override the default field access in order to support @@ -441,7 +441,7 @@ def _read_only_defaults(self): if (field.read_only) and (field.default != empty) and (field.source != '*') and ('.' not in field.source) ] - defaults = OrderedDict() + defaults = {} for field in fields: try: default = field.get_default() @@ -474,8 +474,8 @@ def to_internal_value(self, data): api_settings.NON_FIELD_ERRORS_KEY: [message] }, code='invalid') - ret = OrderedDict() - errors = OrderedDict() + ret = {} + errors = {} fields = self._writable_fields for field in fields: @@ -503,7 +503,7 @@ def to_representation(self, instance): """ Object instance -> Dict of primitive datatypes. """ - ret = OrderedDict() + ret = {} fields = self._readable_fields for field in fields: @@ -1061,7 +1061,7 @@ def get_fields(self): ) # Determine the fields that should be included on the serializer. - fields = OrderedDict() + fields = {} for field_name in field_names: # If the field is explicitly declared on the class then use that. @@ -1546,16 +1546,16 @@ def get_unique_together_validators(self): # which may map onto a model field. Any dotted field name lookups # cannot map to a field, and must be a traversal, so we're not # including those. - field_sources = OrderedDict( - (field.field_name, field.source) for field in self._writable_fields + field_sources = { + field.field_name: field.source for field in self._writable_fields if (field.source != '*') and ('.' not in field.source) - ) + } # Special Case: Add read_only fields with defaults. - field_sources.update(OrderedDict( - (field.field_name, field.source) for field in self.fields.values() + field_sources.update({ + field.field_name: field.source for field in self.fields.values() if (field.read_only) and (field.default != empty) and (field.source != '*') and ('.' not in field.source) - )) + }) # Invert so we can find the serializer field names that correspond to # the model field names in the unique_together sets. This also allows diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index ccd9430b4e..53916d3f28 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -1,5 +1,4 @@ import re -from collections import OrderedDict from django import template from django.template import loader @@ -49,10 +48,10 @@ def with_location(fields, location): @register.simple_tag def form_for_link(link): import coreschema - properties = OrderedDict([ - (field.name, field.schema or coreschema.String()) + properties = { + field.name: field.schema or coreschema.String() for field in link.fields - ]) + } required = [ field.name for field in link.fields @@ -272,7 +271,7 @@ def schema_links(section, sec_key=None): links.update(new_links) if sec_key is not None: - new_links = OrderedDict() + new_links = {} for link_key, link in links.items(): new_key = NESTED_FORMAT % (sec_key, link_key) new_links.update({new_key: link}) diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index 4cc93b8ef5..bd5d9177c0 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -5,7 +5,7 @@ Usage: `get_field_info(model)` returns a `FieldInfo` instance. """ -from collections import OrderedDict, namedtuple +from collections import namedtuple FieldInfo = namedtuple('FieldResult', [ 'pk', # Model field instance @@ -58,7 +58,7 @@ def _get_pk(opts): def _get_fields(opts): - fields = OrderedDict() + fields = {} for field in [field for field in opts.fields if field.serialize and not field.remote_field]: fields[field.name] = field @@ -71,9 +71,9 @@ def _get_to_field(field): def _get_forward_relationships(opts): """ - Returns an `OrderedDict` of field names to `RelationInfo`. + Returns a dict of field names to `RelationInfo`. """ - forward_relations = OrderedDict() + forward_relations = {} for field in [field for field in opts.fields if field.serialize and field.remote_field]: forward_relations[field.name] = RelationInfo( model_field=field, @@ -103,9 +103,9 @@ def _get_forward_relationships(opts): def _get_reverse_relationships(opts): """ - Returns an `OrderedDict` of field names to `RelationInfo`. + Returns a dict of field names to `RelationInfo`. """ - reverse_relations = OrderedDict() + reverse_relations = {} all_related_objects = [r for r in opts.related_objects if not r.field.many_to_many] for relation in all_related_objects: accessor_name = relation.get_accessor_name() @@ -139,19 +139,14 @@ def _get_reverse_relationships(opts): def _merge_fields_and_pk(pk, fields): - fields_and_pk = OrderedDict() - fields_and_pk['pk'] = pk - fields_and_pk[pk.name] = pk + fields_and_pk = {'pk': pk, pk.name: pk} fields_and_pk.update(fields) return fields_and_pk def _merge_relationships(forward_relations, reverse_relations): - return OrderedDict( - list(forward_relations.items()) + - list(reverse_relations.items()) - ) + return {**forward_relations, **reverse_relations} def is_abstract_model(model): diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index 6ca794584b..0e59aa66a2 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -1,6 +1,5 @@ import contextlib import sys -from collections import OrderedDict from collections.abc import Mapping, MutableMapping from django.utils.encoding import force_str @@ -8,7 +7,7 @@ from rest_framework.utils import json -class ReturnDict(OrderedDict): +class ReturnDict(dict): """ Return object from `serializer.data` for the `Serializer` class. Includes a backlink to the serializer instance for renderers @@ -161,7 +160,7 @@ class BindingDict(MutableMapping): def __init__(self, serializer): self.serializer = serializer - self.fields = OrderedDict() + self.fields = {} def __setitem__(self, key, field): self.fields[key] = field diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py index 1c56f61e86..2eba17b4a3 100644 --- a/rest_framework/viewsets.py +++ b/rest_framework/viewsets.py @@ -16,7 +16,6 @@ router.register(r'users', UserViewSet, 'user') urlpatterns = router.urls """ -from collections import OrderedDict from functools import update_wrapper from inspect import getmembers @@ -183,7 +182,7 @@ def get_extra_action_url_map(self): This method will noop if `detail` was not provided as a view initkwarg. """ - action_urls = OrderedDict() + action_urls = {} # exit early if `detail` has not been provided if self.detail is None: diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 419eae632b..c5ac888f55 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -10,7 +10,6 @@ import json # noqa import sys import tempfile -from collections import OrderedDict import django import pytest @@ -762,7 +761,7 @@ class Meta: fields = '__all__' serializer = TestSerializer() - expected = OrderedDict([(1, 'Red Color'), (2, 'Yellow Color'), (3, 'Green Color')]) + expected = {1: 'Red Color', 2: 'Yellow Color', 3: 'Green Color'} self.assertEqual(serializer.fields['color'].choices, expected) def test_custom_display_value(self): @@ -778,7 +777,7 @@ class Meta: fields = '__all__' serializer = TestSerializer() - expected = OrderedDict([(1, 'My Red Color'), (2, 'My Yellow Color'), (3, 'My Green Color')]) + expected = {1: 'My Red Color', 2: 'My Yellow Color', 3: 'My Green Color'} self.assertEqual(serializer.fields['color'].choices, expected) diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 8271608e15..71d734c86c 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -1,5 +1,4 @@ import re -from collections import OrderedDict from collections.abc import MutableMapping import pytest @@ -457,12 +456,12 @@ def test_get_caching(self): class TestJSONIndentationStyles: def test_indented(self): renderer = JSONRenderer() - data = OrderedDict([('a', 1), ('b', 2)]) + data = {"a": 1, "b": 2} assert renderer.render(data) == b'{"a":1,"b":2}' def test_compact(self): renderer = JSONRenderer() - data = OrderedDict([('a', 1), ('b', 2)]) + data = {"a": 1, "b": 2} context = {'indent': 4} assert ( renderer.render(data, renderer_context=context) == @@ -472,7 +471,7 @@ def test_compact(self): def test_long_form(self): renderer = JSONRenderer() renderer.compact = False - data = OrderedDict([('a', 1), ('b', 2)]) + data = {"a": 1, "b": 2} assert renderer.render(data) == b'{"a": 1, "b": 2}' diff --git a/tests/test_viewsets.py b/tests/test_viewsets.py index 8842b0b1cd..8e439c86eb 100644 --- a/tests/test_viewsets.py +++ b/tests/test_viewsets.py @@ -1,4 +1,3 @@ -from collections import OrderedDict from functools import wraps import pytest @@ -261,11 +260,11 @@ def test_list_view(self): response = self.client.get('/api/actions/') view = response.view - expected = OrderedDict([ - ('Custom list action', 'http://testserver/api/actions/custom_list_action/'), - ('List action', 'http://testserver/api/actions/list_action/'), - ('Wrapped list action', 'http://testserver/api/actions/wrapped_list_action/'), - ]) + expected = { + 'Custom list action': 'http://testserver/api/actions/custom_list_action/', + 'List action': 'http://testserver/api/actions/list_action/', + 'Wrapped list action': 'http://testserver/api/actions/wrapped_list_action/', + } self.assertEqual(view.get_extra_action_url_map(), expected) @@ -273,28 +272,28 @@ def test_detail_view(self): response = self.client.get('/api/actions/1/') view = response.view - expected = OrderedDict([ - ('Custom detail action', 'http://testserver/api/actions/1/custom_detail_action/'), - ('Detail action', 'http://testserver/api/actions/1/detail_action/'), - ('Wrapped detail action', 'http://testserver/api/actions/1/wrapped_detail_action/'), + expected = { + 'Custom detail action': 'http://testserver/api/actions/1/custom_detail_action/', + 'Detail action': 'http://testserver/api/actions/1/detail_action/', + 'Wrapped detail action': 'http://testserver/api/actions/1/wrapped_detail_action/', # "Unresolvable detail action" excluded, since it's not resolvable - ]) + } self.assertEqual(view.get_extra_action_url_map(), expected) def test_uninitialized_view(self): - self.assertEqual(ActionViewSet().get_extra_action_url_map(), OrderedDict()) + self.assertEqual(ActionViewSet().get_extra_action_url_map(), {}) def test_action_names(self): # Action 'name' and 'suffix' kwargs should be respected response = self.client.get('/api/names/1/') view = response.view - expected = OrderedDict([ - ('Custom Name', 'http://testserver/api/names/1/named_action/'), - ('Action Names Custom Suffix', 'http://testserver/api/names/1/suffixed_action/'), - ('Unnamed action', 'http://testserver/api/names/1/unnamed_action/'), - ]) + expected = { + 'Custom Name': 'http://testserver/api/names/1/named_action/', + 'Action Names Custom Suffix': 'http://testserver/api/names/1/suffixed_action/', + 'Unnamed action': 'http://testserver/api/names/1/unnamed_action/', + } self.assertEqual(view.get_extra_action_url_map(), expected) From f1a11d41cbd1d16871e8d8426cbf290da57dfb6d Mon Sep 17 00:00:00 2001 From: fdomain Date: Tue, 2 May 2023 02:55:59 +0200 Subject: [PATCH 1023/1262] fix: fallback on CursorPagination ordering if unset on the view (#8954) * this commit fixes the usage of a CursorPagination combined with a view implementing an ordering filter, without a default ordering value. * former behavior was to fetch the ordering value from the filter, and raises an error if the value was None, preventing the fallback on the ordering set on the CursorPagination class itself. * we reversed the logic by getting first the value set on the class, and override it by the ordering filter if the parameter is present --- rest_framework/pagination.py | 37 +++++++++++++++++------------------- tests/test_pagination.py | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 68ab9c786b..af508bef6d 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -801,6 +801,10 @@ def get_ordering(self, request, queryset, view): """ Return a tuple of strings, that may be used in an `order_by` method. """ + # The default case is to check for an `ordering` attribute + # on this pagination instance. + ordering = self.ordering + ordering_filters = [ filter_cls for filter_cls in getattr(view, 'filter_backends', []) if hasattr(filter_cls, 'get_ordering') @@ -811,26 +815,19 @@ def get_ordering(self, request, queryset, view): # then we defer to that filter to determine the ordering. filter_cls = ordering_filters[0] filter_instance = filter_cls() - ordering = filter_instance.get_ordering(request, queryset, view) - assert ordering is not None, ( - 'Using cursor pagination, but filter class {filter_cls} ' - 'returned a `None` ordering.'.format( - filter_cls=filter_cls.__name__ - ) - ) - else: - # The default case is to check for an `ordering` attribute - # on this pagination instance. - ordering = self.ordering - assert ordering is not None, ( - 'Using cursor pagination, but no ordering attribute was declared ' - 'on the pagination class.' - ) - assert '__' not in ordering, ( - 'Cursor pagination does not support double underscore lookups ' - 'for orderings. Orderings should be an unchanging, unique or ' - 'nearly-unique field on the model, such as "-created" or "pk".' - ) + ordering_from_filter = filter_instance.get_ordering(request, queryset, view) + if ordering_from_filter: + ordering = ordering_from_filter + + assert ordering is not None, ( + 'Using cursor pagination, but no ordering attribute was declared ' + 'on the pagination class.' + ) + assert '__' not in ordering, ( + 'Cursor pagination does not support double underscore lookups ' + 'for orderings. Orderings should be an unchanging, unique or ' + 'nearly-unique field on the model, such as "-created" or "pk".' + ) assert isinstance(ordering, (str, list, tuple)), ( 'Invalid ordering. Expected string or tuple, but got {type}'.format( diff --git a/tests/test_pagination.py b/tests/test_pagination.py index 8f9b20a0d4..d606986ab7 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -632,6 +632,24 @@ class MockView: ordering = self.pagination.get_ordering(request, [], MockView()) assert ordering == ('created',) + def test_use_with_ordering_filter_without_ordering_default_value(self): + class MockView: + filter_backends = (filters.OrderingFilter,) + ordering_fields = ['username', 'created'] + + request = Request(factory.get('/')) + ordering = self.pagination.get_ordering(request, [], MockView()) + # it gets the value of `ordering` provided by CursorPagination + assert ordering == ('created',) + + request = Request(factory.get('/', {'ordering': 'username'})) + ordering = self.pagination.get_ordering(request, [], MockView()) + assert ordering == ('username',) + + request = Request(factory.get('/', {'ordering': 'invalid'})) + ordering = self.pagination.get_ordering(request, [], MockView()) + assert ordering == ('created',) + def test_cursor_pagination(self): (previous, current, next, previous_url, next_url) = self.get_pages('/') From d14eb7555da4c8986c8a501836943f9fcbc99fb4 Mon Sep 17 00:00:00 2001 From: Mahdi Rahimi <31624047+mahdirahimi1999@users.noreply.github.com> Date: Tue, 2 May 2023 19:39:19 +0330 Subject: [PATCH 1024/1262] Refactor read function to use context manager for file handling (#8967) Co-authored-by: Mahdi --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d9002fdb91..533ffa97f0 100755 --- a/setup.py +++ b/setup.py @@ -37,7 +37,8 @@ def read(f): - return open(f, 'r', encoding='utf-8').read() + with open(f, 'r', encoding='utf-8') as file: + return file.read() def get_version(package): From e08e606c82afd0d5ec82b2c58badec11a4ce825e Mon Sep 17 00:00:00 2001 From: Saad Aleem Date: Wed, 3 May 2023 12:08:07 +0500 Subject: [PATCH 1025/1262] Fix mapping for choice values (#8968) * fix mapping for choice values * fix tests for ChoiceField IntegerChoices * fix imports * fix imports in tests * Check for multiple choice enums * fix formatting * add tests for text choices class --- rest_framework/fields.py | 13 ++++++++++- tests/test_fields.py | 50 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 1d77ce4abc..f4fd265394 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -16,6 +16,7 @@ MinValueValidator, ProhibitNullCharactersValidator, RegexValidator, URLValidator, ip_address_validators ) +from django.db.models import IntegerChoices, TextChoices from django.forms import FilePathField as DjangoFilePathField from django.forms import ImageField as DjangoImageField from django.utils import timezone @@ -1397,6 +1398,10 @@ def to_internal_value(self, data): if data == '' and self.allow_blank: return '' + if isinstance(data, (IntegerChoices, TextChoices)) and str(data) != \ + str(data.value): + data = data.value + try: return self.choice_strings_to_values[str(data)] except KeyError: @@ -1405,6 +1410,11 @@ def to_internal_value(self, data): def to_representation(self, value): if value in ('', None): return value + + if isinstance(value, (IntegerChoices, TextChoices)) and str(value) != \ + str(value.value): + value = value.value + return self.choice_strings_to_values.get(str(value), value) def iter_options(self): @@ -1428,7 +1438,8 @@ def _set_choices(self, choices): # Allows us to deal with eg. integer choices while supporting either # integer or string input, but still get the correct datatype out. self.choice_strings_to_values = { - str(key): key for key in self.choices + str(key.value) if isinstance(key, (IntegerChoices, TextChoices)) + and str(key) != str(key.value) else str(key): key for key in self.choices } choices = property(_get_choices, _set_choices) diff --git a/tests/test_fields.py b/tests/test_fields.py index 5804d7b3b3..bf25b71b8d 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -5,11 +5,13 @@ import sys import uuid from decimal import ROUND_DOWN, ROUND_UP, Decimal +from enum import auto from unittest.mock import patch import pytest import pytz from django.core.exceptions import ValidationError as DjangoValidationError +from django.db.models import IntegerChoices, TextChoices from django.http import QueryDict from django.test import TestCase, override_settings from django.utils.timezone import activate, deactivate, override @@ -1824,6 +1826,54 @@ def test_edit_choices(self): field.run_validation(2) assert exc_info.value.detail == ['"2" is not a valid choice.'] + def test_integer_choices(self): + class ChoiceCase(IntegerChoices): + first = auto() + second = auto() + # Enum validate + choices = [ + (ChoiceCase.first, "1"), + (ChoiceCase.second, "2") + ] + + field = serializers.ChoiceField(choices=choices) + assert field.run_validation(1) == 1 + assert field.run_validation(ChoiceCase.first) == 1 + assert field.run_validation("1") == 1 + + choices = [ + (ChoiceCase.first.value, "1"), + (ChoiceCase.second.value, "2") + ] + + field = serializers.ChoiceField(choices=choices) + assert field.run_validation(1) == 1 + assert field.run_validation(ChoiceCase.first) == 1 + assert field.run_validation("1") == 1 + + def test_text_choices(self): + class ChoiceCase(TextChoices): + first = auto() + second = auto() + # Enum validate + choices = [ + (ChoiceCase.first, "first"), + (ChoiceCase.second, "second") + ] + + field = serializers.ChoiceField(choices=choices) + assert field.run_validation(ChoiceCase.first) == "first" + assert field.run_validation("first") == "first" + + choices = [ + (ChoiceCase.first.value, "first"), + (ChoiceCase.second.value, "second") + ] + + field = serializers.ChoiceField(choices=choices) + assert field.run_validation(ChoiceCase.first) == "first" + assert field.run_validation("first") == "first" + class TestChoiceFieldWithType(FieldValues): """ From 99e8b4033efa44930ace40fb48a4d7bcd224f9fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ismael=20Jim=C3=A9nez=20S=C3=A1nchez?= Date: Tue, 9 May 2023 16:50:29 +0200 Subject: [PATCH 1026/1262] feat: enforce Decimal type in min_value and max_value arguments of DecimalField (#8972) * add warning when min_value and max_value are not decimal * remove redundant module name in log --------- Co-authored-by: ismaeljs <> --- rest_framework/fields.py | 8 ++++++++ tests/test_fields.py | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index f4fd265394..4f82e4a10e 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -4,6 +4,7 @@ import decimal import functools import inspect +import logging import re import uuid from collections.abc import Mapping @@ -38,6 +39,8 @@ from rest_framework.utils.timezone import valid_datetime from rest_framework.validators import ProhibitSurrogateCharactersValidator +logger = logging.getLogger("rest_framework.fields") + class empty: """ @@ -990,6 +993,11 @@ def __init__(self, max_digits, decimal_places, coerce_to_string=None, max_value= self.max_value = max_value self.min_value = min_value + if self.max_value is not None and not isinstance(self.max_value, decimal.Decimal): + logger.warning("max_value in DecimalField should be Decimal type.") + if self.min_value is not None and not isinstance(self.min_value, decimal.Decimal): + logger.warning("min_value in DecimalField should be Decimal type.") + if self.max_digits is not None and self.decimal_places is not None: self.max_whole_digits = self.max_digits - self.decimal_places else: diff --git a/tests/test_fields.py b/tests/test_fields.py index bf25b71b8d..bcf3884412 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1216,6 +1216,17 @@ class TestMinMaxDecimalField(FieldValues): min_value=10, max_value=20 ) + def test_warning_when_not_decimal_types(self, caplog): + import logging + serializers.DecimalField( + max_digits=3, decimal_places=1, + min_value=10, max_value=20 + ) + assert caplog.record_tuples == [ + ("rest_framework.fields", logging.WARNING, "max_value in DecimalField should be Decimal type."), + ("rest_framework.fields", logging.WARNING, "min_value in DecimalField should be Decimal type.") + ] + class TestAllowEmptyStrDecimalFieldWithValidators(FieldValues): """ From 001d6ec2c907e638496fb4ef0cffa915bf0718fd Mon Sep 17 00:00:00 2001 From: Mathieu Dupuy Date: Sun, 14 May 2023 02:00:13 +0200 Subject: [PATCH 1027/1262] remove django 2.2 from docs index (#8982) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index cab5511ac1..ad241c0a37 100644 --- a/docs/index.md +++ b/docs/index.md @@ -86,7 +86,7 @@ continued development by **[signing up for a paid plan][funding]**. REST framework requires the following: * Python (3.6, 3.7, 3.8, 3.9, 3.10, 3.11) -* Django (2.2, 3.0, 3.1, 3.2, 4.0, 4.1) +* Django (3.0, 3.1, 3.2, 4.0, 4.1) We **highly recommend** and only officially support the latest patch release of each Python and Django series. From 7bebe9772488fcf67949e1221e70af642ed0cb74 Mon Sep 17 00:00:00 2001 From: Mehraz Hossain Rumman <59512321+MehrazRumman@users.noreply.github.com> Date: Mon, 15 May 2023 21:02:17 +0600 Subject: [PATCH 1028/1262] Declared Django 4.2 support in README.md (#8985) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e8375291dc..c2975a418f 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ There is a live example API for testing purposes, [available here][sandbox]. # Requirements * Python 3.6+ -* Django 4.1, 4.0, 3.2, 3.1, 3.0 +* Django 4.2, 4.1, 4.0, 3.2, 3.1, 3.0 We **highly recommend** and only officially support the latest patch release of each Python and Django series. From 332e9560ab0b3a1b8c0ab8f68e95c09bc2d8999f Mon Sep 17 00:00:00 2001 From: Dominik Bruhn Date: Wed, 17 May 2023 07:46:48 +0200 Subject: [PATCH 1029/1262] Fix Links in Documentation to Django `reverse` and `reverse_lazy` (#8986) * Fix Django Docs url in reverse.md Django URLs of the documentation of `reverse` and `reverse_lazy` were wrong. * Update reverse.md --- docs/api-guide/reverse.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index 82dd168818..c3fa52f21f 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -54,5 +54,5 @@ As with the `reverse` function, you should **include the request as a keyword ar api_root = reverse_lazy('api-root', request=request) [cite]: https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5 -[reverse]: https://docs.djangoproject.com/en/stable/topics/http/urls/#reverse -[reverse-lazy]: https://docs.djangoproject.com/en/stable/topics/http/urls/#reverse-lazy +[reverse]: https://docs.djangoproject.com/en/stable/ref/urlresolvers/#reverse +[reverse-lazy]: https://docs.djangoproject.com/en/stable/ref/urlresolvers/#reverse-lazy From a25aac7d6739c43a4997829a0ff79cce12e8c0de Mon Sep 17 00:00:00 2001 From: jornvanwier Date: Thu, 18 May 2023 05:46:40 +0200 Subject: [PATCH 1030/1262] fix URLPathVersioning reverse fallback (#7247) * fix URLPathVersioning reverse fallback * add test for URLPathVersioning reverse fallback * Update tests/test_versioning.py --------- Co-authored-by: Jorn van Wier Co-authored-by: Asif Saif Uddin --- rest_framework/versioning.py | 6 ++++-- tests/test_versioning.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py index 78cfc9dc81..c2764c7a40 100644 --- a/rest_framework/versioning.py +++ b/rest_framework/versioning.py @@ -81,8 +81,10 @@ def determine_version(self, request, *args, **kwargs): def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): if request.version is not None: - kwargs = {} if (kwargs is None) else kwargs - kwargs[self.version_param] = request.version + kwargs = { + self.version_param: request.version, + **(kwargs or {}) + } return super().reverse( viewname, args, kwargs, request, format, **extra diff --git a/tests/test_versioning.py b/tests/test_versioning.py index 93f61d2be4..b216461840 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -152,6 +152,8 @@ class TestURLReversing(URLPatternsTestCase, APITestCase): path('v1/', include((included, 'v1'), namespace='v1')), path('another/', dummy_view, name='another'), re_path(r'^(?P[v1|v2]+)/another/$', dummy_view, name='another'), + re_path(r'^(?P.+)/unversioned/$', dummy_view, name='unversioned'), + ] def test_reverse_unversioned(self): @@ -198,6 +200,14 @@ def test_reverse_url_path_versioning(self): response = view(request) assert response.data == {'url': 'http://testserver/another/'} + # Test fallback when kwargs is not None + request = factory.get('/v1/endpoint/') + request.versioning_scheme = scheme() + request.version = 'v1' + + reversed_url = reverse('unversioned', request=request, kwargs={'foo': 'bar'}) + assert reversed_url == 'http://testserver/bar/unversioned/' + def test_reverse_namespace_versioning(self): class FakeResolverMatch(ResolverMatch): namespace = 'v1' From d252d22343b9c9688db77b59aa72dabd540bd252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Beaul=C3=A9?= Date: Wed, 24 May 2023 10:59:42 -0300 Subject: [PATCH 1031/1262] Make set_value a method within `Serializer` (#8001) * Make set_value a static method for Serializers As an alternative to #7671, let the method be overridden if needed. As the function is only used for serializers, it has a better place in the Serializer class. * Set `set_value` as an object (non-static) method * Add tests for set_value() These tests follow the examples given in the method. --- rest_framework/fields.py | 21 --------------------- rest_framework/serializers.py | 24 ++++++++++++++++++++++-- tests/test_serializer.py | 21 +++++++++++++++++++++ 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 4f82e4a10e..623e72e0ad 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -113,27 +113,6 @@ def get_attribute(instance, attrs): return instance -def set_value(dictionary, keys, value): - """ - Similar to Python's built in `dictionary[key] = value`, - but takes a list of nested keys instead of a single key. - - set_value({'a': 1}, [], {'b': 2}) -> {'a': 1, 'b': 2} - set_value({'a': 1}, ['x'], 2) -> {'a': 1, 'x': 2} - set_value({'a': 1}, ['x', 'y'], 2) -> {'a': 1, 'x': {'y': 2}} - """ - if not keys: - dictionary.update(value) - return - - for key in keys[:-1]: - if key not in dictionary: - dictionary[key] = {} - dictionary = dictionary[key] - - dictionary[keys[-1]] = value - - def to_choices_dict(choices): """ Convert choices into key/value dicts. diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 01bebf5fca..a3d68b03db 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -28,7 +28,7 @@ from rest_framework.compat import postgres_fields from rest_framework.exceptions import ErrorDetail, ValidationError -from rest_framework.fields import get_error_detail, set_value +from rest_framework.fields import get_error_detail from rest_framework.settings import api_settings from rest_framework.utils import html, model_meta, representation from rest_framework.utils.field_mapping import ( @@ -346,6 +346,26 @@ class Serializer(BaseSerializer, metaclass=SerializerMetaclass): 'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.') } + def set_value(self, dictionary, keys, value): + """ + Similar to Python's built in `dictionary[key] = value`, + but takes a list of nested keys instead of a single key. + + set_value({'a': 1}, [], {'b': 2}) -> {'a': 1, 'b': 2} + set_value({'a': 1}, ['x'], 2) -> {'a': 1, 'x': 2} + set_value({'a': 1}, ['x', 'y'], 2) -> {'a': 1, 'x': {'y': 2}} + """ + if not keys: + dictionary.update(value) + return + + for key in keys[:-1]: + if key not in dictionary: + dictionary[key] = {} + dictionary = dictionary[key] + + dictionary[keys[-1]] = value + @cached_property def fields(self): """ @@ -492,7 +512,7 @@ def to_internal_value(self, data): except SkipField: pass else: - set_value(ret, field.source_attrs, validated_value) + self.set_value(ret, field.source_attrs, validated_value) if errors: raise ValidationError(errors) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 1d9efaa434..10fa8afb94 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -762,3 +762,24 @@ class TestSerializer(serializers.Serializer): assert (s.data | {}).__class__ == s.data.__class__ assert ({} | s.data).__class__ == s.data.__class__ + + +class TestSetValueMethod: + # Serializer.set_value() modifies the first parameter in-place. + + s = serializers.Serializer() + + def test_no_keys(self): + ret = {'a': 1} + self.s.set_value(ret, [], {'b': 2}) + assert ret == {'a': 1, 'b': 2} + + def test_one_key(self): + ret = {'a': 1} + self.s.set_value(ret, ['x'], 2) + assert ret == {'a': 1, 'x': 2} + + def test_nested_key(self): + ret = {'a': 1} + self.s.set_value(ret, ['x', 'y'], 2) + assert ret == {'a': 1, 'x': {'y': 2}} From e2a4559c03247f69e85b2e0e7ede4e8b6201adc2 Mon Sep 17 00:00:00 2001 From: Saad Aleem Date: Mon, 29 May 2023 19:03:11 +0500 Subject: [PATCH 1032/1262] Fix validation for ListSerializer (#8979) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: Make the instance variable of child serializer point to the correct list object instead of the entire list when validating ListSerializer * fix formatting issues for list serializer validation fix * fix imports sorting for list serializer tests * remove django 2.2 from docs index (#8982) * Declared Django 4.2 support in README.md (#8985) * Fix Links in Documentation to Django `reverse` and `reverse_lazy` (#8986) * Fix Django Docs url in reverse.md Django URLs of the documentation of `reverse` and `reverse_lazy` were wrong. * Update reverse.md * fix URLPathVersioning reverse fallback (#7247) * fix URLPathVersioning reverse fallback * add test for URLPathVersioning reverse fallback * Update tests/test_versioning.py --------- Co-authored-by: Jorn van Wier Co-authored-by: Asif Saif Uddin * Make set_value a method within `Serializer` (#8001) * Make set_value a static method for Serializers As an alternative to #7671, let the method be overridden if needed. As the function is only used for serializers, it has a better place in the Serializer class. * Set `set_value` as an object (non-static) method * Add tests for set_value() These tests follow the examples given in the method. * fix: Make the instance variable of child serializer point to the correct list object instead of the entire list when validating ListSerializer * Make set_value a method within `Serializer` (#8001) * Make set_value a static method for Serializers As an alternative to #7671, let the method be overridden if needed. As the function is only used for serializers, it has a better place in the Serializer class. * Set `set_value` as an object (non-static) method * Add tests for set_value() These tests follow the examples given in the method. * fix: Make the instance variable of child serializer point to the correct list object instead of the entire list when validating ListSerializer * fix: Make the instance variable of child serializer point to the correct list object instead of the entire list when validating ListSerializer * fix formatting issues for list serializer validation fix * fix: Make the instance variable of child serializer point to the correct list object instead of the entire list when validating ListSerializer * fix formatting issues for list serializer validation fix * fix linting * Update rest_framework/serializers.py Co-authored-by: Sergei Shishov * Update rest_framework/serializers.py Co-authored-by: Sergei Shishov * fix: instance variable in list serializer, remove commented code --------- Co-authored-by: Mathieu Dupuy Co-authored-by: Mehraz Hossain Rumman <59512321+MehrazRumman@users.noreply.github.com> Co-authored-by: Dominik Bruhn Co-authored-by: jornvanwier Co-authored-by: Jorn van Wier Co-authored-by: Asif Saif Uddin Co-authored-by: Étienne Beaulé Co-authored-by: Sergei Shishov --- rest_framework/serializers.py | 14 +++++++- tests/test_serializer.py | 61 +++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index a3d68b03db..56fa918dc8 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -609,6 +609,12 @@ def __init__(self, *args, **kwargs): self.min_length = kwargs.pop('min_length', None) assert self.child is not None, '`child` is a required argument.' assert not inspect.isclass(self.child), '`child` has not been instantiated.' + + instance = kwargs.get('instance', []) + data = kwargs.get('data', []) + if instance and data: + assert len(data) == len(instance), 'Data and instance should have same length' + super().__init__(*args, **kwargs) self.child.bind(field_name='', parent=self) @@ -683,7 +689,13 @@ def to_internal_value(self, data): ret = [] errors = [] - for item in data: + for idx, item in enumerate(data): + if ( + hasattr(self, 'instance') + and self.instance + and len(self.instance) > idx + ): + self.child.instance = self.instance[idx] try: validated = self.child.run_validation(item) except ValidationError as exc: diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 10fa8afb94..39d9238ef9 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -2,6 +2,7 @@ import pickle import re import sys +import unittest from collections import ChainMap from collections.abc import Mapping @@ -783,3 +784,63 @@ def test_nested_key(self): ret = {'a': 1} self.s.set_value(ret, ['x', 'y'], 2) assert ret == {'a': 1, 'x': {'y': 2}} + + +class MyClass(models.Model): + name = models.CharField(max_length=100) + value = models.CharField(max_length=100, blank=True) + + app_label = "test" + + @property + def is_valid(self): + return self.name == 'valid' + + +class MyClassSerializer(serializers.ModelSerializer): + class Meta: + model = MyClass + fields = ('id', 'name', 'value') + + def validate_value(self, value): + if value and not self.instance.is_valid: + raise serializers.ValidationError( + 'Status cannot be set for invalid instance') + return value + + +class TestMultipleObjectsValidation(unittest.TestCase): + def setUp(self): + self.objs = [ + MyClass(name='valid'), + MyClass(name='invalid'), + MyClass(name='other'), + ] + + def test_multiple_objects_are_validated_separately(self): + + serializer = MyClassSerializer( + data=[{'value': 'set', 'id': instance.id} for instance in + self.objs], + instance=self.objs, + many=True, + partial=True, + ) + + assert not serializer.is_valid() + assert serializer.errors == [ + {}, + {'value': ['Status cannot be set for invalid instance']}, + {'value': ['Status cannot be set for invalid instance']} + ] + + def test_exception_raised_when_data_and_instance_length_different(self): + + with self.assertRaises(AssertionError): + MyClassSerializer( + data=[{'value': 'set', 'id': instance.id} for instance in + self.objs], + instance=self.objs[:-1], + many=True, + partial=True, + ) From ff5f647df01d83f1bad32e37ed8d9181da21538c Mon Sep 17 00:00:00 2001 From: Vladimir Kasatkin Date: Wed, 31 May 2023 07:36:21 +0300 Subject: [PATCH 1033/1262] Fix example of `requires_context` attribute (#8952) --- docs/api-guide/validators.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index dac937d9bd..2a1e3e6b36 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -295,13 +295,14 @@ To write a class-based validator, use the `__call__` method. Class-based validat In some advanced cases you might want a validator to be passed the serializer field it is being used with as additional context. You can do so by setting -a `requires_context = True` attribute on the validator. The `__call__` method +a `requires_context = True` attribute on the validator class. The `__call__` method will then be called with the `serializer_field` or `serializer` as an additional argument. - requires_context = True + class MultipleOf: + requires_context = True - def __call__(self, value, serializer_field): - ... + def __call__(self, value, serializer_field): + ... [cite]: https://docs.djangoproject.com/en/stable/ref/validators/ From 03e2ecc9a5afcb33c9069efcb39b5a04a49d7774 Mon Sep 17 00:00:00 2001 From: Alasdair Nicol Date: Fri, 2 Jun 2023 01:29:11 +0100 Subject: [PATCH 1034/1262] Add NullBooleanField deprecation to docs (#8999) --- docs/community/3.12-announcement.md | 10 ++++++++++ docs/community/3.14-announcement.md | 10 ++++++++++ docs/community/release-notes.md | 1 + 3 files changed, 21 insertions(+) diff --git a/docs/community/3.12-announcement.md b/docs/community/3.12-announcement.md index 4a589e39c0..3bfeb65763 100644 --- a/docs/community/3.12-announcement.md +++ b/docs/community/3.12-announcement.md @@ -143,6 +143,16 @@ class PublisherSearchView(generics.ListAPIView): --- +## Deprecations + +### `serializers.NullBooleanField` + +`serializers.NullBooleanField` is now pending deprecation, and will be removed in 3.14. + +Instead use `serializers.BooleanField` field and set `allow_null=True` which does the same thing. + +--- + ## Funding REST framework is a *collaboratively funded project*. If you use diff --git a/docs/community/3.14-announcement.md b/docs/community/3.14-announcement.md index 0543d0d6d6..e7b10ede19 100644 --- a/docs/community/3.14-announcement.md +++ b/docs/community/3.14-announcement.md @@ -60,3 +60,13 @@ See Pull Request [#7522](https://github.com/encode/django-rest-framework/pull/75 ## Minor fixes and improvements There are a number of minor fixes and improvements in this release. See the [release notes](release-notes.md) page for a complete listing. + +--- + +## Deprecations + +### `serializers.NullBooleanField` + +`serializers.NullBooleanField` was moved to pending deprecation in 3.12, and deprecated in 3.13. It has now been removed from the core framework. + +Instead use `serializers.BooleanField` field and set `allow_null=True` which does the same thing. diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md index 887cae3b47..fba7f63d62 100644 --- a/docs/community/release-notes.md +++ b/docs/community/release-notes.md @@ -157,6 +157,7 @@ Date: 28th September 2020 * 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] +* Deprecate `serializers.NullBooleanField` in favour of `serializers.BooleanField` with `allow_null=True` [#7122] --- From 376a5cbbba3f8df9c9db8c03a7c8fa2a6e6c05f4 Mon Sep 17 00:00:00 2001 From: Mathieu Dupuy Date: Sun, 4 Jun 2023 07:24:07 +0200 Subject: [PATCH 1035/1262] remove dependency on pytz (#8984) * remove pytz * Revert "remove pytz" This reverts commit 393609dfaadbc4d82f760e6e4df8a6de6de24f9b. * remove pytz, more subtly --- rest_framework/fields.py | 12 +++++++++--- setup.py | 2 +- tests/test_fields.py | 22 ++++++++++++++-------- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 623e72e0ad..7079e3332c 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -29,7 +29,11 @@ from django.utils.formats import localize_input, sanitize_separators from django.utils.ipv6 import clean_ipv6_address from django.utils.translation import gettext_lazy as _ -from pytz.exceptions import InvalidTimeError + +try: + import pytz +except ImportError: + pytz = None from rest_framework import ISO_8601 from rest_framework.exceptions import ErrorDetail, ValidationError @@ -1148,8 +1152,10 @@ def enforce_timezone(self, value): if not valid_datetime(dt): self.fail('make_aware', timezone=field_timezone) return dt - except InvalidTimeError: - self.fail('make_aware', timezone=field_timezone) + except Exception as e: + if pytz and isinstance(e, pytz.exceptions.InvalidTimeError): + self.fail('make_aware', timezone=field_timezone) + raise e elif (field_timezone is None) and timezone.is_aware(value): return timezone.make_naive(value, datetime.timezone.utc) return value diff --git a/setup.py b/setup.py index 533ffa97f0..6afd5e05e7 100755 --- a/setup.py +++ b/setup.py @@ -83,7 +83,7 @@ def get_version(package): author_email='tom@tomchristie.com', # SEE NOTE BELOW (*) packages=find_packages(exclude=['tests*']), include_package_data=True, - install_requires=["django>=3.0", "pytz", 'backports.zoneinfo;python_version<"3.9"'], + install_requires=["django>=3.0", 'backports.zoneinfo;python_version<"3.9"'], python_requires=">=3.6", zip_safe=False, classifiers=[ diff --git a/tests/test_fields.py b/tests/test_fields.py index bcf3884412..50d9d77938 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -9,7 +9,12 @@ from unittest.mock import patch import pytest -import pytz + +try: + import pytz +except ImportError: + pytz = None + from django.core.exceptions import ValidationError as DjangoValidationError from django.db.models import IntegerChoices, TextChoices from django.http import QueryDict @@ -1604,15 +1609,16 @@ class TestPytzNaiveDayLightSavingTimeTimeZoneDateTimeField(FieldValues): } outputs = {} - class MockTimezone(pytz.BaseTzInfo): - @staticmethod - def localize(value, is_dst): - raise pytz.InvalidTimeError() + if pytz: + class MockTimezone(pytz.BaseTzInfo): + @staticmethod + def localize(value, is_dst): + raise pytz.InvalidTimeError() - def __str__(self): - return 'America/New_York' + def __str__(self): + return 'America/New_York' - field = serializers.DateTimeField(default_timezone=MockTimezone()) + field = serializers.DateTimeField(default_timezone=MockTimezone()) @patch('rest_framework.utils.timezone.datetime_ambiguous', return_value=True) From 02d9bfc2ddee0ddf1d6e9cb0e23a52b80fdb4242 Mon Sep 17 00:00:00 2001 From: Niyaz Date: Mon, 12 Jun 2023 16:28:28 +0300 Subject: [PATCH 1036/1262] Fixes `BrowsableAPIRenderer` for usage with `ListSerializer`. (#7530) Renders list of items in raw_data_form and does not renders form in template while using with `ListSerializer` (`many=True`). --- rest_framework/renderers.py | 6 ++++ tests/test_renderers.py | 59 +++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 8e8c3a9b3c..0a3b03729d 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -506,6 +506,9 @@ def get_rendered_html_form(self, data, view, method, request): return self.render_form_for_serializer(serializer) def render_form_for_serializer(self, serializer): + if isinstance(serializer, serializers.ListSerializer): + return None + if hasattr(serializer, 'initial_data'): serializer.is_valid() @@ -555,10 +558,13 @@ def get_raw_data_form(self, data, view, method, request): context['indent'] = 4 # strip HiddenField from output + is_list_serializer = isinstance(serializer, serializers.ListSerializer) + serializer = serializer.child if is_list_serializer else serializer data = serializer.data.copy() for name, field in serializer.fields.items(): if isinstance(field, serializers.HiddenField): data.pop(name, None) + data = [data] if is_list_serializer else data content = renderer.render(data, accepted, context) # Renders returns bytes, but CharField expects a str. content = content.decode() diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 71d734c86c..247737576f 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -633,6 +633,9 @@ def list_action(self, request): class AuthExampleViewSet(ExampleViewSet): permission_classes = [permissions.IsAuthenticated] + class SimpleSerializer(serializers.Serializer): + name = serializers.CharField() + router = SimpleRouter() router.register('examples', ExampleViewSet, basename='example') router.register('auth-examples', AuthExampleViewSet, basename='auth-example') @@ -640,6 +643,62 @@ class AuthExampleViewSet(ExampleViewSet): def setUp(self): self.renderer = BrowsableAPIRenderer() + self.renderer.accepted_media_type = '' + self.renderer.renderer_context = {} + + def test_render_form_for_serializer(self): + with self.subTest('Serializer'): + serializer = BrowsableAPIRendererTests.SimpleSerializer(data={'name': 'Name'}) + form = self.renderer.render_form_for_serializer(serializer) + assert isinstance(form, str), 'Must return form for serializer' + + with self.subTest('ListSerializer'): + list_serializer = BrowsableAPIRendererTests.SimpleSerializer(data=[{'name': 'Name'}], many=True) + form = self.renderer.render_form_for_serializer(list_serializer) + assert form is None, 'Must not return form for list serializer' + + def test_get_raw_data_form(self): + with self.subTest('Serializer'): + class DummyGenericViewsetLike(APIView): + def get_serializer(self, **kwargs): + return BrowsableAPIRendererTests.SimpleSerializer(**kwargs) + + def get(self, request): + response = Response() + response.view = self + return response + + post = get + + view = DummyGenericViewsetLike.as_view() + _request = APIRequestFactory().get('/') + request = Request(_request) + response = view(_request) + view = response.view + + raw_data_form = self.renderer.get_raw_data_form({'name': 'Name'}, view, 'POST', request) + assert raw_data_form['_content'].initial == '{\n "name": ""\n}' + + with self.subTest('ListSerializer'): + class DummyGenericViewsetLike(APIView): + def get_serializer(self, **kwargs): + return BrowsableAPIRendererTests.SimpleSerializer(many=True, **kwargs) # returns ListSerializer + + def get(self, request): + response = Response() + response.view = self + return response + + post = get + + view = DummyGenericViewsetLike.as_view() + _request = APIRequestFactory().get('/') + request = Request(_request) + response = view(_request) + view = response.view + + raw_data_form = self.renderer.get_raw_data_form([{'name': 'Name'}], view, 'POST', request) + assert raw_data_form['_content'].initial == '[\n {\n "name": ""\n }\n]' def test_get_description_returns_empty_string_for_401_and_403_statuses(self): assert self.renderer.get_description({}, status_code=401) == '' From a180bde0fd965915718b070932418cabc831cee1 Mon Sep 17 00:00:00 2001 From: Nancy Eckenthal Date: Mon, 12 Jun 2023 11:21:18 -0400 Subject: [PATCH 1037/1262] Permit mixed casing of string values for BooleanField validation (#8970) * be more permissive of mixed casing in validating strings for BooleanField values * undo unnecessary change * lint --- rest_framework/fields.py | 51 ++++++++++++++++++++++++---------------- tests/test_fields.py | 32 +++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 7079e3332c..4ce9c79c3e 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -664,22 +664,27 @@ class BooleanField(Field): default_empty_html = False initial = False TRUE_VALUES = { - 't', 'T', - 'y', 'Y', 'yes', 'Yes', 'YES', - 'true', 'True', 'TRUE', - 'on', 'On', 'ON', - '1', 1, - True + 't', + 'y', + 'yes', + 'true', + 'on', + '1', + 1, + True, } FALSE_VALUES = { - 'f', 'F', - 'n', 'N', 'no', 'No', 'NO', - 'false', 'False', 'FALSE', - 'off', 'Off', 'OFF', - '0', 0, 0.0, - False + 'f', + 'n', + 'no', + 'false', + 'off', + '0', + 0, + 0.0, + False, } - NULL_VALUES = {'null', 'Null', 'NULL', '', None} + NULL_VALUES = {'null', '', None} def __init__(self, **kwargs): if kwargs.get('allow_null', False): @@ -687,22 +692,28 @@ def __init__(self, **kwargs): self.initial = None super().__init__(**kwargs) + @staticmethod + def _lower_if_str(value): + if isinstance(value, str): + return value.lower() + return value + def to_internal_value(self, data): with contextlib.suppress(TypeError): - if data in self.TRUE_VALUES: + if self._lower_if_str(data) in self.TRUE_VALUES: return True - elif data in self.FALSE_VALUES: + elif self._lower_if_str(data) in self.FALSE_VALUES: return False - elif data in self.NULL_VALUES and self.allow_null: + elif self._lower_if_str(data) in self.NULL_VALUES and self.allow_null: return None - self.fail('invalid', input=data) + self.fail("invalid", input=data) def to_representation(self, value): - if value in self.TRUE_VALUES: + if self._lower_if_str(value) in self.TRUE_VALUES: return True - elif value in self.FALSE_VALUES: + elif self._lower_if_str(value) in self.FALSE_VALUES: return False - if value in self.NULL_VALUES and self.allow_null: + if self._lower_if_str(value) in self.NULL_VALUES and self.allow_null: return None return bool(value) diff --git a/tests/test_fields.py b/tests/test_fields.py index 50d9d77938..03584431e5 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -695,8 +695,24 @@ class TestBooleanField(FieldValues): Valid and invalid values for `BooleanField`. """ valid_inputs = { + 'True': True, + 'TRUE': True, + 'tRuE': True, + 't': True, + 'T': True, 'true': True, + 'on': True, + 'ON': True, + 'oN': True, + 'False': False, + 'FALSE': False, + 'fALse': False, + 'f': False, + 'F': False, 'false': False, + 'off': False, + 'OFF': False, + 'oFf': False, '1': True, '0': False, 1: True, @@ -709,8 +725,24 @@ class TestBooleanField(FieldValues): None: ['This field may not be null.'] } outputs = { + 'True': True, + 'TRUE': True, + 'tRuE': True, + 't': True, + 'T': True, 'true': True, + 'on': True, + 'ON': True, + 'oN': True, + 'False': False, + 'FALSE': False, + 'fALse': False, + 'f': False, + 'F': False, 'false': False, + 'off': False, + 'OFF': False, + 'oFf': False, '1': True, '0': False, 1: True, From 833313496c8ebbdc3509d87895764c822bfc5dc1 Mon Sep 17 00:00:00 2001 From: Lenno Nagel Date: Tue, 13 Jun 2023 07:27:37 +0300 Subject: [PATCH 1038/1262] Removed usage of field.choices that triggered full table load (#8950) Removed the `{{ field.choices|yesno:",disabled" }}` block because this triggers the loading of full database table worth of objects just to determine whether the multi-select widget should be set as disabled or not. Since this "disabled" marking feature is not present in the normal select field, then I propose to remove it also from the multi-select. --- .../templates/rest_framework/horizontal/select_multiple.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/templates/rest_framework/horizontal/select_multiple.html b/rest_framework/templates/rest_framework/horizontal/select_multiple.html index 36ff9fd0dc..12e781cc65 100644 --- a/rest_framework/templates/rest_framework/horizontal/select_multiple.html +++ b/rest_framework/templates/rest_framework/horizontal/select_multiple.html @@ -11,7 +11,7 @@ {% endif %}
      - {% for select in field.iter_options %} {% if select.start_option_group %} From a16dbfd11018fa01ceaf6dee7df34ab0430282cf Mon Sep 17 00:00:00 2001 From: David Smith <39445562+smithdc1@users.noreply.github.com> Date: Tue, 13 Jun 2023 07:55:22 +0100 Subject: [PATCH 1039/1262] Added Deprecation Warnings for CoreAPI (#7519) * Added Deprecation Warnings for CoreAPI * Bumped removal to DRF315 * Update rest_framework/__init__.py * Update rest_framework/filters.py * Update rest_framework/filters.py * Update tests/schemas/test_coreapi.py * Update rest_framework/filters.py * Update rest_framework/filters.py * Update tests/schemas/test_coreapi.py * Update tests/schemas/test_coreapi.py * Update tests/schemas/test_coreapi.py * Update tests/schemas/test_coreapi.py * Update rest_framework/pagination.py * Update rest_framework/pagination.py * Update rest_framework/pagination.py * Update rest_framework/pagination.py * Update rest_framework/schemas/coreapi.py * Update rest_framework/schemas/coreapi.py * Update rest_framework/schemas/coreapi.py * Update rest_framework/schemas/coreapi.py * Update rest_framework/schemas/coreapi.py * Update tests/schemas/test_coreapi.py * Update setup.cfg * Update tests/schemas/test_coreapi.py * Update tests/schemas/test_coreapi.py * Update tests/schemas/test_coreapi.py * Update tests/schemas/test_coreapi.py * Update tests/schemas/test_coreapi.py * Update tests/schemas/test_coreapi.py * Update rest_framework/pagination.py --------- Co-authored-by: Asif Saif Uddin --- rest_framework/__init__.py | 4 +++ rest_framework/filters.py | 8 +++++ rest_framework/pagination.py | 11 +++++++ rest_framework/schemas/coreapi.py | 12 ++++++- setup.cfg | 2 ++ tests/schemas/test_coreapi.py | 55 +++++++++++++++++++++++++++++-- 6 files changed, 89 insertions(+), 3 deletions(-) diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index cc24ce46c5..da7b88dfa2 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -31,3 +31,7 @@ class RemovedInDRF315Warning(DeprecationWarning): pass + + +class RemovedInDRF317Warning(PendingDeprecationWarning): + pass diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 1ffd9edc02..17e6975eb4 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -3,6 +3,7 @@ returned by list views. """ import operator +import warnings from functools import reduce from django.core.exceptions import ImproperlyConfigured @@ -12,6 +13,7 @@ from django.utils.encoding import force_str from django.utils.translation import gettext_lazy as _ +from rest_framework import RemovedInDRF317Warning from rest_framework.compat import coreapi, coreschema, distinct from rest_framework.settings import api_settings @@ -29,6 +31,8 @@ def filter_queryset(self, request, queryset, view): def get_schema_fields(self, view): assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + if coreapi is not None: + warnings.warn('CoreAPI compatibility is deprecated and will be removed in DRF 3.17', RemovedInDRF317Warning) assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`' return [] @@ -146,6 +150,8 @@ def to_html(self, request, queryset, view): def get_schema_fields(self, view): assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + if coreapi is not None: + warnings.warn('CoreAPI compatibility is deprecated and will be removed in DRF 3.17', RemovedInDRF317Warning) assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`' return [ coreapi.Field( @@ -306,6 +312,8 @@ def to_html(self, request, queryset, view): def get_schema_fields(self, view): assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + if coreapi is not None: + warnings.warn('CoreAPI compatibility is deprecated and will be removed in DRF 3.17', RemovedInDRF317Warning) assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`' return [ coreapi.Field( diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index af508bef6d..ce87785472 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -4,6 +4,8 @@ """ import contextlib +import warnings + from base64 import b64decode, b64encode from collections import namedtuple from urllib import parse @@ -15,6 +17,7 @@ from django.utils.encoding import force_str from django.utils.translation import gettext_lazy as _ +from rest_framework import RemovedInDRF317Warning from rest_framework.compat import coreapi, coreschema from rest_framework.exceptions import NotFound from rest_framework.response import Response @@ -152,6 +155,8 @@ def get_results(self, data): def get_schema_fields(self, view): assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + if coreapi is not None: + warnings.warn('CoreAPI compatibility is deprecated and will be removed in DRF 3.17', RemovedInDRF317Warning) return [] def get_schema_operation_parameters(self, view): @@ -311,6 +316,8 @@ def to_html(self): def get_schema_fields(self, view): assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + if coreapi is not None: + warnings.warn('CoreAPI compatibility is deprecated and will be removed in DRF 3.17', RemovedInDRF317Warning) assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`' fields = [ coreapi.Field( @@ -525,6 +532,8 @@ def get_count(self, queryset): def get_schema_fields(self, view): assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + if coreapi is not None: + warnings.warn('CoreAPI compatibility is deprecated and will be removed in DRF 3.17', RemovedInDRF317Warning) assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`' return [ coreapi.Field( @@ -930,6 +939,8 @@ def to_html(self): def get_schema_fields(self, view): assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + if coreapi is not None: + warnings.warn('CoreAPI compatibility is deprecated and will be removed in DRF 3.17', RemovedInDRF317Warning) assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`' fields = [ coreapi.Field( diff --git a/rest_framework/schemas/coreapi.py b/rest_framework/schemas/coreapi.py index 0713e0cb80..582aba196e 100644 --- a/rest_framework/schemas/coreapi.py +++ b/rest_framework/schemas/coreapi.py @@ -5,7 +5,7 @@ from django.db import models from django.utils.encoding import force_str -from rest_framework import exceptions, serializers +from rest_framework import RemovedInDRF317Warning, exceptions, serializers from rest_framework.compat import coreapi, coreschema, uritemplate from rest_framework.settings import api_settings @@ -118,6 +118,8 @@ class SchemaGenerator(BaseSchemaGenerator): def __init__(self, title=None, url=None, description=None, patterns=None, urlconf=None, version=None): assert coreapi, '`coreapi` must be installed for schema support.' + if coreapi is not None: + warnings.warn('CoreAPI compatibility is deprecated and will be removed in DRF 3.17', RemovedInDRF317Warning) assert coreschema, '`coreschema` must be installed for schema support.' super().__init__(title, url, description, patterns, urlconf) @@ -351,6 +353,9 @@ def __init__(self, manual_fields=None): will be added to auto-generated fields, overwriting on `Field.name` """ super().__init__() + if coreapi is not None: + warnings.warn('CoreAPI compatibility is deprecated and will be removed in DRF 3.17', RemovedInDRF317Warning) + if manual_fields is None: manual_fields = [] self._manual_fields = manual_fields @@ -592,6 +597,9 @@ def __init__(self, fields, description='', encoding=None): * `description`: String description for view. Optional. """ super().__init__() + if coreapi is not None: + warnings.warn('CoreAPI compatibility is deprecated and will be removed in DRF 3.17', RemovedInDRF317Warning) + assert all(isinstance(f, coreapi.Field) for f in fields), "`fields` must be a list of coreapi.Field instances" self._fields = fields self._description = description @@ -613,4 +621,6 @@ def get_link(self, path, method, base_url): def is_enabled(): """Is CoreAPI Mode enabled?""" + if coreapi is not None: + warnings.warn('CoreAPI compatibility is deprecated and will be removed in DRF 3.17', RemovedInDRF317Warning) return issubclass(api_settings.DEFAULT_SCHEMA_CLASS, AutoSchema) diff --git a/setup.cfg b/setup.cfg index 294e9afdd6..487d99db91 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,6 +3,8 @@ license_files = LICENSE.md [tool:pytest] addopts=--tb=short --strict-markers -ra +testspath = tests +filterwarnings = ignore:CoreAPI compatibility is deprecated*:rest_framework.RemovedInDRF317Warning [flake8] ignore = E501,W503,W504 diff --git a/tests/schemas/test_coreapi.py b/tests/schemas/test_coreapi.py index eddc5243ec..98fd46f9fc 100644 --- a/tests/schemas/test_coreapi.py +++ b/tests/schemas/test_coreapi.py @@ -7,16 +7,24 @@ from django.urls import include, path from rest_framework import ( - filters, generics, pagination, permissions, serializers + RemovedInDRF317Warning, filters, generics, pagination, permissions, + serializers ) from rest_framework.compat import coreapi, coreschema from rest_framework.decorators import action, api_view, schema +from rest_framework.filters import ( + BaseFilterBackend, OrderingFilter, SearchFilter +) +from rest_framework.pagination import ( + BasePagination, CursorPagination, LimitOffsetPagination, + PageNumberPagination +) from rest_framework.request import Request from rest_framework.routers import DefaultRouter, SimpleRouter from rest_framework.schemas import ( AutoSchema, ManualSchema, SchemaGenerator, get_schema_view ) -from rest_framework.schemas.coreapi import field_to_schema +from rest_framework.schemas.coreapi import field_to_schema, is_enabled from rest_framework.schemas.generators import EndpointEnumerator from rest_framework.schemas.utils import is_list_view from rest_framework.test import APIClient, APIRequestFactory @@ -1433,3 +1441,46 @@ def test_schema_handles_exception(): response.render() assert response.status_code == 403 assert b"You do not have permission to perform this action." in response.content + + +@pytest.mark.skipif(not coreapi, reason='coreapi is not installed') +def test_coreapi_deprecation(): + with pytest.warns(RemovedInDRF317Warning): + SchemaGenerator() + + with pytest.warns(RemovedInDRF317Warning): + AutoSchema() + + with pytest.warns(RemovedInDRF317Warning): + ManualSchema({}) + + with pytest.warns(RemovedInDRF317Warning): + deprecated_filter = OrderingFilter() + deprecated_filter.get_schema_fields({}) + + with pytest.warns(RemovedInDRF317Warning): + deprecated_filter = BaseFilterBackend() + deprecated_filter.get_schema_fields({}) + + with pytest.warns(RemovedInDRF317Warning): + deprecated_filter = SearchFilter() + deprecated_filter.get_schema_fields({}) + + with pytest.warns(RemovedInDRF317Warning): + paginator = BasePagination() + paginator.get_schema_fields({}) + + with pytest.warns(RemovedInDRF317Warning): + paginator = PageNumberPagination() + paginator.get_schema_fields({}) + + with pytest.warns(RemovedInDRF317Warning): + paginator = LimitOffsetPagination() + paginator.get_schema_fields({}) + + with pytest.warns(RemovedInDRF317Warning): + paginator = CursorPagination() + paginator.get_schema_fields({}) + + with pytest.warns(RemovedInDRF317Warning): + is_enabled() From aed7761a8d7e1691a4f4bbf9c83a447dac44d92a Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Tue, 13 Jun 2023 15:01:29 +0600 Subject: [PATCH 1040/1262] Update copy right timeline --- rest_framework/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index da7b88dfa2..b9e3f9817c 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -13,7 +13,7 @@ __version__ = '3.14.0' __author__ = 'Tom Christie' __license__ = 'BSD 3-Clause' -__copyright__ = 'Copyright 2011-2019 Encode OSS Ltd' +__copyright__ = 'Copyright 2011-2023 Encode OSS Ltd' # Version synonym VERSION = __version__ From 71f87a586400074f1840276c5cf36fc7da1c2c4c Mon Sep 17 00:00:00 2001 From: Konstantin Kuchkov Date: Wed, 14 Jun 2023 06:24:09 -0700 Subject: [PATCH 1041/1262] Fix NamespaceVersioning ignoring DEFAULT_VERSION on non-None namespaces (#7278) * Fix the case where if the namespace is not None and there's no match, NamespaceVersioning always raises NotFound even if DEFAULT_VERSION is set or None is in ALLOWED_VERSIONS * Add test cases --- rest_framework/versioning.py | 19 ++++---- tests/test_versioning.py | 93 +++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 10 deletions(-) diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py index c2764c7a40..a1c0ce4d7b 100644 --- a/rest_framework/versioning.py +++ b/rest_framework/versioning.py @@ -119,15 +119,16 @@ class NamespaceVersioning(BaseVersioning): def determine_version(self, request, *args, **kwargs): resolver_match = getattr(request, 'resolver_match', None) - if resolver_match is None or not resolver_match.namespace: - return self.default_version - - # Allow for possibly nested namespaces. - possible_versions = resolver_match.namespace.split(':') - for version in possible_versions: - if self.is_allowed_version(version): - return version - raise exceptions.NotFound(self.invalid_version_message) + if resolver_match is not None and resolver_match.namespace: + # Allow for possibly nested namespaces. + possible_versions = resolver_match.namespace.split(':') + for version in possible_versions: + if self.is_allowed_version(version): + return version + + if not self.is_allowed_version(self.default_version): + raise exceptions.NotFound(self.invalid_version_message) + return self.default_version def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): if request.version is not None: diff --git a/tests/test_versioning.py b/tests/test_versioning.py index b216461840..1ccecae0bf 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -272,7 +272,7 @@ class FakeResolverMatch(ResolverMatch): assert response.status_code == status.HTTP_404_NOT_FOUND -class TestAllowedAndDefaultVersion: +class TestAcceptHeaderAllowedAndDefaultVersion: def test_missing_without_default(self): scheme = versioning.AcceptHeaderVersioning view = AllowedVersionsView.as_view(versioning_class=scheme) @@ -318,6 +318,97 @@ def test_missing_with_default_and_none_allowed(self): assert response.data == {'version': 'v2'} +class TestNamespaceAllowedAndDefaultVersion: + def test_no_namespace_without_default(self): + class FakeResolverMatch: + namespace = None + + scheme = versioning.NamespaceVersioning + view = AllowedVersionsView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/') + request.resolver_match = FakeResolverMatch + response = view(request) + assert response.status_code == status.HTTP_404_NOT_FOUND + + def test_no_namespace_with_default(self): + class FakeResolverMatch: + namespace = None + + scheme = versioning.NamespaceVersioning + view = AllowedAndDefaultVersionsView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/') + request.resolver_match = FakeResolverMatch + response = view(request) + assert response.status_code == status.HTTP_200_OK + assert response.data == {'version': 'v2'} + + def test_no_match_without_default(self): + class FakeResolverMatch: + namespace = 'no_match' + + scheme = versioning.NamespaceVersioning + view = AllowedVersionsView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/') + request.resolver_match = FakeResolverMatch + response = view(request) + assert response.status_code == status.HTTP_404_NOT_FOUND + + def test_no_match_with_default(self): + class FakeResolverMatch: + namespace = 'no_match' + + scheme = versioning.NamespaceVersioning + view = AllowedAndDefaultVersionsView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/') + request.resolver_match = FakeResolverMatch + response = view(request) + assert response.status_code == status.HTTP_200_OK + assert response.data == {'version': 'v2'} + + def test_with_default(self): + class FakeResolverMatch: + namespace = 'v1' + + scheme = versioning.NamespaceVersioning + view = AllowedAndDefaultVersionsView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/') + request.resolver_match = FakeResolverMatch + response = view(request) + assert response.status_code == status.HTTP_200_OK + assert response.data == {'version': 'v1'} + + def test_no_match_without_default_but_none_allowed(self): + class FakeResolverMatch: + namespace = 'no_match' + + scheme = versioning.NamespaceVersioning + view = AllowedWithNoneVersionsView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/') + request.resolver_match = FakeResolverMatch + response = view(request) + assert response.status_code == status.HTTP_200_OK + assert response.data == {'version': None} + + def test_no_match_with_default_and_none_allowed(self): + class FakeResolverMatch: + namespace = 'no_match' + + scheme = versioning.NamespaceVersioning + view = AllowedWithNoneAndDefaultVersionsView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/') + request.resolver_match = FakeResolverMatch + response = view(request) + assert response.status_code == status.HTTP_200_OK + assert response.data == {'version': 'v2'} + + class TestHyperlinkedRelatedField(URLPatternsTestCase, APITestCase): included = [ path('namespaced//', dummy_pk_view, name='namespaced'), From 9cfa4bd7cca19df0bc8e456d906c3ab7ce285cf4 Mon Sep 17 00:00:00 2001 From: rizwanshaikh Date: Sat, 17 Jun 2023 08:48:25 +0530 Subject: [PATCH 1042/1262] Fix OpenAPI Schema yaml rendering for timedelta (#9007) * fix OpenAPIRenderer for timedelta * added test for rendering openapi with timedelta * fix OpenAPIRenderer for timedelta * added test for rendering openapi with timedelta * Removed usage of field.choices that triggered full table load (#8950) Removed the `{{ field.choices|yesno:",disabled" }}` block because this triggers the loading of full database table worth of objects just to determine whether the multi-select widget should be set as disabled or not. Since this "disabled" marking feature is not present in the normal select field, then I propose to remove it also from the multi-select. * Added Deprecation Warnings for CoreAPI (#7519) * Added Deprecation Warnings for CoreAPI * Bumped removal to DRF315 * Update rest_framework/__init__.py * Update rest_framework/filters.py * Update rest_framework/filters.py * Update tests/schemas/test_coreapi.py * Update rest_framework/filters.py * Update rest_framework/filters.py * Update tests/schemas/test_coreapi.py * Update tests/schemas/test_coreapi.py * Update setup.cfg * Update rest_framework/pagination.py --------- Co-authored-by: Asif Saif Uddin * Update copy right timeline * Fix NamespaceVersioning ignoring DEFAULT_VERSION on non-None namespaces (#7278) * Fix the case where if the namespace is not None and there's no match, NamespaceVersioning always raises NotFound even if DEFAULT_VERSION is set or None is in ALLOWED_VERSIONS * Add test cases * fix OpenAPIRenderer for timedelta * added test for rendering openapi with timedelta * added testcase for rendering yaml with minvalidator for duration field (timedelta) --------- Co-authored-by: Rizwan Shaikh Co-authored-by: Lenno Nagel Co-authored-by: David Smith <39445562+smithdc1@users.noreply.github.com> Co-authored-by: Asif Saif Uddin Co-authored-by: Konstantin Kuchkov --- rest_framework/renderers.py | 2 ++ rest_framework/utils/encoders.py | 11 +++++++++++ tests/schemas/test_openapi.py | 25 +++++++++++++++++++++++++ tests/schemas/views.py | 5 +++++ 4 files changed, 43 insertions(+) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 0a3b03729d..db1fdd128b 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -9,6 +9,7 @@ import base64 import contextlib +import datetime from urllib import parse from django import forms @@ -1062,6 +1063,7 @@ class Dumper(yaml.Dumper): def ignore_aliases(self, data): return True Dumper.add_representer(SafeString, Dumper.represent_str) + Dumper.add_representer(datetime.timedelta, encoders.CustomScalar.represent_timedelta) return yaml.dump(data, default_flow_style=False, sort_keys=False, Dumper=Dumper).encode('utf-8') diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index 35a89eb090..aa45422861 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -65,3 +65,14 @@ def default(self, obj): elif hasattr(obj, '__iter__'): return tuple(item for item in obj) return super().default(obj) + + +class CustomScalar: + """ + CustomScalar that knows how to encode timedelta that renderer + can understand. + """ + @classmethod + def represent_timedelta(cls, dumper, data): + value = str(data.total_seconds()) + return dumper.represent_scalar('tag:yaml.org,2002:str', value) diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index 0ea6d1ff92..1eb5b84b71 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -1162,6 +1162,31 @@ def test_schema_rendering_to_json(self): assert b'"openapi": "' in ret assert b'"default": "0.0"' in ret + def test_schema_rendering_to_yaml(self): + patterns = [ + path('example/', views.ExampleGenericAPIView.as_view()), + ] + generator = SchemaGenerator(patterns=patterns) + + request = create_request('/') + schema = generator.get_schema(request=request) + ret = OpenAPIRenderer().render(schema) + assert b"openapi: " in ret + assert b"default: '0.0'" in ret + + def test_schema_rendering_timedelta_to_yaml_with_validator(self): + + patterns = [ + path('example/', views.ExampleValidatedAPIView.as_view()), + ] + generator = SchemaGenerator(patterns=patterns) + + request = create_request('/') + schema = generator.get_schema(request=request) + ret = OpenAPIRenderer().render(schema) + assert b"openapi: " in ret + assert b"duration:\n type: string\n minimum: \'10.0\'\n" in ret + def test_schema_with_no_paths(self): patterns = [] generator = SchemaGenerator(patterns=patterns) diff --git a/tests/schemas/views.py b/tests/schemas/views.py index f1ed0bd4e3..c08208bf26 100644 --- a/tests/schemas/views.py +++ b/tests/schemas/views.py @@ -134,6 +134,11 @@ class ExampleValidatedSerializer(serializers.Serializer): ip4 = serializers.IPAddressField(protocol='ipv4') ip6 = serializers.IPAddressField(protocol='ipv6') ip = serializers.IPAddressField() + duration = serializers.DurationField( + validators=( + MinValueValidator(timedelta(seconds=10)), + ) + ) class ExampleValidatedAPIView(generics.GenericAPIView): From 8b7e6f2e3405d6e71488db53f2dda515d5a336c6 Mon Sep 17 00:00:00 2001 From: Samiul Sk Date: Tue, 20 Jun 2023 17:13:33 +0530 Subject: [PATCH 1043/1262] Update pre-commit.yml (#9012) --- .github/workflows/pre-commit.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 9c29ed0564..36d3564939 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -8,17 +8,17 @@ on: jobs: pre-commit: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: "3.10" - - uses: pre-commit/action@v2.0.0 + - uses: pre-commit/action@v3.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} From 8dd4250d0234ddf6c6a19a806639678ef7786468 Mon Sep 17 00:00:00 2001 From: Samiul Sk Date: Wed, 21 Jun 2023 10:35:44 +0530 Subject: [PATCH 1044/1262] remove unnecessary line which was causing isort error (#9014) --- rest_framework/pagination.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index ce87785472..7303890b03 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -5,7 +5,6 @@ import contextlib import warnings - from base64 import b64decode, b64encode from collections import namedtuple from urllib import parse From 4f7e9ed3bbb0fb3567884ef4e3d340bc2f77bc68 Mon Sep 17 00:00:00 2001 From: Andrew <54764942+drewg3r@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:57:20 +0200 Subject: [PATCH 1045/1262] Fix SearchFilter renders field with invalid value (#9023) Co-authored-by: Andrii Tarasenko --- rest_framework/filters.py | 4 +--- tests/test_filters.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 17e6975eb4..c48504957c 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -139,11 +139,9 @@ def to_html(self, request, queryset, view): if not getattr(view, 'search_fields', None): return '' - term = self.get_search_terms(request) - term = term[0] if term else '' context = { 'param': self.search_param, - 'term': term + 'term': request.query_params.get(self.search_param, ''), } template = loader.get_template(self.template) return template.render(context) diff --git a/tests/test_filters.py b/tests/test_filters.py index 37ae4c7cf3..2a22e30f97 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -225,6 +225,23 @@ def as_sql(self, compiler, connection): {'id': 2, 'title': 'zz', 'text': 'bcd'}, ] + def test_search_field_with_multiple_words(self): + class SearchListView(generics.ListAPIView): + queryset = SearchFilterModel.objects.all() + serializer_class = SearchFilterSerializer + filter_backends = (filters.SearchFilter,) + search_fields = ('title', 'text') + + search_query = 'foo bar,baz' + view = SearchListView() + request = factory.get('/', {'search': search_query}) + request = view.initialize_request(request) + + rendered_search_field = filters.SearchFilter().to_html( + request=request, queryset=view.queryset, view=view + ) + assert search_query in rendered_search_field + class AttributeModel(models.Model): label = models.CharField(max_length=32) From 66d86d0177673b90723664e3fde2919df66f2b60 Mon Sep 17 00:00:00 2001 From: Burson Date: Thu, 13 Jul 2023 20:50:49 +0800 Subject: [PATCH 1046/1262] Fix choices in ChoiceField to support IntEnum (#8955) Python support Enum in version 3.4, but changed __str__ to int.__str__ until version 3.11 to better support the replacement of existing constants use-case. [https://docs.python.org/3/library/enum.html#enum.IntEnum](https://docs.python.org/3/library/enum.html#enum.IntEnum) rest_frame work support Python 3.6+, this commit will support the Enum in choices of Field. --- rest_framework/fields.py | 17 ++++++----------- tests/test_fields.py | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 4ce9c79c3e..0b56fa7fb6 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -8,6 +8,7 @@ import re import uuid from collections.abc import Mapping +from enum import Enum from django.conf import settings from django.core.exceptions import ObjectDoesNotExist @@ -17,7 +18,6 @@ MinValueValidator, ProhibitNullCharactersValidator, RegexValidator, URLValidator, ip_address_validators ) -from django.db.models import IntegerChoices, TextChoices from django.forms import FilePathField as DjangoFilePathField from django.forms import ImageField as DjangoImageField from django.utils import timezone @@ -1401,11 +1401,8 @@ def __init__(self, choices, **kwargs): def to_internal_value(self, data): if data == '' and self.allow_blank: return '' - - if isinstance(data, (IntegerChoices, TextChoices)) and str(data) != \ - str(data.value): + if isinstance(data, Enum) and str(data) != str(data.value): data = data.value - try: return self.choice_strings_to_values[str(data)] except KeyError: @@ -1414,11 +1411,8 @@ def to_internal_value(self, data): def to_representation(self, value): if value in ('', None): return value - - if isinstance(value, (IntegerChoices, TextChoices)) and str(value) != \ - str(value.value): + if isinstance(value, Enum) and str(value) != str(value.value): value = value.value - return self.choice_strings_to_values.get(str(value), value) def iter_options(self): @@ -1442,8 +1436,7 @@ def _set_choices(self, choices): # Allows us to deal with eg. integer choices while supporting either # integer or string input, but still get the correct datatype out. self.choice_strings_to_values = { - str(key.value) if isinstance(key, (IntegerChoices, TextChoices)) - and str(key) != str(key.value) else str(key): key for key in self.choices + str(key.value) if isinstance(key, Enum) and str(key) != str(key.value) else str(key): key for key in self.choices } choices = property(_get_choices, _set_choices) @@ -1829,6 +1822,7 @@ class HiddenField(Field): constraint on a pair of fields, as we need some way to include the date in the validated data. """ + def __init__(self, **kwargs): assert 'default' in kwargs, 'default is a required argument.' kwargs['write_only'] = True @@ -1858,6 +1852,7 @@ class ExampleSerializer(Serializer): def get_extra_info(self, obj): return ... # Calculate some data to return. """ + def __init__(self, method_name=None, **kwargs): self.method_name = method_name kwargs['source'] = '*' diff --git a/tests/test_fields.py b/tests/test_fields.py index 03584431e5..7006d473c2 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1875,6 +1875,31 @@ def test_edit_choices(self): field.run_validation(2) assert exc_info.value.detail == ['"2" is not a valid choice.'] + def test_enum_integer_choices(self): + from enum import IntEnum + + class ChoiceCase(IntEnum): + first = auto() + second = auto() + # Enum validate + choices = [ + (ChoiceCase.first, "1"), + (ChoiceCase.second, "2") + ] + field = serializers.ChoiceField(choices=choices) + assert field.run_validation(1) == 1 + assert field.run_validation(ChoiceCase.first) == 1 + assert field.run_validation("1") == 1 + # Enum.value validate + choices = [ + (ChoiceCase.first.value, "1"), + (ChoiceCase.second.value, "2") + ] + field = serializers.ChoiceField(choices=choices) + assert field.run_validation(1) == 1 + assert field.run_validation(ChoiceCase.first) == 1 + assert field.run_validation("1") == 1 + def test_integer_choices(self): class ChoiceCase(IntegerChoices): first = auto() From 7658ffd71d4ad07ddada20b2b9538b889ec02403 Mon Sep 17 00:00:00 2001 From: Sergey Klyuykov Date: Sat, 15 Jul 2023 02:26:56 -0700 Subject: [PATCH 1047/1262] Fix: Pagination response schemas. (#9049) --- rest_framework/pagination.py | 3 +++ tests/test_pagination.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 7303890b03..2b20e76af5 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -239,6 +239,7 @@ def get_paginated_response(self, data): def get_paginated_response_schema(self, schema): return { 'type': 'object', + 'required': ['count', 'results'], 'properties': { 'count': { 'type': 'integer', @@ -411,6 +412,7 @@ def get_paginated_response(self, data): def get_paginated_response_schema(self, schema): return { 'type': 'object', + 'required': ['count', 'results'], 'properties': { 'count': { 'type': 'integer', @@ -906,6 +908,7 @@ def get_paginated_response(self, data): def get_paginated_response_schema(self, schema): return { 'type': 'object', + 'required': ['results'], 'properties': { 'next': { 'type': 'string', diff --git a/tests/test_pagination.py b/tests/test_pagination.py index d606986ab7..090eb0d813 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -274,6 +274,7 @@ def test_get_paginated_response_schema(self): assert self.pagination.get_paginated_response_schema(unpaginated_schema) == { 'type': 'object', + 'required': ['count', 'results'], 'properties': { 'count': { 'type': 'integer', @@ -585,6 +586,7 @@ def test_get_paginated_response_schema(self): assert self.pagination.get_paginated_response_schema(unpaginated_schema) == { 'type': 'object', + 'required': ['count', 'results'], 'properties': { 'count': { 'type': 'integer', @@ -937,6 +939,7 @@ def test_get_paginated_response_schema(self): assert self.pagination.get_paginated_response_schema(unpaginated_schema) == { 'type': 'object', + 'required': ['results'], 'properties': { 'next': { 'type': 'string', From 5c3b6e496c9892463f48f6b50cf9a0f1d2c29e78 Mon Sep 17 00:00:00 2001 From: Amin Aminian <47900904+aminiun@users.noreply.github.com> Date: Tue, 25 Jul 2023 09:51:25 +0200 Subject: [PATCH 1048/1262] class name added to unkown field error (#9019) --- rest_framework/serializers.py | 4 ++-- tests/test_model_serializer.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 56fa918dc8..6ee75fbc1f 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1372,8 +1372,8 @@ def build_unknown_field(self, field_name, model_class): Raise an error on any unknown fields. """ raise ImproperlyConfigured( - 'Field name `%s` is not valid for model `%s`.' % - (field_name, model_class.__name__) + 'Field name `%s` is not valid for model `%s` in `%s.%s`.' % + (field_name, model_class.__name__, self.__class__.__module__, self.__class__.__name__) ) def include_extra_kwargs(self, kwargs, extra_kwargs): diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index c5ac888f55..e2d4bbc30c 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -315,7 +315,8 @@ class Meta: model = RegularFieldsModel fields = ('auto_field', 'invalid') - expected = 'Field name `invalid` is not valid for model `RegularFieldsModel`.' + expected = 'Field name `invalid` is not valid for model `RegularFieldsModel` ' \ + 'in `tests.test_model_serializer.TestSerializer`.' with self.assertRaisesMessage(ImproperlyConfigured, expected): TestSerializer().fields From b99df0cf780adc3d65362a4425f9bb6d85410bcb Mon Sep 17 00:00:00 2001 From: Devid Date: Tue, 25 Jul 2023 14:01:23 +0100 Subject: [PATCH 1049/1262] Align SearchFilter behaviour to django.contrib.admin search (#9017) * Use subquery to remove duplicates in SearchFilter * Align SearchFilter behaviour to django.contrib.admin * Add compatibility with older django/python versions * Allow search to split also by comma after smart split * Use generator to build search conditions to reduce iterations * Improve search documentation * Update docs/api-guide/filtering.md --------- Co-authored-by: Asif Saif Uddin --- docs/api-guide/filtering.md | 18 ++++---- rest_framework/compat.py | 8 ---- rest_framework/filters.py | 83 +++++++++++++++++++++++++++---------- tests/test_filters.py | 77 ++++++++++++++++++++++++++++++++-- 4 files changed, 146 insertions(+), 40 deletions(-) diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 47ea8592df..ff5f3c7759 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -213,19 +213,23 @@ This will allow the client to filter the items in the list by making queries suc You can also perform a related lookup on a ForeignKey or ManyToManyField with the lookup API double-underscore notation: search_fields = ['username', 'email', 'profile__profession'] - + For [JSONField][JSONField] and [HStoreField][HStoreField] fields you can filter based on nested values within the data structure using the same double-underscore notation: search_fields = ['data__breed', 'data__owner__other_pets__0__name'] -By default, searches will use case-insensitive partial matches. The search parameter may contain multiple search terms, which should be whitespace and/or comma separated. If multiple search terms are used then objects will be returned in the list only if all the provided terms are matched. +By default, searches will use case-insensitive partial matches. The search parameter may contain multiple search terms, which should be whitespace and/or comma separated. If multiple search terms are used then objects will be returned in the list only if all the provided terms are matched. Searches may contain _quoted phrases_ with spaces, each phrase is considered as a single search term. + -The search behavior may be restricted by prepending various characters to the `search_fields`. +The search behavior may be specified by prefixing field names in `search_fields` with one of the following characters (which is equivalent to adding `__` to the field): -* '^' Starts-with search. -* '=' Exact matches. -* '@' Full-text search. (Currently only supported Django's [PostgreSQL backend][postgres-search].) -* '$' Regex search. +| Prefix | Lookup | | +| ------ | --------------| ------------------ | +| `^` | `istartswith` | Starts-with search.| +| `=` | `iexact` | Exact matches. | +| `$` | `iregex` | Regex search. | +| `@` | `search` | Full-text search (Currently only supported Django's [PostgreSQL backend][postgres-search]). | +| None | `icontains` | Contains search (Default). | For example: diff --git a/rest_framework/compat.py b/rest_framework/compat.py index ac5cbc572a..7e80704e11 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -3,7 +3,6 @@ versions of Django/Python, and compatibility wrappers around optional packages. """ import django -from django.conf import settings from django.views.generic import View @@ -14,13 +13,6 @@ def unicode_http_header(value): return value -def distinct(queryset, base): - if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle": - # distinct analogue for Oracle users - return base.filter(pk__in=set(queryset.values_list('pk', flat=True))) - return queryset.distinct() - - # django.contrib.postgres requires psycopg2 try: from django.contrib.postgres import fields as postgres_fields diff --git a/rest_framework/filters.py b/rest_framework/filters.py index c48504957c..065e72f94a 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -6,18 +6,35 @@ import warnings from functools import reduce -from django.core.exceptions import ImproperlyConfigured +from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured from django.db import models from django.db.models.constants import LOOKUP_SEP from django.template import loader from django.utils.encoding import force_str +from django.utils.text import smart_split, unescape_string_literal from django.utils.translation import gettext_lazy as _ from rest_framework import RemovedInDRF317Warning -from rest_framework.compat import coreapi, coreschema, distinct +from rest_framework.compat import coreapi, coreschema +from rest_framework.fields import CharField from rest_framework.settings import api_settings +def search_smart_split(search_terms): + """generator that first splits string by spaces, leaving quoted phrases togheter, + then it splits non-quoted phrases by commas. + """ + for term in smart_split(search_terms): + # trim commas to avoid bad matching for quoted phrases + term = term.strip(',') + if term.startswith(('"', "'")) and term[0] == term[-1]: + # quoted phrases are kept togheter without any other split + yield unescape_string_literal(term) + else: + # non-quoted tokens are split by comma, keeping only non-empty ones + yield from (sub_term.strip() for sub_term in term.split(',') if sub_term) + + class BaseFilterBackend: """ A base class from which all filter backend classes should inherit. @@ -64,18 +81,41 @@ def get_search_fields(self, view, request): def get_search_terms(self, request): """ Search terms are set by a ?search=... query parameter, - and may be comma and/or whitespace delimited. + and may be whitespace delimited. """ - params = request.query_params.get(self.search_param, '') - params = params.replace('\x00', '') # strip null characters - params = params.replace(',', ' ') - return params.split() + value = request.query_params.get(self.search_param, '') + field = CharField(trim_whitespace=False, allow_blank=True) + return field.run_validation(value) - def construct_search(self, field_name): + def construct_search(self, field_name, queryset): lookup = self.lookup_prefixes.get(field_name[0]) if lookup: field_name = field_name[1:] else: + # Use field_name if it includes a lookup. + opts = queryset.model._meta + lookup_fields = field_name.split(LOOKUP_SEP) + # Go through the fields, following all relations. + prev_field = None + for path_part in lookup_fields: + if path_part == "pk": + path_part = opts.pk.name + try: + field = opts.get_field(path_part) + except FieldDoesNotExist: + # Use valid query lookups. + if prev_field and prev_field.get_lookup(path_part): + return field_name + else: + prev_field = field + if hasattr(field, "path_infos"): + # Update opts to follow the relation. + opts = field.path_infos[-1].to_opts + # django < 4.1 + elif hasattr(field, 'get_path_info'): + # Update opts to follow the relation. + opts = field.get_path_info()[-1].to_opts + # Otherwise, use the field with icontains. lookup = 'icontains' return LOOKUP_SEP.join([field_name, lookup]) @@ -113,26 +153,27 @@ def filter_queryset(self, request, queryset, view): return queryset orm_lookups = [ - self.construct_search(str(search_field)) + self.construct_search(str(search_field), queryset) for search_field in search_fields ] base = queryset - conditions = [] - for search_term in search_terms: - queries = [ - models.Q(**{orm_lookup: search_term}) - for orm_lookup in orm_lookups - ] - conditions.append(reduce(operator.or_, queries)) + # generator which for each term builds the corresponding search + conditions = ( + reduce( + operator.or_, + (models.Q(**{orm_lookup: term}) for orm_lookup in orm_lookups) + ) for term in search_smart_split(search_terms) + ) queryset = queryset.filter(reduce(operator.and_, conditions)) + # Remove duplicates from results, if necessary if self.must_call_distinct(queryset, search_fields): - # Filtering against a many-to-many field requires us to - # call queryset.distinct() in order to avoid duplicate items - # in the resulting queryset. - # We try to avoid this if possible, for performance reasons. - queryset = distinct(queryset, base) + # inspired by django.contrib.admin + # this is more accurate than .distinct form M2M relationship + # also is cross-database + queryset = queryset.filter(pk=models.OuterRef('pk')) + queryset = base.filter(models.Exists(queryset)) return queryset def to_html(self, request, queryset, view): diff --git a/tests/test_filters.py b/tests/test_filters.py index 2a22e30f97..6db0c3deb2 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -6,16 +6,36 @@ from django.db import models from django.db.models import CharField, Transform from django.db.models.functions import Concat, Upper -from django.test import TestCase +from django.test import SimpleTestCase, TestCase from django.test.utils import override_settings from rest_framework import filters, generics, serializers from rest_framework.compat import coreschema +from rest_framework.exceptions import ValidationError from rest_framework.test import APIRequestFactory factory = APIRequestFactory() +class SearchSplitTests(SimpleTestCase): + + def test_keep_quoted_togheter_regardless_of_commas(self): + assert ['hello, world'] == list(filters.search_smart_split('"hello, world"')) + + def test_strips_commas_around_quoted(self): + assert ['hello, world'] == list(filters.search_smart_split(',,"hello, world"')) + assert ['hello, world'] == list(filters.search_smart_split(',,"hello, world",,')) + assert ['hello, world'] == list(filters.search_smart_split('"hello, world",,')) + + def test_splits_by_comma(self): + assert ['hello', 'world'] == list(filters.search_smart_split(',,hello, world')) + assert ['hello', 'world'] == list(filters.search_smart_split(',,hello, world,,')) + assert ['hello', 'world'] == list(filters.search_smart_split('hello, world,,')) + + def test_splits_quotes_followed_by_comma_and_sentence(self): + assert ['"hello', 'world"', 'found'] == list(filters.search_smart_split('"hello, world",found')) + + class BaseFilterTests(TestCase): def setUp(self): self.original_coreapi = filters.coreapi @@ -50,7 +70,8 @@ class Meta: class SearchFilterTests(TestCase): - def setUp(self): + @classmethod + def setUpTestData(cls): # Sequence of title/text is: # # z abc @@ -66,6 +87,9 @@ def setUp(self): ) SearchFilterModel(title=title, text=text).save() + SearchFilterModel(title='A title', text='The long text').save() + SearchFilterModel(title='The title', text='The "text').save() + def test_search(self): class SearchListView(generics.ListAPIView): queryset = SearchFilterModel.objects.all() @@ -186,9 +210,21 @@ def test_search_field_with_null_characters(self): request = factory.get('/?search=\0as%00d\x00f') request = view.initialize_request(request) - terms = filters.SearchFilter().get_search_terms(request) + with self.assertRaises(ValidationError): + filters.SearchFilter().get_search_terms(request) - assert terms == ['asdf'] + def test_search_field_with_custom_lookup(self): + class SearchListView(generics.ListAPIView): + queryset = SearchFilterModel.objects.all() + serializer_class = SearchFilterSerializer + filter_backends = (filters.SearchFilter,) + search_fields = ('text__iendswith',) + view = SearchListView.as_view() + request = factory.get('/', {'search': 'c'}) + response = view(request) + assert response.data == [ + {'id': 1, 'title': 'z', 'text': 'abc'}, + ] def test_search_field_with_additional_transforms(self): from django.test.utils import register_lookup @@ -242,6 +278,32 @@ class SearchListView(generics.ListAPIView): ) assert search_query in rendered_search_field + def test_search_field_with_escapes(self): + class SearchListView(generics.ListAPIView): + queryset = SearchFilterModel.objects.all() + serializer_class = SearchFilterSerializer + filter_backends = (filters.SearchFilter,) + search_fields = ('title', 'text',) + view = SearchListView.as_view() + request = factory.get('/', {'search': '"\\\"text"'}) + response = view(request) + assert response.data == [ + {'id': 12, 'title': 'The title', 'text': 'The "text'}, + ] + + def test_search_field_with_quotes(self): + class SearchListView(generics.ListAPIView): + queryset = SearchFilterModel.objects.all() + serializer_class = SearchFilterSerializer + filter_backends = (filters.SearchFilter,) + search_fields = ('title', 'text',) + view = SearchListView.as_view() + request = factory.get('/', {'search': '"long text"'}) + response = view(request) + assert response.data == [ + {'id': 11, 'title': 'A title', 'text': 'The long text'}, + ] + class AttributeModel(models.Model): label = models.CharField(max_length=32) @@ -284,6 +346,13 @@ def test_must_call_distinct_restores_meta_for_each_field(self): ["%sattribute__label" % prefix, "%stitle" % prefix] ) + def test_custom_lookup_to_related_model(self): + # In this test case the attribute of the fk model comes first in the + # list of search fields. + filter_ = filters.SearchFilter() + assert 'attribute__label__icontains' == filter_.construct_search('attribute__label', SearchFilterModelFk._meta) + assert 'attribute__label__iendswith' == filter_.construct_search('attribute__label__iendswith', SearchFilterModelFk._meta) + class SearchFilterModelM2M(models.Model): title = models.CharField(max_length=20) From 589b5dca9e7613f7742af8baed6ed870476dd23b Mon Sep 17 00:00:00 2001 From: Pierre Chiquet Date: Wed, 26 Jul 2023 06:27:49 +0200 Subject: [PATCH 1050/1262] Allow to override child.run_validation call in ListSerializer (#8035) * Separated run_child_validation method in ListSerializer * fix typo * Add test_update_allow_custom_child_validation --------- Co-authored-by: Pierre Chiquet --- rest_framework/serializers.py | 13 +++++++- tests/test_serializer_lists.py | 57 +++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 6ee75fbc1f..77c181b6cc 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -653,6 +653,17 @@ def run_validation(self, data=empty): return value + def run_child_validation(self, data): + """ + Run validation on child serializer. + You may need to override this method to support multiple updates. For example: + + self.child.instance = self.instance.get(pk=data['id']) + self.child.initial_data = data + return super().run_child_validation(data) + """ + return self.child.run_validation(data) + def to_internal_value(self, data): """ List of dicts of native values <- List of dicts of primitive datatypes. @@ -697,7 +708,7 @@ def to_internal_value(self, data): ): self.child.instance = self.instance[idx] try: - validated = self.child.run_validation(item) + validated = self.run_child_validation(item) except ValidationError as exc: errors.append(exc.detail) else: diff --git a/tests/test_serializer_lists.py b/tests/test_serializer_lists.py index 10463d29ab..4070de7a51 100644 --- a/tests/test_serializer_lists.py +++ b/tests/test_serializer_lists.py @@ -153,6 +153,61 @@ def test_validate_html_input(self): assert serializer.is_valid() assert serializer.validated_data == expected_output + def test_update_allow_custom_child_validation(self): + """ + Update a list of objects thanks custom run_child_validation implementation. + """ + + class TestUpdateSerializer(serializers.Serializer): + integer = serializers.IntegerField() + boolean = serializers.BooleanField() + + def update(self, instance, validated_data): + instance._data.update(validated_data) + return instance + + def validate(self, data): + # self.instance is set to current BasicObject instance + assert isinstance(self.instance, BasicObject) + # self.initial_data is current dictionary + assert isinstance(self.initial_data, dict) + assert self.initial_data["pk"] == self.instance.pk + return super().validate(data) + + class ListUpdateSerializer(serializers.ListSerializer): + child = TestUpdateSerializer() + + def run_child_validation(self, data): + # find related instance in self.instance list + child_instance = next(o for o in self.instance if o.pk == data["pk"]) + # set instance and initial_data for child serializer + self.child.instance = child_instance + self.child.initial_data = data + return super().run_child_validation(data) + + def update(self, instance, validated_data): + return [ + self.child.update(instance, attrs) + for instance, attrs in zip(self.instance, validated_data) + ] + + instance = [ + BasicObject(pk=1, integer=11, private_field="a"), + BasicObject(pk=2, integer=22, private_field="b"), + ] + input_data = [ + {"pk": 1, "integer": "123", "boolean": "true"}, + {"pk": 2, "integer": "456", "boolean": "false"}, + ] + expected_output = [ + BasicObject(pk=1, integer=123, boolean=True, private_field="a"), + BasicObject(pk=2, integer=456, boolean=False, private_field="b"), + ] + serializer = ListUpdateSerializer(instance, data=input_data) + assert serializer.is_valid() + updated_instances = serializer.save() + assert updated_instances == expected_output + class TestNestedListSerializer: """ @@ -481,7 +536,7 @@ class Serializer(serializers.Serializer): assert serializer.validated_data == {} assert serializer.errors == {} - def test_udate_as_field_allow_empty_true(self): + def test_update_as_field_allow_empty_true(self): class ListSerializer(serializers.Serializer): update_field = serializers.IntegerField() store_field = serializers.IntegerField() From 7a9db57eaf829367601fbaac5f1811d1fcb712fb Mon Sep 17 00:00:00 2001 From: rizwanshaikh Date: Mon, 7 Aug 2023 12:05:54 +0530 Subject: [PATCH 1051/1262] Propagate 'default' from model_field to serializer field. (#9030) * Propagate 'default' from model_field to serializer field Fix #7469. Co-authored-by: Nikhil Benesch * updated field default on serializer according to openapi generation and added that to options action response * added notes regarding default value propagation from model to serializer field * updated note * Update docs/api-guide/fields.md * Update docs/api-guide/fields.md * Update docs/api-guide/fields.md * Update docs/api-guide/fields.md * Update docs/api-guide/fields.md * Update docs/api-guide/fields.md --------- Co-authored-by: John Parton Co-authored-by: Nikhil Benesch Co-authored-by: Rizwan Shaikh Co-authored-by: Asif Saif Uddin --- docs/api-guide/fields.md | 8 ++ rest_framework/metadata.py | 4 + rest_framework/utils/field_mapping.py | 4 + tests/test_metadata.py | 129 ++++++++++++++++++++++++++ tests/test_model_serializer.py | 6 +- 5 files changed, 148 insertions(+), 3 deletions(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 4ac3e3c31a..7c27162187 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -68,6 +68,14 @@ When serializing the instance, default will be used if the object attribute or d Note that setting a `default` value implies that the field is not required. Including both the `default` and `required` keyword arguments is invalid and will raise an error. +Notes regarding default value propagation from model to serializer: + +All the default values from model will pass as default to the serializer and the options method. + +If the default is callable then it will be propagated to & evaluated every time in the serializer but not in options method. + +If the value for given field is not given then default value will be present in the serializer and available in serializer's methods. Specified validation on given field will be evaluated on default value as that field will be present in the serializer. + ### `allow_null` Normally an error will be raised if `None` is passed to a serializer field. Set this keyword argument to `True` if `None` should be considered a valid value. diff --git a/rest_framework/metadata.py b/rest_framework/metadata.py index 364ca5b14d..fd0f4e163d 100644 --- a/rest_framework/metadata.py +++ b/rest_framework/metadata.py @@ -11,6 +11,7 @@ from django.utils.encoding import force_str from rest_framework import exceptions, serializers +from rest_framework.fields import empty from rest_framework.request import clone_request from rest_framework.utils.field_mapping import ClassLookupDict @@ -149,4 +150,7 @@ def get_field_info(self, field): for choice_value, choice_name in field.choices.items() ] + if getattr(field, 'default', None) and field.default != empty and not callable(field.default): + field_info['default'] = field.default + return field_info diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index fc63f96fe0..30bb65e0cf 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -9,6 +9,7 @@ from django.utils.text import capfirst from rest_framework.compat import postgres_fields +from rest_framework.fields import empty from rest_framework.validators import UniqueValidator NUMERIC_FIELD_TYPES = ( @@ -127,6 +128,9 @@ def get_field_kwargs(field_name, model_field): kwargs['read_only'] = True return kwargs + if model_field.default is not None and model_field.default != empty and not callable(model_field.default): + kwargs['default'] = model_field.default + if model_field.has_default() or model_field.blank or model_field.null: kwargs['required'] = False diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 1bdc8697c4..387b9ecded 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -184,6 +184,135 @@ def get_serializer(self): assert response.status_code == status.HTTP_200_OK assert response.data == expected + def test_actions_with_default(self): + """ + On generic views OPTIONS should return an 'actions' key with metadata + on the fields with default that may be supplied to PUT and POST requests. + """ + class NestedField(serializers.Serializer): + a = serializers.IntegerField(default=2) + b = serializers.IntegerField() + + class ExampleSerializer(serializers.Serializer): + choice_field = serializers.ChoiceField(['red', 'green', 'blue'], default='red') + integer_field = serializers.IntegerField( + min_value=1, max_value=1000, default=1 + ) + char_field = serializers.CharField( + min_length=3, max_length=40, default="example" + ) + list_field = serializers.ListField( + child=serializers.ListField( + child=serializers.IntegerField(default=1) + ) + ) + nested_field = NestedField() + uuid_field = serializers.UUIDField(label="UUID field") + + class ExampleView(views.APIView): + """Example view.""" + def post(self, request): + pass + + def get_serializer(self): + return ExampleSerializer() + + view = ExampleView.as_view() + response = view(request=request) + expected = { + 'name': 'Example', + 'description': 'Example view.', + 'renders': [ + 'application/json', + 'text/html' + ], + 'parses': [ + 'application/json', + 'application/x-www-form-urlencoded', + 'multipart/form-data' + ], + 'actions': { + 'POST': { + 'choice_field': { + 'type': 'choice', + 'required': False, + 'read_only': False, + 'label': 'Choice field', + "choices": [ + {'value': 'red', 'display_name': 'red'}, + {'value': 'green', 'display_name': 'green'}, + {'value': 'blue', 'display_name': 'blue'} + ], + 'default': 'red' + }, + 'integer_field': { + 'type': 'integer', + 'required': False, + 'read_only': False, + 'label': 'Integer field', + 'min_value': 1, + 'max_value': 1000, + 'default': 1 + }, + 'char_field': { + 'type': 'string', + 'required': False, + 'read_only': False, + 'label': 'Char field', + 'min_length': 3, + 'max_length': 40, + 'default': 'example' + }, + 'list_field': { + 'type': 'list', + 'required': True, + 'read_only': False, + 'label': 'List field', + 'child': { + 'type': 'list', + 'required': True, + 'read_only': False, + 'child': { + 'type': 'integer', + 'required': False, + 'read_only': False, + 'default': 1 + } + } + }, + 'nested_field': { + 'type': 'nested object', + 'required': True, + 'read_only': False, + 'label': 'Nested field', + 'children': { + 'a': { + 'type': 'integer', + 'required': False, + 'read_only': False, + 'label': 'A', + 'default': 2 + }, + 'b': { + 'type': 'integer', + 'required': True, + 'read_only': False, + 'label': 'B' + } + } + }, + 'uuid_field': { + 'type': 'string', + 'required': True, + 'read_only': False, + 'label': 'UUID field' + } + } + } + } + assert response.status_code == status.HTTP_200_OK + assert response.data == expected + def test_global_permissions(self): """ If a user does not have global permissions on an action, then any diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index e2d4bbc30c..20d0319fcb 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -173,7 +173,7 @@ class Meta: TestSerializer(): auto_field = IntegerField(read_only=True) big_integer_field = IntegerField() - boolean_field = BooleanField(required=False) + boolean_field = BooleanField(default=False, required=False) char_field = CharField(max_length=100) comma_separated_integer_field = CharField(max_length=100, validators=[]) date_field = DateField() @@ -182,7 +182,7 @@ class Meta: email_field = EmailField(max_length=100) float_field = FloatField() integer_field = IntegerField() - null_boolean_field = BooleanField(allow_null=True, required=False) + null_boolean_field = BooleanField(allow_null=True, default=False, required=False) positive_integer_field = IntegerField() positive_small_integer_field = IntegerField() slug_field = SlugField(allow_unicode=False, max_length=100) @@ -210,7 +210,7 @@ class Meta: length_limit_field = CharField(max_length=12, min_length=3) blank_field = CharField(allow_blank=True, max_length=10, required=False) null_field = IntegerField(allow_null=True, required=False) - default_field = IntegerField(required=False) + default_field = IntegerField(default=0, required=False) descriptive_field = IntegerField(help_text='Some help text', label='A label') choices_field = ChoiceField(choices=(('red', 'Red'), ('blue', 'Blue'), ('green', 'Green'))) text_choices_field = ChoiceField(choices=(('red', 'Red'), ('blue', 'Blue'), ('green', 'Green'))) From da9288878b27479bd8b031b530337a346a82b104 Mon Sep 17 00:00:00 2001 From: Denis Orehovsky Date: Sun, 13 Aug 2023 08:36:19 +0400 Subject: [PATCH 1052/1262] Partial serializer should not have required fields (#7563) --- rest_framework/schemas/openapi.py | 2 +- tests/schemas/test_openapi.py | 50 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index d8707e1e1f..ad19f6df85 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -525,7 +525,7 @@ def map_serializer(self, serializer): if isinstance(field, serializers.HiddenField): continue - if field.required: + if field.required and not serializer.partial: required.append(self.get_field_name(field)) schema = self.map_field(field) diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index 1eb5b84b71..6645cac728 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -403,6 +403,56 @@ class View(generics.GenericAPIView): assert list(schema['properties']['nested']['properties'].keys()) == ['number'] assert schema['properties']['nested']['required'] == ['number'] + def test_response_body_partial_serializer(self): + path = '/' + method = 'GET' + + class ItemSerializer(serializers.Serializer): + text = serializers.CharField() + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.partial = True + + class View(generics.GenericAPIView): + serializer_class = ItemSerializer + + view = create_view( + View, + method, + create_request(path), + ) + inspector = AutoSchema() + inspector.view = view + + responses = inspector.get_responses(path, method) + assert responses == { + '200': { + 'description': '', + 'content': { + 'application/json': { + 'schema': { + 'type': 'array', + 'items': { + '$ref': '#/components/schemas/Item' + }, + }, + }, + }, + }, + } + components = inspector.get_components(path, method) + assert components == { + 'Item': { + 'type': 'object', + 'properties': { + 'text': { + 'type': 'string', + }, + }, + } + } + def test_list_response_body_generation(self): """Test that an array schema is returned for list views.""" path = '/' From 6ebe6f26003df8f21fec83336da00565a0ee8f5b Mon Sep 17 00:00:00 2001 From: rexes-aka <107841205+rexes-aka@users.noreply.github.com> Date: Tue, 15 Aug 2023 13:56:42 +0900 Subject: [PATCH 1053/1262] Update exceptions.md (#9071) --- docs/api-guide/exceptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index d914924d0f..33590046bc 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -101,7 +101,7 @@ Note that the exception handler will only be called for responses generated by r The **base class** for all exceptions raised inside an `APIView` class or `@api_view`. -To provide a custom exception, subclass `APIException` and set the `.status_code`, `.default_detail`, and `default_code` attributes on the class. +To provide a custom exception, subclass `APIException` and set the `.status_code`, `.default_detail`, and `.default_code` attributes on the class. For example, if your API relies on a third party service that may sometimes be unreachable, you might want to implement an exception for the "503 Service Unavailable" HTTP response code. You could do this like so: From a2430a8cce67ca4d511abb20dfb783344fa4bad4 Mon Sep 17 00:00:00 2001 From: Harald Nezbeda Date: Tue, 15 Aug 2023 07:17:08 +0200 Subject: [PATCH 1054/1262] Add docs validation to release process (#6967) * Add ci stage to test for broken links in documentation * Add docs validation to release process * Update .github/workflows/main.yml * Update .github/workflows/main.yml --------- Co-authored-by: Ryan P Kilby Co-authored-by: Asif Saif Uddin --- .github/workflows/main.yml | 24 +++++++++++++++++++++ docs/community/project-management.md | 3 +++ requirements/requirements-documentation.txt | 3 +++ 3 files changed, 30 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f245f69644..5ffca54f93 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -56,3 +56,27 @@ jobs: - name: Upload coverage run: | codecov -e TOXENV,DJANGO + + test-docs: + name: Test documentation links + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Install dependencies + run: pip install -r requirements/requirements-documentation.txt + + # Start mkdocs server and wait for it to be ready + - run: mkdocs serve & + - run: WAIT_TIME=0 && until nc -vzw 2 localhost 8000 || [ $WAIT_TIME -eq 5 ]; do sleep $(( WAIT_TIME++ )); done + - run: if [ $WAIT_TIME == 5 ]; then echo cannot start mkdocs server on http://localhost:8000; exit 1; fi + + - name: Check links + continue-on-error: true + run: pylinkvalidate.py -P http://localhost:8000/ + + - run: echo "Done" diff --git a/docs/community/project-management.md b/docs/community/project-management.md index 293c65e246..92132ae7e6 100644 --- a/docs/community/project-management.md +++ b/docs/community/project-management.md @@ -112,6 +112,9 @@ The following template should be used for the description of the issue, and serv - [ ] `docs` Python & Django versions - [ ] Update the translations from [transifex](https://www.django-rest-framework.org/topics/project-management/#translations). - [ ] Ensure the pull request increments the version to `*.*.*` in [`restframework/__init__.py`](https://github.com/encode/django-rest-framework/blob/master/rest_framework/__init__.py). + - [ ] Ensure documentation validates + - Build and serve docs `mkdocs serve` + - Validate links `pylinkvalidate.py -P http://127.0.0.1:8000` - [ ] Confirm with @tomchristie that release is finalized and ready to go. - [ ] Ensure that release date is included in pull request. - [ ] Merge the release pull request. diff --git a/requirements/requirements-documentation.txt b/requirements/requirements-documentation.txt index cf2dc26e88..bce03abc5b 100644 --- a/requirements/requirements-documentation.txt +++ b/requirements/requirements-documentation.txt @@ -1,3 +1,6 @@ # MkDocs to build our documentation. mkdocs>=1.1.2,<1.2 jinja2>=2.10,<3.1.0 # contextfilter has been renamed + +# pylinkvalidator to check for broken links in documentation. +pylinkvalidator==0.3 From 2843b925f90f078a789bcd20b0f8dab360c48008 Mon Sep 17 00:00:00 2001 From: Denis Surkov Date: Tue, 15 Aug 2023 07:20:05 +0200 Subject: [PATCH 1055/1262] Add note for HiddenField behavior (#9055) * Add note for HiddenField behavior * Update docs/api-guide/fields.md * Update docs/api-guide/validators.md --------- Co-authored-by: Asif Saif Uddin --- docs/api-guide/fields.md | 6 ++++++ docs/api-guide/validators.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 7c27162187..0766fe3ef6 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -558,6 +558,12 @@ The `HiddenField` class is usually only needed if you have some validation that For further examples on `HiddenField` see the [validators](validators.md) documentation. +--- + +**Note:** `HiddenField()` does not appear in `partial=True` serializer (when making `PATCH` request). This behavior might change in future, follow updates on [github discussion](https://github.com/encode/django-rest-framework/discussions/8259). + +--- + ## ModelField A generic field that can be tied to any arbitrary model field. The `ModelField` class delegates the task of serialization/deserialization to its associated model field. This field can be used to create serializer fields for custom model fields, without having to create a new custom serializer field. diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index 2a1e3e6b36..2a93e4cdca 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -164,6 +164,12 @@ If you want the date field to be entirely hidden from the user, then use `Hidden --- +--- + +**Note:** `HiddenField()` does not appear in `partial=True` serializer (when making `PATCH` request). This behavior might change in future, follow updates on [github discussion](https://github.com/encode/django-rest-framework/discussions/8259). + +--- + # Advanced field defaults Validators that are applied across multiple fields in the serializer can sometimes require a field input that should not be provided by the API client, but that *is* available as input to the validator. From a47adbcd0f7c0a6aba565462954d2bd8d4d52a04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Kr=C3=A1l?= Date: Tue, 15 Aug 2023 07:50:02 +0200 Subject: [PATCH 1056/1262] Document support for http.HTTPMethod in the @action decorator added in Python 3.11. (#9067) * Implement tests for HTTPMethod from Python 3.11 * Update documentation to mention HTTPMethod support in @action --- docs/api-guide/viewsets.md | 7 +++++++ tests/test_decorators.py | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index 5d5491a83f..39db18bca1 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -178,6 +178,13 @@ The `action` decorator will route `GET` requests by default, but may also accept def unset_password(self, request, pk=None): ... +Argument `methods` also supports HTTP methods defined as [HTTPMethod](https://docs.python.org/3/library/http.html#http.HTTPMethod). Example below is identical to the one above: + + from http import HTTPMethod + + @action(detail=True, methods=[HTTPMethod.POST, HTTPMethod.DELETE]) + def unset_password(self, request, pk=None): + ... The decorator allows you to override any viewset-level configuration such as `permission_classes`, `serializer_class`, `filter_backends`...: diff --git a/tests/test_decorators.py b/tests/test_decorators.py index 99ba13e60c..8d0805cbba 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -1,3 +1,5 @@ +import sys + import pytest from django.test import TestCase @@ -187,6 +189,20 @@ def test_action(request): assert str(excinfo.value) == "@action() missing required argument: 'detail'" + @pytest.mark.skipif(sys.version_info < (3, 11), reason="HTTPMethod was added in Python 3.11") + def test_method_mapping_http_method(self): + from http import HTTPMethod + + method_names = [getattr(HTTPMethod, name.upper()) for name in APIView.http_method_names] + + @action(detail=False, methods=method_names) + def test_action(): + raise NotImplementedError + + expected_mapping = {name: test_action.__name__ for name in APIView.http_method_names} + + assert test_action.mapping == expected_mapping + def test_method_mapping_http_methods(self): # All HTTP methods should be mappable @action(detail=False, methods=[]) From 9e05aa59622fc3b859b7bc0b3abb81573a83bed7 Mon Sep 17 00:00:00 2001 From: Ori Roza Date: Tue, 15 Aug 2023 08:58:06 +0300 Subject: [PATCH 1057/1262] Update third-party-packages.md (#9026) added https://github.com/Ori-Roza/drf-api-action to third packages docs --- 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 2f418a3131..2304dfe45f 100644 --- a/docs/community/third-party-packages.md +++ b/docs/community/third-party-packages.md @@ -150,6 +150,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque * [fast-drf] - A model based library for making API development faster and easier. * [django-requestlogs] - Providing middleware and other helpers for audit logging for REST framework. * [drf-standardized-errors][drf-standardized-errors] - DRF exception handler to standardize error responses for all API endpoints. +* [drf-api-action][drf-api-action] - uses the power of DRF also as a library functions [cite]: http://www.software-ecosystems.com/Software_Ecosystems/Ecosystems.html [cookiecutter]: https://github.com/jpadilla/cookiecutter-django-rest-framework @@ -241,3 +242,4 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [fast-drf]: https://github.com/iashraful/fast-drf [django-requestlogs]: https://github.com/Raekkeri/django-requestlogs [drf-standardized-errors]: https://github.com/ghazi-git/drf-standardized-errors +[drf-api-action]: https://github.com/Ori-Roza/drf-api-action From 5c07060ce0dcbdad1e38be8a509f5c23b6e9fc63 Mon Sep 17 00:00:00 2001 From: Devid Date: Wed, 16 Aug 2023 15:11:50 +0100 Subject: [PATCH 1058/1262] Use str as default path converter (#9066) --- rest_framework/routers.py | 2 +- tests/test_routers.py | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/rest_framework/routers.py b/rest_framework/routers.py index fa5d16922b..2b9478e905 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -144,7 +144,7 @@ def __init__(self, trailing_slash=True, use_regex_path=True): self._url_conf = re_path else: self._base_pattern = '<{lookup_value}:{lookup_prefix}{lookup_url_kwarg}>' - self._default_value_pattern = 'path' + self._default_value_pattern = 'str' self._url_conf = path # remove regex characters from routes _routes = [] diff --git a/tests/test_routers.py b/tests/test_routers.py index b4bde5c312..887f601d54 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -99,6 +99,13 @@ def url_path_detail(self, request, *args, **kwargs): kwarg = self.kwargs.get('kwarg', '') return Response({'pk': pk, 'kwarg': kwarg}) + @action(detail=True, url_path='detail//detail/') + def url_path_detail_multiple_params(self, request, *args, **kwargs): + pk = self.kwargs.get('pk', '') + kwarg = self.kwargs.get('kwarg', '') + param = self.kwargs.get('param', '') + return Response({'pk': pk, 'kwarg': kwarg, 'param': param}) + notes_router = SimpleRouter() notes_router.register(r'notes', NoteViewSet) @@ -561,6 +568,18 @@ def test_detail_extra_action(self): assert response.status_code == 200 assert json.loads(response.content.decode()) == {'pk': pk, 'kwarg': kwarg} + def test_detail_extra_other_action(self): + # this to assure that ambiguous patterns are interpreted correctly + # using the `path` converters this URL is recognized to match the pattern + # of `UrlPathViewSet.url_path_detail` when it should match + # `UrlPathViewSet.url_path_detail_multiple_params` + pk = '1' + kwarg = 1234 + param = 2 + response = self.client.get('/path/1/detail/1234/detail/2/') + assert response.status_code == 200 + assert json.loads(response.content.decode()) == {'pk': pk, 'kwarg': kwarg, 'param': param} + def test_defaultrouter_root(self): response = self.client.get('/default/') assert response.status_code == 200 From 40eccb0d6cdb769876dbb79724c5871b4f04163d Mon Sep 17 00:00:00 2001 From: Harald Nezbeda Date: Thu, 17 Aug 2023 12:21:03 +0200 Subject: [PATCH 1059/1262] Fixes documentation link test (#9076) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5ffca54f93..fe9610ce31 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -65,7 +65,7 @@ jobs: - uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.9' - name: Install dependencies run: pip install -r requirements/requirements-documentation.txt From 5a01a4c8a900a98ca0186b742d03d02c55846632 Mon Sep 17 00:00:00 2001 From: Daniel <107224353+daniel-web-developer@users.noreply.github.com> Date: Thu, 31 Aug 2023 10:19:25 +0200 Subject: [PATCH 1060/1262] Update CONTRIBUTING.md (#9095) Removed apostrophe from "it's". --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d567d45a87..fb01f8bf76 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing to REST framework -At this point in it's lifespan we consider Django REST framework to be essentially feature-complete. We may accept pull requests that track the continued development of Django versions, but would prefer not to accept new features or code formatting changes. +At this point in its lifespan we consider Django REST framework to be essentially feature-complete. We may accept pull requests that track the continued development of Django versions, but would prefer not to accept new features or code formatting changes. Apart from minor documentation changes, the [GitHub discussions page](https://github.com/encode/django-rest-framework/discussions) should generally be your starting point. Please only raise an issue or pull request if you've been recommended to do so after discussion. From b6e3a22f7cfc38cb3fd3f2e3352f6276d811b391 Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Sat, 9 Sep 2023 13:51:05 +0600 Subject: [PATCH 1061/1262] added jQuery 3.7.1 support & dropped previous version (#9094) * added jQuery 3.7.1 support & dropped previous version * added jQuery 3.7.1 support & dropped previous version --- rest_framework/static/rest_framework/js/jquery-3.5.1.min.js | 2 -- rest_framework/static/rest_framework/js/jquery-3.6.4.min.js | 2 -- rest_framework/static/rest_framework/js/jquery-3.7.1.min.js | 2 ++ rest_framework/templates/rest_framework/admin.html | 2 +- rest_framework/templates/rest_framework/base.html | 2 +- rest_framework/templates/rest_framework/docs/error.html | 2 +- rest_framework/templates/rest_framework/docs/index.html | 2 +- 7 files changed, 6 insertions(+), 8 deletions(-) delete mode 100644 rest_framework/static/rest_framework/js/jquery-3.5.1.min.js delete mode 100644 rest_framework/static/rest_framework/js/jquery-3.6.4.min.js create mode 100644 rest_framework/static/rest_framework/js/jquery-3.7.1.min.js diff --git a/rest_framework/static/rest_framework/js/jquery-3.5.1.min.js b/rest_framework/static/rest_framework/js/jquery-3.5.1.min.js deleted file mode 100644 index b0614034ad..0000000000 --- a/rest_framework/static/rest_framework/js/jquery-3.5.1.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
      "],col:[2,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
      ",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&v(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!y||!y.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ve(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ye(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ve(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.cssHas=ce(function(){try{return C.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],y=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||y.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||y.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||y.push(".#.+[+~]"),e.querySelectorAll("\\\f"),y.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),d.cssHas||y.push(":has"),y=y.length&&new RegExp(y.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),v=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType&&e.documentElement||e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&v(p,e)?-1:t==C||t.ownerDocument==p&&v(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!y||!y.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),v.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",v.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",v.option=!!ce.lastChild;var ge={thead:[1,"","
      "],col:[2,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],_default:[0,"",""]};function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),v.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
      ",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(v.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return B(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=_e(v.pixelPosition,function(e,t){if(t)return t=Be(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return B(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},M=function(){V()},R=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&U(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&R(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function X(a){return F(function(o){return o=+o,F(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function U(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",M),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="",le.option=!!xe.lastChild;var ke={thead:[1,"","
      "],col:[2,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],_default:[0,"",""]};function Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n",""]);var je=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="
      ",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0{{ error_title }} "csrfToken": "{{ csrf_token }}" } - + diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index 53c964f231..686dd831ff 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -293,7 +293,7 @@

      {{ name }}

      "csrfToken": "{% if request %}{{ csrf_token }}{% endif %}" } - + diff --git a/rest_framework/templates/rest_framework/docs/error.html b/rest_framework/templates/rest_framework/docs/error.html index 0c369e9e8e..91c8a34a5b 100644 --- a/rest_framework/templates/rest_framework/docs/error.html +++ b/rest_framework/templates/rest_framework/docs/error.html @@ -66,6 +66,6 @@

      Overriding this template

      - + diff --git a/rest_framework/templates/rest_framework/docs/index.html b/rest_framework/templates/rest_framework/docs/index.html index 8f8536fbeb..7fd07f7683 100644 --- a/rest_framework/templates/rest_framework/docs/index.html +++ b/rest_framework/templates/rest_framework/docs/index.html @@ -38,7 +38,7 @@ {% include "rest_framework/docs/auth/basic.html" %} {% include "rest_framework/docs/auth/session.html" %} - + From 4bbfa8d4556b5847e91ba95f457cc862b7a0f027 Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Sat, 9 Sep 2023 13:55:32 +0600 Subject: [PATCH 1062/1262] Updated Checkout action version & Ubuntu OS version (#9097) * Updated Checkout action version & Ubuntu OS version * Update .github/workflows/main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fe9610ce31..48b6e7202b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,7 +22,7 @@ jobs: - '3.11' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: From 605cc4f7367f58002056453d9befd3c1918f6a38 Mon Sep 17 00:00:00 2001 From: Marcelo Galigniana Date: Fri, 29 Sep 2023 11:06:16 +0200 Subject: [PATCH 1063/1262] Updated links to CSRF/AJAX Django documentation (#9118) --- docs/api-guide/authentication.md | 2 +- docs/topics/ajax-csrf-cors.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index dc55a1e8f1..d6e6293fd9 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -454,7 +454,7 @@ More information can be found in the [Documentation](https://django-rest-durin.r [basicauth]: https://tools.ietf.org/html/rfc2617 [permission]: permissions.md [throttling]: throttling.md -[csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax +[csrf-ajax]: https://docs.djangoproject.com/en/stable/howto/csrf/#using-csrf-protection-with-ajax [mod_wsgi_official]: https://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIPassAuthorization.html [django-oauth-toolkit-getting-started]: https://django-oauth-toolkit.readthedocs.io/en/latest/rest-framework/getting_started.html [django-rest-framework-oauth]: https://jpadilla.github.io/django-rest-framework-oauth/ diff --git a/docs/topics/ajax-csrf-cors.md b/docs/topics/ajax-csrf-cors.md index 094ecc4a41..678fa00e71 100644 --- a/docs/topics/ajax-csrf-cors.md +++ b/docs/topics/ajax-csrf-cors.md @@ -35,7 +35,7 @@ The best way to deal with CORS in REST framework is to add the required response [cite]: https://blog.codinghorror.com/preventing-csrf-and-xsrf-attacks/ [csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) -[csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax +[csrf-ajax]: https://docs.djangoproject.com/en/stable/howto/csrf/#using-csrf-protection-with-ajax [cors]: https://www.w3.org/TR/cors/ [adamchainz]: https://github.com/adamchainz [django-cors-headers]: https://github.com/adamchainz/django-cors-headers From d32346bae55f3e4718a185fb60e9f7a28e389c85 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Wed, 4 Oct 2023 19:03:10 +0200 Subject: [PATCH 1064/1262] Fix type name of `FieldInfo` namedtuple (#9124) * Fix type name of `FieldInfo` namedtuple * Add test --- rest_framework/utils/model_meta.py | 2 +- tests/test_utils.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index bd5d9177c0..bb0b77f58e 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -7,7 +7,7 @@ """ from collections import namedtuple -FieldInfo = namedtuple('FieldResult', [ +FieldInfo = namedtuple('FieldInfo', [ 'pk', # Model field instance 'fields', # Dict of field name -> model field instance 'forward_relations', # Dict of field name -> RelationInfo diff --git a/tests/test_utils.py b/tests/test_utils.py index c72f680fe4..6c5f5ddf2d 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -9,6 +9,7 @@ from rest_framework.utils import json from rest_framework.utils.breadcrumbs import get_breadcrumbs from rest_framework.utils.formatting import lazy_format +from rest_framework.utils.model_meta import FieldInfo, RelationInfo from rest_framework.utils.urls import remove_query_param, replace_query_param from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet @@ -267,3 +268,9 @@ def test_it_formats_lazily(self): assert message.format.call_count == 1 str(formatted) assert message.format.call_count == 1 + + +class ModelMetaNamedTupleNames(TestCase): + def test_named_tuple_names(self): + assert FieldInfo.__name__ == 'FieldInfo' + assert RelationInfo.__name__ == 'RelationInfo' From 42961892834cb8730cd4ec4f7306caa4f8b44abb Mon Sep 17 00:00:00 2001 From: Terence Honles Date: Thu, 5 Oct 2023 08:33:53 +0200 Subject: [PATCH 1065/1262] fix dist test by moving --no-pkgroot to runtests.py (#9129) This change fixes the dist test by moving the --no-pkgroot option from pytest to the runtests script. The current "filterwarnings" setting for pytest includes rest_framework, which causes an early import of the module. As a result the current --no-pkgroot behavior fails with an assertion error. Trying to remove the module from sys.modules will cause the warning filter to not apply, so this change moves this code before pytest parses the config and loads the "filterwarnings". --- runtests.py | 13 +++++++++++++ tests/conftest.py | 19 +++++-------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/runtests.py b/runtests.py index 98f34c0673..e2838a0e29 100755 --- a/runtests.py +++ b/runtests.py @@ -1,4 +1,5 @@ #! /usr/bin/env python3 +import os import sys import pytest @@ -34,6 +35,18 @@ def is_class(string): '--cov-report', 'xml', ] + pytest_args + try: + pytest_args.remove('--no-pkgroot') + except ValueError: + pass + else: + sys.path.pop(0) + + # import rest_framework before pytest re-adds the package root directory. + import rest_framework + package_dir = os.path.join(os.getcwd(), 'rest_framework') + assert not rest_framework.__file__.startswith(package_dir) + if first_arg.startswith('-'): # `runtests.py [flags]` pytest_args = ['tests'] + pytest_args diff --git a/tests/conftest.py b/tests/conftest.py index 79cabd5e1a..b67475d8a7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,15 +1,10 @@ import os -import sys import django from django.core import management def pytest_addoption(parser): - parser.addoption('--no-pkgroot', action='http://github.com/store_true', default=False, - help='Remove package root directory from sys.path, ensuring that ' - 'rest_framework is imported from the installed site-packages. ' - 'Used for testing the distribution.') parser.addoption('--staticfiles', action='http://github.com/store_true', default=False, help='Run tests with static files collection, using manifest ' 'staticfiles storage. Used for testing the distribution.') @@ -87,19 +82,15 @@ def pytest_configure(config): 'guardian', ) - if config.getoption('--no-pkgroot'): - sys.path.pop(0) - - # import rest_framework before pytest re-adds the package root directory. - import rest_framework - package_dir = os.path.join(os.getcwd(), 'rest_framework') - assert not rest_framework.__file__.startswith(package_dir) - # Manifest storage will raise an exception if static files are not present (ie, a packaging failure). if config.getoption('--staticfiles'): import rest_framework settings.STATIC_ROOT = os.path.join(os.path.dirname(rest_framework.__file__), 'static-root') - settings.STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage' + backend = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage' + if django.VERSION < (4, 2): + settings.STATICFILES_STORAGE = backend + else: + settings.STORAGES['staticfiles']['BACKEND'] = backend django.setup() From d181511155a041cae9b848a4579ada1d50456328 Mon Sep 17 00:00:00 2001 From: Michael Herman Date: Thu, 5 Oct 2023 04:06:07 -0500 Subject: [PATCH 1066/1262] add courses section to community resources (#8845) --- docs/community/tutorials-and-resources.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/community/tutorials-and-resources.md b/docs/community/tutorials-and-resources.md index 23faf79128..f283e0e4cc 100644 --- a/docs/community/tutorials-and-resources.md +++ b/docs/community/tutorials-and-resources.md @@ -19,6 +19,10 @@ There are a wide range of resources available for learning and using Django REST
      +## Courses + +* [Developing RESTful APIs with Django REST Framework][developing-restful-apis-with-django-rest-framework] + ## Tutorials * [Beginner's Guide to the Django REST Framework][beginners-guide-to-the-django-rest-framework] @@ -130,3 +134,4 @@ Want your Django REST Framework talk/tutorial/article to be added to our website [pycon-us-2017]: https://www.youtube.com/watch?v=Rk6MHZdust4 [django-rest-react-valentinog]: https://www.valentinog.com/blog/tutorial-api-django-rest-react/ [doordash-implementing-rest-apis]: https://doordash.engineering/2013/10/07/implementing-rest-apis-with-embedded-privacy/ +[developing-restful-apis-with-django-rest-framework]: https://testdriven.io/courses/django-rest-framework/ From 8da6b696ab7d9bc3f31c672b72638e52bbd6b53f Mon Sep 17 00:00:00 2001 From: Marko <83985775+MarkoM-dot@users.noreply.github.com> Date: Sat, 7 Oct 2023 10:55:25 +0200 Subject: [PATCH 1067/1262] docs: update django requirement to 4.2 (#9128) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index ad241c0a37..81a5563133 100644 --- a/docs/index.md +++ b/docs/index.md @@ -86,7 +86,7 @@ continued development by **[signing up for a paid plan][funding]**. REST framework requires the following: * Python (3.6, 3.7, 3.8, 3.9, 3.10, 3.11) -* Django (3.0, 3.1, 3.2, 4.0, 4.1) +* Django (3.0, 3.1, 3.2, 4.0, 4.1, 4.2) We **highly recommend** and only officially support the latest patch release of each Python and Django series. From 18b02ce00e4c911719297149406cb3c0f054cf22 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Sun, 8 Oct 2023 09:30:10 +0200 Subject: [PATCH 1068/1262] Add missing `DEFAULT_VERSIONING_CLASS` entry to API docs (#9126) --- docs/api-guide/settings.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index d42000260b..47e2ce993f 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -163,6 +163,12 @@ The string that should used for any versioning parameters, such as in the media Default: `'version'` +#### DEFAULT_VERSIONING_CLASS + +The default versioning scheme to use. + +Default: `None` + --- ## Authentication settings From e794e5e5e43d6838d9ffb8eb0a505b5f531b261f Mon Sep 17 00:00:00 2001 From: "J.V. Zammit" Date: Fri, 13 Oct 2023 13:44:45 +0200 Subject: [PATCH 1069/1262] blacken-docs (#8906) * blacken-docs: Manual fixes for command to pass without errors * blacken-docs: Adds blacken-docs step to precommit hook. * blacken-docs: Adds changes made by command itself. * blacken-docs: Modifies blacken-docs step to only process files under "docs" directory * blacken-docs: Updates pre-commit config file to exclude all directories other than "docs" to be compatible with "--all-files" flag. * blacken-docs: Adds commas at the end to make it look identical to pre-black version --- .pre-commit-config.yaml | 7 +++++++ docs/api-guide/caching.md | 16 ++++++++-------- docs/api-guide/schemas.md | 26 ++++++++++++++++---------- docs/api-guide/viewsets.md | 25 +++++++++++++------------ docs/community/3.10-announcement.md | 13 +++++++------ docs/community/3.11-announcement.md | 9 +++++---- docs/community/3.12-announcement.md | 12 +++++++----- docs/community/3.9-announcement.md | 11 ++++------- docs/community/release-notes.md | 6 +++++- docs/topics/documenting-your-api.md | 23 +++++++++++++++-------- 10 files changed, 87 insertions(+), 61 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 83fe0b714b..64ec37770f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,3 +18,10 @@ repos: - id: flake8 additional_dependencies: - flake8-tidy-imports +- repo: https://github.com/adamchainz/blacken-docs + rev: 1.13.0 + hooks: + - id: blacken-docs + exclude: ^(?!docs).*$ + additional_dependencies: + - black==23.1.0 diff --git a/docs/api-guide/caching.md b/docs/api-guide/caching.md index ab4f82cd2f..503acb09e6 100644 --- a/docs/api-guide/caching.md +++ b/docs/api-guide/caching.md @@ -28,33 +28,33 @@ from rest_framework import viewsets class UserViewSet(viewsets.ViewSet): # With cookie: cache requested url for each user for 2 hours - @method_decorator(cache_page(60*60*2)) + @method_decorator(cache_page(60 * 60 * 2)) @method_decorator(vary_on_cookie) def list(self, request, format=None): content = { - 'user_feed': request.user.get_user_feed() + "user_feed": request.user.get_user_feed(), } return Response(content) class ProfileView(APIView): # With auth: cache requested url for each user for 2 hours - @method_decorator(cache_page(60*60*2)) - @method_decorator(vary_on_headers("Authorization",)) + @method_decorator(cache_page(60 * 60 * 2)) + @method_decorator(vary_on_headers("Authorization")) def get(self, request, format=None): content = { - 'user_feed': request.user.get_user_feed() + "user_feed": request.user.get_user_feed(), } return Response(content) class PostView(APIView): # Cache page for the requested url - @method_decorator(cache_page(60*60*2)) + @method_decorator(cache_page(60 * 60 * 2)) def get(self, request, format=None): content = { - 'title': 'Post title', - 'body': 'Post content' + "title": "Post title", + "body": "Post content", } return Response(content) ``` diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 3f0b155306..7af98dbf5e 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -94,11 +94,13 @@ urlpatterns = [ # Use the `get_schema_view()` helper to add a `SchemaView` to project URLs. # * `title` and `description` parameters are passed to `SchemaGenerator`. # * Provide view name for use with `reverse()`. - path('openapi', get_schema_view( - title="Your Project", - description="API for all things …", - version="1.0.0" - ), name='openapi-schema'), + path( + "openapi", + get_schema_view( + title="Your Project", description="API for all things …", version="1.0.0" + ), + name="openapi-schema", + ), # ... ] ``` @@ -259,11 +261,13 @@ class CustomSchema(AutoSchema): """ AutoSchema subclass using schema_extra_info on the view. """ + ... + class CustomView(APIView): schema = CustomSchema() - schema_extra_info = ... some extra info ... + schema_extra_info = ... # some extra info ``` Here, the `AutoSchema` subclass goes looking for `schema_extra_info` on the @@ -278,10 +282,13 @@ class BaseSchema(AutoSchema): """ AutoSchema subclass that knows how to use extra_info. """ + ... + class CustomSchema(BaseSchema): - extra_info = ... some extra info ... + extra_info = ... # some extra info + class CustomView(APIView): schema = CustomSchema() @@ -302,10 +309,9 @@ class CustomSchema(BaseSchema): self.extra_info = kwargs.pop("extra_info") super().__init__(**kwargs) + class CustomView(APIView): - schema = CustomSchema( - extra_info=... some extra info ... - ) + schema = CustomSchema(extra_info=...) # some extra info ``` This saves you having to create a custom subclass per-view for a commonly used option. diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index 39db18bca1..41ba1743c3 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -201,15 +201,16 @@ To view all extra actions, call the `.get_extra_actions()` method. Extra actions can map additional HTTP methods to separate `ViewSet` methods. For example, the above password set/unset methods could be consolidated into a single route. Note that additional mappings do not accept arguments. ```python - @action(detail=True, methods=['put'], name='Change Password') - def password(self, request, pk=None): - """Update the user's password.""" - ... - - @password.mapping.delete - def delete_password(self, request, pk=None): - """Delete the user's password.""" - ... +@action(detail=True, methods=["put"], name="Change Password") +def password(self, request, pk=None): + """Update the user's password.""" + ... + + +@password.mapping.delete +def delete_password(self, request, pk=None): + """Delete the user's password.""" + ... ``` ## Reversing action URLs @@ -220,14 +221,14 @@ Note that the `basename` is provided by the router during `ViewSet` registration Using the example from the previous section: -```python ->>> view.reverse_action('set-password', args=['1']) +```pycon +>>> view.reverse_action("set-password", args=["1"]) 'http://localhost:8000/api/users/1/set_password' ``` Alternatively, you can use the `url_name` attribute set by the `@action` decorator. -```python +```pycon >>> view.reverse_action(view.set_password.url_name, args=['1']) 'http://localhost:8000/api/users/1/set_password' ``` diff --git a/docs/community/3.10-announcement.md b/docs/community/3.10-announcement.md index 23b6330a7b..a2135fd20f 100644 --- a/docs/community/3.10-announcement.md +++ b/docs/community/3.10-announcement.md @@ -41,8 +41,8 @@ update your REST framework settings to include `DEFAULT_SCHEMA_CLASS` explicitly ```python REST_FRAMEWORK = { - ... - 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema' + ...: ..., + "DEFAULT_SCHEMA_CLASS": "rest_framework.schemas.coreapi.AutoSchema", } ``` @@ -74,10 +74,11 @@ urlpatterns = [ # Use the `get_schema_view()` helper to add a `SchemaView` to project URLs. # * `title` and `description` parameters are passed to `SchemaGenerator`. # * Provide view name for use with `reverse()`. - path('openapi', get_schema_view( - title="Your Project", - description="API for all things …" - ), name='openapi-schema'), + path( + "openapi", + get_schema_view(title="Your Project", description="API for all things …"), + name="openapi-schema", + ), # ... ] ``` diff --git a/docs/community/3.11-announcement.md b/docs/community/3.11-announcement.md index 83dd636d19..5a1f2c8e30 100644 --- a/docs/community/3.11-announcement.md +++ b/docs/community/3.11-announcement.md @@ -43,10 +43,11 @@ be extracted from the class docstring: ```python class DocStringExampleListView(APIView): -""" -get: A description of my GET operation. -post: A description of my POST operation. -""" + """ + get: A description of my GET operation. + post: A description of my POST operation. + """ + permission_classes = [permissions.IsAuthenticatedOrReadOnly] def get(self, request, *args, **kwargs): diff --git a/docs/community/3.12-announcement.md b/docs/community/3.12-announcement.md index 3bfeb65763..b192f7290a 100644 --- a/docs/community/3.12-announcement.md +++ b/docs/community/3.12-announcement.md @@ -41,7 +41,7 @@ The tags used for a particular view may also be overridden... ```python class MyOrders(APIView): - schema = AutoSchema(tags=['users', 'orders']) + schema = AutoSchema(tags=["users", "orders"]) ... ``` @@ -68,7 +68,7 @@ may be overridden if needed](https://www.django-rest-framework.org/api-guide/sch ```python class MyOrders(APIView): - schema = AutoSchema(component_name="OrderDetails") + schema = AutoSchema(component_name="OrderDetails") ``` ## More Public API @@ -118,10 +118,11 @@ class SitesSearchView(generics.ListAPIView): 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'] + search_fields = ["site_name", "location__region", "location__country"] ``` ### Searches against annotate fields @@ -135,10 +136,11 @@ 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')) + + queryset = Publisher.objects.annotate(avg_rating=Avg("book__rating")) serializer_class = PublisherSerializer filter_backends = [filters.SearchFilter] - search_fields = ['avg_rating'] + search_fields = ["avg_rating"] ``` --- diff --git a/docs/community/3.9-announcement.md b/docs/community/3.9-announcement.md index d673fdd183..6bc5e3cc39 100644 --- a/docs/community/3.9-announcement.md +++ b/docs/community/3.9-announcement.md @@ -65,15 +65,12 @@ from rest_framework.renderers import JSONOpenAPIRenderer from django.urls import path schema_view = get_schema_view( - title='Server Monitoring API', - url='https://www.example.org/api/', - renderer_classes=[JSONOpenAPIRenderer] + title="Server Monitoring API", + url="https://www.example.org/api/", + renderer_classes=[JSONOpenAPIRenderer], ) -urlpatterns = [ - path('schema.json', schema_view), - ... -] +urlpatterns = [path("schema.json", schema_view), ...] ``` And here's how you can use the `generateschema` management command: diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md index fba7f63d62..d8eadeb32e 100644 --- a/docs/community/release-notes.md +++ b/docs/community/release-notes.md @@ -306,7 +306,11 @@ Be sure to upgrade to Python 3 before upgrading to Django REST Framework 3.10. class NullableCharField(serializers.CharField): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.validators = [v for v in self.validators if not isinstance(v, ProhibitNullCharactersValidator)] + self.validators = [ + v + for v in self.validators + if not isinstance(v, ProhibitNullCharactersValidator) + ] ``` * Add `OpenAPIRenderer` and `generate_schema` management command. [#6229][gh6229] * Add OpenAPIRenderer by default, and add schema docs. [#6233][gh6233] diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md index b85d6310ad..edb989290d 100644 --- a/docs/topics/documenting-your-api.md +++ b/docs/topics/documenting-your-api.md @@ -96,10 +96,14 @@ urlpatterns = [ # ... # Route TemplateView to serve Swagger UI template. # * Provide `extra_context` with view name of `SchemaView`. - path('swagger-ui/', TemplateView.as_view( - template_name='swagger-ui.html', - extra_context={'schema_url':'openapi-schema'} - ), name='swagger-ui'), + path( + "swagger-ui/", + TemplateView.as_view( + template_name="swagger-ui.html", + extra_context={"schema_url": "openapi-schema"}, + ), + name="swagger-ui", + ), ] ``` @@ -145,10 +149,13 @@ urlpatterns = [ # ... # Route TemplateView to serve the ReDoc template. # * Provide `extra_context` with view name of `SchemaView`. - path('redoc/', TemplateView.as_view( - template_name='redoc.html', - extra_context={'schema_url':'openapi-schema'} - ), name='redoc'), + path( + "redoc/", + TemplateView.as_view( + template_name="redoc.html", extra_context={"schema_url": "openapi-schema"} + ), + name="redoc", + ), ] ``` From 2c59206b3ee4772ccfe981fb6ad52e40d59a91e7 Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Tue, 17 Oct 2023 15:40:12 +0600 Subject: [PATCH 1070/1262] Update requirements-documentation.txt (#9141) --- requirements/requirements-documentation.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-documentation.txt b/requirements/requirements-documentation.txt index bce03abc5b..25f5121f2e 100644 --- a/requirements/requirements-documentation.txt +++ b/requirements/requirements-documentation.txt @@ -1,5 +1,5 @@ # MkDocs to build our documentation. -mkdocs>=1.1.2,<1.2 +mkdocs==1.2.4 jinja2>=2.10,<3.1.0 # contextfilter has been renamed # pylinkvalidator to check for broken links in documentation. From f56b85b7dd7e4f786e0769bba6b7609d4507da83 Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Tue, 17 Oct 2023 16:13:10 +0600 Subject: [PATCH 1071/1262] Update pygments>=2.12.0,<2.14.0 in requirements-optionals (#9142) * Update pygments>=2.12.0,<2.16.1 in requirements-optionals * Update requirements/requirements-optionals.txt * Update requirements/requirements-optionals.txt * Update requirements/requirements-optionals.txt --- requirements/requirements-optionals.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt index 0379f8c383..dadfba5bd6 100644 --- a/requirements/requirements-optionals.txt +++ b/requirements/requirements-optionals.txt @@ -6,5 +6,5 @@ django-guardian>=2.4.0,<2.5 inflection==0.5.1 markdown==3.3 psycopg2-binary>=2.9.5,<2.10 -pygments==2.12 +pygments>=2.12.0,<2.14.0 pyyaml>=5.3.1,<5.4 From 1db19f4b2d1500894448634852e02f73043382e4 Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Sun, 5 Nov 2023 11:59:10 +0600 Subject: [PATCH 1072/1262] Update pytest version in requirements-testing.txt (#9158) --- requirements/requirements-testing.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-testing.txt b/requirements/requirements-testing.txt index 0f6d7ff87d..2b39316a00 100644 --- a/requirements/requirements-testing.txt +++ b/requirements/requirements-testing.txt @@ -1,5 +1,5 @@ # Pytest for running the tests. -pytest>=6.2.0,<8.0 +pytest>=7.0.1,<8.0 pytest-cov>=4.0.0,<5.0 pytest-django>=4.5.2,<5.0 importlib-metadata<5.0 From 15f619ade0c94e38873f19b188bad0bc347b2d2d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 29 Nov 2023 14:13:36 +0000 Subject: [PATCH 1073/1262] Add Svix to sponsors (#9173) --- README.md | 5 ++++- docs/img/premium/svix-premium.png | Bin 0 -> 32055 bytes docs/index.md | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 docs/img/premium/svix-premium.png diff --git a/README.md b/README.md index c2975a418f..60c62ebf54 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,9 @@ The initial aim is to provide a single full-time position on REST framework. [![][posthog-img]][posthog-url] [![][cryptapi-img]][cryptapi-url] [![][fezto-img]][fezto-url] +[![][svix-img]][svix-url] -Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Spacinov][spacinov-url], [Retool][retool-url], [bit.io][bitio-url], [PostHog][posthog-url], [CryptAPI][cryptapi-url], and [FEZTO][fezto-url]. +Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Spacinov][spacinov-url], [Retool][retool-url], [bit.io][bitio-url], [PostHog][posthog-url], [CryptAPI][cryptapi-url], [FEZTO][fezto-url], and [Svix][svix-url]. --- @@ -200,6 +201,7 @@ Please see the [security policy][security-policy]. [posthog-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/posthog-readme.png [cryptapi-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/cryptapi-readme.png [fezto-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/fezto-readme.png +[svix-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/svix-readme.png [sentry-url]: https://getsentry.com/welcome/ [stream-url]: https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage @@ -209,6 +211,7 @@ Please see the [security policy][security-policy]. [posthog-url]: https://posthog.com?utm_source=drf&utm_medium=sponsorship&utm_campaign=open-source-sponsorship [cryptapi-url]: https://cryptapi.io [fezto-url]: https://www.fezto.xyz/?utm_source=DjangoRESTFramework +[svix-url]: https://www.svix.com/?utm_source=django-REST&utm_medium=sponsorship [oauth1-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth [oauth2-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit diff --git a/docs/img/premium/svix-premium.png b/docs/img/premium/svix-premium.png new file mode 100644 index 0000000000000000000000000000000000000000..68ff063879bec44644ffea214c86200c476976af GIT binary patch literal 32055 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 literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md index 81a5563133..60eb65744d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -74,10 +74,11 @@ continued development by **[signing up for a paid plan][funding]**.
    • PostHog
    • CryptAPI
    • FEZTO
    • +
    • Svix
    -*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage), [Spacinov](https://www.spacinov.com/), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [PostHog](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [CryptAPI](https://cryptapi.io), and [FEZTO](https://www.fezto.xyz/?utm_source=DjangoRESTFramework).* +*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage), [Spacinov](https://www.spacinov.com/), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [PostHog](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [CryptAPI](https://cryptapi.io), [FEZTO](https://www.fezto.xyz/?utm_source=DjangoRESTFramework), and [Svix](https://www.svix.com/?utm_source=django-REST&utm_medium=sponsorship).* --- From 06022e788e1d23775af6044f02d8a2d577e30b50 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 29 Nov 2023 14:15:53 +0000 Subject: [PATCH 1074/1262] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 60c62ebf54..524edb8c35 100644 --- a/README.md +++ b/README.md @@ -201,7 +201,7 @@ Please see the [security policy][security-policy]. [posthog-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/posthog-readme.png [cryptapi-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/cryptapi-readme.png [fezto-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/fezto-readme.png -[svix-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/svix-readme.png +[svix-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/svix-premium.png [sentry-url]: https://getsentry.com/welcome/ [stream-url]: https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage From 0abb84fa3955956fc6156cc2c51222cd4ceae783 Mon Sep 17 00:00:00 2001 From: Krishna2864 <75547302+Krishna2864@users.noreply.github.com> Date: Thu, 30 Nov 2023 16:37:36 +0530 Subject: [PATCH 1075/1262] removed hyperlink for web browsable API on homepage (#9174) --- README.md | 2 +- docs/index.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 524edb8c35..078ac07116 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Django REST framework is a powerful and flexible toolkit for building Web APIs. Some reasons you might want to use REST framework: -* The [Web browsable API][sandbox] is a huge usability win for your developers. +* The Web browsable API is a huge usability win for your developers. * [Authentication policies][authentication] including optional packages for [OAuth1a][oauth1-section] and [OAuth2][oauth2-section]. * [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources. * Customizable all the way down - just use [regular function-based views][functionview-section] if you don't need the [more][generic-views] [powerful][viewsets] [features][routers]. diff --git a/docs/index.md b/docs/index.md index 60eb65744d..a7f1444a32 100644 --- a/docs/index.md +++ b/docs/index.md @@ -48,7 +48,7 @@ Django REST framework is a powerful and flexible toolkit for building Web APIs. Some reasons you might want to use REST framework: -* The [Web browsable API][sandbox] is a huge usability win for your developers. +* The Web browsable API is a huge usability win for your developers. * [Authentication policies][authentication] including packages for [OAuth1a][oauth1-section] and [OAuth2][oauth2-section]. * [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources. * Customizable all the way down - just use [regular function-based views][functionview-section] if you don't need the [more][generic-views] [powerful][viewsets] [features][routers]. From 0f39e0124d358b0098261f070175fa8e0359b739 Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Wed, 6 Dec 2023 22:44:47 +0600 Subject: [PATCH 1076/1262] Update requirements-optionals.txt (#9181) * Update requirements-optionals.txt * Update requirements-optionals.txt --- requirements/requirements-optionals.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt index dadfba5bd6..e54100f522 100644 --- a/requirements/requirements-optionals.txt +++ b/requirements/requirements-optionals.txt @@ -4,7 +4,7 @@ coreschema==0.0.4 django-filter django-guardian>=2.4.0,<2.5 inflection==0.5.1 -markdown==3.3 +markdown>=3.3.7 psycopg2-binary>=2.9.5,<2.10 pygments>=2.12.0,<2.14.0 pyyaml>=5.3.1,<5.4 From 4c231d5b97bcc1769ba5a6f41c16f011200300b8 Mon Sep 17 00:00:00 2001 From: HGZahn <44118429+HGZahn@users.noreply.github.com> Date: Thu, 4 Jan 2024 13:36:49 +0100 Subject: [PATCH 1077/1262] Clarifying the file to edit (#9189) --- 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 f9b6c5e9a4..6fa2111e7b 100644 --- a/docs/tutorial/6-viewsets-and-routers.md +++ b/docs/tutorial/6-viewsets-and-routers.md @@ -10,7 +10,7 @@ A `ViewSet` class is only bound to a set of method handlers at the last moment, Let's take our current set of views, and refactor them into view sets. -First of all let's refactor our `UserList` and `UserDetail` classes into a single `UserViewSet` class. We can remove the two view classes, and replace them with a single ViewSet class: +First of all let's refactor our `UserList` and `UserDetail` classes into a single `UserViewSet` class. In the `snippets/views.py` file, we can remove the two view classes and replace them with a single ViewSet class: from rest_framework import viewsets From 530baa23cdf9c3fb44595c69f49684469a758374 Mon Sep 17 00:00:00 2001 From: Yousef Abu Shanab <93343012+youzarsiph@users.noreply.github.com> Date: Thu, 4 Jan 2024 04:42:54 -0800 Subject: [PATCH 1078/1262] Add 2 customization packages, rest-framework-redesign and rest-framework-material (#9088) --- docs/community/third-party-packages.md | 7 +++++++ docs/img/rfm.png | Bin 0 -> 137450 bytes docs/img/rfr.png | Bin 0 -> 145437 bytes docs/topics/browsable-api.md | 25 +++++++++++++++++++++++++ 4 files changed, 32 insertions(+) create mode 100644 docs/img/rfm.png create mode 100644 docs/img/rfr.png diff --git a/docs/community/third-party-packages.md b/docs/community/third-party-packages.md index 2304dfe45f..20c62f8d6f 100644 --- a/docs/community/third-party-packages.md +++ b/docs/community/third-party-packages.md @@ -152,6 +152,11 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque * [drf-standardized-errors][drf-standardized-errors] - DRF exception handler to standardize error responses for all API endpoints. * [drf-api-action][drf-api-action] - uses the power of DRF also as a library functions +### Customization + +* [rest-framework-redesign][rest-framework-redesign] - A package for customizing the API using Bootstrap 5. +* [rest-framework-material][rest-framework-material] - Material design for Django REST Framework API. + [cite]: http://www.software-ecosystems.com/Software_Ecosystems/Ecosystems.html [cookiecutter]: https://github.com/jpadilla/cookiecutter-django-rest-framework [new-repo]: https://github.com/new @@ -243,3 +248,5 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [django-requestlogs]: https://github.com/Raekkeri/django-requestlogs [drf-standardized-errors]: https://github.com/ghazi-git/drf-standardized-errors [drf-api-action]: https://github.com/Ori-Roza/drf-api-action +[rest-framework-redesign]: https://github.com/youzarsiph/rest-framework-redesign +[rest-framework-material]: https://github.com/youzarsiph/rest-framework-material diff --git a/docs/img/rfm.png b/docs/img/rfm.png new file mode 100644 index 0000000000000000000000000000000000000000..7c82621af715eca1530d8a244a5b20180bddbb79 GIT binary patch literal 137450 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

    Y2;Ep^b>( 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 Ak^lez literal 0 HcmV?d00001 diff --git a/docs/img/rfr.png b/docs/img/rfr.png new file mode 100644 index 0000000000000000000000000000000000000000..1854511d0da63b3c192323716ad2187453b9fd23 GIT binary patch literal 145437 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≥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 literal 0 HcmV?d00001 diff --git a/docs/topics/browsable-api.md b/docs/topics/browsable-api.md index 2cdf2a884c..39473cdd87 100644 --- a/docs/topics/browsable-api.md +++ b/docs/topics/browsable-api.md @@ -65,6 +65,27 @@ For more specific CSS tweaks than simply overriding the default bootstrap theme --- +### Third party packages for customization + +You can use a third party package for customization, rather than doing it by yourself. Here is 2 packages for customizing the API: + +* [rest-framework-redesign][rest-framework-redesign] - A package for customizing the API using Bootstrap 5. Modern and sleek design, it comes with the support for dark mode. +* [rest-framework-material][rest-framework-material] - Material design for Django REST Framework. + +--- + +![Django REST Framework Redesign][rfr] + +*Screenshot of the rest-framework-redesign* + +--- + +![Django REST Framework Material][rfm] + +*Screenshot of the rest-framework-material* + +--- + ### Blocks All of the blocks available in the browsable API base template that can be used in your `api.html`. @@ -162,3 +183,7 @@ There are [a variety of packages for autocomplete widgets][autocomplete-packages [bcomponentsnav]: https://getbootstrap.com/2.3.2/components.html#navbar [autocomplete-packages]: https://www.djangopackages.com/grids/g/auto-complete/ [django-autocomplete-light]: https://github.com/yourlabs/django-autocomplete-light +[rest-framework-redesign]: https://github.com/youzarsiph/rest-framework-redesign +[rest-framework-material]: https://github.com/youzarsiph/rest-framework-material +[rfr]: ../img/rfr.png +[rfm]: ../img/rfm.png From f4175b4b079ba939bdb917a64d921610f32bd536 Mon Sep 17 00:00:00 2001 From: Matt George <102607949+geomat0101@users.noreply.github.com> Date: Thu, 4 Jan 2024 08:10:53 -0500 Subject: [PATCH 1079/1262] Update quickstart.md (#9185) add .order_by() to eliminate Pagination UnorderedObjectListWarning --- docs/tutorial/quickstart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md index 09f249f6e1..9a9da40437 100644 --- a/docs/tutorial/quickstart.md +++ b/docs/tutorial/quickstart.md @@ -105,7 +105,7 @@ Right, we'd better write some views then. Open `tutorial/quickstart/views.py` a """ API endpoint that allows groups to be viewed or edited. """ - queryset = Group.objects.all() + queryset = Group.objects.all().order_by('name') serializer_class = GroupSerializer permission_classes = [permissions.IsAuthenticated] From 047bec1288040b9f387e4977bb6cb3fd00d8ea40 Mon Sep 17 00:00:00 2001 From: Code Apprentice Date: Thu, 4 Jan 2024 07:16:57 -0700 Subject: [PATCH 1080/1262] Add documentation to show how to add authentication for the browseable api (#9137) --- docs/topics/browsable-api.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/topics/browsable-api.md b/docs/topics/browsable-api.md index 39473cdd87..67bc978908 100644 --- a/docs/topics/browsable-api.md +++ b/docs/topics/browsable-api.md @@ -15,6 +15,18 @@ If you include fully-qualified URLs in your resource output, they will be 'urliz By default, the API will return the format specified by the headers, which in the case of the browser is HTML. The format can be specified using `?format=` in the request, so you can look at the raw JSON response in a browser by adding `?format=json` to the URL. There are helpful extensions for viewing JSON in [Firefox][ffjsonview] and [Chrome][chromejsonview]. +## Authentication + +To quickly add authentication to the browesable api, add a routes named `"login"` and `"logout"` under the namespace `"rest_framework"`. DRF provides default routes for this which you can add to your urlconf: + +```python +urlpatterns = [ + // ... + url(http://github.com/r'%5Eapi-auth/',%20include('rest_framework.urls',%20namespace='rest_framework')) +] +``` + + ## Customizing The browsable API is built with [Twitter's Bootstrap][bootstrap] (v 3.4.1), making it easy to customize the look-and-feel. From 21bb21b65bee2c3f75ac7d083f89f45791e96aef Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Thu, 11 Jan 2024 20:34:46 +0800 Subject: [PATCH 1081/1262] Fix use of ip_address_validators for Django 5.1+ (#9180) * Fix use of ip_address_validators for Django 5.0+ * Change affected django version to 5.1 --- rest_framework/compat.py | 15 +++++++++++++++ rest_framework/fields.py | 5 +++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 7e80704e11..472b8ad244 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -169,6 +169,21 @@ def parse_header_parameters(line): } +if django.VERSION >= (5, 1): + # Django 5.1+: use the stock ip_address_validators function + # Note: Before Django 5.1, ip_address_validators returns a tuple containing + # 1) the list of validators and 2) the error message. Starting from + # Django 5.1 ip_address_validators only returns the list of validators + from django.core.validators import ip_address_validators +else: + # Django <= 5.1: create a compatibility shim for ip_address_validators + from django.core.validators import \ + ip_address_validators as _ip_address_validators + + def ip_address_validators(protocol, unpack_ipv4): + return _ip_address_validators(protocol, unpack_ipv4)[0] + + # `separators` argument to `json.dumps()` differs between 2.x and 3.x # See: https://bugs.python.org/issue22767 SHORT_SEPARATORS = (',', ':') diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 0b56fa7fb6..fda656507b 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -16,7 +16,7 @@ from django.core.validators import ( EmailValidator, MaxLengthValidator, MaxValueValidator, MinLengthValidator, MinValueValidator, ProhibitNullCharactersValidator, RegexValidator, - URLValidator, ip_address_validators + URLValidator ) from django.forms import FilePathField as DjangoFilePathField from django.forms import ImageField as DjangoImageField @@ -36,6 +36,7 @@ pytz = None from rest_framework import ISO_8601 +from rest_framework.compat import ip_address_validators from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.settings import api_settings from rest_framework.utils import html, humanize_datetime, json, representation @@ -866,7 +867,7 @@ def __init__(self, protocol='both', **kwargs): self.protocol = protocol.lower() self.unpack_ipv4 = (self.protocol == 'both') super().__init__(**kwargs) - validators, error_message = ip_address_validators(protocol, self.unpack_ipv4) + validators = ip_address_validators(protocol, self.unpack_ipv4) self.validators.extend(validators) def to_internal_value(self, data): From c4f9d432dde9027f1918d3014c013930eee3b788 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Thu, 18 Jan 2024 15:35:57 +0100 Subject: [PATCH 1082/1262] Update 3.11-announcement.md (#9227) --- docs/community/3.11-announcement.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/community/3.11-announcement.md b/docs/community/3.11-announcement.md index 5a1f2c8e30..2fc37a7647 100644 --- a/docs/community/3.11-announcement.md +++ b/docs/community/3.11-announcement.md @@ -64,7 +64,7 @@ In some circumstances a Validator class or a Default class may need to access th * Uniqueness validators need to be able to determine the name of the field to which they are applied, in order to run an appropriate database query. * The `CurrentUserDefault` needs to be able to determine the context with which the serializer was instantiated, in order to return the current user instance. -Previous our approach to this was that implementations could include a `set_context` method, which would be called prior to validation. However this approach had issues with potential race conditions. We have now move this approach into a pending deprecation state. It will continue to function, but will be escalated to a deprecated state in 3.12, and removed entirely in 3.13. +Our previous approach to this was that implementations could include a `set_context` method, which would be called prior to validation. However this approach had issues with potential race conditions. We have now move this approach into a pending deprecation state. It will continue to function, but will be escalated to a deprecated state in 3.12, and removed entirely in 3.13. Instead, validators or defaults which require the serializer context, should include a `requires_context = True` attribute on the class. From 552a67acde5eb13f84ec9dbdf4363313cdfda2f0 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Fri, 19 Jan 2024 18:10:56 +0800 Subject: [PATCH 1083/1262] Skip coreapi tests in GenerateSchemaTests if coreapi is not installed (#9229) --- tests/schemas/test_managementcommand.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/schemas/test_managementcommand.py b/tests/schemas/test_managementcommand.py index 4104d4ca62..c0713f43ce 100644 --- a/tests/schemas/test_managementcommand.py +++ b/tests/schemas/test_managementcommand.py @@ -8,7 +8,7 @@ from django.test.utils import override_settings from django.urls import path -from rest_framework.compat import uritemplate, yaml +from rest_framework.compat import coreapi, uritemplate, yaml from rest_framework.management.commands import generateschema from rest_framework.utils import formatting, json from rest_framework.views import APIView @@ -91,6 +91,7 @@ def test_writes_schema_to_file_on_parameter(self): os.remove(path) @pytest.mark.skipif(yaml is None, reason='PyYAML is required.') + @pytest.mark.skipif(coreapi is None, reason='coreapi is required.') @override_settings(REST_FRAMEWORK={'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema'}) def test_coreapi_renders_default_schema_with_custom_title_url_and_description(self): expected_out = """info: @@ -113,6 +114,7 @@ def test_coreapi_renders_default_schema_with_custom_title_url_and_description(se self.assertIn(formatting.dedent(expected_out), self.out.getvalue()) + @pytest.mark.skipif(coreapi is None, reason='coreapi is required.') @override_settings(REST_FRAMEWORK={'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema'}) def test_coreapi_renders_openapi_json_schema(self): expected_out = { @@ -142,6 +144,7 @@ def test_coreapi_renders_openapi_json_schema(self): self.assertDictEqual(out_json, expected_out) + @pytest.mark.skipif(coreapi is None, reason='coreapi is required.') @override_settings(REST_FRAMEWORK={'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema'}) def test_renders_corejson_schema(self): expected_out = """{"_type":"document","":{"list":{"_type":"link","url":"/","action":"get"}}}""" From f85d8cb81a87eba525396ad944861a86914cce20 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Thu, 25 Jan 2024 05:44:46 +0800 Subject: [PATCH 1084/1262] Fix comment syntax error in doc that fails pre-commit blacken-docs (#9225) * Fix comment syntax in doc * Pre-commit --- docs/topics/browsable-api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/topics/browsable-api.md b/docs/topics/browsable-api.md index 67bc978908..9a95edfc60 100644 --- a/docs/topics/browsable-api.md +++ b/docs/topics/browsable-api.md @@ -21,8 +21,8 @@ To quickly add authentication to the browesable api, add a routes named `"login" ```python urlpatterns = [ - // ... - url(http://github.com/r'%5Eapi-auth/',%20include('rest_framework.urls',%20namespace='rest_framework')) + # ... + url(http://github.com/r%22%5Eapi-auth/%22,%20include(%22rest_framework.urls%22,%20namespace=%22rest_framework")) ] ``` From 74689b1f44fd3d5fd3f220b6f1e2bae650eba3d8 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 24 Jan 2024 22:47:46 +0100 Subject: [PATCH 1085/1262] pre-commit autoupdate (#9232) --- .pre-commit-config.yaml | 8 ++++---- docs/community/3.5-announcement.md | 8 ++------ rest_framework/decorators.py | 2 +- tests/test_throttling.py | 2 +- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 64ec37770f..c37da7449f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.4.0 + rev: v4.5.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -9,17 +9,17 @@ repos: - id: check-symlinks - id: check-toml - repo: https://github.com/pycqa/isort - rev: 5.12.0 + rev: 5.13.2 hooks: - id: isort - repo: https://github.com/PyCQA/flake8 - rev: 3.9.0 + rev: 7.0.0 hooks: - id: flake8 additional_dependencies: - flake8-tidy-imports - repo: https://github.com/adamchainz/blacken-docs - rev: 1.13.0 + rev: 1.16.0 hooks: - id: blacken-docs exclude: ^(?!docs).*$ diff --git a/docs/community/3.5-announcement.md b/docs/community/3.5-announcement.md index 91bfce4286..43a628dd4e 100644 --- a/docs/community/3.5-announcement.md +++ b/docs/community/3.5-announcement.md @@ -64,14 +64,10 @@ from rest_framework.schemas import get_schema_view from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer schema_view = get_schema_view( - title='Example API', - renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer] + title="Example API", renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer] ) -urlpatterns = [ - path('swagger/', schema_view), - ... -] +urlpatterns = [path("swagger/", schema_view), ...] ``` There have been a large number of fixes to the schema generation. These should diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index 3b572c09ef..864ff73958 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -36,7 +36,7 @@ def decorator(func): # WrappedAPIView.__doc__ = func.doc <--- Not possible to do this # api_view applied without (method_names) - assert not(isinstance(http_method_names, types.FunctionType)), \ + assert not isinstance(http_method_names, types.FunctionType), \ '@api_view missing list of allowed HTTP methods' # api_view applied with eg. string instead of list of strings diff --git a/tests/test_throttling.py b/tests/test_throttling.py index d5a61232d9..be9decebc5 100644 --- a/tests/test_throttling.py +++ b/tests/test_throttling.py @@ -192,7 +192,7 @@ def ensure_response_header_contains_proper_throttle_field(self, view, expected_h if expect is not None: assert response['Retry-After'] == expect else: - assert not'Retry-After' in response + assert 'Retry-After' not in response def test_seconds_fields(self): """ From ab694eccdeb94faeb8c36647f6b5d2e07729fbdb Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 24 Jan 2024 23:17:01 +0100 Subject: [PATCH 1086/1262] Fix typo (#9231) --- docs/api-guide/fields.md | 2 +- docs/community/release-notes.md | 6 +++--- rest_framework/filters.py | 4 ++-- rest_framework/schemas/generators.py | 4 ++-- rest_framework/schemas/openapi.py | 2 +- rest_framework/settings.py | 2 +- tests/schemas/test_openapi.py | 2 +- tests/test_response.py | 2 +- tests/test_serializer_nested.py | 4 ++-- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 0766fe3ef6..ea944e1fff 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -303,7 +303,7 @@ Corresponds to `django.db.models.fields.DecimalField`. * `min_value` Validate that the number provided is no less than this value. * `localize` Set to `True` to enable localization of input and output based on the current locale. This will also force `coerce_to_string` to `True`. Defaults to `False`. Note that data formatting is enabled if you have set `USE_L10N=True` in your settings file. * `rounding` Sets the rounding mode used when quantizing to the configured precision. Valid values are [`decimal` module rounding modes][python-decimal-rounding-modes]. Defaults to `None`. -* `normalize_output` Will normalize the decimal value when serialized. This will strip all trailing zeroes and change the value's precision to the minimum required precision to be able to represent the value without loosing data. Defaults to `False`. +* `normalize_output` Will normalize the decimal value when serialized. This will strip all trailing zeroes and change the value's precision to the minimum required precision to be able to represent the value without losing data. Defaults to `False`. #### Example usage diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md index d8eadeb32e..71f29e5c05 100644 --- a/docs/community/release-notes.md +++ b/docs/community/release-notes.md @@ -47,7 +47,7 @@ Date: 22nd September 2022 * Stop calling `set_context` on Validators. [[#8589](https://github.com/encode/django-rest-framework/pull/8589)] * Return `NotImplemented` from `ErrorDetails.__ne__`. [[#8538](https://github.com/encode/django-rest-framework/pull/8538)] * Don't evaluate `DateTimeField.default_timezone` when a custom timezone is set. [[#8531](https://github.com/encode/django-rest-framework/pull/8531)] -* Make relative URLs clickable in Browseable API. [[#8464](https://github.com/encode/django-rest-framework/pull/8464)] +* Make relative URLs clickable in Browsable API. [[#8464](https://github.com/encode/django-rest-framework/pull/8464)] * Support `ManyRelatedField` falling back to the default value when the attribute specified by dot notation doesn't exist. Matches `ManyRelatedField.get_attribute` to `Field.get_attribute`. [[#7574](https://github.com/encode/django-rest-framework/pull/7574)] * Make `schemas.openapi.get_reference` public. [[#7515](https://github.com/encode/django-rest-framework/pull/7515)] * Make `ReturnDict` support `dict` union operators on Python 3.9 and later. [[#8302](https://github.com/encode/django-rest-framework/pull/8302)] @@ -65,7 +65,7 @@ Date: 15th December 2021 Date: 13th December 2021 -* Django 4.0 compatability. [#8178] +* Django 4.0 compatibility. [#8178] * Add `max_length` and `min_length` options to `ListSerializer`. [#8165] * Add `get_request_serializer` and `get_response_serializer` hooks to `AutoSchema`. [#7424] * Fix OpenAPI representation of null-able read only fields. [#8116] @@ -954,7 +954,7 @@ See the [release announcement][3.6-release]. * Prevent raising exception when limit is 0. ([#4098][gh4098]) * TokenAuthentication: Allow custom keyword in the header. ([#4097][gh4097]) * Handle incorrectly padded HTTP basic auth header. ([#4090][gh4090]) -* LimitOffset pagination crashes Browseable API when limit=0. ([#4079][gh4079]) +* LimitOffset pagination crashes Browsable API when limit=0. ([#4079][gh4079]) * Fixed DecimalField arbitrary precision support. ([#4075][gh4075]) * Added support for custom CSRF cookie names. ([#4049][gh4049]) * Fix regression introduced by #4035. ([#4041][gh4041]) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 065e72f94a..86effe24e3 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -21,14 +21,14 @@ def search_smart_split(search_terms): - """generator that first splits string by spaces, leaving quoted phrases togheter, + """generator that first splits string by spaces, leaving quoted phrases together, then it splits non-quoted phrases by commas. """ for term in smart_split(search_terms): # trim commas to avoid bad matching for quoted phrases term = term.strip(',') if term.startswith(('"', "'")) and term[0] == term[-1]: - # quoted phrases are kept togheter without any other split + # quoted phrases are kept together without any other split yield unescape_string_literal(term) else: # non-quoted tokens are split by comma, keeping only non-empty ones diff --git a/rest_framework/schemas/generators.py b/rest_framework/schemas/generators.py index d3c6446aa4..f59e25c213 100644 --- a/rest_framework/schemas/generators.py +++ b/rest_framework/schemas/generators.py @@ -102,12 +102,12 @@ def get_path_from_regex(self, path_regex): Given a URL conf regex, return a URI template string. """ # ???: Would it be feasible to adjust this such that we generate the - # path, plus the kwargs, plus the type from the convertor, such that we + # path, plus the kwargs, plus the type from the converter, such that we # could feed that straight into the parameter schema object? path = simplify_regex(path_regex) - # Strip Django 2.0 convertors as they are incompatible with uritemplate format + # Strip Django 2.0 converters as they are incompatible with uritemplate format return re.sub(_PATH_PARAMETER_COMPONENT_RE, r'{\g}', path) def should_include_endpoint(self, path, callback): diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index ad19f6df85..c154494e2e 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -84,7 +84,7 @@ def get_schema(self, request=None, public=False): continue if components_schemas[k] == components[k]: continue - warnings.warn('Schema component "{}" has been overriden with a different value.'.format(k)) + warnings.warn('Schema component "{}" has been overridden with a different value.'.format(k)) components_schemas.update(components) diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 96b6645742..b0d7bacecc 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -116,7 +116,7 @@ 'COERCE_DECIMAL_TO_STRING': True, 'UPLOADED_FILES_USE_URL': True, - # Browseable API + # Browsable API 'HTML_SELECT_CUTOFF': 1000, 'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...", diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index 6645cac728..a168cb4661 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -1347,7 +1347,7 @@ def test_duplicate_component_name(self): assert len(w) == 1 assert issubclass(w[-1].category, UserWarning) - assert 'has been overriden with a different value.' in str(w[-1].message) + assert 'has been overridden with a different value.' in str(w[-1].message) assert 'components' in schema assert 'schemas' in schema['components'] diff --git a/tests/test_response.py b/tests/test_response.py index cab19a1eb8..00b5691b0f 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -132,7 +132,7 @@ class HTMLNewModelView(generics.ListCreateAPIView): ] -# TODO: Clean tests bellow - remove duplicates with above, better unit testing, ... +# TODO: Clean tests below - remove duplicates with above, better unit testing, ... @override_settings(ROOT_URLCONF='tests.test_response') class RendererIntegrationTests(TestCase): """ diff --git a/tests/test_serializer_nested.py b/tests/test_serializer_nested.py index 986972a65a..b7195494c4 100644 --- a/tests/test_serializer_nested.py +++ b/tests/test_serializer_nested.py @@ -223,7 +223,7 @@ def test_json_validate(self): input_data = {} serializer = self.Serializer(data=input_data) - # request is empty, therefor 'nested' should not be in serializer.data + # request is empty, therefore 'nested' should not be in serializer.data assert serializer.is_valid() assert 'nested' not in serializer.validated_data @@ -237,7 +237,7 @@ def test_multipart_validate(self): input_data = QueryDict('') serializer = self.Serializer(data=input_data) - # the querydict is empty, therefor 'nested' should not be in serializer.data + # the querydict is empty, therefore 'nested' should not be in serializer.data assert serializer.is_valid() assert 'nested' not in serializer.validated_data From 2797c0f216288bd7ca82e9e32e5f7d699bac74df Mon Sep 17 00:00:00 2001 From: Marcelo Galigniana Date: Thu, 25 Jan 2024 12:01:06 +0100 Subject: [PATCH 1087/1262] Add missing import in /api-guide/viewsets/ example (#9235) --- docs/api-guide/viewsets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index 41ba1743c3..43007e95df 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -311,7 +311,7 @@ You may need to provide custom `ViewSet` classes that do not have the full set o To create a base viewset class that provides `create`, `list` and `retrieve` operations, inherit from `GenericViewSet`, and mixin the required actions: - from rest_framework import mixins + from rest_framework import mixins, viewsets class CreateListRetrieveViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, From 41edb3b9dde2105b9fb015ce6b8171e7822baa30 Mon Sep 17 00:00:00 2001 From: Yuekui Date: Fri, 26 Jan 2024 02:36:18 -0800 Subject: [PATCH 1088/1262] Avoid unnecessary unique together checking (#9154) --- rest_framework/validators.py | 17 +++++++++++++---- tests/test_validators.py | 18 +++++++++++++++++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/rest_framework/validators.py b/rest_framework/validators.py index 07ad11b47b..3f09c15cd6 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -160,10 +160,19 @@ def __call__(self, attrs, serializer): queryset = self.exclude_current_instance(attrs, queryset, serializer.instance) # Ignore validation if any field is None - checked_values = [ - value for field, value in attrs.items() if field in self.fields - ] - if None not in checked_values and qs_exists(queryset): + if serializer.instance is None: + checked_values = [ + value for field, value in attrs.items() if field in self.fields + ] + else: + # Ignore validation if all field values are unchanged + checked_values = [ + value + for field, value in attrs.items() + if field in self.fields and value != getattr(serializer.instance, field) + ] + + if checked_values and None not in checked_values and qs_exists(queryset): field_names = ', '.join(self.fields) message = self.message.format(field_names=field_names) raise ValidationError(message, code='unique') diff --git a/tests/test_validators.py b/tests/test_validators.py index 1cf42ed07e..49b0db63a2 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -1,5 +1,5 @@ import datetime -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch import pytest from django.db import DataError, models @@ -447,6 +447,22 @@ def test_do_not_ignore_validation_for_null_fields(self): serializer = NullUniquenessTogetherSerializer(data=data) assert not serializer.is_valid() + def test_ignore_validation_for_unchanged_fields(self): + """ + If all fields in the unique together constraint are unchanged, + then the instance should skip uniqueness validation. + """ + instance = UniquenessTogetherModel.objects.create( + race_name="Paris Marathon", position=1 + ) + data = {"race_name": "Paris Marathon", "position": 1} + serializer = UniquenessTogetherSerializer(data=data, instance=instance) + with patch( + "rest_framework.validators.qs_exists" + ) as mock: + assert serializer.is_valid() + assert not mock.called + def test_filter_queryset_do_not_skip_existing_attribute(self): """ filter_queryset should add value from existing instance attribute From 5ad467aa2c76a7cfb6218f6dbe22e314131bde3c Mon Sep 17 00:00:00 2001 From: iamkorniichuk <119371190+iamkorniichuk@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:30:29 +0200 Subject: [PATCH 1089/1262] Remove read_only deprecated behaviour from documentation (#9239) --- docs/api-guide/validators.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index 2a93e4cdca..e181d4c61f 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -173,11 +173,9 @@ If you want the date field to be entirely hidden from the user, then use `Hidden # Advanced field defaults Validators that are applied across multiple fields in the serializer can sometimes require a field input that should not be provided by the API client, but that *is* available as input to the validator. +For this purposes use `HiddenField`. This field will be present in `validated_data` but *will not* be used in the serializer output representation. -Two patterns that you may want to use for this sort of validation include: - -* Using `HiddenField`. This field will be present in `validated_data` but *will not* be used in the serializer output representation. -* Using a standard field with `read_only=True`, but that also includes a `default=…` argument. This field *will* be used in the serializer output representation, but cannot be set directly by the user. +**Note:** Using a `read_only=True` field is excluded from writable fields so it won't use a `default=…` argument. Look [3.8 announcement](https://www.django-rest-framework.org/community/3.8-announcement/#altered-the-behaviour-of-read_only-plus-default-on-field). REST framework includes a couple of defaults that may be useful in this context. @@ -189,7 +187,7 @@ A default class that can be used to represent the current user. In order to use default=serializers.CurrentUserDefault() ) -#### CreateOnlyDefault +#### CreateOnlyDefault A default class that can be used to *only set a default argument during create operations*. During updates the field is omitted. From df89f32b88022828ca5d1b5e04be36d54f506e6c Mon Sep 17 00:00:00 2001 From: Victorien <65306057+Viicos@users.noreply.github.com> Date: Wed, 14 Feb 2024 12:02:02 +0100 Subject: [PATCH 1090/1262] Fix typo in 3.14 announcement (#9255) --- docs/community/3.14-announcement.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/community/3.14-announcement.md b/docs/community/3.14-announcement.md index e7b10ede19..991c6fc5af 100644 --- a/docs/community/3.14-announcement.md +++ b/docs/community/3.14-announcement.md @@ -28,10 +28,10 @@ Our requirements are now: * Python 3.6+ * Django 4.1, 4.0, 3.2, 3.1, 3.0 -## `raise_exceptions` argument for `is_valid` is now keyword-only. +## `raise_exception` argument for `is_valid` is now keyword-only. Calling `serializer_instance.is_valid(True)` is no longer acceptable syntax. -If you'd like to use the `raise_exceptions` argument, you must use it as a +If you'd like to use the `raise_exception` argument, you must use it as a keyword argument. See Pull Request [#7952](https://github.com/encode/django-rest-framework/pull/7952) for more details. From 2ef77b18335322f10c8c6160b6f7794ff6bc92b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=9Fuayip=20=C3=BCz=C3=BClmez?= <17948971+realsuayip@users.noreply.github.com> Date: Tue, 20 Feb 2024 01:28:04 +0300 Subject: [PATCH 1091/1262] Use POST method instead of GET to perform logout in browsable API (#9208) * Use POST method instead of GET to perform logout in browsable API * Add a test that checks the presence of the logout form --- rest_framework/templates/rest_framework/admin.html | 2 +- rest_framework/templates/rest_framework/base.html | 2 +- rest_framework/templatetags/rest_framework.py | 13 +++++++++---- tests/browsable_api/test_browsable_api.py | 6 ++++++ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/rest_framework/templates/rest_framework/admin.html b/rest_framework/templates/rest_framework/admin.html index 7c6917e2d3..a5edf529ed 100644 --- a/rest_framework/templates/rest_framework/admin.html +++ b/rest_framework/templates/rest_framework/admin.html @@ -42,7 +42,7 @@