From 3a2ad8e68c669eebcc9847e6c2a664e323e8da1d Mon Sep 17 00:00:00 2001 From: imdark Date: Wed, 17 May 2017 11:49:30 -0700 Subject: [PATCH 001/730] in order to solve the memory leak at #5146 Large encoded string take a very long time to to release from memory, but if we just pass the stream directly into json.load we get much better memory performance. --- rest_framework/parsers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 238382364..1a4c24387 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -22,6 +22,7 @@ from django.utils.six.moves.urllib import parse as urlparse from rest_framework import renderers from rest_framework.exceptions import ParseError +import codecs class DataAndFiles(object): @@ -61,8 +62,8 @@ class JSONParser(BaseParser): encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) try: - data = stream.read().decode(encoding) - return json.loads(data) + decoded_stream = codecs.decode(stream, encoding) + return json.load(decoded_stream) except ValueError as exc: raise ParseError('JSON parse error - %s' % six.text_type(exc)) From 53b3b83b0460e99d22c96cc75625358cdedb96a7 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Mon, 22 May 2017 11:55:19 +0200 Subject: [PATCH 002/730] Add `generator_class` parameter to docs. `get_schema_view` accepts `generator_class` parameter --- docs/api-guide/schemas.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index afa058d94..aa830e47d 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -185,6 +185,12 @@ to be exposed in the schema: patterns=schema_url_patterns, ) +#### `generator_class` + +May be used to specify a `SchemaGenerator` subclass to be passed to the +`SchemaView`. + + ## Using an explicit schema view From d1093b5326b068ec33139e2c3349a56550078122 Mon Sep 17 00:00:00 2001 From: Azim Khakulov Date: Tue, 23 May 2017 02:08:20 +0200 Subject: [PATCH 003/730] Added documentation from where to import get_schema_view --- docs/api-guide/schemas.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index f43ff56bd..0a6f9eb2d 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -107,6 +107,8 @@ add a schema to your API, depending on exactly what you need. The simplest way to include a schema in your project is to use the `get_schema_view()` function. + from rest_framework.schemas import get_schema_view + schema_view = get_schema_view(title="Server Monitoring API") urlpatterns = [ @@ -161,6 +163,7 @@ ROOT_URLCONF setting. May be used to pass the set of renderer classes that can be used to render the API root endpoint. + from rest_framework.schemas import get_schema_view from rest_framework.renderers import CoreJSONRenderer from my_custom_package import APIBlueprintRenderer From 99782c2160de49c106fe9e9ce087de3fc36e4cf5 Mon Sep 17 00:00:00 2001 From: Tadhg O'Higgins Date: Wed, 24 May 2017 16:46:18 -0700 Subject: [PATCH 004/730] Add tests for HTML_CUTOFF setting and fix issue where setting it to None would raise an exception. --- rest_framework/relations.py | 23 +++++++++------ tests/test_relations.py | 58 ++++++++++++++++++++++++++++++++++++- tests/utils.py | 3 ++ 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 54e67cd16..4d3bdba1d 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -75,7 +75,8 @@ class PKOnlyObject(object): # rather than the parent serializer. MANY_RELATION_KWARGS = ( 'read_only', 'write_only', 'required', 'default', 'initial', 'source', - 'label', 'help_text', 'style', 'error_messages', 'allow_empty' + 'label', 'help_text', 'style', 'error_messages', 'allow_empty', + 'html_cutoff', 'html_cutoff_text' ) @@ -86,10 +87,12 @@ class RelatedField(Field): def __init__(self, **kwargs): self.queryset = kwargs.pop('queryset', self.queryset) - self.html_cutoff = kwargs.pop( - 'html_cutoff', - self.html_cutoff or int(api_settings.HTML_SELECT_CUTOFF) - ) + + cutoff_from_settings = api_settings.HTML_SELECT_CUTOFF + if cutoff_from_settings is not None: + cutoff_from_settings = int(cutoff_from_settings) + self.html_cutoff = kwargs.pop('html_cutoff', cutoff_from_settings) + self.html_cutoff_text = kwargs.pop( 'html_cutoff_text', self.html_cutoff_text or _(api_settings.HTML_SELECT_CUTOFF_TEXT) @@ -466,10 +469,12 @@ class ManyRelatedField(Field): def __init__(self, child_relation=None, *args, **kwargs): self.child_relation = child_relation self.allow_empty = kwargs.pop('allow_empty', True) - self.html_cutoff = kwargs.pop( - 'html_cutoff', - self.html_cutoff or int(api_settings.HTML_SELECT_CUTOFF) - ) + + cutoff_from_settings = api_settings.HTML_SELECT_CUTOFF + if cutoff_from_settings is not None: + cutoff_from_settings = int(cutoff_from_settings) + self.html_cutoff = kwargs.pop('html_cutoff', cutoff_from_settings) + self.html_cutoff_text = kwargs.pop( 'html_cutoff_text', self.html_cutoff_text or _(api_settings.HTML_SELECT_CUTOFF_TEXT) diff --git a/tests/test_relations.py b/tests/test_relations.py index c903ee557..32e9b9cc7 100644 --- a/tests/test_relations.py +++ b/tests/test_relations.py @@ -1,12 +1,13 @@ import uuid import pytest +from _pytest.monkeypatch import MonkeyPatch from django.conf.urls import url from django.core.exceptions import ImproperlyConfigured from django.test import override_settings from django.utils.datastructures import MultiValueDict -from rest_framework import serializers +from rest_framework import relations, serializers from rest_framework.fields import empty from rest_framework.test import APISimpleTestCase @@ -25,6 +26,61 @@ class TestStringRelatedField(APISimpleTestCase): assert representation == '' +class MockApiSettings(object): + def __init__(self, cutoff, cutoff_text): + self.HTML_SELECT_CUTOFF = cutoff + self.HTML_SELECT_CUTOFF_TEXT = cutoff_text + + +class TestRelatedFieldHTMLCutoff(APISimpleTestCase): + def setUp(self): + self.queryset = MockQueryset([ + MockObject(pk=i, name=str(i)) for i in range(0, 1100) + ]) + self.monkeypatch = MonkeyPatch() + + def test_no_settings(self): + # The default is 1,000, so sans settings it should be 1,000 plus one. + for many in (False, True): + field = serializers.PrimaryKeyRelatedField(queryset=self.queryset, + many=many) + options = list(field.iter_options()) + assert len(options) == 1001 + assert options[-1].display_text == "More than 1000 items..." + + def test_settings_cutoff(self): + self.monkeypatch.setattr(relations, "api_settings", + MockApiSettings(2, "Cut Off")) + for many in (False, True): + field = serializers.PrimaryKeyRelatedField(queryset=self.queryset, + many=many) + options = list(field.iter_options()) + assert len(options) == 3 # 2 real items plus the 'Cut Off' item. + assert options[-1].display_text == "Cut Off" + + def test_settings_cutoff_none(self): + # Setting it to None should mean no limit; the default limit is 1,000. + self.monkeypatch.setattr(relations, "api_settings", + MockApiSettings(None, "Cut Off")) + for many in (False, True): + field = serializers.PrimaryKeyRelatedField(queryset=self.queryset, + many=many) + options = list(field.iter_options()) + assert len(options) == 1100 + + def test_settings_kwargs_cutoff(self): + # The explicit argument should override the settings. + self.monkeypatch.setattr(relations, "api_settings", + MockApiSettings(2, "Cut Off")) + for many in (False, True): + field = serializers.PrimaryKeyRelatedField(queryset=self.queryset, + many=many, + html_cutoff=100) + options = list(field.iter_options()) + assert len(options) == 101 + assert options[-1].display_text == "Cut Off" + + class TestPrimaryKeyRelatedField(APISimpleTestCase): def setUp(self): self.queryset = MockQueryset([ diff --git a/tests/utils.py b/tests/utils.py index 52582f093..5fb0723f8 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -20,6 +20,9 @@ class MockQueryset(object): def __init__(self, iterable): self.items = iterable + def __getitem__(self, val): + return self.items[val] + def get(self, **lookup): for item in self.items: if all([ From 9a2281167178999ad0472d3c9862af91c077322c Mon Sep 17 00:00:00 2001 From: imdark Date: Wed, 24 May 2017 17:56:49 -0700 Subject: [PATCH 005/730] modified to use a reader modified to use a reader since direct decoding is not supported --- rest_framework/parsers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 1a4c24387..817efd2da 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -7,6 +7,7 @@ on the request, such as form content or json encoded data. from __future__ import unicode_literals import json +import codecs from django.conf import settings from django.core.files.uploadhandler import StopFutureHandlers @@ -22,7 +23,6 @@ from django.utils.six.moves.urllib import parse as urlparse from rest_framework import renderers from rest_framework.exceptions import ParseError -import codecs class DataAndFiles(object): @@ -62,7 +62,7 @@ class JSONParser(BaseParser): encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) try: - decoded_stream = codecs.decode(stream, encoding) + decoded_stream = codecs.getreader(encoding)(stream) return json.load(decoded_stream) except ValueError as exc: raise ParseError('JSON parse error - %s' % six.text_type(exc)) From cdeab1c490fa1073ce985b227e85bbdc56cb08c2 Mon Sep 17 00:00:00 2001 From: imdark Date: Wed, 24 May 2017 18:12:38 -0700 Subject: [PATCH 006/730] fixed to pass isort linting --- rest_framework/parsers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 817efd2da..0e40e1a7a 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -6,8 +6,8 @@ on the request, such as form content or json encoded data. """ from __future__ import unicode_literals -import json import codecs +import json from django.conf import settings from django.core.files.uploadhandler import StopFutureHandlers From 94c37c09c58cc37d61c5a678b37a0111c90936d6 Mon Sep 17 00:00:00 2001 From: Levi Cameron Date: Thu, 25 May 2017 20:07:34 +1000 Subject: [PATCH 007/730] Fix browsable API not supporting multipart/form-data correctly - Autodetect missing boundary parameter for Content-Type header - textarea value normalises EOL chars to \n when multipart/form-data requires \r\n --- rest_framework/static/rest_framework/js/ajax-form.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rest_framework/static/rest_framework/js/ajax-form.js b/rest_framework/static/rest_framework/js/ajax-form.js index ce17729d1..88cb133bf 100644 --- a/rest_framework/static/rest_framework/js/ajax-form.js +++ b/rest_framework/static/rest_framework/js/ajax-form.js @@ -37,6 +37,17 @@ function doAjaxSubmit(e) { if (contentType) { data = form.find('[data-override="content"]').val() || '' + + if (contentType === 'multipart/form-data') { + // We need to add a boundary parameter to the header + var re = /^--([0-9A-Z'()+_,-./:=?]{1,70})[ \f\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]*$/im; + var boundary = re.exec(data); + if (boundary !== null) { + contentType += '; boundary="' + boundary[1] + '"'; + } + // Fix textarea.value EOL normalisation (multipart/form-data should use CR+NL, not NL) + data = data.replace(/\n/g, '\r\n'); + } } else { contentType = form.attr('enctype') || form.attr('encoding') From e6c9f89a123756382cbfa7196ae631e41ef3feaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=ED=99=98?= Date: Thu, 25 May 2017 19:13:45 +0900 Subject: [PATCH 008/730] Fixed curly bracket in regexp of @list_route --- rest_framework/routers.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rest_framework/routers.py b/rest_framework/routers.py index fce968aa0..81f8ebf78 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -35,6 +35,12 @@ DynamicDetailRoute = namedtuple('DynamicDetailRoute', ['url', 'name', 'initkwarg DynamicListRoute = namedtuple('DynamicListRoute', ['url', 'name', 'initkwargs']) +def replace_curly_brackets(url_path): + if ('{' and '}') in url_path: + url_path = url_path.replace('{', '{{').replace('}', '}}') + return url_path + + def replace_methodname(format_string, methodname): """ Partially format a format_string, swapping out any @@ -178,6 +184,7 @@ class SimpleRouter(BaseRouter): initkwargs = route.initkwargs.copy() initkwargs.update(method_kwargs) url_path = initkwargs.pop("url_path", None) or methodname + url_path = replace_curly_brackets(url_path) url_name = initkwargs.pop("url_name", None) or url_path ret.append(Route( url=replace_methodname(route.url, url_path), From a002bb5c67a8d033463d30ea6527fb2825b61adf Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Fri, 26 May 2017 09:25:43 +0200 Subject: [PATCH 009/730] Fixed test_hyperlinked_related_lookup_url_encoded_exists. Space character ' ' is prohibited in IRIs, therefore we shouldn't rely on encoding '%20' to ' ' in the HyperlinkedRelatedField tests. --- tests/test_relations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_relations.py b/tests/test_relations.py index c903ee557..1868374fc 100644 --- a/tests/test_relations.py +++ b/tests/test_relations.py @@ -96,7 +96,7 @@ class TestHyperlinkedRelatedField(APISimpleTestCase): def setUp(self): self.queryset = MockQueryset([ MockObject(pk=1, name='foobar'), - MockObject(pk=2, name='baz qux'), + MockObject(pk=2, name='bazABCqux'), ]) self.field = serializers.HyperlinkedRelatedField( view_name='example', @@ -116,7 +116,7 @@ class TestHyperlinkedRelatedField(APISimpleTestCase): assert instance is self.queryset.items[0] def test_hyperlinked_related_lookup_url_encoded_exists(self): - instance = self.field.to_internal_value('http://example.org/example/baz%20qux/') + instance = self.field.to_internal_value('http://example.org/example/baz%41%42%43qux/') assert instance is self.queryset.items[1] def test_hyperlinked_related_lookup_does_not_exist(self): From 04adfb9c94b2fa68c4f9461fce36091b803840b1 Mon Sep 17 00:00:00 2001 From: Dryice Liu Date: Sun, 28 May 2017 04:14:56 +0800 Subject: [PATCH 010/730] make sure max_length is in FileField kwargs --- rest_framework/utils/field_mapping.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index 44c6d4b70..dff33d8b3 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -192,7 +192,8 @@ def get_field_kwargs(field_name, model_field): # rather than as a validator. max_length = getattr(model_field, 'max_length', None) if max_length is not None and (isinstance(model_field, models.CharField) or - isinstance(model_field, models.TextField)): + isinstance(model_field, models.TextField) or + isinstance(model_field, models.FileField)): kwargs['max_length'] = max_length validator_kwarg = [ validator for validator in validator_kwarg From 973860d9fe91f59d9c46e086fb5ef1aa839a4bbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=ED=99=98?= Date: Sun, 28 May 2017 18:38:09 +0900 Subject: [PATCH 011/730] Added: test for list_route and detail_route with regex url_path --- tests/test_routers.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/test_routers.py b/tests/test_routers.py index 97f43b91a..fee39b2b3 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -65,6 +65,19 @@ class EmptyPrefixViewSet(viewsets.ModelViewSet): return self.queryset[index] +class RegexUrlPathViewSet(viewsets.ViewSet): + @list_route(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})') + def regex_url_path_detail(self, request, *args, **kwargs): + pk = self.kwargs.get('pk', '') + kwarg = self.kwargs.get('kwarg', '') + return Response({'pk': pk, 'kwarg': kwarg}) + + notes_router = SimpleRouter() notes_router.register(r'notes', NoteViewSet) @@ -80,6 +93,9 @@ empty_prefix_urls = [ url(r'^', include(empty_prefix_router.urls)), ] +regex_url_path_router = SimpleRouter() +regex_url_path_router.register(r'', RegexUrlPathViewSet, base_name='regex') + urlpatterns = [ url(r'^non-namespaced/', include(namespaced_router.urls)), url(r'^namespaced/', include(namespaced_router.urls, namespace='example', app_name='example')), @@ -87,6 +103,7 @@ urlpatterns = [ url(r'^example2/', include(kwarged_notes_router.urls)), url(r'^empty-prefix/', include(empty_prefix_urls)), + url(r'^regex/', include(regex_url_path_router.urls)) ] @@ -402,3 +419,19 @@ class TestEmptyPrefix(TestCase): response = self.client.get('/empty-prefix/1/') assert response.status_code == 200 assert json.loads(response.content.decode('utf-8')) == {'uuid': '111', 'text': 'First'} + + +@override_settings(ROOT_URLCONF='tests.test_routers') +class TestRegexUrlPath(TestCase): + def test_regex_url_path_list(self): + kwarg = '1234' + response = self.client.get('/regex/list/{}/'.format(kwarg)) + assert response.status_code == 200 + assert json.loads(response.content.decode('utf-8')) == {'kwarg': kwarg} + + def test_regex_url_path_detail(self): + pk = '1' + kwarg = '1234' + response = self.client.get('/regex/{}/detail/{}/'.format(pk, kwarg)) + assert response.status_code == 200 + assert json.loads(response.content.decode('utf-8')) == {'pk': pk, 'kwarg': kwarg} From 836328594b2ac9a81cf6e1ecf6cdc204e7387326 Mon Sep 17 00:00:00 2001 From: Dryice Liu Date: Mon, 29 May 2017 08:27:07 +0800 Subject: [PATCH 012/730] add test --- tests/test_model_serializer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 4aa0fa35e..ba3edd389 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -63,6 +63,7 @@ class RegularFieldsModel(models.Model): slug_field = models.SlugField(max_length=100) small_integer_field = models.SmallIntegerField() text_field = models.TextField(max_length=100) + file_field = models.FileField(max_length=100) time_field = models.TimeField() url_field = models.URLField(max_length=100) custom_field = CustomField() @@ -181,6 +182,7 @@ class TestRegularFieldMappings(TestCase): slug_field = SlugField(max_length=100) small_integer_field = IntegerField() text_field = CharField(max_length=100, style={'base_template': 'textarea.html'}) + file_field = FileField(max_length=100) time_field = TimeField() url_field = URLField(max_length=100) custom_field = ModelField(model_field=) From 0ad017a5735a532280c8c56ad8c66f950f0b22f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=ED=99=98?= Date: Mon, 29 May 2017 20:55:06 +0900 Subject: [PATCH 013/730] requested changes --- rest_framework/routers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 81f8ebf78..87e58b015 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -35,7 +35,10 @@ DynamicDetailRoute = namedtuple('DynamicDetailRoute', ['url', 'name', 'initkwarg DynamicListRoute = namedtuple('DynamicListRoute', ['url', 'name', 'initkwargs']) -def replace_curly_brackets(url_path): +def escape_curly_brackets(url_path): + """ + Double brackets in regex of url_path for escape string formatting + """ if ('{' and '}') in url_path: url_path = url_path.replace('{', '{{').replace('}', '}}') return url_path @@ -184,7 +187,7 @@ class SimpleRouter(BaseRouter): initkwargs = route.initkwargs.copy() initkwargs.update(method_kwargs) url_path = initkwargs.pop("url_path", None) or methodname - url_path = replace_curly_brackets(url_path) + 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), From d198b1abe6b96bbdd84e4c2622d996f770c69edd Mon Sep 17 00:00:00 2001 From: Andrea Grandi Date: Mon, 29 May 2017 17:07:50 +0100 Subject: [PATCH 014/730] Add Django manage command to create a DRF user Token --- .../authtoken/management/__init__.py | 0 .../authtoken/management/commands/__init__.py | 0 .../management/commands/drf_create_token.py | 20 +++++++++++++++++++ tests/test_authtoken.py | 15 ++++++++++++++ 4 files changed, 35 insertions(+) create mode 100644 rest_framework/authtoken/management/__init__.py create mode 100644 rest_framework/authtoken/management/commands/__init__.py create mode 100644 rest_framework/authtoken/management/commands/drf_create_token.py diff --git a/rest_framework/authtoken/management/__init__.py b/rest_framework/authtoken/management/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/rest_framework/authtoken/management/commands/__init__.py b/rest_framework/authtoken/management/commands/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/rest_framework/authtoken/management/commands/drf_create_token.py b/rest_framework/authtoken/management/commands/drf_create_token.py new file mode 100644 index 000000000..ccd400ed5 --- /dev/null +++ b/rest_framework/authtoken/management/commands/drf_create_token.py @@ -0,0 +1,20 @@ +from django.contrib.auth.models import User +from django.core.management.base import BaseCommand +from rest_framework.authtoken.models import Token + + +class Command(BaseCommand): + help = 'Create DRF Token for a given user' + + def create_user_token(self, username): + user = User.objects.get(username=username) + token = Token.objects.get_or_create(user=user) + return token[0] + + def add_arguments(self, parser): + parser.add_argument('username', type=str, nargs='+') + + def handle(self, *args, **options): + username = options['username'] + token = self.create_user_token(username) + print('Generated token {0} for user {1}'.format(token.key, username)) diff --git a/tests/test_authtoken.py b/tests/test_authtoken.py index 54ac1848d..3b5a618e3 100644 --- a/tests/test_authtoken.py +++ b/tests/test_authtoken.py @@ -4,6 +4,8 @@ from django.contrib.auth.models import User from django.test import TestCase from rest_framework.authtoken.admin import TokenAdmin +from rest_framework.authtoken.management.commands.drf_create_token import \ + Command as AuthTokenCommand from rest_framework.authtoken.models import Token from rest_framework.authtoken.serializers import AuthTokenSerializer from rest_framework.exceptions import ValidationError @@ -33,3 +35,16 @@ class AuthTokenTests(TestCase): self.user.set_password(data['password']) self.user.save() assert AuthTokenSerializer(data=data).is_valid() + + +class AuthTokenCommandTests(TestCase): + + def setUp(self): + self.site = site + self.user = User.objects.create_user(username='test_user') + + def test_command_create_user_token(self): + token = AuthTokenCommand().create_user_token(self.user.username) + assert token is not None + token_saved = Token.objects.first() + assert token.key == token_saved.key From 84e22cc2f3866078a06a464636e4ec6c1a7fec47 Mon Sep 17 00:00:00 2001 From: Bekhzod Tillakhanov Date: Tue, 30 May 2017 00:15:07 +0500 Subject: [PATCH 015/730] Scheme fix when unauth and Flask8 lint fix --- rest_framework/templates/rest_framework/docs/document.html | 3 ++- rest_framework/templates/rest_framework/docs/sidebar.html | 2 ++ tests/test_atomic_requests.py | 1 + tests/test_fields.py | 1 - tests/test_permissions.py | 3 +++ tests/test_renderers.py | 1 + tests/test_request.py | 1 + tests/test_response.py | 1 + tests/test_reverse.py | 1 + tests/test_schemas.py | 1 + tests/test_validation.py | 1 + 11 files changed, 14 insertions(+), 2 deletions(-) diff --git a/rest_framework/templates/rest_framework/docs/document.html b/rest_framework/templates/rest_framework/docs/document.html index ef5f5966b..274eee4e3 100644 --- a/rest_framework/templates/rest_framework/docs/document.html +++ b/rest_framework/templates/rest_framework/docs/document.html @@ -13,7 +13,7 @@ {% if 'javascript' in langs %}{% include "rest_framework/docs/langs/javascript-intro.html" %}{% endif %} - +{% if document.data %} {% for section_key, section in document.data|items %} {% if section_key %}

{{ section_key }} @@ -28,3 +28,4 @@ {% for link_key, link in document.links|items %} {% include "rest_framework/docs/link.html" %} {% endfor %} +{% endif %} diff --git a/rest_framework/templates/rest_framework/docs/sidebar.html b/rest_framework/templates/rest_framework/docs/sidebar.html index c6ac26f66..f5f84a2e5 100644 --- a/rest_framework/templates/rest_framework/docs/sidebar.html +++ b/rest_framework/templates/rest_framework/docs/sidebar.html @@ -5,6 +5,7 @@

{% endif %} - {% for link_key, link in section.links|items %} + {% for link_key, link in section|schema_links|items %} {% include "rest_framework/docs/link.html" %} {% endfor %} {% endfor %} diff --git a/rest_framework/templates/rest_framework/docs/sidebar.html b/rest_framework/templates/rest_framework/docs/sidebar.html index 7e20e2485..c4eaa120b 100644 --- a/rest_framework/templates/rest_framework/docs/sidebar.html +++ b/rest_framework/templates/rest_framework/docs/sidebar.html @@ -10,7 +10,7 @@ From a1546cc26633b748dd5c79c9dc222ff84b79d43e Mon Sep 17 00:00:00 2001 From: Woile Date: Thu, 17 Aug 2017 21:14:26 +0200 Subject: [PATCH 070/730] [NEW] Tests for templatetags.schema_links --- rest_framework/templatetags/rest_framework.py | 2 +- tests/test_templatetags.py | 315 +++++++++++++++++- 2 files changed, 315 insertions(+), 2 deletions(-) diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index 5fd189548..a2ee5ccdd 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -249,7 +249,7 @@ def schema_links(section, sec_key=None): """ Recursively find every link in a schema, even nested. """ - NESTED_FORMAT = '%s > %s' + NESTED_FORMAT = '%s > %s' # this format is used in docs/js/api.js:normalizeKeys links = section.links if section.data: data = section.data.items() diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py index 87d5cbc81..dfbd5d9de 100644 --- a/tests/test_templatetags.py +++ b/tests/test_templatetags.py @@ -1,13 +1,16 @@ # encoding: utf-8 from __future__ import unicode_literals +import unittest + from django.test import TestCase +from rest_framework.compat import coreapi, coreschema from rest_framework.relations import Hyperlink from rest_framework.templatetags import rest_framework from rest_framework.templatetags.rest_framework import ( add_nested_class, add_query_param, as_string, break_long_headers, - format_value, get_pagination_html, urlize_quoted_links + format_value, get_pagination_html, schema_links, urlize_quoted_links ) from rest_framework.test import APIRequestFactory @@ -300,3 +303,313 @@ class URLizerTests(TestCase): data['"foo_set": [\n "http://api/foos/1/"\n], '] = \ '"foo_set": [\n "http://api/foos/1/"\n], ' self._urlize_dict_check(data) + + +@unittest.skipUnless(coreapi, 'coreapi is not installed') +class SchemaLinksTests(TestCase): + + def test_schema_with_empty_links(self): + schema = coreapi.Document( + url='', + title='Example API', + content={ + 'users': { + 'list': {} + } + } + ) + section = schema['users'] + flat_links = schema_links(section) + assert len(flat_links) is 0 + + def test_single_action(self): + schema = coreapi.Document( + url='', + title='Example API', + content={ + 'users': { + 'list': coreapi.Link( + url='/users/', + action='get', + fields=[] + ) + } + } + ) + section = schema['users'] + flat_links = schema_links(section) + assert len(flat_links) is 1 + assert 'list' in flat_links + + def test_default_actions(self): + schema = coreapi.Document( + url='', + title='Example API', + content={ + 'users': { + 'create': coreapi.Link( + url='/users/', + action='post', + fields=[] + ), + 'list': coreapi.Link( + url='/users/', + action='get', + fields=[] + ), + 'read': coreapi.Link( + url='/users/{id}/', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ), + 'update': coreapi.Link( + url='/users/{id}/', + action='patch', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ) + } + } + ) + section = schema['users'] + flat_links = schema_links(section) + assert len(flat_links) is 4 + assert 'list' in flat_links + assert 'create' in flat_links + assert 'read' in flat_links + assert 'update' in flat_links + + def test_default_actions_and_single_custom_action(self): + schema = coreapi.Document( + url='', + title='Example API', + content={ + 'users': { + 'create': coreapi.Link( + url='/users/', + action='post', + fields=[] + ), + 'list': coreapi.Link( + url='/users/', + action='get', + fields=[] + ), + 'read': coreapi.Link( + url='/users/{id}/', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ), + 'update': coreapi.Link( + url='/users/{id}/', + action='patch', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ), + 'friends': coreapi.Link( + url='/users/{id}/friends', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ) + } + } + ) + section = schema['users'] + flat_links = schema_links(section) + assert len(flat_links) is 5 + assert 'list' in flat_links + assert 'create' in flat_links + assert 'read' in flat_links + assert 'update' in flat_links + assert 'friends' in flat_links + + def test_default_actions_and_single_custom_action_two_methods(self): + schema = coreapi.Document( + url='', + title='Example API', + content={ + 'users': { + 'create': coreapi.Link( + url='/users/', + action='post', + fields=[] + ), + 'list': coreapi.Link( + url='/users/', + action='get', + fields=[] + ), + 'read': coreapi.Link( + url='/users/{id}/', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ), + 'update': coreapi.Link( + url='/users/{id}/', + action='patch', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ), + 'friends': { + 'list': coreapi.Link( + url='/users/{id}/friends', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ), + 'create': coreapi.Link( + url='/users/{id}/friends', + action='post', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ) + } + } + } + ) + section = schema['users'] + flat_links = schema_links(section) + assert len(flat_links) is 6 + assert 'list' in flat_links + assert 'create' in flat_links + assert 'read' in flat_links + assert 'update' in flat_links + assert 'friends > list' in flat_links + assert 'friends > create' in flat_links + + def test_multiple_nested_routes(self): + schema = coreapi.Document( + url='', + title='Example API', + content={ + 'animals': { + 'dog': { + 'vet': { + 'list': coreapi.Link( + url='/animals/dog/{id}/vet', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ) + }, + 'read': coreapi.Link( + url='/animals/dog/{id}', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ) + }, + 'cat': { + 'list': coreapi.Link( + url='/animals/cat/', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ), + 'create': coreapi.Link( + url='/aniamls/cat', + action='post', + fields=[] + ) + } + } + } + ) + section = schema['animals'] + flat_links = schema_links(section) + assert len(flat_links) is 4 + assert 'cat > create' in flat_links + assert 'cat > list' in flat_links + assert 'dog > read' in flat_links + assert 'dog > vet > list' in flat_links + + def test_multiple_resources_with_multiple_nested_routes(self): + schema = coreapi.Document( + url='', + title='Example API', + content={ + 'animals': { + 'dog': { + 'vet': { + 'list': coreapi.Link( + url='/animals/dog/{id}/vet', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ) + }, + 'read': coreapi.Link( + url='/animals/dog/{id}', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ) + }, + 'cat': { + 'list': coreapi.Link( + url='/animals/cat/', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ), + 'create': coreapi.Link( + url='/aniamls/cat', + action='post', + fields=[] + ) + } + }, + 'farmers': { + 'silo': { + 'soy': { + 'list': coreapi.Link( + url='/farmers/silo/{id}/soy', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ) + }, + 'list': coreapi.Link( + url='/farmers/silo', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ) + } + } + } + ) + section = schema['animals'] + flat_links = schema_links(section) + assert len(flat_links) is 4 + assert 'cat > create' in flat_links + assert 'cat > list' in flat_links + assert 'dog > read' in flat_links + assert 'dog > vet > list' in flat_links + + section = schema['farmers'] + flat_links = schema_links(section) + assert len(flat_links) is 2 + assert 'silo > list' in flat_links + assert 'silo > soy > list' in flat_links From c868378c71d7bd9883c1448977e185b1cd7c756c Mon Sep 17 00:00:00 2001 From: kycool Date: Fri, 18 Aug 2017 12:12:01 +0800 Subject: [PATCH 071/730] Update fields.py modify to_choices_dict document --- rest_framework/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 9cc9ab03f..242d0f978 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -142,7 +142,7 @@ def to_choices_dict(choices): to_choices_dict([1]) -> {1: 1} to_choices_dict([(1, '1st'), (2, '2nd')]) -> {1: '1st', 2: '2nd'} - to_choices_dict([('Group', ((1, '1st'), 2))]) -> {'Group': {1: '1st', 2: '2nd'}} + to_choices_dict([('Group', ((1, '1st'), 2))]) -> {'Group': {1: '1st', 2: '2'}} """ # Allow single, paired or grouped choices style: # choices = [1, 2, 3] From 56021f9e7769138e0ae69115f01ed45b5bc8be3f Mon Sep 17 00:00:00 2001 From: Lim H Date: Sun, 20 Aug 2017 17:12:56 +0100 Subject: [PATCH 072/730] Add tests for list field to schema --- tests/test_schemas.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 2b8673480..b435dfdd7 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -39,6 +39,11 @@ class ExampleSerializer(serializers.Serializer): hidden = serializers.HiddenField(default='hello') +class AnotherSerializerWithListFields(serializers.Serializer): + a = serializers.ListField(child=serializers.IntegerField()) + b = serializers.ListSerializer(child=serializers.CharField()) + + class AnotherSerializer(serializers.Serializer): c = serializers.CharField(required=True) d = serializers.CharField(required=False) @@ -57,6 +62,13 @@ class ExampleViewSet(ModelViewSet): """ return super(ExampleSerializer, self).retrieve(self, request) + @detail_route(methods=['post'], 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() def custom_list_action(self, request): return super(ExampleViewSet, self).list(self, request) @@ -174,6 +186,17 @@ class TestRouterGeneratedSchema(TestCase): coreapi.Field('d', required=False, location='form', schema=coreschema.String(title='D')), ] ), + 'custom_action_with_list_fields': coreapi.Link( + url='/example/{id}/custom_action_with_list_fields/', + action='post', + encoding='application/json', + description='A custom action using both list field and list serializer in the serializer.', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()), + coreapi.Field('a', required=True, location='form', schema=coreschema.Array(title='A', items=coreschema.Integer())), + coreapi.Field('b', required=True, location='form', schema=coreschema.Array(title='B', items=coreschema.String())), + ] + ), 'custom_list_action': coreapi.Link( url='/example/custom_list_action/', action='get' From 81527a2863cbac78acf392f081df2e7868ea285b Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Mon, 21 Aug 2017 11:50:00 +0200 Subject: [PATCH 073/730] Release notes for 3.6.4 --- docs/topics/release-notes.md | 59 +++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 7bdd7b0b1..f4a83324c 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,34 @@ You can determine your currently installed version using `pip freeze`: ## 3.6.x series +### 3.6.4 + +**Date**: [21st August 2017][3.6.4-milestone] + +* Ignore any invalidly formed query parameters for OrderingFilter. [#5131][gh5131] +* Improve memory footprint when reading large JSON requests. [#5147][gh5147] +* Fix schema generation for pagination. [#5161][gh5161] +* Fix exception when `HTML_CUTOFF` is set to `None`. [#5174][gh5174] +* Fix browsable API not supporting `multipart/form-data` correctly. [#5176][gh5176] +* Fixed `test_hyperlinked_related_lookup_url_encoded_exists`. [#5179][gh5179] +* Make sure max_length is in FileField kwargs. [#5186][gh5186] +* Fix `list_route` & `detail_route` with kwargs contains curly bracket in `url_path` [#5187][gh5187] +* Add Django manage command to create a DRF user Token. [#5188][gh5188] +* Ensure API documentation templates do not check for user authentication [#5162][gh5162] +* Fix special case where OneToOneField is also primary key. [#5192][gh5192] +* Added aria-label and a new region for accessibility purposes in base.html [#5196][gh5196] +* Quote nested API parameters in api.js. [#5214][gh5214] +* Set ViewSet args/kwargs/request before dispatch. [#5229][gh5229] +* Added unicode support to SlugField. [#5231][gh5231] +* Fix HiddenField appears in Raw Data form initial content. [#5259][gh5259] +* Raise validation error on invalid timezone parsing. [#5261][gh5261] +* Fix SearchFilter to-many behavior/performance. [#5264][gh5264] +* Simplified chained comparisons and minor code fixes. [#5276][gh5276] +* RemoteUserAuthentication, docs, and tests. [#5306][gh5306] +* Revert "Cached the field's root and context property" [#5313][gh5313] +* Fix introspection of list field in schema. [#5326][gh5326] +* Fix interactive docs for multiple nested and extra methods. [#5334][gh5334] + ### 3.6.3 **Date**: [12th May 2017][3.6.3-milestone] @@ -716,6 +744,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.6.1-milestone]: https://github.com/encode/django-rest-framework/issues?q=milestone%3A%223.6.1+Release%22 [3.6.2-milestone]: https://github.com/encode/django-rest-framework/issues?q=milestone%3A%223.6.2+Release%22 [3.6.3-milestone]: https://github.com/encode/django-rest-framework/issues?q=milestone%3A%223.6.3+Release%22 +[3.6.4-milestone]: https://github.com/encode/django-rest-framework/issues?q=milestone%3A%223.6.4+Release%22 [gh2013]: https://github.com/encode/django-rest-framework/issues/2013 @@ -1326,8 +1355,8 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh4955]: https://github.com/encode/django-rest-framework/issues/4955 [gh4956]: https://github.com/encode/django-rest-framework/issues/4956 [gh4949]: https://github.com/encode/django-rest-framework/issues/4949 - + [gh5126]: https://github.com/encode/django-rest-framework/issues/5126 [gh5085]: https://github.com/encode/django-rest-framework/issues/5085 [gh4437]: https://github.com/encode/django-rest-framework/issues/4437 @@ -1360,3 +1389,31 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh4968]: https://github.com/encode/django-rest-framework/issues/4968 [gh5089]: https://github.com/encode/django-rest-framework/issues/5089 [gh5117]: https://github.com/encode/django-rest-framework/issues/5117 + + +[gh5334]: https://github.com/encode/django-rest-framework/issues/5334 +[gh5326]: https://github.com/encode/django-rest-framework/issues/5326 +[gh5313]: https://github.com/encode/django-rest-framework/issues/5313 +[gh5306]: https://github.com/encode/django-rest-framework/issues/5306 +[gh5276]: https://github.com/encode/django-rest-framework/issues/5276 +[gh5264]: https://github.com/encode/django-rest-framework/issues/5264 +[gh5261]: https://github.com/encode/django-rest-framework/issues/5261 +[gh5259]: https://github.com/encode/django-rest-framework/issues/5259 +[gh5231]: https://github.com/encode/django-rest-framework/issues/5231 +[gh5229]: https://github.com/encode/django-rest-framework/issues/5229 +[gh5214]: https://github.com/encode/django-rest-framework/issues/5214 +[gh5196]: https://github.com/encode/django-rest-framework/issues/5196 +[gh5192]: https://github.com/encode/django-rest-framework/issues/5192 +[gh5162]: https://github.com/encode/django-rest-framework/issues/5162 +[gh5188]: https://github.com/encode/django-rest-framework/issues/5188 +[gh5187]: https://github.com/encode/django-rest-framework/issues/5187 +[gh5186]: https://github.com/encode/django-rest-framework/issues/5186 +[gh5179]: https://github.com/encode/django-rest-framework/issues/5179 +[gh5176]: https://github.com/encode/django-rest-framework/issues/5176 +[gh5174]: https://github.com/encode/django-rest-framework/issues/5174 +[gh5161]: https://github.com/encode/django-rest-framework/issues/5161 +[gh5147]: https://github.com/encode/django-rest-framework/issues/5147 +[gh5131]: https://github.com/encode/django-rest-framework/issues/5131 + + + From 68d818fcc72b15851f1427296f7a4ed57cb4524e Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Mon, 21 Aug 2017 12:02:14 +0200 Subject: [PATCH 074/730] Update content from Transifex --- .../locale/ar/LC_MESSAGES/django.po | 23 +- .../locale/ca/LC_MESSAGES/django.po | 2 +- .../locale/cs/LC_MESSAGES/django.po | 2 +- .../locale/da/LC_MESSAGES/django.po | 28 +- .../locale/de/LC_MESSAGES/django.po | 28 +- .../locale/el/LC_MESSAGES/django.po | 2 +- .../locale/es/LC_MESSAGES/django.po | 11 +- .../locale/et/LC_MESSAGES/django.po | 2 +- .../locale/fi/LC_MESSAGES/django.po | 30 +- .../locale/fr/LC_MESSAGES/django.po | 10 +- .../locale/hu/LC_MESSAGES/django.po | 2 +- .../locale/it/LC_MESSAGES/django.po | 2 +- .../locale/ja/LC_MESSAGES/django.po | 13 +- .../locale/ko_KR/LC_MESSAGES/django.po | 35 +- .../locale/lv/LC_MESSAGES/django.po | 440 ++++++++++++++++++ .../locale/mk/LC_MESSAGES/django.po | 58 +-- .../locale/nb/LC_MESSAGES/django.po | 2 +- .../locale/nl/LC_MESSAGES/django.po | 15 +- .../locale/pl/LC_MESSAGES/django.po | 13 +- .../locale/pt_BR/LC_MESSAGES/django.po | 2 +- .../locale/ro/LC_MESSAGES/django.po | 2 +- .../locale/ru/LC_MESSAGES/django.po | 25 +- .../locale/sk/LC_MESSAGES/django.po | 2 +- .../locale/sl/LC_MESSAGES/django.po | 440 ++++++++++++++++++ .../locale/sv/LC_MESSAGES/django.po | 10 +- .../locale/tr/LC_MESSAGES/django.po | 4 +- .../locale/tr_TR/LC_MESSAGES/django.po | 4 +- .../locale/uk/LC_MESSAGES/django.po | 11 +- .../locale/zh_CN/LC_MESSAGES/django.po | 18 +- .../locale/zh_Hans/LC_MESSAGES/django.po | 29 +- 30 files changed, 1078 insertions(+), 187 deletions(-) create mode 100644 rest_framework/locale/lv/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/sl/LC_MESSAGES/django.po diff --git a/rest_framework/locale/ar/LC_MESSAGES/django.po b/rest_framework/locale/ar/LC_MESSAGES/django.po index 314356654..ea53a2905 100644 --- a/rest_framework/locale/ar/LC_MESSAGES/django.po +++ b/rest_framework/locale/ar/LC_MESSAGES/django.po @@ -3,15 +3,16 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# Bashar Al-Abdulhadi, 2016 +# aymen chaieb , 2017 +# Bashar Al-Abdulhadi, 2016-2017 # Eyad Toma , 2015 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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-15 17:08+0000\n" +"Last-Translator: aymen chaieb \n" "Language-Team: Arabic (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ar/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -54,7 +55,7 @@ msgstr "" #: authentication.py:195 msgid "Invalid token." -msgstr "رمز غير صحيح" +msgstr "رمز غير صحيح." #: authtoken/apps.py:7 msgid "Auth Token" @@ -316,15 +317,15 @@ msgstr "أرسل" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "تصاعدي" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "تنازلي" #: pagination.py:193 msgid "Invalid page." -msgstr "صفحة غير صحيحة" +msgstr "صفحة غير صحيحة." #: pagination.py:427 msgid "Invalid cursor" @@ -382,13 +383,13 @@ msgstr "الترتيب" #: templates/rest_framework/filters/search.html:2 msgid "Search" -msgstr "البحث" +msgstr "بحث" #: templates/rest_framework/horizontal/radio.html:2 #: templates/rest_framework/inline/radio.html:2 #: templates/rest_framework/vertical/radio.html:2 msgid "None" -msgstr "" +msgstr "لا شيء" #: templates/rest_framework/horizontal/select_multiple.html:2 #: templates/rest_framework/inline/select_multiple.html:2 @@ -398,7 +399,7 @@ msgstr "" #: validators.py:43 msgid "This field must be unique." -msgstr "" +msgstr "هذا الحقل يجب أن يكون وحيد" #: validators.py:97 msgid "The fields {field_names} must make a unique set." @@ -438,4 +439,4 @@ msgstr "" #: views.py:88 msgid "Permission denied." -msgstr "" +msgstr "حق غير مصرح به" diff --git a/rest_framework/locale/ca/LC_MESSAGES/django.po b/rest_framework/locale/ca/LC_MESSAGES/django.po index 56f46319f..f82de0068 100644 --- a/rest_framework/locale/ca/LC_MESSAGES/django.po +++ b/rest_framework/locale/ca/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ 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: 2016-07-12 15:14+0000\n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" "Last-Translator: Thomas Christie \n" "Language-Team: Catalan (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ca/)\n" "MIME-Version: 1.0\n" diff --git a/rest_framework/locale/cs/LC_MESSAGES/django.po b/rest_framework/locale/cs/LC_MESSAGES/django.po index 8ba979350..b6ee1ea48 100644 --- a/rest_framework/locale/cs/LC_MESSAGES/django.po +++ b/rest_framework/locale/cs/LC_MESSAGES/django.po @@ -10,7 +10,7 @@ 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: 2016-07-12 15:14+0000\n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" "Last-Translator: Thomas Christie \n" "Language-Team: Czech (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/cs/)\n" "MIME-Version: 1.0\n" diff --git a/rest_framework/locale/da/LC_MESSAGES/django.po b/rest_framework/locale/da/LC_MESSAGES/django.po index 2903376ad..900695649 100644 --- a/rest_framework/locale/da/LC_MESSAGES/django.po +++ b/rest_framework/locale/da/LC_MESSAGES/django.po @@ -3,15 +3,15 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# Mads Jensen , 2015-2016 +# Mads Jensen , 2015-2017 # Mikkel Munch Mortensen <3xm@detfalskested.dk>, 2015 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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: Mads Jensen \n" "Language-Team: Danish (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/da/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -62,31 +62,31 @@ msgstr "" #: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "Nøgle" #: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "Bruger" #: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "Oprettet" #: 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 "Brugernavn" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "Kodeord" #: authtoken/serializers.py:20 msgid "User account is disabled." @@ -316,15 +316,15 @@ msgstr "Indsend." #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "stigende" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "faldende" #: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "Ugyldig side" #: pagination.py:427 msgid "Invalid cursor" @@ -426,7 +426,7 @@ msgstr "Ugyldig version i URL-stien." #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "Ugyldig version in URLen. Den stemmer ikke overens med nogen versionsnumre." #: versioning.py:147 msgid "Invalid version in hostname." diff --git a/rest_framework/locale/de/LC_MESSAGES/django.po b/rest_framework/locale/de/LC_MESSAGES/django.po index 057a69f1c..725a0f757 100644 --- a/rest_framework/locale/de/LC_MESSAGES/django.po +++ b/rest_framework/locale/de/LC_MESSAGES/django.po @@ -4,6 +4,8 @@ # # Translators: # Fabian Büchler , 2015 +# datKater , 2017 +# Lukas Bischofberger , 2017 # Mads Jensen , 2015 # Niklas P , 2015-2016 # Thomas Tanner, 2015 @@ -14,8 +16,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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: Lukas Bischofberger \n" "Language-Team: German (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -74,7 +76,7 @@ msgstr "Benutzer" #: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "Erzeugt" #: authtoken/models.py:29 msgid "Token" @@ -98,7 +100,7 @@ msgstr "Benutzerkonto ist gesperrt." #: authtoken/serializers.py:23 msgid "Unable to log in with provided credentials." -msgstr "Kann nicht mit den angegeben Zugangsdaten anmelden." +msgstr "Die angegebenen Zugangsdaten stimmen nicht." #: authtoken/serializers.py:26 msgid "Must include \"username\" and \"password\"." @@ -122,7 +124,7 @@ msgstr "Anmeldedaten fehlen." #: exceptions.py:99 msgid "You do not have permission to perform this action." -msgstr "Sie sind nicht berechtigt, diese Aktion durchzuführen." +msgstr "Sie sind nicht berechtigt diese Aktion durchzuführen." #: exceptions.py:104 views.py:81 msgid "Not found." @@ -151,7 +153,7 @@ msgstr "Dieses Feld ist erforderlich." #: fields.py:270 msgid "This field may not be null." -msgstr "Dieses Feld darf nicht Null sein." +msgstr "Dieses Feld darf nicht null sein." #: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." @@ -193,7 +195,7 @@ msgstr "\"{value}\" ist keine gültige UUID." #: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." -msgstr "Geben Sie eine gültige UPv4 oder IPv6 Adresse an" +msgstr "Geben Sie eine gültige IPv4 oder IPv6 Adresse an" #: fields.py:821 msgid "A valid integer is required." @@ -272,7 +274,7 @@ msgstr "Diese Auswahl darf nicht leer sein" #: fields.py:1339 msgid "\"{input}\" is not a valid path choice." -msgstr "\"{input}\" ist ein ungültiger Pfad Wahl." +msgstr "\"{input}\" ist ein ungültiger Pfad." #: fields.py:1358 msgid "No file was submitted." @@ -308,7 +310,7 @@ msgstr "Diese Liste darf nicht leer sein." #: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." -msgstr "Erwarte ein Dictionary mit Elementen, erhielt aber den Typ \"{input_type}\"." +msgstr "Erwartete ein Dictionary mit Elementen, erhielt aber den Typ \"{input_type}\"." #: fields.py:1549 msgid "Value must be valid JSON." @@ -320,15 +322,15 @@ msgstr "Abschicken" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "Aufsteigend" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "Absteigend" #: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "Ungültige Seite." #: pagination.py:427 msgid "Invalid cursor" @@ -430,7 +432,7 @@ msgstr "Ungültige Version im URL Pfad." #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "Ungültige Version im URL-Pfad. Entspricht keinem Versions-Namensraum." #: versioning.py:147 msgid "Invalid version in hostname." diff --git a/rest_framework/locale/el/LC_MESSAGES/django.po b/rest_framework/locale/el/LC_MESSAGES/django.po index be9bdf717..18eb371c9 100644 --- a/rest_framework/locale/el/LC_MESSAGES/django.po +++ b/rest_framework/locale/el/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ 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: 2016-07-12 15:14+0000\n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" "Last-Translator: Thomas Christie \n" "Language-Team: Greek (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/el/)\n" "MIME-Version: 1.0\n" diff --git a/rest_framework/locale/es/LC_MESSAGES/django.po b/rest_framework/locale/es/LC_MESSAGES/django.po index b8a89aeb6..c9b6e9455 100644 --- a/rest_framework/locale/es/LC_MESSAGES/django.po +++ b/rest_framework/locale/es/LC_MESSAGES/django.po @@ -6,6 +6,7 @@ # Ernesto Rico-Schmidt , 2015 # José Padilla , 2015 # Miguel Gonzalez , 2015 +# Miguel Gonzalez , 2016 # Miguel Gonzalez , 2015-2016 # Sergio Infante , 2015 msgid "" @@ -13,8 +14,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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: Miguel Gonzalez \n" "Language-Team: Spanish (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -319,11 +320,11 @@ msgstr "Enviar" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "ascendiente" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "descendiente" #: pagination.py:193 msgid "Invalid page." @@ -429,7 +430,7 @@ msgstr "Versión inválida en la ruta de la URL." #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "La versión especificada en la ruta de la URL no es válida. No coincide con ninguna del espacio de nombres de versiones." #: versioning.py:147 msgid "Invalid version in hostname." diff --git a/rest_framework/locale/et/LC_MESSAGES/django.po b/rest_framework/locale/et/LC_MESSAGES/django.po index c9701cca7..cc2c2e3f0 100644 --- a/rest_framework/locale/et/LC_MESSAGES/django.po +++ b/rest_framework/locale/et/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ 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: 2016-07-12 15:14+0000\n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" "Last-Translator: Thomas Christie \n" "Language-Team: Estonian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/et/)\n" "MIME-Version: 1.0\n" diff --git a/rest_framework/locale/fi/LC_MESSAGES/django.po b/rest_framework/locale/fi/LC_MESSAGES/django.po index bf1dd8c10..0791a3005 100644 --- a/rest_framework/locale/fi/LC_MESSAGES/django.po +++ b/rest_framework/locale/fi/LC_MESSAGES/django.po @@ -4,14 +4,14 @@ # # Translators: # Aarni Koskela, 2015 -# Aarni Koskela, 2015 +# Aarni Koskela, 2015-2016 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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: Aarni Koskela\n" "Language-Team: Finnish (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/fi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -58,35 +58,35 @@ msgstr "Epäkelpo Token." #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "Autentikaatiotunniste" #: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "Avain" #: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "Käyttäjä" #: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "Luotu" #: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "Tunniste" #: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "Tunnisteet" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "Käyttäjänimi" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "Salasana" #: authtoken/serializers.py:20 msgid "User account is disabled." @@ -316,15 +316,15 @@ msgstr "Lähetä" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "nouseva" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "laskeva" #: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "Epäkelpo sivu." #: pagination.py:427 msgid "Invalid cursor" @@ -426,7 +426,7 @@ msgstr "Epäkelpo versio URL-polussa." #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "URL-polun versio ei täsmää mihinkään versionimiavaruuteen." #: versioning.py:147 msgid "Invalid version in hostname." diff --git a/rest_framework/locale/fr/LC_MESSAGES/django.po b/rest_framework/locale/fr/LC_MESSAGES/django.po index 284999a8b..25b39e453 100644 --- a/rest_framework/locale/fr/LC_MESSAGES/django.po +++ b/rest_framework/locale/fr/LC_MESSAGES/django.po @@ -12,8 +12,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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: Xavier Ordoquy \n" "Language-Team: French (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -318,11 +318,11 @@ msgstr "Envoyer" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "croissant" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "décroissant" #: pagination.py:193 msgid "Invalid page." @@ -428,7 +428,7 @@ msgstr "Version non valide dans l'URL." #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "Version invalide dans l'URL. Ne correspond à aucune version de l'espace de nommage." #: versioning.py:147 msgid "Invalid version in hostname." diff --git a/rest_framework/locale/hu/LC_MESSAGES/django.po b/rest_framework/locale/hu/LC_MESSAGES/django.po index 7f3081fff..9002f8e61 100644 --- a/rest_framework/locale/hu/LC_MESSAGES/django.po +++ b/rest_framework/locale/hu/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ 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: 2016-07-12 15:14+0000\n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" "Last-Translator: Thomas Christie \n" "Language-Team: Hungarian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/hu/)\n" "MIME-Version: 1.0\n" diff --git a/rest_framework/locale/it/LC_MESSAGES/django.po b/rest_framework/locale/it/LC_MESSAGES/django.po index 6a48c53a7..a48f8645d 100644 --- a/rest_framework/locale/it/LC_MESSAGES/django.po +++ b/rest_framework/locale/it/LC_MESSAGES/django.po @@ -12,7 +12,7 @@ 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: 2016-07-12 15:14+0000\n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" "Last-Translator: Thomas Christie \n" "Language-Team: Italian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/it/)\n" "MIME-Version: 1.0\n" diff --git a/rest_framework/locale/ja/LC_MESSAGES/django.po b/rest_framework/locale/ja/LC_MESSAGES/django.po index d2881dec9..a5e72d9a1 100644 --- a/rest_framework/locale/ja/LC_MESSAGES/django.po +++ b/rest_framework/locale/ja/LC_MESSAGES/django.po @@ -4,13 +4,14 @@ # # Translators: # Hiroaki Nakamura , 2016 +# Kouichi Nishizawa , 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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: Kouichi Nishizawa \n" "Language-Team: Japanese (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ja/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -315,15 +316,15 @@ msgstr "提出" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "昇順" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "降順" #: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "不正なページです。" #: pagination.py:427 msgid "Invalid cursor" @@ -425,7 +426,7 @@ msgstr "URLパス内のバージョンが不正です。" #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "不正なバージョンのURLのパスです。どのバージョンの名前空間にも一致しません。" #: versioning.py:147 msgid "Invalid version in hostname." diff --git a/rest_framework/locale/ko_KR/LC_MESSAGES/django.po b/rest_framework/locale/ko_KR/LC_MESSAGES/django.po index 4ca53b3c3..152bc7b00 100644 --- a/rest_framework/locale/ko_KR/LC_MESSAGES/django.po +++ b/rest_framework/locale/ko_KR/LC_MESSAGES/django.po @@ -3,14 +3,15 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Joon Hwan 김준환 , 2017 # SUN CHOI , 2015 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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: Joon Hwan 김준환 \n" "Language-Team: Korean (Korea) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ko_KR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -247,7 +248,7 @@ msgstr "Time의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 #: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." -msgstr "" +msgstr "Duration의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}." #: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." @@ -263,11 +264,11 @@ msgstr "아이템 리스트가 예상되었으나 \"{input_type}\"를 받았습 #: fields.py:1302 msgid "This selection may not be empty." -msgstr "" +msgstr "이 선택 항목은 비워 둘 수 없습니다." #: fields.py:1339 msgid "\"{input}\" is not a valid path choice." -msgstr "" +msgstr "\"{input}\"이 유효하지 않은 경로 선택입니다." #: fields.py:1358 msgid "No file was submitted." @@ -299,7 +300,7 @@ msgstr "유효한 이미지 파일을 업로드 하십시오. 업로드 하신 #: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." -msgstr "" +msgstr "이 리스트는 비워 둘 수 없습니다." #: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." @@ -307,7 +308,7 @@ msgstr "아이템 딕셔너리가 예상되었으나 \"{input_type}\" 타입을 #: fields.py:1549 msgid "Value must be valid JSON." -msgstr "" +msgstr "Value 는 유효한 JSON형식이어야 합니다." #: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" @@ -315,15 +316,15 @@ msgstr "" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "오름차순" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "내림차순" #: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "페이지가 유효하지 않습니다." #: pagination.py:427 msgid "Invalid cursor" @@ -381,7 +382,7 @@ msgstr "" #: templates/rest_framework/filters/search.html:2 msgid "Search" -msgstr "" +msgstr "검색" #: templates/rest_framework/horizontal/radio.html:2 #: templates/rest_framework/inline/radio.html:2 @@ -401,19 +402,19 @@ msgstr "이 칸은 반드시 고유해야 합니다." #: validators.py:97 msgid "The fields {field_names} must make a unique set." -msgstr "" +msgstr "{field_names} 필드는 반드시 고유하게 설정해야 합니다." #: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." -msgstr "" +msgstr "이 칸은 \"{date_field}\"날짜에 대해 고유해야합니다." #: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." -msgstr "" +msgstr "이 칸은 \"{date_field}\" 월에 대해 고유해야합니다." #: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." -msgstr "" +msgstr "이 칸은 \"{date_field}\" 년에 대해 고유해야합니다." #: versioning.py:42 msgid "Invalid version in \"Accept\" header." @@ -425,7 +426,7 @@ msgstr "URL path내 버전이 유효하지 않습니다." #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "URL 경로에 유효하지 않은 버전이 있습니다. 버전 네임 스페이스와 일치하지 않습니다." #: versioning.py:147 msgid "Invalid version in hostname." @@ -437,4 +438,4 @@ msgstr "쿼리 파라메터내 버전이 유효하지 않습니다." #: views.py:88 msgid "Permission denied." -msgstr "" +msgstr "사용 권한이 거부되었습니다." diff --git a/rest_framework/locale/lv/LC_MESSAGES/django.po b/rest_framework/locale/lv/LC_MESSAGES/django.po new file mode 100644 index 000000000..2bc978866 --- /dev/null +++ b/rest_framework/locale/lv/LC_MESSAGES/django.po @@ -0,0 +1,440 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# peterisb , 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-05 12:13+0000\n" +"Last-Translator: peterisb \n" +"Language-Team: Latvian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/lv/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: lv\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);\n" + +#: authentication.py:73 +msgid "Invalid basic header. No credentials provided." +msgstr "Nederīgs pieprasījuma sākums. Akreditācijas parametri nav nodrošināti." + +#: authentication.py:76 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "Nederīgs pieprasījuma sākums. Akreditācijas parametriem jābūt bez atstarpēm." + +#: authentication.py:82 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "Nederīgs pieprasījuma sākums. Akreditācijas parametri nav korekti base64 kodēti." + +#: authentication.py:99 +msgid "Invalid username/password." +msgstr "Nederīgs lietotājvārds/parole." + +#: authentication.py:102 authentication.py:198 +msgid "User inactive or deleted." +msgstr "Lietotājs neaktīvs vai dzēsts." + +#: authentication.py:176 +msgid "Invalid token header. No credentials provided." +msgstr "Nederīgs pilnvaras sākums. Akreditācijas parametri nav nodrošināti." + +#: authentication.py:179 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Nederīgs pilnvaras sākums. Pilnvaras parametros nevar būt tukšumi." + +#: authentication.py:185 +msgid "" +"Invalid token header. Token string should not contain invalid characters." +msgstr "Nederīgs pilnvaras sākums. Pilnvaras parametros nevar būt nederīgas zīmes." + +#: authentication.py:195 +msgid "Invalid token." +msgstr "Nederīga pilnavara." + +#: authtoken/apps.py:7 +msgid "Auth Token" +msgstr "Autorizācijas pilnvara" + +#: authtoken/models.py:15 +msgid "Key" +msgstr "Atslēga" + +#: authtoken/models.py:18 +msgid "User" +msgstr "Lietotājs" + +#: authtoken/models.py:20 +msgid "Created" +msgstr "Izveidots" + +#: authtoken/models.py:29 +msgid "Token" +msgstr "Pilnvara" + +#: authtoken/models.py:30 +msgid "Tokens" +msgstr "Pilnvaras" + +#: authtoken/serializers.py:8 +msgid "Username" +msgstr "Lietotājvārds" + +#: authtoken/serializers.py:9 +msgid "Password" +msgstr "Parole" + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Lietotāja konts ir atslēgts." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Neiespējami pieteikties sistēmā ar nodrošinātajiem akreditācijas datiem." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Jābūt iekļautam \"username\" un \"password\"." + +#: exceptions.py:49 +msgid "A server error occurred." +msgstr "Notikusi servera kļūda." + +#: exceptions.py:84 +msgid "Malformed request." +msgstr "Nenoformēts pieprasījums." + +#: exceptions.py:89 +msgid "Incorrect authentication credentials." +msgstr "Nekorekti autentifikācijas parametri." + +#: exceptions.py:94 +msgid "Authentication credentials were not provided." +msgstr "Netika nodrošināti autorizācijas parametri." + +#: exceptions.py:99 +msgid "You do not have permission to perform this action." +msgstr "Tev nav tiesību veikt šo darbību." + +#: exceptions.py:104 views.py:81 +msgid "Not found." +msgstr "Nav atrasts." + +#: exceptions.py:109 +msgid "Method \"{method}\" not allowed." +msgstr "Metode \"{method}\" nav atļauta." + +#: exceptions.py:120 +msgid "Could not satisfy the request Accept header." +msgstr "Nevarēja apmierināt pieprasījuma Accept header." + +#: exceptions.py:132 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Pieprasījumā neatbalstīts datu tips \"{media_type}\" ." + +#: exceptions.py:145 +msgid "Request was throttled." +msgstr "Pieprasījums tika apturēts." + +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 +msgid "This field is required." +msgstr "Šis lauks ir obligāts." + +#: fields.py:270 +msgid "This field may not be null." +msgstr "Šis lauks nevar būt null." + +#: fields.py:608 fields.py:639 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" ir nederīga loģiskā vērtība." + +#: fields.py:674 +msgid "This field may not be blank." +msgstr "Šis lauks nevar būt tukšs." + +#: fields.py:675 fields.py:1675 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Pārliecinies, ka laukā nav vairāk par {max_length} zīmēm." + +#: fields.py:676 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Pārliecinies, ka laukā ir vismaz {min_length} zīmes." + +#: fields.py:713 +msgid "Enter a valid email address." +msgstr "Ievadi derīgu e-pasta adresi." + +#: fields.py:724 +msgid "This value does not match the required pattern." +msgstr "Šī vērtība neatbilst prasītajam pierakstam." + +#: fields.py:735 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Ievadi derīgu \"slug\" vērtību, kura sastāv no burtiem, skaitļiem, apakš-svītras vai defises." + +#: fields.py:747 +msgid "Enter a valid URL." +msgstr "Ievadi derīgu URL." + +#: fields.py:760 +msgid "\"{value}\" is not a valid UUID." +msgstr "\"{value}\" ir nedrīgs UUID." + +#: fields.py:796 +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Ievadi derīgu IPv4 vai IPv6 adresi." + +#: fields.py:821 +msgid "A valid integer is required." +msgstr "Prasīta ir derīga skaitliska vērtība." + +#: fields.py:822 fields.py:857 fields.py:891 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Pārliecinies, ka šī vērtība ir mazāka vai vienāda ar {max_value}." + +#: fields.py:823 fields.py:858 fields.py:892 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Pārliecinies, ka šī vērtība ir lielāka vai vienāda ar {min_value}." + +#: fields.py:824 fields.py:859 fields.py:896 +msgid "String value too large." +msgstr "Teksta vērtība pārāk liela." + +#: fields.py:856 fields.py:890 +msgid "A valid number is required." +msgstr "Derīgs skaitlis ir prasīts." + +#: fields.py:893 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Pārliecinies, ka nav vairāk par {max_digits} zīmēm kopā." + +#: fields.py:894 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Pārliecinies, ka nav vairāk par {max_decimal_places} decimālajām zīmēm." + +#: fields.py:895 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Pārliecinies, ka nav vairāk par {max_whole_digits} zīmēm pirms komata." + +#: fields.py:1025 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "Datuma un laika formāts ir nepareizs. Lieto vienu no norādītajiem formātiem: \"{format}.\"" + +#: fields.py:1026 +msgid "Expected a datetime but got a date." +msgstr "Tika gaidīts datums un laiks, saņemts datums.." + +#: fields.py:1103 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "Datumam ir nepareizs formāts. Lieto vienu no norādītajiem formātiem: {format}." + +#: fields.py:1104 +msgid "Expected a date but got a datetime." +msgstr "Tika gaidīts datums, saņemts datums un laiks." + +#: fields.py:1170 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Laikam ir nepareizs formāts. Lieto vienu no norādītajiem formātiem: {format}." + +#: fields.py:1232 +msgid "Duration has wrong format. Use one of these formats instead: {format}." +msgstr "Ilgumam ir nepreizs formāts. Lieto vienu no norādītajiem formātiem: {format}." + +#: fields.py:1251 fields.py:1300 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" ir nederīga izvēle." + +#: fields.py:1254 relations.py:71 relations.py:441 +msgid "More than {count} items..." +msgstr "Vairāk par {count} ierakstiem..." + +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Tika gaidīts saraksts ar ierakstiem, bet tika saņemts \"{input_type}\" tips." + +#: fields.py:1302 +msgid "This selection may not be empty." +msgstr "Šī daļa nevar būt tukša." + +#: fields.py:1339 +msgid "\"{input}\" is not a valid path choice." +msgstr "\"{input}\" ir nederīga ceļa izvēle." + +#: fields.py:1358 +msgid "No file was submitted." +msgstr "Neviens fails netika pievienots." + +#: fields.py:1359 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "Pievienotie dati nebija fails. Pārbaudi kodējuma tipu formā." + +#: fields.py:1360 +msgid "No filename could be determined." +msgstr "Faila nosaukums nevar tikt noteikts." + +#: fields.py:1361 +msgid "The submitted file is empty." +msgstr "Pievienotais fails ir tukšs." + +#: fields.py:1362 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "Pārliecinies, ka faila nosaukumā ir vismaz {max_length} zīmes (tajā ir {length})." + +#: fields.py:1410 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Augšupielādē derīgu attēlu. Pievienotā datne nebija attēls vai bojāts attēls." + +#: fields.py:1449 relations.py:438 serializers.py:525 +msgid "This list may not be empty." +msgstr "Šis saraksts nevar būt tukšs." + +#: fields.py:1502 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "Tika gaidīta vārdnīca ar ierakstiem, bet tika saņemts \"{input_type}\" tips." + +#: fields.py:1549 +msgid "Value must be valid JSON." +msgstr "Vērtībai ir jābūt derīgam JSON." + +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 +msgid "Submit" +msgstr "Iesniegt" + +#: filters.py:336 +msgid "ascending" +msgstr "augoši" + +#: filters.py:337 +msgid "descending" +msgstr "dilstoši" + +#: pagination.py:193 +msgid "Invalid page." +msgstr "Nederīga lapa." + +#: pagination.py:427 +msgid "Invalid cursor" +msgstr "Nederīgs kursors" + +#: relations.py:207 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "Nederīga pk \"{pk_value}\" - objekts neeksistē." + +#: relations.py:208 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "Nepareizs tips. Tika gaidīta pk vērtība, saņemts {data_type}." + +#: relations.py:240 +msgid "Invalid hyperlink - No URL match." +msgstr "Nederīga hipersaite - Nav URL sakritība." + +#: relations.py:241 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "Nederīga hipersaite - Nederīga URL sakritība." + +#: relations.py:242 +msgid "Invalid hyperlink - Object does not exist." +msgstr "Nederīga hipersaite - Objekts neeksistē." + +#: relations.py:243 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "Nepareizs tips. Tika gaidīts URL teksts, saņemts {data_type}." + +#: relations.py:401 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Objekts ar {slug_name}={value} neeksistē." + +#: relations.py:402 +msgid "Invalid value." +msgstr "Nedrīga vērtība." + +#: serializers.py:326 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "Nederīgi dati. Tika gaidīta vārdnīca, saņemts {datatype}." + +#: templates/rest_framework/admin.html:116 +#: templates/rest_framework/base.html:128 +msgid "Filters" +msgstr "Filtri" + +#: templates/rest_framework/filters/django_filter.html:2 +#: templates/rest_framework/filters/django_filter_crispyforms.html:4 +msgid "Field filters" +msgstr "Lauka filtri" + +#: templates/rest_framework/filters/ordering.html:3 +msgid "Ordering" +msgstr "Kārtošana" + +#: templates/rest_framework/filters/search.html:2 +msgid "Search" +msgstr "Meklēt" + +#: templates/rest_framework/horizontal/radio.html:2 +#: templates/rest_framework/inline/radio.html:2 +#: templates/rest_framework/vertical/radio.html:2 +msgid "None" +msgstr "Nekas" + +#: templates/rest_framework/horizontal/select_multiple.html:2 +#: templates/rest_framework/inline/select_multiple.html:2 +#: templates/rest_framework/vertical/select_multiple.html:2 +msgid "No items to select." +msgstr "Nav ierakstu, ko izvēlēties." + +#: validators.py:43 +msgid "This field must be unique." +msgstr "Šim laukam ir jābūt unikālam." + +#: validators.py:97 +msgid "The fields {field_names} must make a unique set." +msgstr "Laukiem {field_names} jāveido unikālas kombinācijas." + +#: validators.py:245 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "Šim laukam ir jābūt unikālam priekš \"{date_field}\" datuma." + +#: validators.py:260 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "Šim laukam ir jābūt unikālam priekš \"{date_field}\" mēneša." + +#: validators.py:273 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "Šim laukam ir jābūt unikālam priekš \"{date_field}\" gada." + +#: versioning.py:42 +msgid "Invalid version in \"Accept\" header." +msgstr "Nederīga versija \"Accept\" galvenē." + +#: versioning.py:73 +msgid "Invalid version in URL path." +msgstr "Nederīga versija URL ceļā." + +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "Nederīga versija URL ceļā. Nav atbilstības esošo versiju telpā." + +#: versioning.py:147 +msgid "Invalid version in hostname." +msgstr "Nederīga versija servera nosaukumā." + +#: versioning.py:169 +msgid "Invalid version in query parameter." +msgstr "Nederīga versija pieprasījuma parametros." + +#: views.py:88 +msgid "Permission denied." +msgstr "Pieeja liegta." diff --git a/rest_framework/locale/mk/LC_MESSAGES/django.po b/rest_framework/locale/mk/LC_MESSAGES/django.po index d53a30677..0e59663d0 100644 --- a/rest_framework/locale/mk/LC_MESSAGES/django.po +++ b/rest_framework/locale/mk/LC_MESSAGES/django.po @@ -3,14 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# Filip Dimitrovski , 2015 +# Filip Dimitrovski , 2015-2016 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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: Filip Dimitrovski \n" "Language-Team: Macedonian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/mk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -57,7 +57,7 @@ msgstr "Невалиден токен." #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "Автентикациски токен" #: authtoken/models.py:15 msgid "Key" @@ -65,7 +65,7 @@ msgstr "" #: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "Корисник" #: authtoken/models.py:20 msgid "Created" @@ -73,19 +73,19 @@ msgstr "" #: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "Токен" #: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "Токени" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "Корисничко име" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "Лозинка" #: authtoken/serializers.py:20 msgid "User account is disabled." @@ -184,11 +184,11 @@ msgstr "Внесете валиден URL." #: fields.py:760 msgid "\"{value}\" is not a valid UUID." -msgstr "" +msgstr "\"{value}\" не е валиден UUID." #: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." -msgstr "" +msgstr "Внеси валидна IPv4 или IPv6 адреса." #: fields.py:821 msgid "A valid integer is required." @@ -255,11 +255,11 @@ msgstr "„{input}“ не е валиден избор." #: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." -msgstr "" +msgstr "Повеќе од {count} ставки..." #: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." -msgstr "Очекувана беше листа, а внесено беше „{input_type}“." +msgstr "Очекувана беше листа од ставки, а внесено беше „{input_type}“." #: fields.py:1302 msgid "This selection may not be empty." @@ -299,35 +299,35 @@ msgstr "Качете (upload-ирајте) валидна слика. Фајло #: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." -msgstr "" +msgstr "Оваа листа не смее да биде празна." #: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." -msgstr "" +msgstr "Очекуван беше dictionary од ставки, a внесен беше тип \"{input_type}\"." #: fields.py:1549 msgid "Value must be valid JSON." -msgstr "" +msgstr "Вредноста мора да биде валиден JSON." #: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" -msgstr "" +msgstr "Испрати" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "растечки" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "опаѓачки" #: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "Невалидна вредност за страна." #: pagination.py:427 msgid "Invalid cursor" -msgstr "" +msgstr "Невалиден покажувач (cursor)" #: relations.py:207 msgid "Invalid pk \"{pk_value}\" - object does not exist." @@ -368,32 +368,32 @@ msgstr "Невалидни податоци. Очекуван беше dictionar #: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" -msgstr "" +msgstr "Филтри" #: templates/rest_framework/filters/django_filter.html:2 #: templates/rest_framework/filters/django_filter_crispyforms.html:4 msgid "Field filters" -msgstr "" +msgstr "Филтри на полиња" #: templates/rest_framework/filters/ordering.html:3 msgid "Ordering" -msgstr "" +msgstr "Подредување" #: templates/rest_framework/filters/search.html:2 msgid "Search" -msgstr "" +msgstr "Пребарај" #: templates/rest_framework/horizontal/radio.html:2 #: templates/rest_framework/inline/radio.html:2 #: templates/rest_framework/vertical/radio.html:2 msgid "None" -msgstr "" +msgstr "Ништо" #: templates/rest_framework/horizontal/select_multiple.html:2 #: templates/rest_framework/inline/select_multiple.html:2 #: templates/rest_framework/vertical/select_multiple.html:2 msgid "No items to select." -msgstr "" +msgstr "Нема ставки за избирање." #: validators.py:43 msgid "This field must be unique." @@ -425,7 +425,7 @@ msgstr "Невалидна верзија во URL патеката." #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "Верзијата во URL патеката не е валидна. Не се согласува со ниеден version namespace (именски простор за верзии)." #: versioning.py:147 msgid "Invalid version in hostname." @@ -437,4 +437,4 @@ msgstr "Невалидна верзија во query параметарот." #: views.py:88 msgid "Permission denied." -msgstr "" +msgstr "Барањето не е дозволено." diff --git a/rest_framework/locale/nb/LC_MESSAGES/django.po b/rest_framework/locale/nb/LC_MESSAGES/django.po index 634a24642..f9ecada63 100644 --- a/rest_framework/locale/nb/LC_MESSAGES/django.po +++ b/rest_framework/locale/nb/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ 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: 2016-07-12 15:14+0000\n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" "Last-Translator: Thomas Christie \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" diff --git a/rest_framework/locale/nl/LC_MESSAGES/django.po b/rest_framework/locale/nl/LC_MESSAGES/django.po index 6b9dd127b..370f3aa41 100644 --- a/rest_framework/locale/nl/LC_MESSAGES/django.po +++ b/rest_framework/locale/nl/LC_MESSAGES/django.po @@ -4,16 +4,17 @@ # # Translators: # Hans van Luttikhuizen , 2016 -# mikedingjan , 2015 -# mikedingjan , 2015 +# Mike Dingjan , 2015 +# Mike Dingjan , 2017 +# Mike Dingjan , 2015 # Hans van Luttikhuizen , 2016 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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: Mike Dingjan \n" "Language-Team: Dutch (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/nl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -318,11 +319,11 @@ msgstr "Verzenden" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "oplopend" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "aflopend" #: pagination.py:193 msgid "Invalid page." @@ -428,7 +429,7 @@ msgstr "Ongeldige versie in URL-pad." #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "Ongeldige versie in het URL pad, komt niet overeen met een geldige versie namespace" #: versioning.py:147 msgid "Invalid version in hostname." diff --git a/rest_framework/locale/pl/LC_MESSAGES/django.po b/rest_framework/locale/pl/LC_MESSAGES/django.po index b8592e9b7..611426556 100644 --- a/rest_framework/locale/pl/LC_MESSAGES/django.po +++ b/rest_framework/locale/pl/LC_MESSAGES/django.po @@ -5,20 +5,21 @@ # Translators: # Janusz Harkot , 2015 # Piotr Jakimiak , 2015 +# m_aciek , 2016 # m_aciek , 2015-2016 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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: m_aciek \n" "Language-Team: Polish (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/pl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: pl\n" -"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" #: authentication.py:73 msgid "Invalid basic header. No credentials provided." @@ -317,11 +318,11 @@ msgstr "Wyślij" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "rosnąco" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "malejąco" #: pagination.py:193 msgid "Invalid page." @@ -427,7 +428,7 @@ msgstr "Błędna wersja w ścieżce URL." #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "Niepoprawna wersja w ścieżce URL. Nie pasuje do przestrzeni nazw żadnej wersji." #: versioning.py:147 msgid "Invalid version in hostname." diff --git a/rest_framework/locale/pt_BR/LC_MESSAGES/django.po b/rest_framework/locale/pt_BR/LC_MESSAGES/django.po index 2c90f14ca..3a57b6770 100644 --- a/rest_framework/locale/pt_BR/LC_MESSAGES/django.po +++ b/rest_framework/locale/pt_BR/LC_MESSAGES/django.po @@ -12,7 +12,7 @@ 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: 2016-07-12 15:14+0000\n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" "Last-Translator: Thomas Christie \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" diff --git a/rest_framework/locale/ro/LC_MESSAGES/django.po b/rest_framework/locale/ro/LC_MESSAGES/django.po index bb3b5e3c0..d144d847e 100644 --- a/rest_framework/locale/ro/LC_MESSAGES/django.po +++ b/rest_framework/locale/ro/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ 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: 2016-07-12 15:14+0000\n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" "Last-Translator: Thomas Christie \n" "Language-Team: Romanian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ro/)\n" "MIME-Version: 1.0\n" diff --git a/rest_framework/locale/ru/LC_MESSAGES/django.po b/rest_framework/locale/ru/LC_MESSAGES/django.po index b73270906..7e09b227e 100644 --- a/rest_framework/locale/ru/LC_MESSAGES/django.po +++ b/rest_framework/locale/ru/LC_MESSAGES/django.po @@ -3,6 +3,7 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Grigory Mishchenko , 2017 # Kirill Tarasenko, 2015 # koodjo , 2015 # Mike TUMS , 2015 @@ -12,8 +13,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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: Grigory Mishchenko \n" "Language-Team: Russian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -161,11 +162,11 @@ msgstr "Это поле не может быть пустым." #: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." -msgstr "Убедитесь что в этом поле не больше {max_length} символов." +msgstr "Убедитесь, что в этом поле не больше {max_length} символов." #: fields.py:676 msgid "Ensure this field has at least {min_length} characters." -msgstr "Убедитесь что в этом поле как минимум {min_length} символов." +msgstr "Убедитесь, что в этом поле как минимум {min_length} символов." #: fields.py:713 msgid "Enter a valid email address." @@ -199,11 +200,11 @@ msgstr "Требуется целочисленное значение." #: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." -msgstr "Убедитесь что значение меньше или равно {max_value}." +msgstr "Убедитесь, что значение меньше или равно {max_value}." #: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." -msgstr "Убедитесь что значение больше или равно {min_value}." +msgstr "Убедитесь, что значение больше или равно {min_value}." #: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." @@ -215,18 +216,18 @@ msgstr "Требуется численное значение." #: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." -msgstr "Убедитесь что в числе не больше {max_digits} знаков." +msgstr "Убедитесь, что в числе не больше {max_digits} знаков." #: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." -msgstr "Убедитесь что в числе не больше {max_decimal_places} знаков в дробной части." +msgstr "Убедитесь, что в числе не больше {max_decimal_places} знаков в дробной части." #: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." -msgstr "Убедитесь что в цисле не больше {max_whole_digits} знаков в целой части." +msgstr "Убедитесь, что в числе не больше {max_whole_digits} знаков в целой части." #: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." @@ -279,7 +280,7 @@ msgstr "Не был загружен файл." #: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." -msgstr "Загруженный файл не является корректным файлом. " +msgstr "Загруженный файл не является корректным файлом." #: fields.py:1360 msgid "No filename could be determined." @@ -292,7 +293,7 @@ msgstr "Загруженный файл пуст." #: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." -msgstr "Убедитесь что имя файла меньше {max_length} символов (сейчас {length})." +msgstr "Убедитесь, что имя файла меньше {max_length} символов (сейчас {length})." #: fields.py:1410 msgid "" @@ -338,7 +339,7 @@ msgstr "Недопустимый первичный ключ \"{pk_value}\" - о #: relations.py:208 msgid "Incorrect type. Expected pk value, received {data_type}." -msgstr "Некорректный тип. Ожилалось значение первичного ключа, получен {data_type}." +msgstr "Некорректный тип. Ожидалось значение первичного ключа, получен {data_type}." #: relations.py:240 msgid "Invalid hyperlink - No URL match." diff --git a/rest_framework/locale/sk/LC_MESSAGES/django.po b/rest_framework/locale/sk/LC_MESSAGES/django.po index 1c22d09f0..119430e90 100644 --- a/rest_framework/locale/sk/LC_MESSAGES/django.po +++ b/rest_framework/locale/sk/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ 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: 2016-07-12 15:14+0000\n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" "Last-Translator: Thomas Christie \n" "Language-Team: Slovak (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/sk/)\n" "MIME-Version: 1.0\n" diff --git a/rest_framework/locale/sl/LC_MESSAGES/django.po b/rest_framework/locale/sl/LC_MESSAGES/django.po new file mode 100644 index 000000000..9af0fc8fc --- /dev/null +++ b/rest_framework/locale/sl/LC_MESSAGES/django.po @@ -0,0 +1,440 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Gregor Cimerman, 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: Gregor Cimerman\n" +"Language-Team: Slovenian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/sl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: sl\n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n" + +#: authentication.py:73 +msgid "Invalid basic header. No credentials provided." +msgstr "Napačno enostavno zagalvje. Ni podanih poverilnic." + +#: authentication.py:76 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "Napačno enostavno zaglavje. Poverilniški niz ne sme vsebovati presledkov." + +#: authentication.py:82 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "Napačno enostavno zaglavje. Poverilnice niso pravilno base64 kodirane." + +#: authentication.py:99 +msgid "Invalid username/password." +msgstr "Napačno uporabniško ime ali geslo." + +#: authentication.py:102 authentication.py:198 +msgid "User inactive or deleted." +msgstr "Uporabnik neaktiven ali izbrisan." + +#: authentication.py:176 +msgid "Invalid token header. No credentials provided." +msgstr "Neveljaven žeton v zaglavju. Ni vsebovanih poverilnic." + +#: authentication.py:179 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Neveljaven žeton v zaglavju. Žeton ne sme vsebovati presledkov." + +#: authentication.py:185 +msgid "" +"Invalid token header. Token string should not contain invalid characters." +msgstr "Neveljaven žeton v zaglavju. Žeton ne sme vsebovati napačnih znakov." + +#: authentication.py:195 +msgid "Invalid token." +msgstr "Neveljaven žeton." + +#: authtoken/apps.py:7 +msgid "Auth Token" +msgstr "Prijavni žeton" + +#: authtoken/models.py:15 +msgid "Key" +msgstr "Ključ" + +#: authtoken/models.py:18 +msgid "User" +msgstr "Uporabnik" + +#: authtoken/models.py:20 +msgid "Created" +msgstr "Ustvarjen" + +#: authtoken/models.py:29 +msgid "Token" +msgstr "Žeton" + +#: authtoken/models.py:30 +msgid "Tokens" +msgstr "Žetoni" + +#: authtoken/serializers.py:8 +msgid "Username" +msgstr "Uporabniško ime" + +#: authtoken/serializers.py:9 +msgid "Password" +msgstr "Geslo" + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Uporabniški račun je onemogočen." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Neuspešna prijava s podanimi poverilnicami." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Mora vsebovati \"uporabniško ime\" in \"geslo\"." + +#: exceptions.py:49 +msgid "A server error occurred." +msgstr "Napaka na strežniku." + +#: exceptions.py:84 +msgid "Malformed request." +msgstr "Okvarjen zahtevek." + +#: exceptions.py:89 +msgid "Incorrect authentication credentials." +msgstr "Napačni avtentikacijski podatki." + +#: exceptions.py:94 +msgid "Authentication credentials were not provided." +msgstr "Avtentikacijski podatki niso bili podani." + +#: exceptions.py:99 +msgid "You do not have permission to perform this action." +msgstr "Nimate dovoljenj za izvedbo te akcije." + +#: exceptions.py:104 views.py:81 +msgid "Not found." +msgstr "Ni najdeno" + +#: exceptions.py:109 +msgid "Method \"{method}\" not allowed." +msgstr "Metoda \"{method}\" ni dovoljena" + +#: exceptions.py:120 +msgid "Could not satisfy the request Accept header." +msgstr "Ni bilo mogoče zagotoviti zaglavja Accept zahtevka." + +#: exceptions.py:132 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Nepodprt medijski tip \"{media_type}\" v zahtevku." + +#: exceptions.py:145 +msgid "Request was throttled." +msgstr "Zahtevek je bil pridržan." + +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 +msgid "This field is required." +msgstr "To polje je obvezno." + +#: fields.py:270 +msgid "This field may not be null." +msgstr "To polje ne sme biti null." + +#: fields.py:608 fields.py:639 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" ni veljaven boolean." + +#: fields.py:674 +msgid "This field may not be blank." +msgstr "To polje ne sme biti prazno." + +#: fields.py:675 fields.py:1675 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "To polje ne sme biti daljše od {max_length} znakov." + +#: fields.py:676 +msgid "Ensure this field has at least {min_length} characters." +msgstr "To polje mora vsebovati vsaj {min_length} znakov." + +#: fields.py:713 +msgid "Enter a valid email address." +msgstr "Vnesite veljaven elektronski naslov." + +#: fields.py:724 +msgid "This value does not match the required pattern." +msgstr "Ta vrednost ne ustreza zahtevanemu vzorcu." + +#: fields.py:735 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Vnesite veljaven \"slug\", ki vsebuje črke, številke, podčrtaje ali vezaje." + +#: fields.py:747 +msgid "Enter a valid URL." +msgstr "Vnesite veljaven URL." + +#: fields.py:760 +msgid "\"{value}\" is not a valid UUID." +msgstr "\"{value}\" ni veljaven UUID" + +#: fields.py:796 +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Vnesite veljaven IPv4 ali IPv6 naslov." + +#: fields.py:821 +msgid "A valid integer is required." +msgstr "Zahtevano je veljavno celo število." + +#: fields.py:822 fields.py:857 fields.py:891 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Vrednost mora biti manjša ali enaka {max_value}." + +#: fields.py:823 fields.py:858 fields.py:892 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Vrednost mora biti večija ali enaka {min_value}." + +#: fields.py:824 fields.py:859 fields.py:896 +msgid "String value too large." +msgstr "Niz je prevelik." + +#: fields.py:856 fields.py:890 +msgid "A valid number is required." +msgstr "Zahtevano je veljavno število." + +#: fields.py:893 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Vnesete lahko največ {max_digits} števk." + +#: fields.py:894 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Vnesete lahko največ {max_decimal_places} decimalnih mest." + +#: fields.py:895 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Vnesete lahko največ {max_whole_digits} števk pred decimalno piko." + +#: fields.py:1025 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "Datim in čas v napačnem formatu. Uporabite eno izmed naslednjih formatov: {format}." + +#: fields.py:1026 +msgid "Expected a datetime but got a date." +msgstr "Pričakovan datum in čas, prejet le datum." + +#: fields.py:1103 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "Datum je v napačnem formatu. Uporabnite enega izmed naslednjih: {format}." + +#: fields.py:1104 +msgid "Expected a date but got a datetime." +msgstr "Pričakovan datum vendar prejet datum in čas." + +#: fields.py:1170 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Čas je v napačnem formatu. Uporabite enega izmed naslednjih: {format}." + +#: fields.py:1232 +msgid "Duration has wrong format. Use one of these formats instead: {format}." +msgstr "Trajanje je v napačnem formatu. Uporabite enega izmed naslednjih: {format}." + +#: fields.py:1251 fields.py:1300 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" ni veljavna izbira." + +#: fields.py:1254 relations.py:71 relations.py:441 +msgid "More than {count} items..." +msgstr "Več kot {count} elementov..." + +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Pričakovan seznam elementov vendar prejet tip \"{input_type}\"." + +#: fields.py:1302 +msgid "This selection may not be empty." +msgstr "Ta izbria ne sme ostati prazna." + +#: fields.py:1339 +msgid "\"{input}\" is not a valid path choice." +msgstr "\"{input}\" ni veljavna izbira poti." + +#: fields.py:1358 +msgid "No file was submitted." +msgstr "Datoteka ni bila oddana." + +#: fields.py:1359 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "Oddani podatki niso datoteka. Preverite vrsto kodiranja na formi." + +#: fields.py:1360 +msgid "No filename could be determined." +msgstr "Imena datoteke ni bilo mogoče določiti." + +#: fields.py:1361 +msgid "The submitted file is empty." +msgstr "Oddana datoteka je prazna." + +#: fields.py:1362 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "Ime datoteke lahko vsebuje največ {max_length} znakov (ta jih ima {length})." + +#: fields.py:1410 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Naložite veljavno sliko. Naložena datoteka ni bila slika ali pa je okvarjena." + +#: fields.py:1449 relations.py:438 serializers.py:525 +msgid "This list may not be empty." +msgstr "Seznam ne sme biti prazen." + +#: fields.py:1502 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "Pričakovan je slovar elementov, prejet element je tipa \"{input_type}\"." + +#: fields.py:1549 +msgid "Value must be valid JSON." +msgstr "Vrednost mora biti veljaven JSON." + +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 +msgid "Submit" +msgstr "Potrdi" + +#: filters.py:336 +msgid "ascending" +msgstr "naraščujoče" + +#: filters.py:337 +msgid "descending" +msgstr "padajoče" + +#: pagination.py:193 +msgid "Invalid page." +msgstr "Neveljavna stran." + +#: pagination.py:427 +msgid "Invalid cursor" +msgstr "Neveljaven kazalec" + +#: relations.py:207 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "Neveljaven pk \"{pk_value}\" - objekt ne obstaja." + +#: relations.py:208 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "Neveljaven tip. Pričakovana vrednost pk, prejet {data_type}." + +#: relations.py:240 +msgid "Invalid hyperlink - No URL match." +msgstr "Neveljavna povezava - Ni URL." + +#: relations.py:241 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "Ni veljavna povezava - Napačen URL." + +#: relations.py:242 +msgid "Invalid hyperlink - Object does not exist." +msgstr "Ni veljavna povezava - Objekt ne obstaja." + +#: relations.py:243 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "Napačen tip. Pričakovan URL niz, prejet {data_type}." + +#: relations.py:401 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Objekt z {slug_name}={value} ne obstaja." + +#: relations.py:402 +msgid "Invalid value." +msgstr "Neveljavna vrednost." + +#: serializers.py:326 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "Napačni podatki. Pričakovan slovar, prejet {datatype}." + +#: templates/rest_framework/admin.html:116 +#: templates/rest_framework/base.html:128 +msgid "Filters" +msgstr "Filtri" + +#: templates/rest_framework/filters/django_filter.html:2 +#: templates/rest_framework/filters/django_filter_crispyforms.html:4 +msgid "Field filters" +msgstr "Filter polj" + +#: templates/rest_framework/filters/ordering.html:3 +msgid "Ordering" +msgstr "Razvrščanje" + +#: templates/rest_framework/filters/search.html:2 +msgid "Search" +msgstr "Iskanje" + +#: templates/rest_framework/horizontal/radio.html:2 +#: templates/rest_framework/inline/radio.html:2 +#: templates/rest_framework/vertical/radio.html:2 +msgid "None" +msgstr "None" + +#: templates/rest_framework/horizontal/select_multiple.html:2 +#: templates/rest_framework/inline/select_multiple.html:2 +#: templates/rest_framework/vertical/select_multiple.html:2 +msgid "No items to select." +msgstr "Ni elementov za izbiro." + +#: validators.py:43 +msgid "This field must be unique." +msgstr "To polje mora biti unikatno." + +#: validators.py:97 +msgid "The fields {field_names} must make a unique set." +msgstr "Polja {field_names} morajo skupaj sestavljati unikaten niz." + +#: validators.py:245 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "Polje mora biti unikatno za \"{date_field}\" dan." + +#: validators.py:260 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "Polje mora biti unikatno za \"{date_field} mesec.\"" + +#: validators.py:273 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "Polje mora biti unikatno za \"{date_field}\" leto." + +#: versioning.py:42 +msgid "Invalid version in \"Accept\" header." +msgstr "Neveljavna verzija v \"Accept\" zaglavju." + +#: versioning.py:73 +msgid "Invalid version in URL path." +msgstr "Neveljavna različca v poti URL." + +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "Neveljavna različica v poti URL. Se ne ujema z nobeno različico imenskega prostora." + +#: versioning.py:147 +msgid "Invalid version in hostname." +msgstr "Neveljavna različica v imenu gostitelja." + +#: versioning.py:169 +msgid "Invalid version in query parameter." +msgstr "Neveljavna verzija v poizvedbenem parametru." + +#: views.py:88 +msgid "Permission denied." +msgstr "Dovoljenje zavrnjeno." diff --git a/rest_framework/locale/sv/LC_MESSAGES/django.po b/rest_framework/locale/sv/LC_MESSAGES/django.po index 82dde0d87..00acf5644 100644 --- a/rest_framework/locale/sv/LC_MESSAGES/django.po +++ b/rest_framework/locale/sv/LC_MESSAGES/django.po @@ -10,8 +10,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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: Joakim Soderlund\n" "Language-Team: Swedish (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/sv/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -316,11 +316,11 @@ msgstr "Skicka" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "stigande" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "fallande" #: pagination.py:193 msgid "Invalid page." @@ -426,7 +426,7 @@ msgstr "Ogiltig version i URL-resursen." #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "Ogiltig version i URL-resursen. Matchar inget versions-namespace." #: versioning.py:147 msgid "Invalid version in hostname." diff --git a/rest_framework/locale/tr/LC_MESSAGES/django.po b/rest_framework/locale/tr/LC_MESSAGES/django.po index 17e6e4a73..d327ab9e2 100644 --- a/rest_framework/locale/tr/LC_MESSAGES/django.po +++ b/rest_framework/locale/tr/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ # Dogukan Tufekci , 2015 # Emrah BİLBAY , 2015 # Ertaç Paprat , 2015 -# Yusuf (Josè) Luis , 2016 +# José Luis , 2016 # Mesut Can Gürle , 2015 # Murat Çorlu , 2015 # Recep KIRMIZI , 2015 @@ -16,7 +16,7 @@ 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: 2016-07-12 15:14+0000\n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" "Last-Translator: Thomas Christie \n" "Language-Team: Turkish (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/tr/)\n" "MIME-Version: 1.0\n" diff --git a/rest_framework/locale/tr_TR/LC_MESSAGES/django.po b/rest_framework/locale/tr_TR/LC_MESSAGES/django.po index 171826a63..94856c70f 100644 --- a/rest_framework/locale/tr_TR/LC_MESSAGES/django.po +++ b/rest_framework/locale/tr_TR/LC_MESSAGES/django.po @@ -3,13 +3,13 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# Yusuf (Josè) Luis , 2015-2016 +# José Luis , 2015-2016 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: 2016-07-12 15:14+0000\n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" "Last-Translator: Thomas Christie \n" "Language-Team: Turkish (Turkey) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/tr_TR/)\n" "MIME-Version: 1.0\n" diff --git a/rest_framework/locale/uk/LC_MESSAGES/django.po b/rest_framework/locale/uk/LC_MESSAGES/django.po index 51909058f..2bd4369f8 100644 --- a/rest_framework/locale/uk/LC_MESSAGES/django.po +++ b/rest_framework/locale/uk/LC_MESSAGES/django.po @@ -3,16 +3,17 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# Denis Podlesniy , 2016 +# Денис Подлесный , 2016 # Illarion , 2016 # Kirill Tarasenko, 2016 +# Victor Mireyev , 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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: Victor Mireyev \n" "Language-Team: Ukrainian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/uk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -317,11 +318,11 @@ msgstr "Відправити" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "в порядку зростання" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "у порядку зменшення" #: pagination.py:193 msgid "Invalid page." diff --git a/rest_framework/locale/zh_CN/LC_MESSAGES/django.po b/rest_framework/locale/zh_CN/LC_MESSAGES/django.po index c21604b42..345bcfac8 100644 --- a/rest_framework/locale/zh_CN/LC_MESSAGES/django.po +++ b/rest_framework/locale/zh_CN/LC_MESSAGES/django.po @@ -4,15 +4,15 @@ # # Translators: # hunter007 , 2015 -# Lele Long , 2015 +# Lele Long , 2015,2017 # Ming Chen , 2015-2016 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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: Lele Long \n" "Language-Team: Chinese (China) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/zh_CN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -63,7 +63,7 @@ msgstr "认证令牌" #: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "键" #: authtoken/models.py:18 msgid "User" @@ -71,7 +71,7 @@ msgstr "用户" #: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "已创建" #: authtoken/models.py:29 msgid "Token" @@ -317,15 +317,15 @@ msgstr "保存" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "升序" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "降序" #: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "无效页。" #: pagination.py:427 msgid "Invalid cursor" @@ -427,7 +427,7 @@ msgstr "URL路径包含无效版本。" #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "URL路径中存在无效版本。版本空间中无法匹配上。" #: versioning.py:147 msgid "Invalid version in hostname." diff --git a/rest_framework/locale/zh_Hans/LC_MESSAGES/django.po b/rest_framework/locale/zh_Hans/LC_MESSAGES/django.po index 9f5cd24f1..aa56ccc45 100644 --- a/rest_framework/locale/zh_Hans/LC_MESSAGES/django.po +++ b/rest_framework/locale/zh_Hans/LC_MESSAGES/django.po @@ -6,13 +6,14 @@ # cokky , 2015 # hunter007 , 2015 # nypisces , 2015 +# ppppfly , 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: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-08-03 14:58+0000\n" +"Last-Translator: ppppfly \n" "Language-Team: Chinese Simplified (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/zh-Hans/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -59,35 +60,35 @@ msgstr "认证令牌无效。" #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "认证令牌" #: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "键" #: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "用户" #: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "已创建" #: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "令牌" #: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "令牌" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "用户名" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "密码" #: authtoken/serializers.py:20 msgid "User account is disabled." @@ -317,15 +318,15 @@ msgstr "提交" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "正排序" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "倒排序" #: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "无效页面。" #: pagination.py:427 msgid "Invalid cursor" @@ -427,7 +428,7 @@ msgstr "URL路径包含无效版本。" #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "在URL路径中发现无效的版本。无法匹配任何的版本命名空间。" #: versioning.py:147 msgid "Invalid version in hostname." From d875fb3272fbcb71bb153ca421881907bb1d0bfa Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Mon, 21 Aug 2017 12:05:25 +0200 Subject: [PATCH 075/730] Update compiled translations. --- .../locale/ar/LC_MESSAGES/django.mo | Bin 5476 -> 5766 bytes .../locale/ca/LC_MESSAGES/django.mo | Bin 9646 -> 9646 bytes .../locale/cs/LC_MESSAGES/django.mo | Bin 9031 -> 9031 bytes .../locale/da/LC_MESSAGES/django.mo | Bin 9881 -> 10401 bytes .../locale/de/LC_MESSAGES/django.mo | Bin 10609 -> 10926 bytes .../locale/el/LC_MESSAGES/django.mo | Bin 13507 -> 13507 bytes .../locale/es/LC_MESSAGES/django.mo | Bin 10779 -> 11064 bytes .../locale/et/LC_MESSAGES/django.mo | Bin 8732 -> 8732 bytes .../locale/fi/LC_MESSAGES/django.mo | Bin 9993 -> 10549 bytes .../locale/fr/LC_MESSAGES/django.mo | Bin 10770 -> 11014 bytes .../locale/hu/LC_MESSAGES/django.mo | Bin 9123 -> 9123 bytes .../locale/it/LC_MESSAGES/django.mo | Bin 10359 -> 10359 bytes .../locale/ja/LC_MESSAGES/django.mo | Bin 11941 -> 12295 bytes .../locale/ko_KR/LC_MESSAGES/django.mo | Bin 10022 -> 11720 bytes .../locale/lv/LC_MESSAGES/django.mo | Bin 0 -> 10841 bytes .../locale/mk/LC_MESSAGES/django.mo | Bin 10623 -> 12660 bytes .../locale/nb/LC_MESSAGES/django.mo | Bin 9803 -> 9803 bytes .../locale/nl/LC_MESSAGES/django.mo | Bin 10373 -> 10608 bytes .../locale/pl/LC_MESSAGES/django.mo | Bin 10801 -> 11121 bytes .../locale/pt_BR/LC_MESSAGES/django.mo | Bin 10238 -> 10238 bytes .../locale/ro/LC_MESSAGES/django.mo | Bin 10895 -> 10895 bytes .../locale/ru/LC_MESSAGES/django.mo | Bin 13430 -> 13441 bytes .../locale/sk/LC_MESSAGES/django.mo | Bin 9411 -> 9411 bytes .../locale/sl/LC_MESSAGES/django.mo | Bin 0 -> 10390 bytes .../locale/sv/LC_MESSAGES/django.mo | Bin 10426 -> 10625 bytes .../locale/tr/LC_MESSAGES/django.mo | Bin 10550 -> 10550 bytes .../locale/tr_TR/LC_MESSAGES/django.mo | Bin 10522 -> 10522 bytes .../locale/uk/LC_MESSAGES/django.mo | Bin 13352 -> 13478 bytes .../locale/zh_CN/LC_MESSAGES/django.mo | Bin 10007 -> 10342 bytes .../locale/zh_Hans/LC_MESSAGES/django.mo | Bin 9796 -> 10362 bytes 30 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 rest_framework/locale/lv/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/sl/LC_MESSAGES/django.mo diff --git a/rest_framework/locale/ar/LC_MESSAGES/django.mo b/rest_framework/locale/ar/LC_MESSAGES/django.mo index 06471de238d2249bd794f744ebb7c4c710622b4f..bda2ce995f0a4ecdd7f4f3dfe1260c3af1a10a34 100644 GIT binary patch delta 1536 zcmYk+Uu;uV9Ki8k$H1`(+ODFTY#s!Gf$iEN8w}6^2~h-GZ+zE($fT!kgp^VguEWuGU_!qv0)2RLb#n-WUu1Gz0U<5bg z3z)$zcmlWKb?nB#JdsY3f^4C(fg7LWhj^- ze+px?pW;F^bxaQ1QQO;5*LDC`@cVLz$|7!@MHVCHQCBjGd}O@pRn&?6iOf;{u4+*y z_7Gpi=hb$2ekC(;)Cugwc07)nk)Lobzc2Tw=zvqW8lRv}B+M+Du&L@AWNg`q3EYQt zm2Z%b6uGq4i2RHzXy1HMWGg;KgKeZOj@xl1p2ET_R4!J(xPkfuGx#+=!*B63@}^r; z_Y(Vxdr(*K0}i5(-RQh5@-ALL-O?%4t!xQa7P23ii)8U_ECk8F9-eV-?8RHCDQqRr zI#37d_j%O4`wn;D2rk7LY`|b+Wouec`^8Wf_8w{m_o8MvgSx;^u^kJIq@qvPG~o>SQ)`pIEnAzebfa7Ibb?&8|rhtRZkYE z1h}E4gV2YxLEqhzISJ2|U70>}G?Vv|QTZsFbh9b% znDjWg+=)y!DLrmBoyz4>8P6nLFXbkq#h$v;;iDT0^; zpEmY7%}L{*wO8$=y<+Soe^81)h8`uf?--BJ%kMYz8n>5>{ly=k?w|38v@*oE!*)Xa mLHnyeSacRHDxGWmC(vv!Gu)W{!%q0aVlVpxG=B5@Y5WJ2&F_f- delta 1215 zcmY+@Ur1A77{~E7t7qK@il9U=RgC5f*e|U3npRA#@Y;{p}or9(>Mc?{Pct`##Tm?goD~q<;9l zvxW$hRpf%lm|kqk=0Y6v8dHNEXyGuH<0R_)vsj5sn2#SZh^v^3x!a8C!6NL#3)qbh zvE7)I`NoIi-0$$%cfE{S$Q9g$lkWIFsuC}-89$(Q{s-Bk z>GnC#>BC0GgXm>`Gr@=5c++(b`Hfk^7JQ8rS;lN&9b?~ir$u437+*)V?*Z21JKTwz z?)_ZKrRR0vH1^;N&Sim3mu3{nexI+!(-h+>h-vTsilTOT8uVum<*BXdjZLMG8KY+H4!cOFSJ^PcB|5|p3E800aQg4 zY$_9K1qIBMRtGjr21&&Mhs<>!*DQD3#hEgP$UUT9iyBfDQ0SEM8fQZ9JnhefTC$g{ zPe1m0y!O+)O8ZmZbUNm%^4RnFsWV4|q4r?7#R?zk47W9gi2Q*_A{iVSkBlX*MUwIH zPHSj19*ZQb-qG=BA{iaAP9)>8Zt|*oBRx_O$+1^TVs>#^xjpD#wyOgj*^8@p?0Dd3 J`gZBM=Pz{ueBA&5 diff --git a/rest_framework/locale/ca/LC_MESSAGES/django.mo b/rest_framework/locale/ca/LC_MESSAGES/django.mo index 7418c1ed05769800a93e799c05042bf4a73c7c7e..28faf17fffec2592a0bd0b1c29165312edb30459 100644 GIT binary patch delta 26 hcmZ4Iz0P|>peV1ou7QQFfw6+2iIu6v<~Y#+0RU`@2P6Oh delta 26 hcmZ4Iz0P|>peV1Iu7SC(p^<{2sg@-tR@hQL@^TejeQVI@DhSACd8n`2aJRes3dBPB${Bne1Egkn0Uf}Kj+MLXU_Sb z{~1mVyt6s+Lt)mNhGr9sh@MPij^RW$AGFtVjH$pAXkiMM;J5fFKEMUI@S*hYS7I6C zA*{u5T#l#Ek6&UpUd8psB+R^Aw_`@|DZV&^oj8r#v1Yb0&6vQ|_%Zh3eQd*yIqC18 z!+yr+u?qjeLafS5Ppk=beXHkTT+Q>%X*!L3@g4HdF3m8JsJKjazaPd52N^uox zJcwG!9@Lh-fJ*7x-uRaH{ok0$^-9v9OjPrsEo(*HZv+!d={R&s@HAR@9_QmXn1$2G zZkV6&3H$@ucC(Z}lJ&?X(~7!JKdO92@nJmZjgMkI<723;{$xJ+*9tO<(tlWv8aJbg zXc(Jt5>*4|yzx!c3QCI8f42fH#sjDcM$pX&&SHEEHKA!-hQHtnEG;4bJLvQ?ZLRcu zT!t!Xti>JP_+`|@KST?!B2l3s>-v-;>=Z4k=(&*%wnJ#S%#gs6<6aM$O$xGqf+}lK8CkZ)t^_+iNN)!iNAsy z@H#5>k6LaqC(I@~y5U~bgHEH4-DT9o?jc9n6jh{GP=Rcp*^PY|!!G<3Ibm+zS)?f- z{~FigVr)lU-;erT0?Ty%zoN683)85T6mhonMk_-dvj#p$h8e*coI(oNoIw_AQpia) z*HFcF8#Up3s9N|PRV$Uu!jH>Q6KlY7o^N{SWML55J~M{e`IH5HPIaUU^C_z5+OP*KjbAe}7Y8Dch}dSbUtLNAjxB0%T`G)$-_mJpkX zHH2C_vBT~C_t8ODN3qhovBv^{cEE z4Q-7r%j#*l{Q)OlI~)r{oQXg@8f&u#0zt>>wIhxlvDSnS+MQ#O;GxlRr!E=`rOuWF zGjfI|V|F}l$5Yu0&t+#hV?jIhUiqZ2HDa+&JHjIQsS~%uVLN7xjgQ+_lr?fkD{Kc@ ze2A~TMLUs0;h3HDRrF;KUcL~Tuv2{%g+6~@G-zwRyDP8%Z@u+P?j-Z8+jF~PheBHM XR3zD3Jyht#$GB_IHqQnof^O$u5PCpc delta 2151 zcmYk+TWl0n9LMqhwxy*-DQ#&Bt=MTxX)E2bTYE2;UZK#7uG9)Orfj&bwybF@8lzbs zd@+Vd7>Ut`5+d4|CLqoO8k-o5iBBpaCXk3mNF{0rQ88Yk4}O2UgW;tA{hTw~ojK?H z|7T7`-W*DNm!EOMXk)~E#LYCb1THM$gZ5#TSvh`$4t|4G_%Gg%6-&)lVHfK6V_1sM zVh~-d!wcArw{R>jgx>_JUv6m@;t_YKtjKE@_3^-ntYav{hEi?R;X3VTowieW3B#x3|Q_TZZ3X7w0F z{_GeZ`oEJ{gI7>H7sxex04p(s!&r|A+^(%YLuV8Ih5W{vSD2MxA8Nb@wSqWmMXvAr z{`D_VEB+Ogl8n66?<=vI@n$T-1L)w3Sb(#b$e{B99d_5w;|BZ`Nt6ACy751#8!Ts# ziB(vPb^f>)LyQMdseA!7(a%u-cg-LFjjD|zp5BZN1?<0a81pZjLaqD?>K|{TgSjhH z6APh=Xb-N(w=s-g;cg5RnytpieV_4t4I8-r9)2D$yNde#>0yx?0MA0-$8cOzQ+Ol1NDGzhxFmg$lUC8e|!$t z>Q!@rj=s2oEY{M?Q!k7%WOr>4RsH*sx!NnJ<28qrhh0SGW{b!HuypdLch^efWLpU; z*VV`?#&)76^aPga{7=%+DL9Inz;RUdpFtJPdDIU5jHJ_An2n4ei?pYFUqw~*JZg)t zV-uFLOf4{i%+U@bS+`@@%=2xYj&gk+^}sN1pKjcT2XPLy@>~)wgQyh`U_CyG0ld>x zplVvBQliQ95!(oEm3#)+2$9g1DUWK(yILBtli;`}w{R0`#X2EsY(%oknQyX1e7B%d z!V5CFGydfyb$qIRbZT^B)F@D`Kh-i}E1{iG+f8&5V?+kQZYEc&;$p$c9pbdwL&V)} zBLn5Rhfq4yv|CC=FQFY&Qw{z9eC7M@roy|^9;4Gm>>yM~YN`k2P)(;~1EF)Rlc1KB zB6)H~=|AF+b9^86t<{Wpd09H4;%g)Ni9A9@qqc;2l-Nw{BGep0?*kQ7$UC~UE#Q5b z9n19kR!juk-}CCdb@|HzUVFh@ntQ8o!980Pa(^rua(5Ib`kRBHmSDKS2{*Ne8`p6zGmXX8wKvXj{BU#Q<3pY*aom8pdhvKov)ffO{2w^~+7bW& diff --git a/rest_framework/locale/de/LC_MESSAGES/django.mo b/rest_framework/locale/de/LC_MESSAGES/django.mo index 317124886528d1de71f4021441d60acd700235dc..eb0ddf66dd4a54ffac32403317a4b4db1bd2dacc 100644 GIT binary patch delta 2630 zcmYM#TWl0n9LMpqEwrUbxlafgh;?(c=W+l48#WoZ!d%dH4+m+j1OLr1Y@EhB>MgBPW6QUe$E+Y&Ybf<=S=Un zy}LPmYt*1qhSEz+BEtE`9Kq)b_@SIDGG;2ijTTvQL;61$Gtf5kkTTmUmifiygT!EpHxeixh8SV8! zJBFIcKGe+KLQ-lj1?`8y|4(50`CMOt%0vS{TDo>jt3!{Ldpb{lFl zC2$cQMXm9bp#1agK0lR4JeP%&BY<($bSu$S}t^A6gAUN zZ~}fF_ycOD4}x|+(|L?`IkJDv22^Ic@Jh%S2lay4bTS>=QA@QSAH_4Mz3_3GiZ;iO zs2hhe9d%TVOwKIBMYsWVY+k`9@i=zlZEVMS(zF>*;d=ZHpTauQsJ+yWPvH01jdRP5 zX~FdCRGO*WKy^^c&QM0;NZ;lxYG9XfIo=MOG$nV;I#I{=0&3vDqf-1gl5O)3HsDw* zcN(^!2J!~3)%m|gWic1#kPmGh8|%?WrT7EXsrVVSrlsVMjcr=77CTYTA4hfk1=6t@ zOTKkqH}a@S2JMqrN&5m$;r-@oDw@Hc*$bqc>FP9WMa}3CYAKGPIy#Tql%L@|yo0>c zl<<*Iy9P;;*^bJ@^Eez|LQUW#s^3qsi1(Z8R5$?UCbA0VE)K@xnYk2>Ks_)MS7Hrr z!9Az}eTjPR*Pva*=ykmUTQD4W0-I^yL<`H=cxm0Zl8R=~jY{bu)TX+G8c4~U+)T!x zcJ)MDfvwnwhp`Cnp)&IqS~$AG7{0=0Au1ycsLkAsL-EZD@?T8lJQqT!S=ABBhHe-r z`U5kBPwq0*(I4YG~s zCRz#2T3;*`ecQFOhY_oZ8H9>btE}}9+Q^R+Dmtzznz^#1LV~j8X|4Z;pizTOLES=a zmaRk{(Mu?`9fVS;l24QptB6iQg+CeD&1z$WC?r(0%sLGN#i25YNblfBYow#YzQ}$& zmf=ofA+edz@nUb8HbU#JqK(*RWQ+bXQfjhin~z_% zEai6-5zaNUUoIUT6@3d-`VSVBW!j2ng$kRJj_o^<%)TMd6y)1cC-d#_y`iejZDDJV z?e|q#%?Zb|+=Oq%ZNI0_vfcgGvrf{BC0xt3Ljh@ai@N7BDt?>cP!IU>WBKTkH0&tC3(O}MSat3`>A;U)UtJ%p=B5HLu>lqEB`L@ z+T{Cr1=c1f<~y0h)J$kT^Qhd=Ye%ZA7T5RoBx5~&zO~DVxlY{b$W6nmT+cjQFKMUZ znc`_h`Gefl?%n;{W-iKvXPygHG{+of9d)A4PRDgzt1A_y=|#wzYk7Vw&cl|gjx&>I uFAUXMk(lE-R>Lk|If|r`J$(mKy;nc#OFC|qX-M^Yz7va*Dbuj?zv{ox)kHM_ delta 2313 zcmZA2e@s+Ud=x*#OuT`*u{-Yk1J}ZIT`$62(5dEt*O5PSnU6L2IV#f|s2{qAwOH|xF^w3+ z7QBGvxPttdjTz2yrT94G5!4F4g{8QFTk#f_^ZRD)I%9TW9qzzc)P)u?7nj`euc*xa zL1mQpuww&iem}kF2_B#|rF0w$mJU zokRXiln-6#JgQu-p%*`M$KT;r#y_C8c0E6$8y`R&H2v~V7EgI}PE>TgWMhDV$m^5YYXJFpVN_&lCRC2F!+{|%T(8ZsZ1X`MT6 z#%+uT@VdvCQ>Y8}Q}RVPi`wH0Sco@KdwtKH&*q{!t`W7OqsTp)DXhUaB6Rl9xq^e3 zN}9bG!aiKYZuFA&etZE{E1%*sn8NZ6Vn4RwWo*R9Ov8~O>iV-HhrZcbtoo<*(XGL~Z| z%f!}2jGs<56EC6md>(bdWz+@BSXT%Z8>c=Kb9BiPFLB3k$x>i7)ORRWc$ zmFz{;&>SY>GA7}V_$0324z#G;NRZB9I?CiaYH60x!WHCgY0^ua70E?yQ5||PflzTO z`_;CK4iAal^b+IkY`?}E&}YwbK3W9VQvOcU_Et?VmQrGn z&@$H(T4pb?hsY;*W7}I&fb6RM-?j^LSM%>=&_tvY)S+G3amW^%X9?|lGI5Ad)1#+X z>rO&@tft3m6LDArnqE{rcF);w)tK6D2m7;472QIp;we!3EtrF=Z4DhgKC5jnokn*M zkIh6%?EU0cPweNE@ub+`x{xQ9k?Bv22E38zRc}f{b$w-3^sd)i)Ek_d7@D?Pf-jFx zhsOif_VCn16EWgWl#fhJ#ENsmoV;qw_zoAD)+B=Pha#z-tE|M{_kricyG@%W-MrFs7WS?l&l diff --git a/rest_framework/locale/el/LC_MESSAGES/django.mo b/rest_framework/locale/el/LC_MESSAGES/django.mo index c7fb97b2ca50c64f7f8d5b0a30b4d462f148575b..d275dba3b79ea42efc138a5fc288084865e6a860 100644 GIT binary patch delta 26 hcmX?{c{p=Jj5x2ku7QQFfw6+2iIu6v=1lP@SpavY2fF|O delta 26 hcmX?{c{p=Jj5x2Eu7SC(p^<{2sgJl(qXj!|qTfX=kxpP!L>s zP$3u}*zmwrNQ?>5_)-IkQ36Cv47^~B#t_6rNJtD1CcZRH^!u9~jVC?(Ip^%`ng9Qs zGt)DjUv#DK7mhq>l-<;4s2#ax$M8^$56Xp6W;5|3I(QRj;qN#DM;Dk)!Fj0PH{mqg ziKFpNtiX410e+78_!~BvrR_0|Rt6SuwM=0fUd5G|Gsdh7o3R#8<1)O9?Kpm{Sv_{6 zzCVc_cn8aH>NvAPY(NFL0W}_krp->!(1e$93I2)v+1&BjNNZ3TH=u5mz&iX88}Vms z#wkykEyQiekR3-|cN*v752%$K#`!p70{7wmwuVL(Zb40O6xZMtT!AAdW+!aKsr1)H zeIJ#{8>r0AB1>vtM*ZRF`x4U6Wqbu{C06mFt?R_JCQQ+ogCAfKUPcG+;3Ry4BQb{# zyJGoRiG|29u%^g%c{*eJ?Fnx0N!g^GHBkC|E zu?CN$_V`BBA3`Ni{dD%a4s_`6K?QgW!xh0i`opMza)_=R3yN6(Y8p!z*or-glrSW-heha!g-} z8jrAxfig~pB0Y#&`b)SJzeO$S-_iG#M60dafm-5^k@ISgF&~FeOPu3mpOS_668!)- z;WtPkX)By*wwi%Gs0ltpJxj{TX-{cS0Y0}Sj62h8rFPG=tbqC-=I#A3_Q!5zqIS{nJYvwX04b^|r> z10)t3%f@RZDpBu)T2vsd$h*n*;u!94hiUMr+A$rfN;PGOy)NDruy(m8vp< z%6lw)EEAb?`P0%Ky^++NR4vb1Dtl<_sLQCksCqf*MW7AUns20PZ&VtndO7^Rtfa%s zD?DHt-$9L0bzoGQs5;e@+2Ow}we%t=QG=qBrw6w~9m<-)?V}nCGULEaKPE`^{4z~-~?`;m)h&by}|b;cI8cWQ*kdy_(6{)ynmaSdqr=z)>c&2 zRa8CiR4=YyQdeF@8QtNg1}ZxH-5}NL4kY{Qowa_?KCjnlO9ltsUhkl@ywC3m+xy-A zl|6l~-#d7rxNdAwhnv0q&B4Io=3d<2?Z@5BwX))NqGZU~fAeCmpKupC>yl19=?8H? z;n5&mLfaR(PJ;V{ar;Ty{HkwPKfj|HCFZ3vN6MxQJ}Ik+l|)H-!GQN)T4CoO^6Den delta 2132 zcmYk-Z%oxy9LMo53Ks|kyCfIFuQtj?Fgb16R&kZLanH{C>X&opJ8#oOAE*{QI8s zQN*0lIM|QGtEY@HN*#Le}-8xzJLxUa3P+<`FIuc@gLOl;Y_naT!s%| z3qFDExCCFtbUcfVX0BbJ&_u)iz&KB>r~W$D;}vYj+$^)zxCg88V{E}GtirNc{`Y;@ zN_`9;#oHLh?AiVVi&5>ho~~IB1$Foumg5BSXMgjdnTE3c6=$OcieUu~U>%;uINrjg zxFm%PR9B0!04QdB|K!t1?)sGt8QY@s9hjDbU19R~Zrs60P1$zgV;c+C~_G92A z@@Lb0sGobNAQp zME&j;bns8q1oP&4hs&BUjrs^`0;9MX-^D0S%w_+#Q@BTiR{9jvFTgE<{iu}=2lcmb z1@$v{DaGtMmQWw(3M|5JQ6apG4i?jyV&90mHG8oL-$w1sS(ieL!q-@f|Dv|ChUIO= z&DeqCxE)hiZUc4(j^WePZ{S*tvVBeX5_aR4xCVK~vc>i$lFXRU~nQHj;4 zEqoF6gHxzj{fyjKo5%DykG2zaJNBct^kuBWleh_Q;uefK{z8u6I_guX=SzycTjN?e zg(@0`P>16jK90Ykb|PniKd}XfeWqB^{WjALj6{)MY>X=(5VM1^z= zwXh4wPwZFJ%4e`0ZMYJ<(+8L<*CD&-r8u8DfjzoGI!WujW-fW4_ADbPGh;7lxjMc|QF5bK^5dXu(LtUO zv}p^rCO%5fEvTimEohyCyoT&~a&O}M^g@N$d$~M^IvA~FLSavmwawjRy#o{zC2g@z zxbCfzLZZ9>|5QgMkE{cww1cb=>U=0QkadVji@ZaklJ0YX3Zx41GvpRkNX^MNGU_su zIpK!1L|?>Bd=be^Ew75k5`RP@PTQ`5{_a61zU#p5!GpW|oHYjr`s>NPK|``EzhYMM IT1h zZU3-ilIjhT-nsNXl?-MAmK z@JTGgBe)7bzzqBan~WJTH^{VbV--(J4>#k-xB&w*jM;_FSc7A@9xr1n&Yo#ZEq0;4 zKaOp9376o4S;ox6dQ=Cuqwc$LM~r!ej4C{fRrnY3F)L;#dRmPdaXqS`9$bfSU;|#o zW}H9Am~z~Q+%iW|&yC>p^+qe=JX44Msn=NE2a5t)g7jO%n!#Yfvo2ak>7f^0b zmV>B~Jb@b7Ysi$EkCNrv$?ppoek%9tP&3iUMN8L-BdX9NvmB3MKAuGjFJTV;j%gU6 zz^a%GT#fUPZD5*`wjz(3PE3( z3=8m!q}Na*olKTf8P2_w3z7Y6wxMRG3(uq&cC0t!0%A!zl7=I z0GTMNp65}&JB_OE6D*{CbDc~BHv-hIwQ52=(1)7xmykz{pY$y9F+XrA!#k+omGUF~ zZaWv2-$Zc+9z*SoF)YRt$?`XtN&Dt|GV19yr5 z^QbBR4Yy$qt!bn^sF@i;KIT;}n(_-s6Xp`?+~2|sTvo#TD^pg&TMl;<>ZxWy6;37n z!RVN+O5RXsrjjBJ&JgW%vWJIw!fGD>WCvnifStm{7V zj}e=RQbIjf(z{(}SN{<$gl0@>Bhg886M73iKqx(wAii{&$voqYr$@B@nnES*`sR2p z@siLK?v={aeO4$T2#rRR~LCvlbha!Hu;sq`ap*=RTp9}w# z@%3Jg?ZkTGabhj8lhCHo`ssMp5lZz$S3LLsWdqrKVoCDWqd1LNnk+XV@11zkKO?C! z{*R|ksB|B(gV6a^(wj{iNJ+0-CI6p{1^(rXirA=h&4LB|`i zyB+`K>^y%WyF9hkIUM^VduB?8?Rken;hxy!+;;;r6HUgip8t041-~n=Ds!jjgjIom zEU!J+_PQOnC+PN?9%m}+|CskoWldSdy0VHjR^{5-s&%Cmq^vgEi4BbB8@(d$w!G{$6}BkkdKry6Sr>EzC zd7l5_qn*$6X3qxwuNZBBxQh6#z$}Z?g?!K^i_NNV3S)Q%6ZkJyVD)0NGVDP8egLn; zhp-MEti{i96aJ1{up!`@wTC&_%7xFc9g8nB>%%U*9*UMosks2TQq7#|M+LX4DKjQ5|J)BfgDo_#Jj)d5Kv) z4k3Sbh!5R&1Z!{>wQ{~vvo#pUB=%xGX0cmK{XPdb;9tmZY{N3MC~iib529u;ikgw* zIpJNOLCyFaDkc8q`QOKJHRr7u#yc>Ek7Ed5$E=@&4>@3U?HFE*$B{JIAE*cagL*&- zCqlcei2n0VS3+y>q4x*a+vWhyos9mEb7Md7{k&l z@&ikvif9nm;#=5&U*SIVtuR}Ow|G9_`6904`cXXTGy4{mqIV*!zjFO0!_*SbV+_kE zY3{Kmtim0r0o{ky_&jO`6Sxb1!G3I}_ilU{(|8tDD{*Gkhj(HMKSlliV%Ga2$78#z-$E- z)55_Z_TXVub46A4x@aj`@2vBx(8Kk4)Q9n_s}hByqfdl z-uWDAK=Y{jvqGNg=fX;4wXFiRWGPgRIJ)Kmv* zT4ojTMq)Fet;r+ZodC9*$Z9FnshSE1%!XoskgCe1(JieL-T>z(hzc4C0=6IuZkrizpayH%`w?;sX76*K=z z+-;}ysVQxZM4C{osc9wu?_c>|*vb~1?B!T_y^&b!T~}qZbKRy_w9=(qEAMK>dEA|$ zeXkv%rh-&cTGg)6hE8 z_U7gSnWEg_vb%jwX|O)m5nSTS?FqeG-~__c&KKdNvnaCN8IEL~=}5rem}*$>oQp&f zyAOh diff --git a/rest_framework/locale/fr/LC_MESSAGES/django.mo b/rest_framework/locale/fr/LC_MESSAGES/django.mo index 2bc60c63a1e7c38332b1c06124ebff5b552c2e06..e3ba4a2c5065d049f697094a8cece8e499b8db56 100644 GIT binary patch delta 2367 zcmYM!e@s&np=)PdVkJ2y%pU|>=_uoBnZ|ASIo4zLbYnWP2{+(*ti>O&4QI|UrUtuE zzn{Y>-o$0Npum_pxCJ%9J*a-yH)G6M0zL2wR^wmDpDCFcjC4I}##>M?O5jF(2kY<_ zHsSn7j9H6^kS=oub=`R^!5>j8If-j<@hslM`%NoB1@1#V;5BT;tGF4nW(N6L0 z8tjV6!zXYKk_M(Rv<w`gy&Z$5GEeiyF{XjNqSZn6H-RA3CydAGKz*BeyBDiAb+v z1b)%Z?x1QoT5N9cU)6wns4Ci&7C#Ek&LQGwsoSWfRArNjvWE|W|B>H{E2t{XRIO{A zt?)NuyLu_@0sbdwS{O5BzbKb?hq2B_Qy5#QwLD18rgl@c=$)g-@(PRVhz?LybZWFp zHZ?JNF>hhUAyVcYp=x<{QrSEIpx5Ga)GDeDyw;OE@DIC=gUS|ammfX+S5L^t%TFlv zK10o=YF|_usY)KoNDirA=i2GG<0g`Bk4ZTHg`>aBd$oB(dBw)^igi|H zRZaEA(h5p`)K2%8$5OVN?zQ{9RE@RY9!xqZt38$QjtvZ3n})m~s_#v@-eA%mA1kVz z(Ux=v?cQXqJPf`MAZuGiOR a_c-IK`xYi*=D8q5_*`z1$YKa@ER84->CnGa?Fac1|P&W ztj12P!b6yapW#}wq@APD!oWReoPV{_KZ1?;BX(jq*K7q2U_G9~Hk`&7R_3L@PvA!S z?_e4JiXqIOnVw)NYJ81v(yW(;Zg?1%;S}<*zqn|o!Tj`!^HC3qVjT`+1AdHg{23Qx zRRIsfU8vs;U zQCq*)Iqt?k!WoQzi`s$jQ6am9x{n5v3$d8S9E_ufJ28w0F@O_D6zpxR!FQ2x+hylf z66P%A{avTD{%?dqC(soCjOfFY4^i#Q2m>zB)W&ovGCz^Y&WC&W2hBe zK>h9~^zcvA1n125lgnB#lm0kr0u#6hk7F%P&1V0*XxwE$E8WEOi*T!R2({8N*MAG2 zpnn>_&M>=y+QAPv0`u__Duj1Xq4BtxYcZl6`8x8LEZO?8~+yr zV)i^3q;L zP3$Nt#2;cA&$mx$Xe+N^JzmESjFhLddoL=0@} zCy_bZSEvd8g37HT-by{0G!is|IEeaUKPsjda1d`}E^dE3eeC*BJMao>WhYS&_#8E% zYnXwZ)Rn2xU_7a1JxS$8e%a1-165Dg-YONzY-^-$p{ghb&3-F=M0U~MO>LnrqAF=r zlq4Kq|DC-K**(9+x$5Zj@2uQHsv{LU+TN8Vujd$xjc(X6lF0Xu&1aoY7aF^Rm@cgr#($w zr4B{e-{rUdzckR~-Q&ksquo@6P+?GMqAFJ?<$g@mQZ_4*RFo*~RK;6m{p6e34LOqq zp{C4KB9csf5y=TGi`7O`cOnt5qknj)=YSXQ-@oU;$ex6^YGin*k=o}5CMyc-^Cr*E HUlIHdDaz3+ diff --git a/rest_framework/locale/hu/LC_MESSAGES/django.mo b/rest_framework/locale/hu/LC_MESSAGES/django.mo index cb27fb740a8f8efca1fd9c8264bb56df60d5b078..8b884fbed0e46c3ff25408f56c75c10a2fc7ffce 100644 GIT binary patch delta 26 icmZ4NzSw=kVj*60T>}eU17ih46Dw1T%^QS13jhFerwB&? delta 21 dcmZ4NzSw=kVxh^^LYyq7R)!{30su7QQFfw6+2iIu6v<_s}@Apm?$2bTZ< delta 26 hcmew!@I7Edv>30Mu7SC(p^<{2sg=2aW&$ diff --git a/rest_framework/locale/ja/LC_MESSAGES/django.mo b/rest_framework/locale/ja/LC_MESSAGES/django.mo index 1f934cc378a47eb5d4f15aec1e1bde9e58f6acf2..9ce42cfb36dd3d3b65145832e854667adfe857d3 100644 GIT binary patch delta 2580 zcmZA2drVe!9LMn=pxi|8ii)?Bnt2031rQ1b>H;SrwF=W#xiX068uR5XS& z(L$}I>*CDSKQI+*HfwWh>OWmJx3;n!Z~T*8)*m@HdweH0;JC{1r=$iJD$AWmHV#ZW+O)cnOO!zMnB`a1qYN6F3jQ!*WbdGo}#Nqkcbz z6?g;3V^)7-2H^r!2P;waA=jueN66@f=P@7uME=aAbg!ocs1YwfHB^st@B>_kH*paT z&oCwjHzHN04RzlMoP^(_X7UkE!7&49hxW}fGP$@G^@0Pq3@>02#trmdxDc}_ukg!3 z)JS%rM)n>urKZO(Kk|Pc!SEBPFG9^k2^TG06-M>K2$^T_5N6_ewD1NF#^10H##3Nb zOcGAVLC7{RrM~6Jt)>d~o(9x5dJ~7?KEM1vYQP^2X8pC+KT@F)j2`0MScodGLT#on z7GN7{jlcHG4^aci8|vLxffnUPR0j{DJ0sYa@*`A-;_2N~Ovz;a^T^~=u?`zhBfWwn z@T%`E)JPxtbTi+&$DD$kP00lUd>$ zK~3Rl)D7KOiN7HG#^f;_)mY+t%J(sDq`sPI)2a9Zt1xLa`vo`RT0DmvFo6w_kI@=G za}0M=aSaz^*;uawZ8)FudDKY$@||LNFSKzz^*u-*%n-IXC(q=f22g}`*od3(ItFpd zIAdyc{*RJTkN-y6HYIH7by$n4zl2LLae`N_KsD5f%ds0ZLys|xm7LWrcm?&oNfW)z zwiH=@v&%0Za*Z1E6&XGFJI=&fj>1Abii!9GY9v3QW+0KntPT|60$hh$!cNrlSCL!I zL)7!*CVS_8GU|I!j;h~_8MJTO$moS9QODwff5Uy$2p;(5bb6tg7>ez<0o8CX>b_x9 zy@m^rwoNs@h^@Z2eCJN{266gl&+)bJmuwI0V?tFZ>t@UrhU)bBU_--q(GYNywZXU{pli^OU@(j>DPPMp{VCLKrf%|RO zM(R(L5$rQJE#|5tS4ZdwKT9Y*@8Ry9WIxY}x@nWvf2Cj0hiH+Xw~$$K&uTnwA+$%< z5L#=c1cL93JEb}?N&^T!ipD1Di6laaQ)dRcIqwpHK18&Ji`M8lLVH0Ui+RLmLTk2~ zm`zM1RuM&n(gH%~{=cc1LMFk9b(8jxmY0+1rc$n>{Jc&ADbM|n$JfwJ+B`216<*Q( zFPP!y_1RSFXiLuO+ML{(lwdamx*lZgjLTWQs>0fCH#O#1i^GA46$&?5+w7*gM#~Pp zX1yBN5ebGvR>t?LD>M^d4^){{lIDXGZ`4 delta 2218 zcmYk-eN5F=9LMo5aPjhRQNrXxXk3A`5)?$Z#j`w!5A=YTg&|!pqk+X}3TxWxy5(kV z^oIjWa&FF^W$O>`a;-H>-Tb4Kvn{t=WzDARX>C|rYyH#v^ZT9KI^%m^=bU@)`JKlf zhhlH-Ok67n95GUySV~l8n7xQi3-};y%raY!n=pb;;axa|cj9R*!Y@(3{{>4hm~EDW z5$50(z#XImPSI)T2pIHHF zM)l44sQ zOQ?2lU<5xw4e$r#P+9&GvrOzm4X7ViVGL_<3j6Ww64qZcEn@h^xXc+v&9v8*ci>ve z6L>z&>>bn=?dIs$#x!LGXRe@l?*r6SC~>K8Q2;DE^3#W8*UaFu&&f0{2s&!9nQ67f=ICTp-g#=6BRg zR+ak3Q60|W4xC4xX+05c0FIzKn8YDGfjcp4=kl#e3y3A;t6 zi;9+Vzu`gLO!+cuWlAc{#&Hav#P5)?S|@i>hiVAfPMdM%Q_gR&mio{Ne{0%MXW;;9 z;D@kY_y05*&Fp7v!A$Ok_N)`t!5-w#-sWQgUP2~iS5Wu+FIQj9CD#^4QT=tJH$c?y zW>5n^=E@&o7tgnIWM0AGJ^q7_pc-C4`nKyx6U$-Pk72EI%6S8|buAomwVy;i_&927 zFX15OR{C4F+xaXe)Zic)H8_VFNQCKUbfXthP)2EL!|?hN=)9{rgroO=f2Qk z>?U+emJ(V)9pqHX^D;btJDD6;y8-iEzS;SIbkzokO++2R&Un{Nt58TN>Hf3gwu{id zHxgQ{BI02pLh$CbM~E`be+3aIdI%jHr7gsIVj;nS_x?YmqxM&~qKn8Pv~5b-rW#@+ zkx3}6A-MMTgbE~f*S2~&|DfxPX%O~+s}$PjPNJ0H26?aBVoaqV8C|zj>Lb(U3K`fz zWG9bjeGp8>@}EviE-F}`IXxUsOn)5C4%9W&tec(>ha=lY#>aN;jdYCc8QnWEIvi=5 a7$0jRhFnE*ZP7?5d8VQvJsDd$oAoax!qzMR diff --git a/rest_framework/locale/ko_KR/LC_MESSAGES/django.mo b/rest_framework/locale/ko_KR/LC_MESSAGES/django.mo index 6410f0b1cd6cd733df113762a4664c01ecae63ff..f83b7ed71b63d3d110a39732d083b34041064211 100644 GIT binary patch delta 3028 zcmb8vdvH|M9l-Ik5kh$6K?slbTVLk6|H-gE9fkKf(%+kd?;_F3_aq@uJDONdW%l#1c-O#V?)vy@tfXHericpH9- zOED);sZv~qGJXw~;a;r82;PM+;e+^FY{vPsU1O?^%0pau5!d5&+=F+_QEC;&@IL$* zZpFXj8eBIwd;b`2qdkFD_#Z6Bs(IN7twg!r>e-9(ytCLSOZ`0+Sy6s|_Q4IsW_11xoR!9_63(^Wz=(GRjVUh<9QJ8KO!Hm8!?(SciLYGd_*?%T|9(g}$nw zNU2+~9HrfaGV_N}w)P;(7WR4V554Kc4>v1K@QFWnA=qKL&kG%F@P|n`RTUKH@$`S7J+Jh(y{3S}i z&la%%n##YpAQLGo$*yoK*3fp+ZWrd&M$3Bu3 zC1>7XHLLLpl%uOGW&dSm`$*wx9K>3D%WMA$TWQbWtqs_Q2c#c%;$J=Yu&jq^Uqi|6 zxh(Jd*n&IpBFgm~woQ(FGj7BWVqT@7T&VyTTCoX#fij_gqU^v@zBO6l4$qS)U&&?M zhWQoT!$)x+zJ{A{mR9O{+<`-Q9eXiGrrss~gUVx6sw=Z+`;_MgxP|NU$v{b_$57e{ z^yBYPR^Lq z*@;ak6L^Y$%t?)-yyzq3bkvt9{pRouQdJvJa_1oC;twbljD_^1=dKc$T-$Xfz2MO5; zDZ8`8+$^3!Lymxjx<4?o{ZjZ$-7Rz*-apBe*~EH6eq{C%QW}W|2tF9MkVbBi&oUWso9)HBW*%PxsSSN3=;s+aVn(g63O`w*J40a}W$MZ&JB+Bg-yL_n zQcJ^nwWzH8W*vG$!DzSixalK~GAlY%t_=9y*B^CPe!$db&!OniI<>3YA7LH=vt71} z`Q6wd{a%sbE8SxRn60}2Mm}x?{2h8r>m%E(>f*){BhqdLJN&^e)nQIot+wKL^QzkV zrrP=jz2e?AjZHQ6lsVgsNVIlW*a$`fMl=*&qql^3>84&IsHZ05&R9HiCZ*Rt5o|xu zx#FJnT|I_BkY2Ljq|Y)-D)Of$uGudQYbP<9IXRO4aY=5@|BiX}qJQVj&ODoPMlLxQ z;@0Qo4e6~F$8z#_w`|kdan8_lR;Xm*T6-elj3(%(opXIo|4;3KK6_xSPG_%cJ2B>* zOK7Km%zIk@m~$qsosp}~WXd{RwlMwPvY-2woOs=NVO&pL9?T?%O9?dMX? zP@nZg)vDa7iMVs}ht``_)$49dgG@4ETf=t$X>DIjQF8`{^;CM)86M3fQ&SUztZ&R2 zO=gmpoa9xVNsgy~Q&r@P-Iy8niBadmFiX<*V4SXBKS2gD##@wfmpOUG8Hs;0wVbo3 z|DWNt-yCnBO5A+B^?r3@nY`OR|GMmC{}p$=lL_bSptgUMvaVK_-sT=73w*JL Ta4x>$^bITf#3lRuRj=`1FkPP- delta 1789 zcmY+^Z){Ul7{~GF){PYy49f;(Lns~0v94?#j6oSt*+hXsB+MDzAn8D4T3oU=(fGn9 zj2RMrA>QGpW)Ky8L6(?!eQS&)ifDwm_)b|2Mj}alqshz?6aD^fOQI)z?&qG<+url% zIp^#Cj}GN$LuD6?vX59zoCuhuak`8PrFDVXI!s^$kKh{2VHjQ1@20U1Z(tmM#b#Vy zVYUZTxEDw9G5iKoW_b$*&H89Kf*p7jcihTHG2+ozfQfj}38sS~k)X(F?7_Kr~ zfhmmOQ@9*oMh0tdU<*#5p1Y1KaTc`%bC}l)cP%S^(T}QUQET}os;AS~g4a=N`4`48 z`5<|LC-G^V#8vo5$x3?NM!f-VIy{3~%EoH5?RdPJ`ETWBiiY)g6XW=2slKj;yr8}p zH{-iFsORt`CK%R1oJ1wpT|A0$hItrYN44KZ&GdRQwi_>(ycec5L&GMzt+jm*)sY`C zf%B*l#Fz$o4Eyjr_TX>$1hz9>{rDO_j(6~Fj4_=Pcnx2|XC5lmw73-c zgWPE3=TJ%Y1@f_bTr?AvWG1s^3Dk4@QENYndhTO<4rfrmPx2$)VS}h`b{6$NSGhFf zty25nIH)m2dFlXPjG;#UJ!{vRc#lc z!tvnG9R2st1-0!abT(|znr-^S$IUdx+>4F%UT|a3ac?z6y~DA(KviMt(@5ds N`NG6lVd`we{spIM&$j>o diff --git a/rest_framework/locale/lv/LC_MESSAGES/django.mo b/rest_framework/locale/lv/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..2dc5956f3c07ccea0842db3d6cd6549f1f0e9dc3 GIT binary patch literal 10841 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 delta 1849 zcmY+^TTC2P7{Ku}T-pVJg>_lCKw-Gw=`K)ig|c0!pe3<2TaqSia6k&!1q{1TA4ryJ z;sdB@JgI48V$y1SXw#-~Y)qrAml&gsnzU;0N#B}ijfuCIv_}1ZGpoig`S_hPJItK( zopa{b$-f>=edEhIrHB!-jErR{W#MyK{2>arC{>9?XkrHjF@|#gJeJ}*w&Ls9f?r}3 zjjc*Oh{GuF*uV&Wf#XW0l;5LNHy4iKE_@aD;kVd_%{lJxM{$h)E9k>JClR)DZ7i2^wu&cnjr*+t`2=dG5?dv5Ec@7{XI1_kV-~ z_!|~rN4`=e7)9wHK^a&aWuPb1UQA!Vfj-`^exy;32LDx$CI)a6O`Jdmt=4b{Zlr&I z8y}#53uR#6qm;PR>%MRb<@dF8|1`={UBNEAi76?)r_j9+L7BmE+<`Bmtl zg)xkWa0<8KwX~bqPX8zT&QPj`b!wzv;CBbugYw)tl%+o7r~aKZzT!eZ<`yfpA4f2b zr*RFlDaQmpiwE#FE@KbV8^uc)!{6~q9Ak$*j@R%ptYVbM@fobbdl<&@66$}5#(t)4 z;dva#JnAGfnZkqkQraJIoqm)BeE~niBp%+b)KR>NGQb|za}TcLKKvBNFj%3~Bu=CJ z-^VE$vi1X}QoHdm*5Jz+!S_%qV^q4kdN&T!Pa^M9SFsy6aS**sZx=p-^1Ka{&3psp zIlrLnkq|pmmL~NSjTsu}a0PRDyWDsJTktye;vL+JwVbIVcnl}-6ST0g&OJRFC`f!RE%W~`q|6dq$-ShME1Q(abWt2xXZ>aON<>LHQmy0= zDZ%DeTSyLr^PytpoTcOxJ0VB8f((&mvI)va`SP(e&X*3HO{pLDl<26ZNpE<@^+bNa z&dBM_)DwBjx-h@lK9GOV&{qmB+X-)#p}#J)bganIpBMG&7GE%sT$)>$F;}ek^0b*; zTr}g>(#&+5J?pz<=yv~tzUY5PFBFIL`^6nPCy*NMY7KR_hTF~X&c1MGbBM?tvl7YH z*pju7h+E0Ur9Lw@yEtzp%%Rz(xkPeq+U!p*&PT|p^aXn}@U)@Vg6FiiHe#=DyP9RM zmIn=euOgtU&35~Qxtd|ORF4?;`I=yc9lwsJ!^hfeB1xc_g}isxwn diff --git a/rest_framework/locale/nb/LC_MESSAGES/django.mo b/rest_framework/locale/nb/LC_MESSAGES/django.mo index d3dfe100a7f558c9f067d9a720b6889c1a501b9f..d942abc2cfb380462f026a8dbfa09219dbc7b2dd 100644 GIT binary patch delta 26 hcmX@@bJ}M^v>30su7QQFfw6+2iIu6v<_xjN`~Y;C2nYZG delta 26 hcmX@@bJ}M^v>30Mu7SC(p^<{2sgWxX*L&2QTI3EeB6Us z_z0HcF)YKkFay8EMze(dNM!>j%D7s_aU;Hm>o8@ySvNM}Dm;U$@pIgaGiR99VGru} zQy9W=ydCG=VwR2ds1A0Z&PRO{W+$oWfn!*Wzaf8CGBerJ8q|pEQ7`JpT6_^3@JnpM zxwo2C;7;U}ji9bOgC+O{Y9=S~4qP;g_waserBaF8Q4e?uTk#!Si&JJNAJ~BNXtxFJ z2x=q`p+@!sGNpDgXio;e7cl%(&aXwy#Ci@|x-LxUfpIEJ@dW1K7&bl9>;t<8nn-%27Eq;_19Wo;eUad~4u?=+J&X@ZAIzjr1xm#6OW` zw>b<;Nm7iOs&>>1p1@7`40hrb?7|YJX)8X4EqEF4!F(RBBn@LTzKky3K+SxjjkoH7 z&*8oJH7XZs$q?=9y?7fQ#&SFz_zCKPf1qAa$3^T&dl)nD7&>?YtMPSQjo+iTZK31u znuHBf(FJcJ32Q%KJ>EoRcWq&?txz*Eg1T-DH8Y>$O1z2cP(@L4+r?2c`5G?9_fhwK z7qoxJ1>~O<@D@#74QdIVM&0lVrs6o#X}gU4*{>W}CClX^&D3&a5^Ota%@3hw?i{A! zMb!7;11!UF)KX4J-f!uAUC1k&6}S}BXxAfoWKF0f>%|r5q1OI1s$(~hjbJmDBy*`S zupVnT-y3)mHPfG=2Jkl~)bj#{!R%QrDyw_29iPQ?yoOWpI%xm9RMXzY_YspmINqsM|knN6%)= zPk7Xn0DB1ji`f=}DYrI4d+0twDZh$_TOP0RVA6$M`bsmHC?O* zrIFy9pt8jOx20MIp-fS^i_kU+sX=MIuxnaF=4fJeX0g*8^Wsi4HtY<#!+rZ5H+tAP zG&D?yy8+FzU(psm39>?J$u{jZ;I6kSQrjtvdOhUlre`+~|p DZs87K delta 2136 zcmYM#OKenC9LMp0?L1m5EzD4+wTjb%t<<3~wuMp(EgdXHYRjWmC?d3VA~a40q&pKQ z60xb#lsM=L0*W!pjtnkHj2adaUl7^&MlmrlE=Wy=q$XYX{oQ*KPx`-~bIwffJ&*sn zGq?NXuH3Ci;BBK!5DyS-rDiiY5aL2PT4q*{$1sk0d>Aj|a{LKv@L$ybN6O9Wa1Gvz zJ8(6QU;e`Jbn8 zfc9Bjfq!EJD;N0#tVi{4_RN`$Q_&4y!*;xi{MkGgjWks0&$trxpf>ElEOz6En8LrX z1rt>~4ELkHH;Iqn1=LD@gUxsc7x8?nU2L`%n@~5{i`#J;H{o}v8&)kbyC0XM+8aHtIg=%r#*hm1UU1IF4czU%~*+BD-LxaSfhBw%cwv ze?b21HW%Heh)SOdZW6-?s@;rhF^SsZ@hJPRkzaIQyoPH3hDxFZT!%GF{kVUYF=Y5=qND87S9yjsor@1s(rLo*#@__eslnL*8T+O<#N zW3(^g7bRxD;wswja|E=v*PU}1ryb#DvK2M4VSEseA-ig`IVwtq^Qfiy8nx65*pFeR zxf^%lFkZktIFFCx0Mj4Bw@}IW2X4g%o;HThqn7$h)cuz5^u0KW+WK693WK$u@gXeY zYFtXD$X?V9kD?y%F>>PUE{3s$e~M!{wqrAH#6zg#c?P}H;XY5q83g^~xLN0aii)zh zfXe>wQ7aOv_rI7#txO8ra0)e`kFW#ppq8?!!E7aVpdN6*wX>+DKaI@IF1h|2SjzLQ zNQJ@KJZc~{Tv&bULM_n}~k7$d8ft>%l7nUp#Vr>PR}?HI>cu5EHJWWg?p?tg30KD4bjIS@1tZ C(9Nv? diff --git a/rest_framework/locale/pl/LC_MESSAGES/django.mo b/rest_framework/locale/pl/LC_MESSAGES/django.mo index 8af27437f01d85967413cde8c9ce0b58231f0f36..99840f55c0c90c1b3946d92ecedde7aaf2086cd4 100644 GIT binary patch delta 2495 zcmYM#TWnNC9LMpYKyPT37D@qufkLIi;%;eaDcx;pD~Qx`D@Cs5xIHaf+TGeMRG?vf zK_DcGYVB(XBiKdDvF~KW|CTM~oG4URa#0T;6gbiN4zq3a>VfHgKXE`(T|IcjS zX#2fA^-bZVvxd?|eSq3B(U_AsoXrR2Qm!%c@ny950WQKHaW3ZP8B>DgsQ=%Hv#|s7 z@dyU+SzL;5Vh(`V-p@SDZ`RQW;x^O^PT)Fx8^btZYUYLYIE#L( z-;beo@-%8^=aG<_QNMrJe?F7#Ph>odio|L@l)5%d>4ixe58`uJjF-{kbu7Z$I0>`p zkQI}I4`U&63{0c%dgQC74fURG)G<1WGw_(-zku5CD@EjAxxUGOb}(mJ=8Ltc{$|u+ zO5h4Sh05{!et!(Lfzb5K_gc`>--lY@N%SIulj+|@EhvlCEy28E;vb?>#lUv#M(y-H zoQcFQ_w6#A+8{9ctknivL0y zhfxz=L|vy*R7!53CK^Lc6d)WCti~tsAa20Va4XIuY&w(&a5KJzt1x#?=8!gH7yT31 zjHzG!MkOcWC7|89OhNiU=xXJ5dokj+Hoy3h`~!tqIvoi0e_OeGRV0eYgc* z#Xa~p?$`a_&9}6YkCF740^$?Fg;<4A)DB+6a=eKg7xRxlUc?n>puYii?@y!N_ciJ; zE+D+RZdFM3%w87-&cZd!!EaHA>J~QPY-T6z zrUNzM5md^~AbT;d-vHL&8tlT!_!?&82!`X+>fDwiSRs~w$2qHfM3K``&4Qt%UM;eVp;eSq0>anBMS=h5h9U;=8Dwdt+dO{qFs z>#3~FD?00sQMDW;fj4Rom23Rhp@7 z)F}7ZTlb?HrRajGte|S$Ds0^=yZPMcw{3qsr*)`lzGWjOMi{@tE94$stc?;rm}8icW!-NIyE(Kk!?=6NgGcL z+CFD6y3acCL-wFMkc=hbHtzJf$$ls5jvSxbK6#pxjJom8SiIYGy8kuPUll(SsSX5d z0>R}rR9Rb9vm{8#Z*h`?fwloBp6qo769cujufvJP-2Jw$PxdTJ^zKhIboV*2-t?sz zzfGu2zfdwgwK%>g6pVx`Y+0FAvo2g&uC_NGtP54x;bHGVFcRWPy$#t~8=oHz{r{y% zI5_^FL*@Fu|I(`Q@s;D_%*I>&=?a)I^WF;HyL{x!lKd%otuePh(Ldk}r9Ua1wan7B z{Z8^gk83*2rGB H9>iIyPSvju4 zeC)!N*pDmlMa;p|xW+7HXDD>gaLY5!Q|qa}gl+gO_G58@+5I?*P52ge;U#Ru+S@XJ zPhvOqV|Wk#jsYxOkQv|-RC|kS%50c|UO0sfcnX6Q1zT_fPGbzeLA|i(4zoM40#%Qq zX3~jT`Z3QLul*g&qWufh3VekM*=5vw)RwI1+CA z+VgwlXP3F?J=am&r+}A~VgOaI#Sn&3As#L!{u=oy@5awj^*Pigx`p>)*}_b0drDDPvN}zWT?_cor+=}R;;7A6o*j*J%G#bP2^{vanY%nOHoh=ZlYf7a5o3R zda(t!;TC)Yd+-Ks!gWkro9`$-fat0MhXoy%%EoQ3D)4hNcgRibyRy4+i)-He1C{~VHQV#{brr0V>O6G#ips3kg#8u3}L{UWM^ztD&I1fHX8MX1fU82Khy z4{C)5krQmg$RgUL*M1nw=--Y|$ieqfo9QgZ@hTElt7jVOFo6nH66xFah36^waSCI25cTEy5*0%q@z9J$kkzvZWNem3 zE&UnPz|W)3x8g7l`pMDs0e?KD0B$7nLN{r+8_4QiYp%d425sbr$V!^(TDO!L_8{fQ z$r`$*=k|p96e>RYX0Ib_WJ+2>?Zja|XznoAtCf_<;C@MRy)q+r)1zE<&XlzM*SMw3 z4pMLj$n(86t?j1tiJamf8`(Xj`h!BvXULu)kEAc;lq*NYNVdAA|1Yf+wI>t~rEO$|Q2Rouovh~yj;mHwzS&)3ixj-;=ZmO6dgC&q^+o%r_MJ0|z+NIK1XCdS*y zBVI##Y1yBN)lJT>vAw&8#$wewW3jN)>V%v|XJ=(N)Do+As;b;#b1b4_wVFfC;gGX$ gpWCi`LNz=S=9&7Mru_7kVEOEK!7KT*fy!?Ge}IML2><{9 diff --git a/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo b/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo index 1dd7287f36ab955d7ab8281100108cc84f22c8dd..482c07cdb98db3dec8f311ac8f3b150e21096025 100644 GIT binary patch delta 26 hcmez8|IdFzv>30su7QQFfw6+2iIu6v<_xiEf&hMq2q^#n delta 26 hcmez8|IdFzv>30Mu7SC(p^<{2sgmRVW9?N>{!_NSR!)1~P^)6i*U&cdjzlI_=Qlet`3FbtxH z3ZhZaQ@;763A2~VpqHSB#Dwape^0$c5QIVW(D$li+|Rl9cXxNr@1EtvgfZ(g}yT}MG zqMC;^$xA=M3r!@8R-4ELyoFKxf$9fg`)YzO*R0e<9 zPLUzzAsoahq&6`*@*?XAG>3>EU3eVN;RHU#n;6(7q$)Cx1~ylV)Zi3e#05NyjVy-o z5vn)%9rs{>*ObH@gfp)m?RC1)jn~m_mw|X)MQg$Suidtiyk3K@X>> z-e^QC8kocDcoKgjxl5RARIlVv74jJQ=H&yU+GysK(|88e{5dvag?BaM9#nsP8wYS6 z)s<{wQ6=&KRicxqdT1IWXx%UJ3Ju(X&id7!X~R~bGw%}Ed9Z*X{E0`=TRc*_aYJXo z?QL*->-9Qc(BI(n^09QsMzih{nOJIcIF?Okf_g{Bh^I4G^{_FTIG-3wCDVFSJi{cJ zPNuW1@k=pdxH!4_iMhm8=+(woMo;07R=X0p3thGjqGg+{Fl#%!5=9v!_7O%+uPS`C XH}K{iGymbppPq?|+*DwZ z$d|PuZ6e~@AaW3|;}OiD2aC)iJFy+>@FrH{G;YPk8%3J16i;Cs^YH_o$636D$2N%! z;51&uy%tGH3yo(Mkp$*jMY?bZJMcTku+b(G#HS0cP$GBfhfxQf$8Fe9#7D3f_u#7q z=dqi9r9B&P3|X7Z*;68`XcQFl5ETY6h{O00ub^YI5LM&}M$la%vIWQR9KOX4bTZhC zw@~lkEN({!+tiD#IEZiXHttRl`!bCwWNz{Cww@r0JMaZI;Sa1r2ZMDO#=|&{%kUSn zD4D}jG&@Af(1-e{22lrogthn?otP@*ZGBetxCWbXABK^|%YDqp_sA*9Cv@X)G@+eY zbZt1X5KrR;9Ke(K6UirybYjC+Mo7ju`!$YWx zbrSWFm>ASSZlDf&2hZX-261UQ+l^;%J*r*VnyJBhp)yBk*m&>`_v1G_iY4hod8-Q= ztDLTCr@LIaD?RQChl`)dA4(*hE%8u)A{t7@;vUt~6YC2ll(#1yNhBlPsx}$xtLN%k z7)U=@_jrl%(NboNSp3+!Qf01XMrK~CnMswoYJ@Dx2v~i_KdaSvZasjVMyGAS$k^Jr X%xSN>mz(o4{k6E?l%vxAvdX;wT%wuh diff --git a/rest_framework/locale/sk/LC_MESSAGES/django.mo b/rest_framework/locale/sk/LC_MESSAGES/django.mo index dda693e32c59738c5bcd1218feb7157b3a5a6078..83d43c8222cc9a35707717997913dba52a1ad0e6 100644 GIT binary patch delta 26 hcmX@?dDwG%;%2Uq|A delta 26 hcmX@?dDwG%->2TuS1 diff --git a/rest_framework/locale/sl/LC_MESSAGES/django.mo b/rest_framework/locale/sl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..9ac13843f70a0543b748fb73af9bbb41eba8e880 GIT binary patch literal 10390 zcmbuEdyHJyUB_<{H)&1!AV6qZlAdmw)+V0uuI<=n!zT4J*~DINy=#}4w&v{2*_k_Y z=icePcgEh`7!arxm1F)WDt}ZB#TDWY{y;)Pt%^h>zzYH?NDv`aNIZ&=C=wu1@mCcI zKHqciyu7=%ORo0J_dd?!cYg2l+kbpy*H;3rRoYL{&O8tVUjko!kRPt!es>Uj6ub!< z@Xx@fz<&jw0C&G92tEkz1vUOS_+juexEtI6E8rKwec&I04}sqWkAwdXo(A{PS(t$@ zfPVyj61@LMg5V-}0z3kK6?_)_TkvJ@eGdo0EO-gj_pg9wz&F7s!4JJR2p$2S14ZAb zK)sJk{4%Kdz6m}9ejns7*z>-E&cmSg`y8ls>fllE>)`X?cfb?i2j3qARq!()uY&8K z`h6AL1HJ`{k8guN4LO2j%kvY~}#ZkAtXgun3ATm%#7c7X%Ize;1hKaj*kUf!9IN`}-iI z1b+#B4E!hXBj7GRq8h=+z`fuh@DTVpQ0x2#_!9U!cmez#C_R4;CP{yBUh{_J0DDUx`5J<2ool z|1yXugWm)n0{;NS^n-7MqT{bX$@gzS?eD!fBlSNAYF{l7Q3k(MzW*91{(K9Roc|4! zy*-9-)$b)x;~O9%2z~{W-h2am5quMT5PTG7iJr$n@#QH{^PC6Ifxik$FW&;$dhib* ztPk#Ev!ee=P|t@zrVK8E@{89%je8Be5BxN3mWCVfdIb1Oyj4G~txLQdT+$m|&(IK? zzkZfq=@rxZ>*x4=iS{m<_NhH;-{Rq?Xu2fNQ~s`y>1VkA1=ALo-OLR+ClHX|> zEcMq(ewS%AnsntwnlAB2m-PSL{;tpw-F}v~SiX~dPL%fsl-z!vc0X;Eh6(%FC;ij) z08KjfJna>lF7aA=U}<&QLo{9Dz3gRhIc|2*F4G>S$rm89V1Xw6TA>}H$tGl1lG%CM z98K4Av`hYO^z%t>af@F5diy!rgEUmtU&r~C&gqhE>yj_Q<@xJp_+6r%DIR^?dw+R< zpu}KmBaFMf{MwWWGZQDdv8HdMux?h8Byu*c?%bo+Ov0MGLx---n`YFDd3$c_LKiN~ zpR5MQOy<(QON~p@BsEE`)=N`Y*N1|hFwR|r?~s|g&-cQKk8!WFvgKQk)t8gDi-Tj_ zx;PJOHV>27)EKKr8)fE-OP$|$H%>%p45X>*azt@WHTouqgjn~jdWdb#duVaG<7yOFKA?3yW`P4Qd} z?s=ZD5#}225&oDw$!&D+6I^Mcu$PPB!)aC=?A0#Zh_D1z8k-h{S`8f%7r)pnT924F zI$?Y{a&aSXUPFg%YHPVmvz^Ap$?bf(`&fF|QraxrN&KpCbn8g7Cq&~QZ=?LScajco zS+b0V*Tz&*iw$Okq1XsU7TYNdaEGCh%d+jpLC|O%EEqb7sVwR>rc5n~voOm;lpZCI zTrM{4GlfNR(~Gf_tcDaaQPf=PVv3%RmvK6XC7+xUWT zlQoCl7){G9ej)41(}?)(=2%E3{QJnQ54sarvLqdj7CXjxhUN?#n?-?CyhUsZrYrCM*Rq?k)$E{%|whE@D4=wCx_bf_&%xuFbM5NlMTj ztmCzI>=AQP$VY9aipV`R*!^h8sE=f$tYzB?h9AwzfpEN}jg8j~sf%AoWO?ehouY}8 zSC4H!e!l1MGF?JV$KeL5!OLzfIBO#%2pF!i9ukmhaMtC`q>kX}#tj*j&)cFXxuW#t z>?l6hl3tu&E4)^<%BLO4Z3SW-ed* zsKwNT%oT-IXeC&d9SRRg#K~NQ9&1qS_{qt92nE%k)bA@H+1Z9d$YsrUZEnL$j=(+O zZK?R@QsM_LELbdM9V}|S%yS!W6Kdh}1#djTc{5G&JkskWXVY3USn@uwaA*X>#6&ig z6)pL#1=%WRys9Z!My?q|fabmdZbn`WR9402*j8fjQ_Z_?DMUNC#A{)0S2914;dr>s` zO&eoWxhbgeR87gvxyzmhXq6uRAHzDhX$)wb&U;#J4Po73Soz7v)UmydbE{A}nfneM zyo!x9tRp||cRpaRj30I8)U*7~6LIe@1OJ-^7h=1DMPV3G(vUmy1~7lc| zCaE$REI+h`?#SSIC^HSsF?0zRx=~`4_7oA3gdF`gg<`YnTGBH;--Q`v)DG(-mGc^l zb5wU_U)D$LURMhja|9eMUOZuH=KW!c%ztXYbZKCXdV}o_$k;pH#cLRH`9hL#p3}UXGV_)&8x2tJ{{uI z0X`kv`|R$qQbZgQaibPm6K_|k7=Dhe3D;M`)Q*=awth9;BoEcadk96;vH4Y?2o`MD zwyj}45>vPNMjW<#)!>qC=B{t!#I#UtPZ0Mt7bRx%CSSrR*{Rd$`(hfh5mwxM!{rIp zioPThwrwqJW$h5!@WfyoW{Ft|qvA~ry$hk9b@Xf`w{AGIZaM8x#>hk6Mz-IwgL=if zr?d?pE@XM%rY*`MXnL>XDQ90@d+SE*I%R>PN9h7nu`4kFowVIG#7LzfvCXjjxD_^U zt6hX0xujb+a4-Fx=PRa5woF#8oAk$L#ez_JSS#+{ZO*%qcrm8_!e%1dY~yrntM6{z zxMK|?4x9L2rCfI!Th=YSJPE;f=sH=UCt;y2FdTiTCe{2NBri`irwiyz^qt@n4{4&Qr)oKQng~6bCI-*-i?#OWpzG$r; z3s3N8a-7*i!h`%3BE~Bc7uiXyDuyT86Cf6&+=jCvN+bjFb(O-(;MLX@FF(CccjkL|dwKOo@VnMvjE&CEBeYehSA*zYVd=)}Hd>KNTF4k^Y zlY_uEZwY+uoZ_EMb}$sv#vl+7o*hPGZ8)sDO(pe~^P?ZeG*?tNg^V+#~pRB;`7J+AM9%88ewJ>;tCTw5H*kK0x7qOD4 zPzE{zmMAGaWAy$I^GpbQB)nnAD6mB>oU zsz^%C^>Wne-MSH+YZqAz3ZNX!5qc?%U@y3+QDKA_nDiA%V;fvlyl+#9*eL5FLucu0 zBa@MK>UL@H4>D>Dn9LAm-Hkf zW3qjEp0?nV(Jss6%MU5`fyyJNEmab@cF+BlPr6!l z>fT38>3=HY)Q+;N0aYq=`M`bqE*+#Td(BEIB9vbh{1DBMZzGT5Oo#v9XEc$s5Qi+hla!?+bs2O?|iIJIY0Gc-xsD82Qt_{>1wROx?cmD@vwL-4| literal 0 HcmV?d00001 diff --git a/rest_framework/locale/sv/LC_MESSAGES/django.mo b/rest_framework/locale/sv/LC_MESSAGES/django.mo index cbecec44db41c28528e50379a8db597b0f7bb909..232b5bceeef9fb4c7b8088db98509889225297e0 100644 GIT binary patch delta 2317 zcmYM#TWl0n9LMp0TY5#%wn!3rKQE*Y;C)=2owrJTMD!wjNR!aw!6B!7>LAG zi5Da>v6TlKiAdl{jSn>#i9V=EVoZ%uNsKX#C@&`J1Mx*{jNjkv5Ki{Y=bYKung2Qe zb7n8^dA>I}SFqrWQHH52sNET6r|?7;2gk=!TcD9&aKaTemnBX#;A+ZKwx@u?gSCcKi`L zaoOEw)p!s&Wv5ZsP2)QJ9yOEm_yCsO!*h7PbW1yOoc^w$ zKZY8~G1SQ3LZ;L{4*K)K_r(l9gY&JZnb^cZsq4d}ZWyPr7GJ}qcnKYx#X|fG7hpOa zvSQh|0Sl02U>$)wkV~x(b)ON`GI|z^@Wr5i7B%1th2&qkzQzfSpma&4g=bhJDxjXgBr0|9VF>?5cBIv>Oa;0L`PeoN8o($v;(65e{0RrO z|JORH8=ORThF!rn`~j7tLiUWdPdirNC~A97qNey9hVWb5g8AjFaNLhlg+Hc#!%>P$XNOY56E_+>xG?No)M zN!COv2}+#iLWLD;kNBTd0py>!^FF+EdC!4^_!k(e@eeTXzm2n!LRHJnO9TmPJ*b zR63|yY?L+r-b*VPRWUDqG#2+F o)y_^u;Km#t;U#WI5wE%v#{1=qvI^pfu@N^C_H5W4ANL#o1K|VzUjP6A delta 2137 zcmYM#e@sArA>IS>;9-PE>{1$b?qUC0{;!4!G z74;_DP)q-WbIP5c!6lqOk6M9wRLU-+?xT~LTCAW`iYfGP5KHhm%*AOW3-$)a@g$ON z`^Nbr@@JPh=ss6b#zx%Q7IlSA^)2BX?Ni{)OZ0^L^p69mfaHMb|-2) ziF$)CP}lv09xkFLSh~V5F6+h+<0;exrtvO3fz3F#g7x1`=PD=kraPE^Ii{WCs5hN- z1|o0U)3U05;!7yc(=ontNF(Y9(4w6G@{!e;#$C*WLINRB@d{ZO4nKef_oI4^1ivqSYvFi5(Gv`9+wAeY&ivV;XjdpZ8p~wQ z#`1I5wlycR|HNXe`}a?b4;}JS`=1^=ba-sk+jw|lJV}hW6OAJi;~leQWt9c9&#bx@ F`VYHh&N~1A diff --git a/rest_framework/locale/tr/LC_MESSAGES/django.mo b/rest_framework/locale/tr/LC_MESSAGES/django.mo index 818aad27929ca67ead8eca3a42804446d8192935..586b494c3235f43b2b20e6ebf1f094336286b3ef 100644 GIT binary patch delta 26 hcmdlMv@K{uj5x2ku7QQFfw6+2iIu6v=1lRkf&g)*2igDt delta 26 hcmdlMv@K{uj5x2Eu7SC(p^<{2sg^J9<-7m%5hfrn+cS_sPs)7M;yZtBdSTTUA}V ztEBM(iA0LJ#1dtfMm20AE??dWUnI;22@(n-vVZjR#*pMCvA#^8Q5uoE-zK90fb&s1q&2LTtrZcnjxZzeM_A z8PX&>Q2Xt{FY!;*lYD_+VZvbMVSbrL;KUNt0lvpY*nuP|MI z?(7%KQ&#&;>`nV~)Dw7#S~44nj$>eWB0&+sQ1qaI+c6mrV;?++R6#CcHufN8msghW zkRP$}kdBjrdVMl64Rfr#6rJQ1s3mSprvAF}J66YMsQD+Qh0;&L3# z_9(y9VgQ@*2Yib=u!iGm#@DzK>qd(Fi1$!m>2H{=0WYKWbB(6Vcpcps&M-s{5$r}6 zjvd45aW$4>6IS7U%dD}ng|y&$+TWqpcr7EvfopL>pT zaS(MVzzUqf{L(^DLc>keS|@Nr^noLIjKyApE<8aL1wQ)wqAVj(tQ z0Y)(i&*40?h+Uvj&A?a30Cz2*zZa|U4mMy$vRN4hu>$|bg*bpSF+U~#dowPge*%BT z=jg_?)OdiosPWm~jhL;ap$Xfu1pAP;edJ4#y3*o1PD3qJj59EdbFc>k_zb6FK{|`! za@2J}oP?)QnY@Ad_zF{5-$oBN^Wj9)1b^ah*nzX~HfqB3AI-*K4ys>{+R3k|)US8! zbjHu%AjYqwGH?@BvL~o{G#HtPV`+@U0D7?zGjKZ¤^b_k2`6jE-x<#-=?+Y`Pt z&pXudNoEocx>5an^r0VB;1VSc}W> zFUQZQKp4l#FM+KO~pGK4V=*8!_NzZ>Wt83w% z_zT{|d6-FlSj+yv)wm5;;s1CHD|n`&co*5MHBKh|xD|EZc?@7bmSQ&bPzmaB0UpGm ztZxr!=!U1L)9K=bsK#Ze3xlZff3O^%I=^QZnDO6Ywa78B6{y;_Ayu>(rr=f7{CBVj z-{UYGU&x;j>zj`T2iL+F#3(A|uTc}O;!lAr+F{gAE+Vno8*IcZW?hL}Q2l#YfqtS^ zMnX6pW4IUxa0~i)1R4}c4-G}yi*xZZ)?y-QIE{6v06(BIkiu;9u^N^7eW?3lsD)on z?+vG9;%w}eA$HhTjtn_4%w ZwKjVz+rptLK26TR)TVH#_r52Z^$m$t$A$m^ diff --git a/rest_framework/locale/zh_CN/LC_MESSAGES/django.mo b/rest_framework/locale/zh_CN/LC_MESSAGES/django.mo index 5ba81a865ad7aef3a93f1a08a8d83700339ef844..00afcdb9a64f237048f4d2880683e581c708e5e8 100644 GIT binary patch delta 2527 zcmYM#e@vBC9LMo4P`xXD-rM;(}mlUxlj(J8Z!Y8qlF*i?RXK#V|tn~qj4(g^A>uETVE z6m#(@%)w)rg1=yeF%dIBWj-A_+%3bn0Q+z*Cfs1mO02|LcmQYPH@E~dZZxI@>rwyj z#Q>hi+i>hn#tg$U)B`R@^@m&|#ym$w10Kgh{1f>z*%|RCEkez>4E05gSd1@YIew3o zIC_XNdAJ7YGP_at9l&h-7PXRtcn6Lj%J=a7W)YQqT!k8-6BpqLEJa^te86%XOS{@@ zH=|~<6*aSi$dZ~n?6Yrlq?*jEuM%EcwG zBbn*2am_?j|8$&;)u=Ce2+L4;tHLw582yZ{SIopOhHm5yfsAHJHs9K?!I1}e#32wnsdCaD z8N`p1P+y#ldf-CTCfp$TezTd1mToI*$veFp4x?sp#A~0y8MHsayYU~?{pCEBtn=KA zYzwmswX&~z{)pNenQSQCUxg9UW7bm947MX$1teD(mR*;VMG8dGKSHb3K^aPnAZ((MNFh{|4t@@<7l&FR5n0DYyK^Z)y45gg?ItJs*^q~Qd9^D7)+W0nm}gbBIbkc* z)?u~U9SuRt4sElxIPKx)wvZLFTb=MGyTR$1npx>z;%tj<%p96D!VWh$p~mJ=lWBCW zReK&8xubMeZhmoY{tT;NW=Uc3w0uf>zz%oh*0kH9aEsm1)?Q)-oEFClw1t|iIpKz& z(`q+_=Qg$4%`MTgQ8fu^u@mpcI$w;v-xqy$%;{wRz~T3zl~%W}oag=R#M`kmPhLEE zG&Gl>u0LAu&+{BwXMCU6InRB5obx^B zJbb_Vg z>ahsxa69hAaqKno?9HHJe_$U!v~X7kK8gGAB5uX<0<%`!gM)YmTkvz#@1-}I4dF0W z;Vi~+9@Vi+?)nKe^s;)3JIpS{ju1)fKZbRPAh%eW0Iip<(^KX&0+OyQTv zpA{4b*OlW2+IvwMoW|Sm95&zrrg*>oO=SRUV`gnQg?i8&mg5Jm{RL`dKcPlcv@&ob z>U=+H#1p8g@lf}l!y0@KSK~LB#Ovs-qOyj&B3O@P+Zu5bwjtAKlg>wxKl3=~K{Kdj z@&+dGE!X}S8)z@0rqp~65ffcnJ}$^!>A6t zg1YcCY{iv4RSx4l_$(^bUpr&u`Ci(cScfkmeX<4ABK#KXF|sbuYox+Hv^4I**O2vS ze<3^4qI6p~#;_k7Q7J!(TK)5=`*SLST{M6iv4^_uIn*v#bY4g8s>&qO!)4Y-rG*pI z*oN=mUHB(5P1eKPd50ZDJ@|3dR6Om@&pDUyR?h#1tUD{YC3sN{l3W``W$Fl$EIaG# zv52VXh9wN+cc>R#L3(J{Fp9NIylg;aXe%nUeeQf3)!`$qeF|@<{S@}#yQu5`aVF?( z8Sl3SDon2pU=u#%oJFmHCDe^!@}^9zMU9{p`N>9c2Tr3pc)|H0YLPCWcGWc;!MgRq zbx)wD2R}oFtl6umIlqW{@Tbn-F+@8;3={0m|BBWGo5L^u*N>VOrlwR2P-PpDBz60gTE(pkWBllzYFt-LxhUvPph|=$R$)#1l!r}Q-h)}-afy^zgb-8 z>Vr$&sVSCJ#>~yN(_opE@x_Z+5gQl>0As3Cs}y diff --git a/rest_framework/locale/zh_Hans/LC_MESSAGES/django.mo b/rest_framework/locale/zh_Hans/LC_MESSAGES/django.mo index 396aded071dcd23c423b058c51168a736bea1410..a784846b2d7c96703a6999d8c77f9186efd9aa49 100644 GIT binary patch delta 2679 zcmYM#dr*{B7{~FmpxnG6f}v@y8mKAWQ9?xl^TtL>sEL+svLdOw49jF{x>#!91uq+8 zW8QNdol+auMDbQy{Zme+U8A8aCVCOBSYzt(wKxux0}00T#e?X?tC)c;_!uT78k3HLP}djWqqq>0a5ZM( zMjV2tF&=-!0%HQEjmi`{hH$p@<5XW>@h$0zWyWS+zG&2%cmaW3ixbvPZHZ~{8IMsGL~d(tkp+m)!9 zyo#FH9%M<)IlFz|{=Em&kEMSCY9;bHD0QV6&<*`m24Dkr$1~`}YuF8c!*&=$gRGc% z9EK^#HZTRYMaZe96m_2p)HYg+kKhKoy%#m%!`;Zga(#;q&7gN`^u%0Ly9BkFd^iR- zqjG%3Zr?*qU_@H?{zb3*WSb|{L{8Sqtoj;3ybk>WMi8MYM_6ku5clmu*XASJHq>jF!d>;C92||)Fc;V01U!q+;9p3(&6Ix8m0W;Y z+104j)LH!jV@^?-MaNfG2O}k6kJB!|Sgb(p>ZPawtVObA4x^Uv60*wXciRqJtn&w8 zEasvfoQE245o#0GN}g}tq@o!$pq6|e>J!<7n!#n%1-Eb%euqzCGNaM?b8LOKb;!0b z2T?0~!S*g{Zw$~<5(=jQe18SejPQS zuaVU^cTjsFg*@uMy=?PQ{fh^Ze^sjJaNt}*x#V+T_`E+Xx~b07##GU(_Mm7*R5VjE zXqE9C^}m7JTID&8dP~|9Y&UBXTA_TNZ{?o4VPjj%s=9Xi0^&ttDxr^sa;>6oyH-vA zBc>3ns#PX))M_jul>cdjiZ-Ll7$U){MK_?T&ny2k?M{7&3hlZRhY|CM7-BJ@6wM)c zv#g@c#dpT~Cp0hGWs)(Ka1-T3JfWg=bhT>Hg9q9XfrT8jJD(;!{3*8Dag$)9a-2B=IcaQiC#` z(3_#6ovRZ3H@;`^M*OD<&Qjk}kJsb}i#zoQ9#6=RFRb>sYdqzlUlU%Ai*;9cLar_= z97AW9xSUn)nk7S>lYJh))9b5oE_c@~THZ++~couFY*L;B&P+h zCJ&7*@~jNqN$%)~cl-S-eAVTldtHykbc{aII{Jf$QZEL}(y|j~`#sgVL9ijMIMwZ6 zS;I#;M~u$R&KW$MlH_vxYqCnK-CqAPca5()*I7kb zylka&{PHTdw_;L7<*F5xHU7~4?ujuSP8_~{a$U>$!;$(e!6WHY6T?lX!gcS2&o_sD zNxu*m*LLv3w!Mc!`}%Bk6o>a5W!ATwPKGb7Z#jEBylqFM;e*y)2U;8JBD>c|>g!tf zy#G_}>*@(LhPRw++px9e{P~uPjSqhf@3S31B$ca|A1W8T5SW4xc!3lUF$9&?gf*KtVa%}AAGEvm zV{JLB?OAgxlS-TRPisA`a^B@)uC=z-a!ct4ZMkM^>&MJLEb5QmpL-u&opJ8#oQLOm z&N<)nJ?&F0~u5HIE{O`Z~c4KIvStIU8{r_d` z!Als$(nV%LOrRz-h`K)Ec^vh=^Vp!J-lVaZ3)TFvC~HEkFo7Bg{QCsZ~4s~BOt5fogdY(s1P=P}f=I*+~RUrE$(7jm#{1ohk@+=j3Ow%)3*CG&HD+j*^O>+D}#Z6+-Q&La&5VAt1Jcs!6Eq%98myhG_GFG}7RI zB}PvlBW=ZY?>~I8wf<~T$UCXZUX^{+R;o@xE|q9zKAYXhf-_0Usm&xA+%8*aDdQ7V zePmRWSK69(D*2u%O6J?+>udMlqHy0TduX&#b*}Z%QR$&-&sFq^-ayrs=>(`KpH!Nt zgVg)fp)AN^eV*$yqh-__s*++eRp)alm7|;~^Y|U0Do6Hau4KQ8`XnnK;?Aq{uR6~K z_P9R>l6mfM;i%6k2-di5!G%6|Aaus>1d7tmXGL)*C%nTs5KcMia3Hs#v9{j19u7x# z9~>VWnTR9~K9Za`k~|REa%6n0mHMD}!Ch4x@H_P-9eLM|zj^J%l=Ef9W_P+|GSBT@ g{gls%MmycHXo=5#zHHL(6jy%a%vLryE7o@X3l4nR*8l(j From 1a7ed296395a941c4db0d8ef02855ca079e7ff0b Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Mon, 21 Aug 2017 12:06:14 +0200 Subject: [PATCH 076/730] Update version number --- 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 c0b5c4c04..9da9989e9 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____ __ """ __title__ = 'Django REST framework' -__version__ = '3.6.3' +__version__ = '3.6.4' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2017 Tom Christie' From e389336ad7538b1216e40e57d8c92d178334e2a9 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 21 Aug 2017 14:47:43 +0200 Subject: [PATCH 077/730] docs/link.html: fix/remove undefined template var "schema" --- rest_framework/templates/rest_framework/docs/link.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/templates/rest_framework/docs/link.html b/rest_framework/templates/rest_framework/docs/link.html index f18d64825..fc2320a5f 100644 --- a/rest_framework/templates/rest_framework/docs/link.html +++ b/rest_framework/templates/rest_framework/docs/link.html @@ -99,4 +99,4 @@ -{% include "rest_framework/docs/interact.html" with link=link schema=schema %} +{% include "rest_framework/docs/interact.html" with link=link %} From 0a0bb6a871441bf0db00598eba64222f5bc6d073 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Mon, 21 Aug 2017 20:33:51 +0200 Subject: [PATCH 078/730] Update release notes For the last minute #5346 --- docs/topics/release-notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index f4a83324c..78dd334ee 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -67,6 +67,7 @@ You can determine your currently installed version using `pip freeze`: * Revert "Cached the field's root and context property" [#5313][gh5313] * Fix introspection of list field in schema. [#5326][gh5326] * Fix interactive docs for multiple nested and extra methods. [#5334][gh5334] +* Fix/remove undefined template var "schema" [#5346][gh5346] ### 3.6.3 @@ -1391,6 +1392,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh5117]: https://github.com/encode/django-rest-framework/issues/5117 +[gh5346]: https://github.com/encode/django-rest-framework/issues/5346 [gh5334]: https://github.com/encode/django-rest-framework/issues/5334 [gh5326]: https://github.com/encode/django-rest-framework/issues/5326 [gh5313]: https://github.com/encode/django-rest-framework/issues/5313 From 5fd01d06ab375c10f8821863617277fddd2a422c Mon Sep 17 00:00:00 2001 From: Felipe Bidu Date: Tue, 22 Aug 2017 11:00:19 -0300 Subject: [PATCH 079/730] Adding a more explicit error message when a view does have a get_queryset method but it returned nothing --- rest_framework/permissions.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index f24775278..484630ae7 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -122,6 +122,9 @@ class DjangoModelPermissions(BasePermission): if hasattr(view, 'get_queryset'): queryset = view.get_queryset() + assert queryset is not None, ( + 'The `.get_queryset()` method from the view did not return anything.' + ) else: queryset = getattr(view, 'queryset', None) From 6f2c3bcb1287db58a191ab7ee4d9723a3377e716 Mon Sep 17 00:00:00 2001 From: Felipe Bidu Date: Tue, 22 Aug 2017 12:13:22 -0300 Subject: [PATCH 080/730] Further clarifying the message when get_queryset returns None to include the class name that was called --- rest_framework/permissions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 484630ae7..01b74e877 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -123,7 +123,7 @@ class DjangoModelPermissions(BasePermission): if hasattr(view, 'get_queryset'): queryset = view.get_queryset() assert queryset is not None, ( - 'The `.get_queryset()` method from the view did not return anything.' + 'Return of {}.get_queryset() was None'.format(view.__class__.__name__) ) else: queryset = getattr(view, 'queryset', None) From 9b829bec2d0bb687c356b4c57091d175e9282fcd Mon Sep 17 00:00:00 2001 From: qwhex Date: Tue, 22 Aug 2017 20:37:31 +0200 Subject: [PATCH 081/730] Update 2-requests-and-responses.md: consistency Made it consistent with Part I. Catched it when commiting the code into my local tutorial repo. --- docs/tutorial/2-requests-and-responses.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index 5c020a1f7..bba52d82e 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -47,7 +47,7 @@ We don't need our `JSONResponse` class in `views.py` anymore, so go ahead and de @api_view(['GET', 'POST']) def snippet_list(request): """ - List all snippets, or create a new snippet. + List all code snippets, or create a new snippet. """ if request.method == 'GET': snippets = Snippet.objects.all() @@ -68,7 +68,7 @@ Here is the view for an individual snippet, in the `views.py` module. @api_view(['GET', 'PUT', 'DELETE']) def snippet_detail(request, pk): """ - Retrieve, update or delete a snippet instance. + Retrieve, update or delete a code snippet. """ try: snippet = Snippet.objects.get(pk=pk) From 6a3b8cfa4c2622ce38c5e0b217fcc5acb624a701 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Tue, 22 Aug 2017 20:44:19 +0200 Subject: [PATCH 082/730] Adjust wording --- rest_framework/permissions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 01b74e877..57de3a35c 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -123,7 +123,7 @@ class DjangoModelPermissions(BasePermission): if hasattr(view, 'get_queryset'): queryset = view.get_queryset() assert queryset is not None, ( - 'Return of {}.get_queryset() was None'.format(view.__class__.__name__) + '{}.get_queryset() returned None'.format(view.__class__.__name__) ) else: queryset = getattr(view, 'queryset', None) From eb88687e28a8c75fff1e875d3a1568cd51a35c99 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 22 Aug 2017 15:02:18 -0400 Subject: [PATCH 083/730] Test RequestFactory with empty body --- tests/test_testing.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_testing.py b/tests/test_testing.py index 4a68a1e1e..ba42da971 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -274,3 +274,12 @@ class TestAPIRequestFactory(TestCase): assert dict(request.GET) == {'demo': ['testé']} request = factory.get('/view/', {'demo': 'testé'}) assert dict(request.GET) == {'demo': ['testé']} + + def test_empty_request_content_type(self): + factory = APIRequestFactory() + request = factory.post( + '/post-view/', + data=None, + content_type='application/json', + ) + assert request.content_type == 'application/json' From 807b9c716ca2cf6131993828ba37d37cfa673b98 Mon Sep 17 00:00:00 2001 From: Scott Kelly Date: Wed, 23 Aug 2017 21:30:56 -0500 Subject: [PATCH 084/730] Fix doc Response data attribute description --- docs/api-guide/responses.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index 8ee14eefa..e9c2d41f1 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -42,7 +42,7 @@ Arguments: ## .data -The unrendered content of a `Request` object. +The unrendered, serialized data of the response. ## .status_code From 26d4977cd065e54cee54e77d8998d11af458a3b6 Mon Sep 17 00:00:00 2001 From: Ashish Patil Date: Thu, 24 Aug 2017 15:06:48 +0400 Subject: [PATCH 085/730] ~api-clients documentation: installation code fix --- docs/topics/api-clients.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/topics/api-clients.md b/docs/topics/api-clients.md index 73434416d..85c806428 100644 --- a/docs/topics/api-clients.md +++ b/docs/topics/api-clients.md @@ -419,7 +419,7 @@ The `SessionAuthentication` class allows session cookies to provide the user authentication. You'll want to provide a standard HTML login flow, to allow the user to login, and then instantiate a client using session authentication: - let auth = coreapi.auth.SessionAuthentication({ + let auth = new coreapi.auth.SessionAuthentication({ csrfCookieName: 'csrftoken', csrfHeaderName: 'X-CSRFToken' }) @@ -433,7 +433,7 @@ requests for unsafe HTTP methods. The `TokenAuthentication` class can be used to support REST framework's built-in `TokenAuthentication`, as well as OAuth and JWT schemes. - let auth = coreapi.auth.TokenAuthentication({ + let auth = new coreapi.auth.TokenAuthentication({ scheme: 'JWT' token: '' }) @@ -471,7 +471,7 @@ For example, using the "Django REST framework JWT" package The `BasicAuthentication` class can be used to support HTTP Basic Authentication. - let auth = coreapi.auth.BasicAuthentication({ + let auth = new coreapi.auth.BasicAuthentication({ username: '', password: '' }) From c0475d059d500f4ab8abf394305df7c65856eec1 Mon Sep 17 00:00:00 2001 From: Vadim Laletin Date: Tue, 29 Aug 2017 10:39:52 +0700 Subject: [PATCH 086/730] Update link to drf-writable-nested repository in third-party serializers --- 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 a3f7fdcc1..e95f2849b 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -1180,4 +1180,4 @@ The [drf-writable-nested][drf-writable-nested] package provides writable nested [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/Brogency/drf-writable-nested +[drf-writable-nested]: http://github.com/beda-software/drf-writable-nested From ff2fa7a8663bdcdf962d31277bc8e29d22a2943e Mon Sep 17 00:00:00 2001 From: Benedek Kiss Date: Tue, 29 Aug 2017 22:22:00 +0200 Subject: [PATCH 087/730] Fix excludes I definitely see files from `__pycache__` as well as `.pyc` files in the package. Fixed according to https://www.reddit.com/r/Python/comments/40s8qw/simplify_your_manifestin_commands and https://github.com/django/django/pull/5817 --- MANIFEST.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 66488aae6..15bfe4caa 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,5 +2,5 @@ include README.md include LICENSE.md recursive-include rest_framework/static *.js *.css *.png *.eot *.svg *.ttf *.woff recursive-include rest_framework/templates *.html -recursive-exclude * __pycache__ -recursive-exclude * *.py[co] +global-exclude __pycache__ +global-exclude *.py[co] From 94e5d05caa285180356fa36e946433b5c93745b1 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 30 Aug 2017 14:06:43 -0400 Subject: [PATCH 088/730] Add failing test for #5371 --- tests/test_serializer.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 91430a193..3044d7c6b 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -411,6 +411,19 @@ class TestDefaultOutput: serializer = self.Serializer(instance) assert serializer.data == {'has_default': 'def', 'has_default_callable': 'ghi', 'no_default': 'abc'} + def test_default_for_source_source(self): + """ + 'default="something"' should be used when a traversed attribute is missing from input. + """ + class Serializer(serializers.Serializer): + traversed = serializers.CharField(default='x', source='traversed.attr') + + assert Serializer({}).data == {'traversed': 'x'} + assert Serializer({'traversed': {}}).data == {'traversed': 'x'} + assert Serializer({'traversed': None}).data == {'traversed': 'x'} + + assert Serializer({'traversed': {'attr': 'abc'}}).data == {'traversed': 'abc'} + class TestCacheSerializerData: def test_cache_serializer_data(self): From 79c1f2154adfbb13fa5d7e24e9624afa41652a6c Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 30 Aug 2017 16:52:16 -0400 Subject: [PATCH 089/730] Fix authorization few perms tests --- tests/test_permissions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 6fbf766a0..70fb38caa 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -201,11 +201,11 @@ class ModelPermissionsIntegrationTests(TestCase): self.assertEqual(response.status_code, status.HTTP_200_OK) def test_calling_method_not_allowed(self): - request = factory.generic('METHOD_NOT_ALLOWED', '/') + request = factory.generic('METHOD_NOT_ALLOWED', '/', HTTP_AUTHORIZATION=self.permitted_credentials) response = root_view(request) self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) - request = factory.generic('METHOD_NOT_ALLOWED', '/1') + request = factory.generic('METHOD_NOT_ALLOWED', '/1', HTTP_AUTHORIZATION=self.permitted_credentials) response = instance_view(request, pk='1') self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) @@ -396,7 +396,7 @@ class ObjectPermissionsIntegrationTests(TestCase): self.assertListEqual(response.data, []) def test_cannot_method_not_allowed(self): - request = factory.generic('METHOD_NOT_ALLOWED', '/') + request = factory.generic('METHOD_NOT_ALLOWED', '/', HTTP_AUTHORIZATION=self.credentials['readonly']) response = object_permissions_list_view(request) self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) From 2ea368e80f16311f50b31563abb70571b95fff86 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 30 Aug 2017 16:53:08 -0400 Subject: [PATCH 090/730] Add failing test for #5367 --- tests/test_permissions.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 70fb38caa..ed5c7af7a 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -209,6 +209,16 @@ class ModelPermissionsIntegrationTests(TestCase): response = instance_view(request, pk='1') self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) + def test_check_auth_before_queryset_call(self): + class View(RootView): + def get_queryset(_): + self.fail('should not reach due to auth check') + view = View.as_view() + + request = factory.get('/', HTTP_AUTHORIZATION='') + response = view(request) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + class BasicPermModel(models.Model): text = models.CharField(max_length=100) From c8773671e7adfe8e9de162f3215470f0ed879f23 Mon Sep 17 00:00:00 2001 From: Denis Untevskiy Date: Fri, 25 Aug 2017 22:14:33 +0200 Subject: [PATCH 091/730] + Rejecting anonymous in DjangoModelPermissions *before* the .get_queryset call --- rest_framework/permissions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 57de3a35c..26728b2d6 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -120,6 +120,10 @@ class DjangoModelPermissions(BasePermission): if getattr(view, '_ignore_model_permissions', False): return True + if not request.user or ( + not is_authenticated(request.user) and self.authenticated_users_only): + return False + if hasattr(view, 'get_queryset'): queryset = view.get_queryset() assert queryset is not None, ( @@ -135,11 +139,7 @@ class DjangoModelPermissions(BasePermission): perms = self.get_required_permissions(request.method, queryset.model) - return ( - request.user and - (is_authenticated(request.user) or not self.authenticated_users_only) and - request.user.has_perms(perms) - ) + return request.user.has_perms(perms) class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions): From 07258ca032e062334310c112469d6432f6eeb818 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 30 Aug 2017 14:15:33 -0400 Subject: [PATCH 092/730] Remove None handling from fields.get_attribute() --- rest_framework/fields.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 242d0f978..5730ca571 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -93,9 +93,6 @@ def get_attribute(instance, attrs): Also accepts either attribute lookup on objects or dictionary lookups. """ for attr in attrs: - if instance is None: - # Break out early if we get `None` at any point in a nested lookup. - return None try: if isinstance(instance, collections.Mapping): instance = instance[attr] From 0ec915e6234bdc602e131f08e8cff46fcf3dc3ff Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 30 Aug 2017 21:36:05 -0400 Subject: [PATCH 093/730] Force content_type inclusion in APIRequestFactory --- rest_framework/request.py | 2 +- rest_framework/test.py | 9 +++++++++ tests/test_testing.py | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/rest_framework/request.py b/rest_framework/request.py index 6f4269fe5..4f413e03f 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -303,7 +303,7 @@ class Request(object): stream = None if stream is None or media_type is None: - if media_type and not is_form_media_type(media_type): + if media_type and is_form_media_type(media_type): empty_data = QueryDict('', encoding=self._request._encoding) else: empty_data = {} diff --git a/rest_framework/test.py b/rest_framework/test.py index 87255bca0..ebad19a4e 100644 --- a/rest_framework/test.py +++ b/rest_framework/test.py @@ -227,6 +227,15 @@ class APIRequestFactory(DjangoRequestFactory): data, content_type = self._encode_data(data, format, content_type) return self.generic('OPTIONS', path, data, content_type, **extra) + def generic(self, method, path, data='', + content_type='application/octet-stream', secure=False, **extra): + # Include the CONTENT_TYPE, regardless of whether or not data is empty. + if content_type is not None: + extra['CONTENT_TYPE'] = str(content_type) + + return super(APIRequestFactory, self).generic( + method, path, data, content_type, secure, **extra) + def request(self, **kwargs): request = super(APIRequestFactory, self).request(**kwargs) request._dont_enforce_csrf_checks = not self.enforce_csrf_checks diff --git a/tests/test_testing.py b/tests/test_testing.py index ba42da971..1af6ef02e 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -282,4 +282,4 @@ class TestAPIRequestFactory(TestCase): data=None, content_type='application/json', ) - assert request.content_type == 'application/json' + assert request.META['CONTENT_TYPE'] == 'application/json' From fff3db5517ef80ce0ecb7448feba5aeca46d85d3 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Thu, 31 Aug 2017 12:19:03 +0200 Subject: [PATCH 094/730] Fix doc for ErrorDetail --- rest_framework/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index aac31c453..7cc3ba049 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -64,7 +64,7 @@ def _get_full_details(detail): class ErrorDetail(six.text_type): """ - A string-like object that can additionally + A string-like object that can additionally have a code. """ code = None From bc49746dd37e22385facfe509bf4bb89582e7f98 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Thu, 31 Aug 2017 08:26:14 -0400 Subject: [PATCH 095/730] Fix test name --- tests/test_serializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 3044d7c6b..af5206a9f 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -411,7 +411,7 @@ class TestDefaultOutput: serializer = self.Serializer(instance) assert serializer.data == {'has_default': 'def', 'has_default_callable': 'ghi', 'no_default': 'abc'} - def test_default_for_source_source(self): + def test_default_for_dotted_source(self): """ 'default="something"' should be used when a traversed attribute is missing from input. """ From af460d2b6906ebce0680105f36eeada867a35d7e Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Fri, 1 Sep 2017 13:37:01 -0400 Subject: [PATCH 096/730] Add PR 5376 to release notes --- docs/topics/release-notes.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 78dd334ee..f4617ac23 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,10 @@ You can determine your currently installed version using `pip freeze`: ## 3.6.x series +### 3.6.5 + +* Fix `DjangoModelPermissions` to ensure user authentication before calling the view's `get_queryset()` method. As a side effect, this changes the order of the HTTP method permissions and authentication checks, and 405 responses will only be returned when authenticated. If you want to replicate the old behavior, see the PR for details. [#5376][gh5376] + ### 3.6.4 **Date**: [21st August 2017][3.6.4-milestone] @@ -1417,5 +1421,5 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh5147]: https://github.com/encode/django-rest-framework/issues/5147 [gh5131]: https://github.com/encode/django-rest-framework/issues/5131 - - + +[gh5376]: https://github.com/encode/django-rest-framework/issues/5376 From 23b2d8099bbcdf509c177c06eb56ab8b58bf5b8b Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Fri, 1 Sep 2017 13:37:58 -0400 Subject: [PATCH 097/730] Unify QS handling for model/object permissions --- rest_framework/permissions.py | 41 +++++++++++++++-------------------- tests/test_permissions.py | 23 +++++++++++++++++++- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 26728b2d6..dee0032f9 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -114,6 +114,21 @@ class DjangoModelPermissions(BasePermission): return [perm % kwargs for perm in self.perms_map[method]] + def _queryset(self, view): + assert hasattr(view, 'get_queryset') \ + or getattr(view, 'queryset', None) is not None, ( + 'Cannot apply {} on a view that does not set ' + '`.queryset` or have a `.get_queryset()` method.' + ).format(self.__class__.__name__) + + if hasattr(view, 'get_queryset'): + queryset = view.get_queryset() + assert queryset is not None, ( + '{}.get_queryset() returned None'.format(view.__class__.__name__) + ) + return queryset + return view.queryset + def has_permission(self, request, view): # Workaround to ensure DjangoModelPermissions are not applied # to the root view when using DefaultRouter. @@ -124,19 +139,7 @@ class DjangoModelPermissions(BasePermission): not is_authenticated(request.user) and self.authenticated_users_only): return False - if hasattr(view, 'get_queryset'): - queryset = view.get_queryset() - assert queryset is not None, ( - '{}.get_queryset() returned None'.format(view.__class__.__name__) - ) - else: - queryset = getattr(view, 'queryset', None) - - assert queryset is not None, ( - 'Cannot apply DjangoModelPermissions on a view that ' - 'does not set `.queryset` or have a `.get_queryset()` method.' - ) - + queryset = self._queryset(view) perms = self.get_required_permissions(request.method, queryset.model) return request.user.has_perms(perms) @@ -183,16 +186,8 @@ class DjangoObjectPermissions(DjangoModelPermissions): return [perm % kwargs for perm in self.perms_map[method]] def has_object_permission(self, request, view, obj): - if hasattr(view, 'get_queryset'): - queryset = view.get_queryset() - else: - queryset = getattr(view, 'queryset', None) - - assert queryset is not None, ( - 'Cannot apply DjangoObjectPermissions on a view that ' - 'does not set `.queryset` or have a `.get_queryset()` method.' - ) - + # authentication checks have already executed via has_permission + queryset = self._queryset(view) model_cls = queryset.model user = request.user diff --git a/tests/test_permissions.py b/tests/test_permissions.py index ed5c7af7a..f673c3671 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -9,7 +9,7 @@ from django.test import TestCase from rest_framework import ( HTTP_HEADER_ENCODING, authentication, generics, permissions, serializers, - status + status, views ) from rest_framework.compat import ResolverMatch, guardian, set_many from rest_framework.filters import DjangoObjectPermissionsFilter @@ -219,6 +219,27 @@ class ModelPermissionsIntegrationTests(TestCase): response = view(request) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + def test_queryset_assertions(self): + class View(views.APIView): + authentication_classes = [authentication.BasicAuthentication] + permission_classes = [permissions.DjangoModelPermissions] + view = View.as_view() + + request = factory.get('/', HTTP_AUTHORIZATION=self.permitted_credentials) + msg = 'Cannot apply DjangoModelPermissions on a view that does not set `.queryset` or have a `.get_queryset()` method.' + with self.assertRaisesMessage(AssertionError, msg): + view(request) + + # Faulty `get_queryset()` methods should trigger the above "view does not have a queryset" assertion. + class View(RootView): + def get_queryset(self): + return None + view = View.as_view() + + request = factory.get('/', HTTP_AUTHORIZATION=self.permitted_credentials) + with self.assertRaisesMessage(AssertionError, 'View.get_queryset() returned None'): + view(request) + class BasicPermModel(models.Model): text = models.CharField(max_length=100) From e42eb42d4912f1541bebfb220ab0a9c4d0e08c08 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Mon, 4 Sep 2017 10:04:48 +0100 Subject: [PATCH 098/730] Don't make the content mandatory in the generic content form (#5372) Sometimes, probably in the upgrade from Django 1.9 to 1.10, a post with empty content is forbidden by javascript, with the message "Please fill in this field". Filling the form with '{}' allows an application/json request to be submitted. The API call itself works perfectly well with a post with empty content: the interface shouldn't make assumptions about it. --- rest_framework/renderers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 779f0dd44..687cb0e1e 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -579,7 +579,8 @@ class BrowsableAPIRenderer(BaseRenderer): _content = forms.CharField( label='Content', widget=forms.Textarea(attrs={'data-override': 'content'}), - initial=content + initial=content, + required=False ) return GenericContentForm() From 79be20a7c68e7c90dd4d5d23a9e6ee08b5f586ae Mon Sep 17 00:00:00 2001 From: Igor Tokarev Date: Mon, 4 Sep 2017 14:11:53 +0500 Subject: [PATCH 099/730] Updated supported values for the NullBooleanField (#5387) * Updated supported values for the NullBooleanField. * Added check for unhashable types in NullBooleanField. --- rest_framework/fields.py | 33 +++++++++++++++++++++++++-------- tests/test_fields.py | 4 ++-- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 5730ca571..d2079d5d6 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -688,8 +688,22 @@ class NullBooleanField(Field): 'invalid': _('"{input}" is not a valid boolean.') } initial = None - TRUE_VALUES = {'t', 'T', 'true', 'True', 'TRUE', '1', 1, True} - FALSE_VALUES = {'f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False} + TRUE_VALUES = { + 't', 'T', + 'y', 'Y', 'yes', 'YES', + 'true', 'True', 'TRUE', + 'on', 'On', 'ON', + '1', 1, + True + } + FALSE_VALUES = { + 'f', 'F', + 'n', 'N', 'no', 'NO', + 'false', 'False', 'FALSE', + 'off', 'Off', 'OFF', + '0', 0, 0.0, + False + } NULL_VALUES = {'n', 'N', 'null', 'Null', 'NULL', '', None} def __init__(self, **kwargs): @@ -698,12 +712,15 @@ class NullBooleanField(Field): super(NullBooleanField, self).__init__(**kwargs) def to_internal_value(self, data): - if data in self.TRUE_VALUES: - return True - elif data in self.FALSE_VALUES: - return False - elif data in self.NULL_VALUES: - return None + try: + if data in self.TRUE_VALUES: + return True + elif data in self.FALSE_VALUES: + return False + elif data in self.NULL_VALUES: + return None + except TypeError: # Input is an unhashable type + pass self.fail('invalid', input=data) def to_representation(self, value): diff --git a/tests/test_fields.py b/tests/test_fields.py index d6b233227..c3d2bf57d 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -587,7 +587,7 @@ class TestBooleanField(FieldValues): [], {}, ) - field = serializers.BooleanField() + field = self.field for input_value in inputs: with pytest.raises(serializers.ValidationError) as exc_info: field.run_validation(input_value) @@ -595,7 +595,7 @@ class TestBooleanField(FieldValues): assert exc_info.value.detail == expected -class TestNullBooleanField(FieldValues): +class TestNullBooleanField(TestBooleanField): """ Valid and invalid values for `BooleanField`. """ From 3c1bf6bfd5d69f36c822f92d0bb22a6f338c6431 Mon Sep 17 00:00:00 2001 From: jhg14 Date: Mon, 4 Sep 2017 11:47:53 +0100 Subject: [PATCH 100/730] Add failing test for named attribute Fix test crudely Remove comment --- rest_framework/serializers.py | 4 +++- tests/test_model_serializer.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index a4b51ae9d..b1c34b92a 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1010,7 +1010,9 @@ class ModelSerializer(Serializer): continue extra_field_kwargs = extra_kwargs.get(field_name, {}) - source = extra_field_kwargs.get('source', '*') != '*' or field_name + source = extra_field_kwargs.get('source', '*') + if source == '*': + source = field_name # Determine the serializer field class and keyword arguments. field_class, field_kwargs = self.build_field( diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index ba3edd389..ce054f695 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -557,6 +557,39 @@ class TestRelationalFieldMappings(TestCase): self.maxDiff = None self.assertEqual(unicode_repr(TestSerializer()), expected) + def test_nested_hyperlinked_relations_named_source(self): + class TestSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = RelationalModel + depth = 1 + fields = '__all__' + + extra_kwargs = { + 'url': { + 'source': 'url' + }} + + expected = dedent(""" + TestSerializer(): + url = HyperlinkedIdentityField(source='url', view_name='relationalmodel-detail') + foreign_key = NestedSerializer(read_only=True): + url = HyperlinkedIdentityField(view_name='foreignkeytargetmodel-detail') + name = CharField(max_length=100) + one_to_one = NestedSerializer(read_only=True): + url = HyperlinkedIdentityField(view_name='onetoonetargetmodel-detail') + name = CharField(max_length=100) + many_to_many = NestedSerializer(many=True, read_only=True): + url = HyperlinkedIdentityField(view_name='manytomanytargetmodel-detail') + name = CharField(max_length=100) + through = NestedSerializer(many=True, read_only=True): + url = HyperlinkedIdentityField(view_name='throughtargetmodel-detail') + name = CharField(max_length=100) + """) + self.maxDiff = None + self.assertEqual(unicode_repr(TestSerializer()), expected) + + + def test_nested_unique_together_relations(self): class TestSerializer(serializers.HyperlinkedModelSerializer): class Meta: From 43458944452fe11132fa1ab5f2bf1934b8fc108f Mon Sep 17 00:00:00 2001 From: jhg14 Date: Mon, 4 Sep 2017 15:41:04 +0100 Subject: [PATCH 101/730] Add simplest possible failing test --- tests/test_model_serializer.py | 55 ++++++++++++++-------------------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index ce054f695..430be71c6 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -557,39 +557,6 @@ class TestRelationalFieldMappings(TestCase): self.maxDiff = None self.assertEqual(unicode_repr(TestSerializer()), expected) - def test_nested_hyperlinked_relations_named_source(self): - class TestSerializer(serializers.HyperlinkedModelSerializer): - class Meta: - model = RelationalModel - depth = 1 - fields = '__all__' - - extra_kwargs = { - 'url': { - 'source': 'url' - }} - - expected = dedent(""" - TestSerializer(): - url = HyperlinkedIdentityField(source='url', view_name='relationalmodel-detail') - foreign_key = NestedSerializer(read_only=True): - url = HyperlinkedIdentityField(view_name='foreignkeytargetmodel-detail') - name = CharField(max_length=100) - one_to_one = NestedSerializer(read_only=True): - url = HyperlinkedIdentityField(view_name='onetoonetargetmodel-detail') - name = CharField(max_length=100) - many_to_many = NestedSerializer(many=True, read_only=True): - url = HyperlinkedIdentityField(view_name='manytomanytargetmodel-detail') - name = CharField(max_length=100) - through = NestedSerializer(many=True, read_only=True): - url = HyperlinkedIdentityField(view_name='throughtargetmodel-detail') - name = CharField(max_length=100) - """) - self.maxDiff = None - self.assertEqual(unicode_repr(TestSerializer()), expected) - - - def test_nested_unique_together_relations(self): class TestSerializer(serializers.HyperlinkedModelSerializer): class Meta: @@ -1168,3 +1135,25 @@ class Test5004UniqueChoiceField(TestCase): serializer = TestUniqueChoiceSerializer(data={'name': 'choice1'}) assert not serializer.is_valid() assert serializer.errors == {'name': ['unique choice model with this name already exists.']} + +class TestFieldSource(TestCase): + + def test_named_field_source(self): + class TestSerializer(serializers.ModelSerializer): + + class Meta: + model = RegularFieldsModel + fields = ('number_field',) + extra_kwargs = { + 'number_field': { + 'source': 'integer_field' + } + } + + expected = dedent(""" + TestSerializer(): + number_field = IntegerField(source='integer_field') + """) + self.maxDiff = None + self.assertEqual(unicode_repr(TestSerializer()), expected) + From 66b2c6149ed0121b1064fe42615aaa8d75aa5d8b Mon Sep 17 00:00:00 2001 From: jhg14 Date: Mon, 4 Sep 2017 16:17:43 +0100 Subject: [PATCH 102/730] Fix code style --- tests/test_model_serializer.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 430be71c6..3411c44b5 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -1136,13 +1136,13 @@ class Test5004UniqueChoiceField(TestCase): assert not serializer.is_valid() assert serializer.errors == {'name': ['unique choice model with this name already exists.']} + class TestFieldSource(TestCase): - def test_named_field_source(self): class TestSerializer(serializers.ModelSerializer): class Meta: - model = RegularFieldsModel + model = RegularFieldsModel fields = ('number_field',) extra_kwargs = { 'number_field': { @@ -1156,4 +1156,3 @@ class TestFieldSource(TestCase): """) self.maxDiff = None self.assertEqual(unicode_repr(TestSerializer()), expected) - From b11f37eaf314b983c7a21abcea231f69ea31da1b Mon Sep 17 00:00:00 2001 From: Irvan Date: Thu, 7 Sep 2017 11:06:44 +0800 Subject: [PATCH 103/730] Fixed the MultipleFieldLookupMixin example to properly check for object level permission. --- docs/api-guide/generic-views.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 0170256f2..381f1fe73 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -330,7 +330,9 @@ For example, if you need to lookup objects based on multiple fields in the URL c for field in self.lookup_fields: if self.kwargs[field]: # Ignore empty fields. filter[field] = self.kwargs[field] - return get_object_or_404(queryset, **filter) # Lookup the object + obj = get_object_or_404(queryset, **filter) # Lookup the object + self.check_object_permissions(self.request, obj) + return obj You can then simply apply this mixin to a view or viewset anytime you need to apply the custom behavior. From 13222e45bcbf6492fee59ef85c88c3eae0a4b74e Mon Sep 17 00:00:00 2001 From: ersel-ionova <31036948+ersel-ionova@users.noreply.github.com> Date: Fri, 8 Sep 2017 16:53:17 +0100 Subject: [PATCH 104/730] Make status_code documentation more readable. (#5400) * Make status_code documentation more readable. * Update status-codes.md --- docs/api-guide/status-codes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/status-codes.md b/docs/api-guide/status-codes.md index f6ec3598f..16a0e63c8 100644 --- a/docs/api-guide/status-codes.md +++ b/docs/api-guide/status-codes.md @@ -6,7 +6,7 @@ source: status.py > > — [RFC 2324][rfc2324], Hyper Text Coffee Pot Control Protocol -Using bare status codes in your responses isn't recommended. REST framework includes a set of named constants that you can use to make more code more obvious and readable. +Using bare status codes in your responses isn't recommended. REST framework includes a set of named constants that you can use to make your code more obvious and readable. from rest_framework import status from rest_framework.response import Response From 0e341c24b49c1ae8011fd4e99a13e9cb43ea64f5 Mon Sep 17 00:00:00 2001 From: Sanjuro Jogdeo Date: Fri, 8 Sep 2017 09:51:16 -0700 Subject: [PATCH 105/730] Update get_object() example in permissions.md (#5401) * Update get_object() example in permissions.md I'm a bit confused about the example that's provided in the 'Object level permissions' section. Other examples (e.g. Tutorial 3 - Class Based Views) provided a pk to get_object(). It doesn't seem like this example has any way of identifying a specific object. Just in case I'm correct, I've prepared this pull request. But if I'm wrong, would it be possible for you to explain the example I modified? Many Thanks... * Adjust patch --- 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 548b14438..ef9ce3abd 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -44,7 +44,7 @@ This will either raise a `PermissionDenied` or `NotAuthenticated` exception, or For example: def get_object(self): - obj = get_object_or_404(self.get_queryset()) + obj = get_object_or_404(self.get_queryset(), pk=self.kwargs["pk"]) self.check_object_permissions(self.request, obj) return obj From ae95ed1ec2514becb6e2e41d47b8fd3c8b9c8f66 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 11 Sep 2017 05:18:39 -0400 Subject: [PATCH 106/730] Add repr(value) to the assert msg in FieldValues --- tests/test_fields.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test_fields.py b/tests/test_fields.py index c3d2bf57d..4173e6ab5 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -533,7 +533,8 @@ class FieldValues: Ensure that valid values return the expected validated data. """ for input_value, expected_output in get_items(self.valid_inputs): - assert self.field.run_validation(input_value) == expected_output + assert self.field.run_validation(input_value) == expected_output, \ + 'input value: {}'.format(repr(input_value)) def test_invalid_inputs(self): """ @@ -542,11 +543,13 @@ class FieldValues: for input_value, expected_failure in get_items(self.invalid_inputs): with pytest.raises(serializers.ValidationError) as exc_info: self.field.run_validation(input_value) - assert exc_info.value.detail == expected_failure + assert exc_info.value.detail == expected_failure, \ + 'input value: {}'.format(repr(input_value)) def test_outputs(self): for output_value, expected_output in get_items(self.outputs): - assert self.field.to_representation(output_value) == expected_output + assert self.field.to_representation(output_value) == expected_output, \ + 'output value: {}'.format(repr(output_value)) # Boolean types... From 7037ce88e94665076b33919917d44308e13ef027 Mon Sep 17 00:00:00 2001 From: Jozef Date: Tue, 12 Sep 2017 13:08:32 +0200 Subject: [PATCH 107/730] Fix throttling documentation about Remote-Addr (#5414) Clarify in docs that REMOTE_ADDR is part of the WSGI environ, not an HTTP header. --- docs/api-guide/throttling.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 58578a23e..10a0bb087 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -68,9 +68,9 @@ Or, if you're using the `@api_view` decorator with function based views. ## How clients are identified -The `X-Forwarded-For` and `Remote-Addr` HTTP headers 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` header will be used. +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. -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` header will always be used as the identifying IP address. +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. From 9aaea2586bf5a5fab0294943d069e92969b497f2 Mon Sep 17 00:00:00 2001 From: Sergei Azarkin Date: Tue, 12 Sep 2017 16:03:29 +0300 Subject: [PATCH 108/730] Fix authtoken managment command (#5415) * Fix authtoken managment command username param --- .../authtoken/management/commands/drf_create_token.py | 2 +- tests/test_authtoken.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/rest_framework/authtoken/management/commands/drf_create_token.py b/rest_framework/authtoken/management/commands/drf_create_token.py index 417bdd780..da10bfc90 100644 --- a/rest_framework/authtoken/management/commands/drf_create_token.py +++ b/rest_framework/authtoken/management/commands/drf_create_token.py @@ -19,7 +19,7 @@ class Command(BaseCommand): return token[0] def add_arguments(self, parser): - parser.add_argument('username', type=str, nargs='+') + parser.add_argument('username', type=str) parser.add_argument( '-r', diff --git a/tests/test_authtoken.py b/tests/test_authtoken.py index 6374d7141..5df053da3 100644 --- a/tests/test_authtoken.py +++ b/tests/test_authtoken.py @@ -1,7 +1,9 @@ import pytest from django.contrib.admin import site from django.contrib.auth.models import User +from django.core.management import call_command from django.test import TestCase +from django.utils.six import StringIO from rest_framework.authtoken.admin import TokenAdmin from rest_framework.authtoken.management.commands.drf_create_token import \ @@ -68,3 +70,11 @@ class AuthTokenCommandTests(TestCase): second_token_key = Token.objects.first().key assert first_token_key == second_token_key + + def test_command_output(self): + out = StringIO() + call_command('drf_create_token', self.user.username, stdout=out) + token_saved = Token.objects.first() + self.assertIn('Generated token', out.getvalue()) + self.assertIn(self.user.username, out.getvalue()) + self.assertIn(token_saved.key, out.getvalue()) From 5ea810d5268d222e1582a9a33738b2a73f88c07b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 14 Sep 2017 09:44:59 +0100 Subject: [PATCH 109/730] Drop unnecessary TODO notes. --- rest_framework/response.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/rest_framework/response.py b/rest_framework/response.py index cb0f290ce..bf0663255 100644 --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -88,8 +88,6 @@ class Response(SimpleTemplateResponse): Returns reason text corresponding to our HTTP response status code. Provided for convenience. """ - # TODO: Deprecate and use a template tag instead - # TODO: Status code text for RFC 6585 status codes return responses.get(self.status_code, '') def __getstate__(self): From d54df8c438d224616a8bcd745589b65e16db0989 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Thu, 14 Sep 2017 10:46:34 +0200 Subject: [PATCH 110/730] Refactor schema generation to allow per-view customisation (#5354) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial Refactor Step * Add descriptor class * call from generator * proxy back to generator for implementation. * Move `get_link` to descriptor * Move `get_description` to descriptor * Remove need for generator in get_description * Move get_path_fields to descriptor * Move `get_serializer_fields` to descriptor * Move `get_pagination_fields` to descriptor * Move `get_filter_fields` to descriptor * Move `get_encoding` to descriptor. * Pass just `url` from SchemaGenerator to descriptor * Make `view` a property Encapsulates check for a view instance. * Adjust API Reference docs * Add `ManualSchema` class * Refactor to `ViewInspector` plus `AutoSchema` The interface then is **just** `get_link()` * Add `manual_fields` kwarg to AutoSchema * Add schema decorator for FBVs * Adjust comments * Docs: Provide full params in example Ref feedback https://github.com/encode/django-rest-framework/pull/5354/files/b52e372f8f936204753b17fe7c9bfb517b93a045#r137254795 * Add docstring for ViewInstpector.__get__ descriptor method. Ref https://github.com/encode/django-rest-framework/pull/5354#discussion_r137265022 * Make `schemas` a package. * Split generators, inspectors, views. * Adjust imports * Rename to EndpointEnumerator * Adjust ManualSchema to take `fields` … and `description`. Allows `url` and `action` to remain dynamic * Add package/module docstrings --- docs/api-guide/schemas.md | 270 ++++++++++-- docs/api-guide/views.md | 22 + rest_framework/decorators.py | 10 + rest_framework/routers.py | 3 +- rest_framework/schemas/__init__.py | 43 ++ .../{schemas.py => schemas/generators.py} | 347 +-------------- rest_framework/schemas/inspectors.py | 399 ++++++++++++++++++ rest_framework/schemas/utils.py | 21 + rest_framework/schemas/views.py | 34 ++ rest_framework/views.py | 2 + tests/test_decorators.py | 17 +- tests/test_schemas.py | 83 +++- 12 files changed, 868 insertions(+), 383 deletions(-) create mode 100644 rest_framework/schemas/__init__.py rename rest_framework/{schemas.py => schemas/generators.py} (51%) create mode 100644 rest_framework/schemas/inspectors.py create mode 100644 rest_framework/schemas/utils.py create mode 100644 rest_framework/schemas/views.py diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 836ad4b6a..f913f046f 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -10,7 +10,14 @@ API schemas are a useful tool that allow for a range of use cases, including generating reference documentation, or driving dynamic client libraries that can interact with your API. -## Representing schemas internally +## Install Core API + +You'll need to install the `coreapi` package in order to add schema support +for REST framework. + + pip install coreapi + +## Internal schema representation REST framework uses [Core API][coreapi] in order to model schema information in a format-independent representation. This information can then be rendered @@ -68,9 +75,34 @@ has to be rendered into the actual bytes that are used in the response. REST framework includes a renderer class for handling this media type, which is available as `renderers.CoreJSONRenderer`. +### Alternate schema formats + Other schema formats such as [Open API][open-api] ("Swagger"), -[JSON HyperSchema][json-hyperschema], or [API Blueprint][api-blueprint] can -also be supported by implementing a custom renderer class. +[JSON HyperSchema][json-hyperschema], or [API Blueprint][api-blueprint] can also +be supported by implementing a custom renderer class that handles converting a +`Document` instance into a bytestring representation. + +If there is a Core API codec package that supports encoding into the format you +want to use then implementing the renderer class can be done by using the codec. + +#### Example + +For example, the `openapi_codec` package provides support for encoding or decoding +to the Open API ("Swagger") format: + + from rest_framework import renderers + from openapi_codec import OpenAPICodec + + class SwaggerRenderer(renderers.BaseRenderer): + media_type = 'application/openapi+json' + format = 'swagger' + + def render(self, data, media_type=None, renderer_context=None): + codec = OpenAPICodec() + return codec.dump(data) + + + ## Schemas vs Hypermedia @@ -89,18 +121,121 @@ document, detailing both the current state and the available interactions. Further information and support on building Hypermedia APIs with REST framework is planned for a future version. + --- -# Adding a schema - -You'll need to install the `coreapi` package in order to add schema support -for REST framework. - - pip install coreapi +# Creating a schema REST framework includes functionality for auto-generating a schema, -or allows you to specify one explicitly. There are a few different ways to -add a schema to your API, depending on exactly what you need. +or allows you to specify one explicitly. + +## Manual Schema Specification + +To manually specify a schema you create a Core API `Document`, similar to the +example above. + + schema = coreapi.Document( + title='Flight Search API', + content={ + ... + } + ) + + +## Automatic Schema Generation + +Automatic schema generation is provided by the `SchemaGenerator` class. + +`SchemaGenerator` processes a list of routed URL pattterns and compiles the +appropriately structured Core API Document. + +Basic usage is just to provide the title for your schema and call +`get_schema()`: + + generator = schemas.SchemaGenerator(title='Flight Search API') + schema = generator.get_schema() + +### Per-View Schema Customisation + +By default, view introspection is performed by an `AutoSchema` instance +accessible via the `schema` attribute on `APIView`. This provides the +appropriate Core API `Link` object for the view, request method and path: + + auto_schema = view.schema + coreapi_link = auto_schema.get_link(...) + +(In compiling the schema, `SchemaGenerator` calls `view.schema.get_link()` for +each view, allowed method and path.) + +To customise the `Link` generation you may: + +* Instantiate `AutoSchema` on your view with the `manual_fields` kwarg: + + from rest_framework.views import APIView + from rest_framework.schemas import AutoSchema + + class CustomView(APIView): + ... + schema = AutoSchema( + manual_fields=[ + coreapi.Field("extra_field", ...), + ] + ) + + This allows extension for the most common case without subclassing. + +* Provide an `AutoSchema` subclass with more complex customisation: + + from rest_framework.views import APIView + from rest_framework.schemas import AutoSchema + + class CustomSchema(AutoSchema): + def get_link(...): + # Implemet custom introspection here (or in other sub-methods) + + class CustomView(APIView): + ... + schema = CustomSchema() + + This provides complete control over view introspection. + +* Instantiate `ManualSchema` on your view, providing the Core API `Fields` for + the view explicitly: + + from rest_framework.views import APIView + from rest_framework.schemas import ManualSchema + + class CustomView(APIView): + ... + schema = ManualSchema(fields=[ + coreapi.Field( + "first_field", + required=True, + location="path", + schema=coreschema.String() + ), + coreapi.Field( + "second_field", + required=True, + location="path", + schema=coreschema.String() + ), + ]) + + This allows manually specifying the schema for some views whilst maintaining + automatic generation elsewhere. + +--- + +**Note**: For full details on `SchemaGenerator` plus the `AutoSchema` and +`ManualSchema` descriptors see the [API Reference below](#api-reference). + +--- + +# Adding a schema view + +There are a few different ways to add a schema view to your API, depending on +exactly what you need. ## The get_schema_view shortcut @@ -342,38 +477,12 @@ A generic viewset with sections in the class docstring, using multi-line style. --- -# Alternate schema formats - -In order to support an alternate schema format, you need to implement a custom renderer -class that handles converting a `Document` instance into a bytestring representation. - -If there is a Core API codec package that supports encoding into the format you -want to use then implementing the renderer class can be done by using the codec. - -## Example - -For example, the `openapi_codec` package provides support for encoding or decoding -to the Open API ("Swagger") format: - - from rest_framework import renderers - from openapi_codec import OpenAPICodec - - class SwaggerRenderer(renderers.BaseRenderer): - media_type = 'application/openapi+json' - format = 'swagger' - - def render(self, data, media_type=None, renderer_context=None): - codec = OpenAPICodec() - return codec.dump(data) - ---- - # API Reference ## SchemaGenerator -A class that deals with introspecting your API views, which can be used to -generate a schema. +A class that walks a list of routed URL patterns, requests the schema for each view, +and collates the resulting CoreAPI Document. Typically you'll instantiate `SchemaGenerator` with a single argument, like so: @@ -406,39 +515,108 @@ Return a nested dictionary containing all the links that should be included in t This is a good point to override if you want to modify the resulting structure of the generated schema, as you can build a new dictionary with a different layout. -### get_link(self, path, method, view) + +## AutoSchema + +A class that deals with introspection of individual views for schema generation. + +`AutoSchema` is attached to `APIView` via the `schema` attribute. + +The `AutoSchema` constructor takes a single keyword argument `manual_fields`. + +**`manual_fields`**: a `list` of `coreapi.Field` instances that will be added to +the generated fields. Generated fields with a matching `name` will be overwritten. + + class CustomView(APIView): + schema = AutoSchema(manual_fields=[ + coreapi.Field( + "my_extra_field", + required=True, + location="path", + schema=coreschema.String() + ), + ]) + +For more advanced customisation subclass `AutoSchema` to customise schema generation. + + class CustomViewSchema(AutoSchema): + """ + Overrides `get_link()` to provide Custom Behavior X + """ + + def get_link(self, path, method, base_url): + link = super().get_link(path, method, base_url) + # Do something to customize link here... + return link + + class MyView(APIView): + schema = CustomViewSchema() + +The following methods are available to override. + +### get_link(self, path, method, base_url) Returns a `coreapi.Link` instance corresponding to the given view. +This is the main entry point. You can override this if you need to provide custom behaviors for particular views. -### get_description(self, path, method, view) +### get_description(self, path, method) Returns a string to use as the link description. By default this is based on the view docstring as described in the "Schemas as Documentation" section above. -### get_encoding(self, path, method, view) +### get_encoding(self, path, method) Returns a string to indicate the encoding for any request body, when interacting with the given view. Eg. `'application/json'`. May return a blank string for views that do not expect a request body. -### get_path_fields(self, path, method, view): +### get_path_fields(self, path, method): Return a list of `coreapi.Link()` instances. One for each path parameter in the URL. -### get_serializer_fields(self, path, method, view) +### get_serializer_fields(self, path, method) Return a list of `coreapi.Link()` instances. One for each field in the serializer class used by the view. -### get_pagination_fields(self, path, method, view +### get_pagination_fields(self, path, method) Return a list of `coreapi.Link()` instances, as returned by the `get_schema_fields()` method on any pagination class used by the view. -### get_filter_fields(self, path, method, view) +### get_filter_fields(self, path, method) Return a list of `coreapi.Link()` instances, as returned by the `get_schema_fields()` method of any filter classes used by the view. + +## ManualSchema + +Allows manually providing a list of `coreapi.Field` instances for the schema, +plus an optional description. + + class MyView(APIView): + schema = ManualSchema(fields=[ + coreapi.Field( + "first_field", + required=True, + location="path", + schema=coreschema.String() + ), + coreapi.Field( + "second_field", + required=True, + location="path", + schema=coreschema.String() + ), + ] + ) + +The `ManualSchema` constructor takes two arguments: + +**`fields`**: A list of `coreapi.Field` instances. Required. + +**`description`**: A string description. Optional. + --- ## Core API diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 4fa36d0fc..24dd42578 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -184,6 +184,28 @@ The available decorators are: Each of these decorators takes a single argument which must be a list or tuple of classes. + +## View schema decorator + +To override the default schema generation for function based views you may use +the `@schema` decorator. This must come *after* (below) the `@api_view` +decorator. For example: + + from rest_framework.decorators import api_view, schema + from rest_framework.schemas import AutoSchema + + class CustomAutoSchema(AutoSchema): + def get_link(self, path, method, base_url): + # override view introspection here... + + @api_view(['GET']) + @schema(CustomAutoSchema()) + def view(request): + return Response({"message": "Hello for today! See you tomorrow!"}) + +This decorator takes a single `AutoSchema` instance, an `AutoSchema` subclass +instance or `ManualSchema` instance as described in the [Schemas documentation][schemas], + [cite]: http://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 diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index bf9b32aaa..1297f96b4 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -72,6 +72,9 @@ def api_view(http_method_names=None, exclude_from_schema=False): WrappedAPIView.permission_classes = getattr(func, 'permission_classes', APIView.permission_classes) + WrappedAPIView.schema = getattr(func, 'schema', + APIView.schema) + WrappedAPIView.exclude_from_schema = exclude_from_schema return WrappedAPIView.as_view() return decorator @@ -112,6 +115,13 @@ def permission_classes(permission_classes): return decorator +def schema(view_inspector): + def decorator(func): + func.schema = view_inspector + 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. diff --git a/rest_framework/routers.py b/rest_framework/routers.py index a04bffc1a..01daa7e7d 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -26,7 +26,8 @@ from rest_framework import views from rest_framework.compat import NoReverseMatch from rest_framework.response import Response from rest_framework.reverse import reverse -from rest_framework.schemas import SchemaGenerator, SchemaView +from rest_framework.schemas import SchemaGenerator +from rest_framework.schemas.views import SchemaView from rest_framework.settings import api_settings from rest_framework.urlpatterns import format_suffix_patterns diff --git a/rest_framework/schemas/__init__.py b/rest_framework/schemas/__init__.py new file mode 100644 index 000000000..fc551640e --- /dev/null +++ b/rest_framework/schemas/__init__.py @@ -0,0 +1,43 @@ +""" +rest_framework.schemas + +schemas: + __init__.py + generators.py # Top-down schema generation + inspectors.py # Per-endpoint view introspection + utils.py # Shared helper functions + views.py # Houses `SchemaView`, `APIView` subclass. + +We expose a minimal "public" API directly from `schemas`. This covers the +basic use-cases: + + from rest_framework.schemas import ( + AutoSchema, + ManualSchema, + get_schema_view, + SchemaGenerator, + ) + +Other access should target the submodules directly +""" +from .generators import SchemaGenerator +from .inspectors import AutoSchema, ManualSchema # noqa + + +def get_schema_view( + title=None, url=None, description=None, urlconf=None, renderer_classes=None, + public=False, patterns=None, generator_class=SchemaGenerator): + """ + Return a schema view. + """ + # Avoid import cycle on APIView + from .views import SchemaView + generator = generator_class( + title=title, url=url, description=description, + urlconf=urlconf, patterns=patterns, + ) + return SchemaView.as_view( + renderer_classes=renderer_classes, + schema_generator=generator, + public=public, + ) diff --git a/rest_framework/schemas.py b/rest_framework/schemas/generators.py similarity index 51% rename from rest_framework/schemas.py rename to rest_framework/schemas/generators.py index 437413355..8344f64f0 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas/generators.py @@ -1,86 +1,26 @@ -import re +""" +generators.py # Top-down schema generation + +See schemas.__init__.py for package overview. +""" from collections import OrderedDict from importlib import import_module from django.conf import settings from django.contrib.admindocs.views import simplify_regex from django.core.exceptions import PermissionDenied -from django.db import models from django.http import Http404 from django.utils import six -from django.utils.encoding import force_text, smart_text -from django.utils.translation import ugettext_lazy as _ -from rest_framework import exceptions, renderers, serializers +from rest_framework import exceptions from rest_framework.compat import ( - RegexURLPattern, RegexURLResolver, coreapi, coreschema, uritemplate, - urlparse + RegexURLPattern, RegexURLResolver, coreapi, coreschema ) from rest_framework.request import clone_request -from rest_framework.response import Response from rest_framework.settings import api_settings -from rest_framework.utils import formatting from rest_framework.utils.model_meta import _get_pk -from rest_framework.views import APIView -header_regex = re.compile('^[a-zA-Z][0-9A-Za-z_]*:') - - -def field_to_schema(field): - title = force_text(field.label) if field.label else '' - description = force_text(field.help_text) if field.help_text else '' - - if isinstance(field, (serializers.ListSerializer, serializers.ListField)): - child_schema = field_to_schema(field.child) - return coreschema.Array( - items=child_schema, - title=title, - description=description - ) - elif isinstance(field, serializers.Serializer): - return coreschema.Object( - properties=OrderedDict([ - (key, field_to_schema(value)) - for key, value - in field.fields.items() - ]), - title=title, - description=description - ) - elif isinstance(field, serializers.ManyRelatedField): - return coreschema.Array( - items=coreschema.String(), - title=title, - description=description - ) - elif isinstance(field, serializers.RelatedField): - return coreschema.String(title=title, description=description) - elif isinstance(field, serializers.MultipleChoiceField): - return coreschema.Array( - items=coreschema.Enum(enum=list(field.choices.keys())), - title=title, - description=description - ) - elif isinstance(field, serializers.ChoiceField): - return coreschema.Enum( - enum=list(field.choices.keys()), - title=title, - description=description - ) - elif isinstance(field, serializers.BooleanField): - return coreschema.Boolean(title=title, description=description) - elif isinstance(field, (serializers.DecimalField, serializers.FloatField)): - return coreschema.Number(title=title, description=description) - elif isinstance(field, serializers.IntegerField): - return coreschema.Integer(title=title, description=description) - - if field.style.get('base_template') == 'textarea.html': - return coreschema.String( - title=title, - description=description, - format='textarea' - ) - return coreschema.String(title=title, description=description) +from .utils import is_list_view def common_path(paths): @@ -104,6 +44,8 @@ def is_api_view(callback): """ Return `True` if the given view callback is a REST framework view/viewset. """ + # Avoid import cycle on APIView + from rest_framework.views import APIView cls = getattr(callback, 'cls', None) return (cls is not None) and issubclass(cls, APIView) @@ -130,22 +72,6 @@ def is_custom_action(action): ]) -def is_list_view(path, method, view): - """ - Return True if the given path/method appears to represent a list view. - """ - if hasattr(view, 'action'): - # Viewsets have an explicitly defined action, which we can inspect. - return view.action == 'list' - - if method.lower() != 'get': - return False - path_components = path.strip('/').split('/') - if path_components and '{' in path_components[-1]: - return False - return True - - def endpoint_ordering(endpoint): path, method, callback = endpoint method_priority = { @@ -158,21 +84,7 @@ def endpoint_ordering(endpoint): return (path, method_priority) -def get_pk_description(model, model_field): - if isinstance(model_field, models.AutoField): - value_type = _('unique integer value') - elif isinstance(model_field, models.UUIDField): - value_type = _('UUID string') - else: - value_type = _('unique value') - - return _('A {value_type} identifying this {name}.').format( - value_type=value_type, - name=model._meta.verbose_name, - ) - - -class EndpointInspector(object): +class EndpointEnumerator(object): """ A class to determine the available API endpoints that a project exposes. """ @@ -265,7 +177,7 @@ class SchemaGenerator(object): 'patch': 'partial_update', 'delete': 'destroy', } - endpoint_inspector_cls = EndpointInspector + endpoint_inspector_cls = EndpointEnumerator # Map the method names we use for viewset actions onto external schema names. # These give us names that are more suitable for the external representation. @@ -341,7 +253,7 @@ class SchemaGenerator(object): for path, method, view in view_endpoints: if not self.has_view_permissions(path, method, view): continue - link = self.get_link(path, method, view) + link = view.schema.get_link(path, method, base_url=self.url) subpath = path[len(prefix):] keys = self.get_keys(subpath, method, view) insert_into(links, keys, link) @@ -433,197 +345,6 @@ class SchemaGenerator(object): field_name = 'id' return path.replace('{pk}', '{%s}' % field_name) - # Methods for generating each individual `Link` instance... - - def get_link(self, path, method, view): - """ - Return a `coreapi.Link` instance for the given endpoint. - """ - fields = self.get_path_fields(path, method, view) - fields += self.get_serializer_fields(path, method, view) - fields += self.get_pagination_fields(path, method, view) - fields += self.get_filter_fields(path, method, view) - - if fields and any([field.location in ('form', 'body') for field in fields]): - encoding = self.get_encoding(path, method, view) - else: - encoding = None - - description = self.get_description(path, method, view) - - if self.url and path.startswith('/'): - path = path[1:] - - return coreapi.Link( - url=urlparse.urljoin(self.url, path), - action=method.lower(), - encoding=encoding, - fields=fields, - description=description - ) - - def get_description(self, path, method, view): - """ - Determine a link description. - - This will be based on the method docstring if one exists, - or else the class docstring. - """ - method_name = getattr(view, 'action', method.lower()) - method_docstring = getattr(view, method_name, None).__doc__ - if method_docstring: - # An explicit docstring on the method or action. - return formatting.dedent(smart_text(method_docstring)) - - description = view.get_view_description() - lines = [line.strip() for line in description.splitlines()] - current_section = '' - sections = {'': ''} - - for line in lines: - if header_regex.match(line): - current_section, seperator, lead = line.partition(':') - sections[current_section] = lead.strip() - else: - sections[current_section] += '\n' + line - - header = getattr(view, 'action', method.lower()) - if header in sections: - return sections[header].strip() - if header in self.coerce_method_names: - if self.coerce_method_names[header] in sections: - return sections[self.coerce_method_names[header]].strip() - return sections[''].strip() - - def get_encoding(self, path, method, view): - """ - Return the 'encoding' parameter to use for a given endpoint. - """ - # Core API supports the following request encodings over HTTP... - supported_media_types = set(( - 'application/json', - 'application/x-www-form-urlencoded', - 'multipart/form-data', - )) - parser_classes = getattr(view, 'parser_classes', []) - for parser_class in parser_classes: - media_type = getattr(parser_class, 'media_type', None) - if media_type in supported_media_types: - return media_type - # Raw binary uploads are supported with "application/octet-stream" - if media_type == '*/*': - return 'application/octet-stream' - - return None - - def get_path_fields(self, path, method, view): - """ - Return a list of `coreapi.Field` instances corresponding to any - templated path variables. - """ - model = getattr(getattr(view, 'queryset', None), 'model', None) - fields = [] - - for variable in uritemplate.variables(path): - title = '' - description = '' - schema_cls = coreschema.String - kwargs = {} - if model is not None: - # Attempt to infer a field description if possible. - try: - model_field = model._meta.get_field(variable) - except: - model_field = None - - if model_field is not None and model_field.verbose_name: - title = force_text(model_field.verbose_name) - - if model_field is not None and model_field.help_text: - description = force_text(model_field.help_text) - elif model_field is not None and model_field.primary_key: - description = get_pk_description(model, model_field) - - if hasattr(view, 'lookup_value_regex') and view.lookup_field == variable: - kwargs['pattern'] = view.lookup_value_regex - elif isinstance(model_field, models.AutoField): - schema_cls = coreschema.Integer - - field = coreapi.Field( - name=variable, - location='path', - required=True, - schema=schema_cls(title=title, description=description, **kwargs) - ) - fields.append(field) - - return fields - - def get_serializer_fields(self, path, method, view): - """ - Return a list of `coreapi.Field` instances corresponding to any - request body input, as determined by the serializer class. - """ - if method not in ('PUT', 'PATCH', 'POST'): - return [] - - if not hasattr(view, 'get_serializer'): - return [] - - serializer = view.get_serializer() - - if isinstance(serializer, serializers.ListSerializer): - return [ - coreapi.Field( - name='data', - location='body', - required=True, - schema=coreschema.Array() - ) - ] - - if not isinstance(serializer, serializers.Serializer): - return [] - - fields = [] - for field in serializer.fields.values(): - if field.read_only or isinstance(field, serializers.HiddenField): - continue - - required = field.required and method != 'PATCH' - field = coreapi.Field( - name=field.field_name, - location='form', - required=required, - schema=field_to_schema(field) - ) - fields.append(field) - - return fields - - def get_pagination_fields(self, path, method, view): - if not is_list_view(path, method, view): - return [] - - pagination = getattr(view, 'pagination_class', None) - if not pagination: - return [] - - paginator = view.pagination_class() - return paginator.get_schema_fields(view) - - def get_filter_fields(self, path, method, view): - if not is_list_view(path, method, view): - return [] - - if not getattr(view, 'filter_backends', None): - return [] - - fields = [] - for filter_backend in view.filter_backends: - fields += filter_backend().get_schema_fields(view) - return fields - # Method for generating the link layout.... def get_keys(self, subpath, method, view): @@ -669,45 +390,3 @@ class SchemaGenerator(object): # Default action, eg "/users/", "/users/{pk}/" return named_path_components + [action] - - -class SchemaView(APIView): - _ignore_model_permissions = True - exclude_from_schema = True - renderer_classes = None - schema_generator = None - public = False - - def __init__(self, *args, **kwargs): - super(SchemaView, self).__init__(*args, **kwargs) - if self.renderer_classes is None: - if renderers.BrowsableAPIRenderer in api_settings.DEFAULT_RENDERER_CLASSES: - self.renderer_classes = [ - renderers.CoreJSONRenderer, - renderers.BrowsableAPIRenderer, - ] - else: - self.renderer_classes = [renderers.CoreJSONRenderer] - - def get(self, request, *args, **kwargs): - schema = self.schema_generator.get_schema(request, self.public) - if schema is None: - raise exceptions.PermissionDenied() - return Response(schema) - - -def get_schema_view( - title=None, url=None, description=None, urlconf=None, renderer_classes=None, - public=False, patterns=None, generator_class=SchemaGenerator): - """ - Return a schema view. - """ - generator = generator_class( - title=title, url=url, description=description, - urlconf=urlconf, patterns=patterns, - ) - return SchemaView.as_view( - renderer_classes=renderer_classes, - schema_generator=generator, - public=public, - ) diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py new file mode 100644 index 000000000..cd9fa73da --- /dev/null +++ b/rest_framework/schemas/inspectors.py @@ -0,0 +1,399 @@ +""" +inspectors.py # Per-endpoint view introspection + +See schemas.__init__.py for package overview. +""" +import re +from collections import OrderedDict + +from django.db import models +from django.utils.encoding import force_text, smart_text +from django.utils.translation import ugettext_lazy as _ + +from rest_framework import serializers +from rest_framework.compat import coreapi, coreschema, uritemplate, urlparse +from rest_framework.settings import api_settings +from rest_framework.utils import formatting + +from .utils import is_list_view + +header_regex = re.compile('^[a-zA-Z][0-9A-Za-z_]*:') + + +def field_to_schema(field): + title = force_text(field.label) if field.label else '' + description = force_text(field.help_text) if field.help_text else '' + + if isinstance(field, (serializers.ListSerializer, serializers.ListField)): + child_schema = field_to_schema(field.child) + return coreschema.Array( + items=child_schema, + title=title, + description=description + ) + elif isinstance(field, serializers.Serializer): + return coreschema.Object( + properties=OrderedDict([ + (key, field_to_schema(value)) + for key, value + in field.fields.items() + ]), + title=title, + description=description + ) + elif isinstance(field, serializers.ManyRelatedField): + return coreschema.Array( + items=coreschema.String(), + title=title, + description=description + ) + elif isinstance(field, serializers.RelatedField): + return coreschema.String(title=title, description=description) + elif isinstance(field, serializers.MultipleChoiceField): + return coreschema.Array( + items=coreschema.Enum(enum=list(field.choices.keys())), + title=title, + description=description + ) + elif isinstance(field, serializers.ChoiceField): + return coreschema.Enum( + enum=list(field.choices.keys()), + title=title, + description=description + ) + elif isinstance(field, serializers.BooleanField): + return coreschema.Boolean(title=title, description=description) + elif isinstance(field, (serializers.DecimalField, serializers.FloatField)): + return coreschema.Number(title=title, description=description) + elif isinstance(field, serializers.IntegerField): + return coreschema.Integer(title=title, description=description) + + if field.style.get('base_template') == 'textarea.html': + return coreschema.String( + title=title, + description=description, + format='textarea' + ) + return coreschema.String(title=title, description=description) + + +def get_pk_description(model, model_field): + if isinstance(model_field, models.AutoField): + value_type = _('unique integer value') + elif isinstance(model_field, models.UUIDField): + value_type = _('UUID string') + else: + value_type = _('unique value') + + return _('A {value_type} identifying this {name}.').format( + value_type=value_type, + name=model._meta.verbose_name, + ) + + +class ViewInspector(object): + """ + Descriptor class on APIView. + + Provide subclass for per-view schema generation + """ + def __get__(self, instance, owner): + """ + Enables `ViewInspector` as a Python _Descriptor_. + + This is how `view.schema` knows about `view`. + + `__get__` is called when the descriptor is accessed on the owner. + (That will be when view.schema is called in our case.) + + `owner` is always the owner class. (An APIView, or subclass for us.) + `instance` is the view instance or `None` if accessed from the class, + rather than an instance. + + See: https://docs.python.org/3/howto/descriptor.html for info on + descriptor usage. + """ + self.view = instance + return self + + @property + def view(self): + """View property.""" + assert self._view is not None, "Schema generation REQUIRES a view instance. (Hint: you accessed `schema` from the view class rather than an instance.)" + return self._view + + @view.setter + def view(self, value): + self._view = value + + @view.deleter + def view(self): + self._view = None + + def get_link(self, path, method, base_url): + """ + Generate `coreapi.Link` for self.view, path and method. + + This is the main _public_ access point. + + Parameters: + + * path: Route path for view from URLConf. + * method: The HTTP request method. + * base_url: The project "mount point" as given to SchemaGenerator + """ + raise NotImplementedError(".get_link() must be overridden.") + + +class AutoSchema(ViewInspector): + """ + Default inspector for APIView + + Responsible for per-view instrospection and schema generation. + """ + def __init__(self, manual_fields=None): + """ + Parameters: + + * `manual_fields`: list of `coreapi.Field` instances that + will be added to auto-generated fields, overwriting on `Field.name` + """ + + self._manual_fields = manual_fields + + def get_link(self, path, method, base_url): + fields = self.get_path_fields(path, method) + fields += self.get_serializer_fields(path, method) + fields += self.get_pagination_fields(path, method) + fields += self.get_filter_fields(path, method) + + if self._manual_fields is not None: + by_name = {f.name: f for f in fields} + for f in self._manual_fields: + by_name[f.name] = f + fields = list(by_name.values()) + + if fields and any([field.location in ('form', 'body') for field in fields]): + encoding = self.get_encoding(path, method) + else: + encoding = None + + description = self.get_description(path, method) + + if base_url and path.startswith('/'): + path = path[1:] + + return coreapi.Link( + url=urlparse.urljoin(base_url, path), + action=method.lower(), + encoding=encoding, + fields=fields, + description=description + ) + + def get_description(self, path, method): + """ + Determine a link description. + + This will be based on the method docstring if one exists, + or else the class docstring. + """ + view = self.view + + method_name = getattr(view, 'action', method.lower()) + method_docstring = getattr(view, method_name, None).__doc__ + if method_docstring: + # An explicit docstring on the method or action. + return formatting.dedent(smart_text(method_docstring)) + + description = view.get_view_description() + lines = [line.strip() for line in description.splitlines()] + current_section = '' + sections = {'': ''} + + for line in lines: + if header_regex.match(line): + current_section, seperator, lead = line.partition(':') + sections[current_section] = lead.strip() + else: + sections[current_section] += '\n' + line + + # TODO: SCHEMA_COERCE_METHOD_NAMES appears here and in `SchemaGenerator.get_keys` + coerce_method_names = api_settings.SCHEMA_COERCE_METHOD_NAMES + header = getattr(view, 'action', method.lower()) + if header in sections: + return sections[header].strip() + if header in coerce_method_names: + if coerce_method_names[header] in sections: + return sections[coerce_method_names[header]].strip() + return sections[''].strip() + + def get_path_fields(self, path, method): + """ + Return a list of `coreapi.Field` instances corresponding to any + templated path variables. + """ + view = self.view + model = getattr(getattr(view, 'queryset', None), 'model', None) + fields = [] + + for variable in uritemplate.variables(path): + title = '' + description = '' + schema_cls = coreschema.String + kwargs = {} + if model is not None: + # Attempt to infer a field description if possible. + try: + model_field = model._meta.get_field(variable) + except: + model_field = None + + if model_field is not None and model_field.verbose_name: + title = force_text(model_field.verbose_name) + + if model_field is not None and model_field.help_text: + description = force_text(model_field.help_text) + elif model_field is not None and model_field.primary_key: + description = get_pk_description(model, model_field) + + if hasattr(view, 'lookup_value_regex') and view.lookup_field == variable: + kwargs['pattern'] = view.lookup_value_regex + elif isinstance(model_field, models.AutoField): + schema_cls = coreschema.Integer + + field = coreapi.Field( + name=variable, + location='path', + required=True, + schema=schema_cls(title=title, description=description, **kwargs) + ) + fields.append(field) + + return fields + + def get_serializer_fields(self, path, method): + """ + Return a list of `coreapi.Field` instances corresponding to any + request body input, as determined by the serializer class. + """ + view = self.view + + if method not in ('PUT', 'PATCH', 'POST'): + return [] + + if not hasattr(view, 'get_serializer'): + return [] + + serializer = view.get_serializer() + + if isinstance(serializer, serializers.ListSerializer): + return [ + coreapi.Field( + name='data', + location='body', + required=True, + schema=coreschema.Array() + ) + ] + + if not isinstance(serializer, serializers.Serializer): + return [] + + fields = [] + for field in serializer.fields.values(): + if field.read_only or isinstance(field, serializers.HiddenField): + continue + + required = field.required and method != 'PATCH' + field = coreapi.Field( + name=field.field_name, + location='form', + required=required, + schema=field_to_schema(field) + ) + fields.append(field) + + return fields + + def get_pagination_fields(self, path, method): + view = self.view + + if not is_list_view(path, method, view): + return [] + + pagination = getattr(view, 'pagination_class', None) + if not pagination: + return [] + + paginator = view.pagination_class() + return paginator.get_schema_fields(view) + + def get_filter_fields(self, path, method): + view = self.view + + if not is_list_view(path, method, view): + return [] + + if not getattr(view, 'filter_backends', None): + return [] + + fields = [] + for filter_backend in view.filter_backends: + fields += filter_backend().get_schema_fields(view) + return fields + + def get_encoding(self, path, method): + """ + Return the 'encoding' parameter to use for a given endpoint. + """ + view = self.view + + # Core API supports the following request encodings over HTTP... + supported_media_types = set(( + 'application/json', + 'application/x-www-form-urlencoded', + 'multipart/form-data', + )) + parser_classes = getattr(view, 'parser_classes', []) + for parser_class in parser_classes: + media_type = getattr(parser_class, 'media_type', None) + if media_type in supported_media_types: + return media_type + # Raw binary uploads are supported with "application/octet-stream" + if media_type == '*/*': + return 'application/octet-stream' + + return None + + +class ManualSchema(ViewInspector): + """ + Allows providing a list of coreapi.Fields, + plus an optional description. + """ + def __init__(self, fields, description=''): + """ + Parameters: + + * `fields`: list of `coreapi.Field` instances. + * `descripton`: String description for view. Optional. + """ + 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 + + def get_link(self, path, method, base_url): + + if base_url and path.startswith('/'): + path = path[1:] + + return coreapi.Link( + url=urlparse.urljoin(base_url, path), + action=method.lower(), + encoding=None, + fields=self._fields, + description=self._description + ) + + return self._link diff --git a/rest_framework/schemas/utils.py b/rest_framework/schemas/utils.py new file mode 100644 index 000000000..1542b6154 --- /dev/null +++ b/rest_framework/schemas/utils.py @@ -0,0 +1,21 @@ +""" +utils.py # Shared helper functions + +See schemas.__init__.py for package overview. +""" + + +def is_list_view(path, method, view): + """ + Return True if the given path/method appears to represent a list view. + """ + if hasattr(view, 'action'): + # Viewsets have an explicitly defined action, which we can inspect. + return view.action == 'list' + + if method.lower() != 'get': + return False + path_components = path.strip('/').split('/') + if path_components and '{' in path_components[-1]: + return False + return True diff --git a/rest_framework/schemas/views.py b/rest_framework/schemas/views.py new file mode 100644 index 000000000..932b5a487 --- /dev/null +++ b/rest_framework/schemas/views.py @@ -0,0 +1,34 @@ +""" +views.py # Houses `SchemaView`, `APIView` subclass. + +See schemas.__init__.py for package overview. +""" +from rest_framework import exceptions, renderers +from rest_framework.response import Response +from rest_framework.settings import api_settings +from rest_framework.views import APIView + + +class SchemaView(APIView): + _ignore_model_permissions = True + exclude_from_schema = True + renderer_classes = None + schema_generator = None + public = False + + def __init__(self, *args, **kwargs): + super(SchemaView, self).__init__(*args, **kwargs) + if self.renderer_classes is None: + if renderers.BrowsableAPIRenderer in api_settings.DEFAULT_RENDERER_CLASSES: + self.renderer_classes = [ + renderers.CoreJSONRenderer, + renderers.BrowsableAPIRenderer, + ] + else: + self.renderer_classes = [renderers.CoreJSONRenderer] + + def get(self, request, *args, **kwargs): + schema = self.schema_generator.get_schema(request, self.public) + if schema is None: + raise exceptions.PermissionDenied() + return Response(schema) diff --git a/rest_framework/views.py b/rest_framework/views.py index 8ec5f14ab..ccc2047ee 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -19,6 +19,7 @@ from rest_framework import exceptions, status from rest_framework.compat import set_rollback from rest_framework.request import Request from rest_framework.response import Response +from rest_framework.schemas import AutoSchema from rest_framework.settings import api_settings from rest_framework.utils import formatting @@ -113,6 +114,7 @@ class APIView(View): # Mark the view as being included or excluded from schema generation. exclude_from_schema = False + schema = AutoSchema() @classmethod def as_view(cls, **initkwargs): diff --git a/tests/test_decorators.py b/tests/test_decorators.py index b187e5fd6..6331742db 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -6,12 +6,13 @@ 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, throttle_classes + renderer_classes, schema, throttle_classes ) from rest_framework.parsers import JSONParser from rest_framework.permissions import IsAuthenticated from rest_framework.renderers import JSONRenderer from rest_framework.response import Response +from rest_framework.schemas import AutoSchema from rest_framework.test import APIRequestFactory from rest_framework.throttling import UserRateThrottle from rest_framework.views import APIView @@ -151,3 +152,17 @@ class DecoratorTestCase(TestCase): response = view(request) assert response.status_code == status.HTTP_429_TOO_MANY_REQUESTS + + def test_schema(self): + """ + Checks CustomSchema class is set on view + """ + class CustomSchema(AutoSchema): + pass + + @api_view(['GET']) + @schema(CustomSchema()) + def view(request): + return Response({}) + + assert isinstance(view.cls.schema, CustomSchema) diff --git a/tests/test_schemas.py b/tests/test_schemas.py index b435dfdd7..14ed0f6b6 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -1,5 +1,6 @@ import unittest +import pytest from django.conf.urls import include, url from django.core.exceptions import PermissionDenied from django.http import Http404 @@ -10,7 +11,9 @@ from rest_framework.compat import coreapi, coreschema from rest_framework.decorators import detail_route, list_route from rest_framework.request import Request from rest_framework.routers import DefaultRouter -from rest_framework.schemas import SchemaGenerator, get_schema_view +from rest_framework.schemas import ( + AutoSchema, ManualSchema, SchemaGenerator, get_schema_view +) from rest_framework.test import APIClient, APIRequestFactory from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet @@ -496,3 +499,81 @@ class Test4605Regression(TestCase): '/auth/convert-token/' ]) assert prefix == '/' + + +class TestDescriptor(TestCase): + + def test_apiview_schema_descriptor(self): + view = APIView() + assert hasattr(view, 'schema') + assert isinstance(view.schema, AutoSchema) + + def test_get_link_requires_instance(self): + descriptor = APIView.schema # Accessed from class + with pytest.raises(AssertionError): + descriptor.get_link(None, None, None) # ???: Do the dummy arguments require a tighter assert? + + def test_manual_fields(self): + + class CustomView(APIView): + schema = AutoSchema(manual_fields=[ + coreapi.Field( + "my_extra_field", + required=True, + location="path", + schema=coreschema.String() + ), + ]) + + view = CustomView() + link = view.schema.get_link('/a/url/{id}/', 'GET', '') + fields = link.fields + + assert len(fields) == 2 + assert "my_extra_field" in [f.name for f in fields] + + def test_view_with_manual_schema(self): + + path = '/example' + method = 'get' + base_url = None + + fields = [ + coreapi.Field( + "first_field", + required=True, + location="path", + schema=coreschema.String() + ), + coreapi.Field( + "second_field", + required=True, + location="path", + schema=coreschema.String() + ), + coreapi.Field( + "third_field", + required=True, + location="path", + schema=coreschema.String() + ), + ] + description = "A test endpoint" + + class CustomView(APIView): + """ + ManualSchema takes list of fields for endpoint. + - Provides url and action, which are always dynamic + """ + schema = ManualSchema(fields, description) + + expected = coreapi.Link( + url=path, + action=method, + fields=fields, + description=description + ) + + view = CustomView() + link = view.schema.get_link(path, method, base_url) + assert link == expected From efff9ff338bb0994b3249fa9907bb7018d7b6d5e Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Thu, 14 Sep 2017 13:20:41 +0200 Subject: [PATCH 111/730] 5378 fix schema generation markdown (#5421) * Test case for #5240 * Remove unnecessary strip() from get_description Closes #5240 * Adjust test case --- rest_framework/schemas/inspectors.py | 2 +- tests/test_schemas.py | 36 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index cd9fa73da..a91205cde 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -207,7 +207,7 @@ class AutoSchema(ViewInspector): return formatting.dedent(smart_text(method_docstring)) description = view.get_view_description() - lines = [line.strip() for line in description.splitlines()] + lines = [line for line in description.splitlines()] current_section = '' sections = {'': ''} diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 14ed0f6b6..f8a63aa89 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -15,6 +15,7 @@ from rest_framework.schemas import ( AutoSchema, ManualSchema, SchemaGenerator, get_schema_view ) from rest_framework.test import APIClient, APIRequestFactory +from rest_framework.utils import formatting from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet @@ -577,3 +578,38 @@ class TestDescriptor(TestCase): view = CustomView() link = view.schema.get_link(path, method, base_url) assert link == expected + + +def test_docstring_is_not_stripped_by_get_description(): + class ExampleDocstringAPIView(APIView): + """ + === title + + * item a + * item a-a + * item a-b + * item b + + - item 1 + - item 2 + + code block begin + code + code + code + code block end + + the end + """ + + def get(self, *args, **kwargs): + pass + + def post(self, request, *args, **kwargs): + pass + + view = ExampleDocstringAPIView() + schema = view.schema + descr = schema.get_description('example', 'get') + # the first and last character are '\n' correctly removed by get_description + assert descr == formatting.dedent(ExampleDocstringAPIView.__doc__[1:][:-1]) From 7b1582e00e91001ec07cd394520ec5cdd2c2add1 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 20 Sep 2017 11:29:47 +0200 Subject: [PATCH 112/730] Allow `schema = None`. Deprecate `exclude_from_schema` (#5422) * Add tests for schema exclusions * Move exclusion check to should_include_endpoint * Update docs * Switch to using `schema = None` * Test PendingDeprecationWarnings * Add note to release notes. * s/deprecated/pending deprecation/ * Add PR link to release notes * Correct typo in test class name * Test 'exclude_from_schema' deprecation warning message (#1) * Correct deprecation warning message --- docs/api-guide/schemas.md | 6 ++ docs/api-guide/views.md | 17 +++-- docs/topics/release-notes.md | 3 + rest_framework/decorators.py | 10 ++- rest_framework/routers.py | 2 +- rest_framework/schemas/generators.py | 14 +++- rest_framework/schemas/views.py | 2 +- rest_framework/views.py | 2 - tests/test_schemas.py | 109 ++++++++++++++++++++++++++- 9 files changed, 149 insertions(+), 16 deletions(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index f913f046f..fc848199c 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -225,6 +225,12 @@ To customise the `Link` generation you may: This allows manually specifying the schema for some views whilst maintaining automatic generation elsewhere. +You may disable schema generation for a view by setting `schema` to `None`: + + class CustomView(APIView): + ... + schema = None # Will not appear in schema + --- **Note**: For full details on `SchemaGenerator` plus the `AutoSchema` and diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 24dd42578..2f9f51cf9 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -130,7 +130,7 @@ REST framework also allows you to work with regular function based views. It pr ## @api_view() -**Signature:** `@api_view(http_method_names=['GET'], exclude_from_schema=False)` +**Signature:** `@api_view(http_method_names=['GET'])` 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: @@ -150,12 +150,6 @@ By default only `GET` methods will be accepted. Other methods will respond with return Response({"message": "Got some data!", "data": request.data}) return Response({"message": "Hello, world!"}) -You can also mark an API view as being omitted from any [auto-generated schema][schemas], -using the `exclude_from_schema` argument.: - - @api_view(['GET'], exclude_from_schema=True) - def api_docs(request): - ... ## API policy decorators @@ -204,7 +198,14 @@ decorator. For example: return Response({"message": "Hello for today! See you tomorrow!"}) This decorator takes a single `AutoSchema` instance, an `AutoSchema` subclass -instance or `ManualSchema` instance as described in the [Schemas documentation][schemas], +instance or `ManualSchema` instance as described in the [Schemas documentation][schemas]. +You may pass `None` in order to exclude the view from schema generation. + + @api_view(['GET']) + @schema(None) + def view(request): + return Response({"message": "Will not appear in schema!"}) + [cite]: http://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 diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index f4617ac23..b9fff2ce0 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -43,6 +43,8 @@ You can determine your currently installed version using `pip freeze`: ### 3.6.5 * Fix `DjangoModelPermissions` to ensure user authentication before calling the view's `get_queryset()` method. As a side effect, this changes the order of the HTTP method permissions and authentication checks, and 405 responses will only be returned when authenticated. If you want to replicate the old behavior, see the PR for details. [#5376][gh5376] +* Deprecated `exclude_from_schema` on `APIView` and `api_view` decorator. Set `schema = None` or `@schema(None)` as appropriate. [#5422][gh5422] + ### 3.6.4 @@ -1423,3 +1425,4 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh5376]: https://github.com/encode/django-rest-framework/issues/5376 +[gh5422]: https://github.com/encode/django-rest-framework/issues/5422 diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index 1297f96b4..cdbd59e99 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -9,6 +9,7 @@ used to annotate methods on viewsets that should be included by routers. from __future__ import unicode_literals import types +import warnings from django.utils import six @@ -75,7 +76,14 @@ def api_view(http_method_names=None, exclude_from_schema=False): WrappedAPIView.schema = getattr(func, 'schema', APIView.schema) - WrappedAPIView.exclude_from_schema = exclude_from_schema + if exclude_from_schema: + warnings.warn( + "The `exclude_from_schema` argument to `api_view` is pending deprecation. " + "Use the `schema` decorator instead, passing `None`.", + PendingDeprecationWarning + ) + WrappedAPIView.exclude_from_schema = exclude_from_schema + return WrappedAPIView.as_view() return decorator diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 01daa7e7d..3b5ef46d8 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -291,7 +291,7 @@ class APIRootView(views.APIView): The default basic root view for DefaultRouter """ _ignore_model_permissions = True - exclude_from_schema = True + schema = None # exclude from schema api_root_dict = None def get(self, request, *args, **kwargs): diff --git a/rest_framework/schemas/generators.py b/rest_framework/schemas/generators.py index 8344f64f0..3e927527c 100644 --- a/rest_framework/schemas/generators.py +++ b/rest_framework/schemas/generators.py @@ -3,6 +3,7 @@ generators.py # Top-down schema generation See schemas.__init__.py for package overview. """ +import warnings from collections import OrderedDict from importlib import import_module @@ -148,6 +149,17 @@ class EndpointEnumerator(object): if not is_api_view(callback): return False # Ignore anything except REST framework views. + if hasattr(callback.cls, 'exclude_from_schema'): + fmt = ("The `{}.exclude_from_schema` attribute is pending deprecation. " + "Set `schema = None` instead.") + msg = fmt.format(callback.cls.__name__) + warnings.warn(msg, PendingDeprecationWarning) + if getattr(callback.cls, 'exclude_from_schema', False): + return False + + if callback.cls.schema is None: + return False + if path.endswith('.{format}') or path.endswith('.{format}/'): return False # Ignore .json style URLs. @@ -239,8 +251,6 @@ class SchemaGenerator(object): view_endpoints = [] for path, method, callback in self.endpoints: view = self.create_view(callback, method, request) - if getattr(view, 'exclude_from_schema', False): - continue path = self.coerce_path(path, method, view) paths.append(path) view_endpoints.append((path, method, view)) diff --git a/rest_framework/schemas/views.py b/rest_framework/schemas/views.py index 932b5a487..b13eadea9 100644 --- a/rest_framework/schemas/views.py +++ b/rest_framework/schemas/views.py @@ -11,7 +11,7 @@ from rest_framework.views import APIView class SchemaView(APIView): _ignore_model_permissions = True - exclude_from_schema = True + schema = None # exclude from schema renderer_classes = None schema_generator = None public = False diff --git a/rest_framework/views.py b/rest_framework/views.py index ccc2047ee..dfed15888 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -112,8 +112,6 @@ class APIView(View): # Allow dependency injection of other settings to make testing easier. settings = api_settings - # Mark the view as being included or excluded from schema generation. - exclude_from_schema = False schema = AutoSchema() @classmethod diff --git a/tests/test_schemas.py b/tests/test_schemas.py index f8a63aa89..184401a86 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -8,12 +8,15 @@ from django.test import TestCase, override_settings from rest_framework import filters, pagination, permissions, serializers from rest_framework.compat import coreapi, coreschema -from rest_framework.decorators import detail_route, list_route +from rest_framework.decorators import ( + api_view, detail_route, list_route, schema +) from rest_framework.request import Request from rest_framework.routers import DefaultRouter from rest_framework.schemas import ( AutoSchema, ManualSchema, SchemaGenerator, get_schema_view ) +from rest_framework.schemas.generators import EndpointEnumerator from rest_framework.test import APIClient, APIRequestFactory from rest_framework.utils import formatting from rest_framework.views import APIView @@ -613,3 +616,107 @@ def test_docstring_is_not_stripped_by_get_description(): descr = schema.get_description('example', 'get') # the first and last character are '\n' correctly removed by get_description assert descr == formatting.dedent(ExampleDocstringAPIView.__doc__[1:][:-1]) + + +# Views for SchemaGenerationExclusionTests +class ExcludedAPIView(APIView): + schema = None + + def get(self, request, *args, **kwargs): + pass + + +@api_view(['GET']) +@schema(None) +def excluded_fbv(request): + pass + + +@api_view(['GET']) +def included_fbv(request): + pass + + +@unittest.skipUnless(coreapi, 'coreapi is not installed') +class SchemaGenerationExclusionTests(TestCase): + def setUp(self): + self.patterns = [ + url('^excluded-cbv/$', ExcludedAPIView.as_view()), + url('^excluded-fbv/$', excluded_fbv), + url('^included-fbv/$', included_fbv), + ] + + def test_schema_generator_excludes_correctly(self): + """Schema should not include excluded views""" + generator = SchemaGenerator(title='Exclusions', patterns=self.patterns) + schema = generator.get_schema() + expected = coreapi.Document( + url='', + title='Exclusions', + content={ + 'included-fbv': { + 'list': coreapi.Link(url='/included-fbv/', action='get') + } + } + ) + + assert len(schema.data) == 1 + assert 'included-fbv' in schema.data + assert schema == expected + + def test_endpoint_enumerator_excludes_correctly(self): + """It is responsibility of EndpointEnumerator to exclude views""" + inspector = EndpointEnumerator(self.patterns) + endpoints = inspector.get_api_endpoints() + + assert len(endpoints) == 1 + path, method, callback = endpoints[0] + assert path == '/included-fbv/' + + def test_should_include_endpoint_excludes_correctly(self): + """This is the specific method that should handle the exclusion""" + inspector = EndpointEnumerator(self.patterns) + + # Not pretty. Mimics internals of EndpointEnumerator to put should_include_endpoint under test + pairs = [(inspector.get_path_from_regex(pattern.regex.pattern), pattern.callback) + for pattern in self.patterns] + + should_include = [ + inspector.should_include_endpoint(*pair) for pair in pairs + ] + + expected = [False, False, True] + + assert should_include == expected + + def test_deprecations(self): + with pytest.warns(PendingDeprecationWarning) as record: + @api_view(["GET"], exclude_from_schema=True) + def view(request): + pass + + assert len(record) == 1 + assert str(record[0].message) == ( + "The `exclude_from_schema` argument to `api_view` is pending " + "deprecation. Use the `schema` decorator instead, passing `None`." + ) + + class OldFashionedExcludedView(APIView): + exclude_from_schema = True + + def get(self, request, *args, **kwargs): + pass + + patterns = [ + url('^excluded-old-fashioned/$', OldFashionedExcludedView.as_view()), + ] + + inspector = EndpointEnumerator(patterns) + with pytest.warns(PendingDeprecationWarning) as record: + inspector.get_api_endpoints() + + assert len(record) == 1 + assert str(record[0].message) == ( + "The `OldFashionedExcludedView.exclude_from_schema` attribute is " + "pending deprecation. Set `schema = None` instead." + ) From c0a48622e150badb5440792d3e4b7cb3568a2147 Mon Sep 17 00:00:00 2001 From: Jeremy Nauta Date: Wed, 20 Sep 2017 03:33:50 -0600 Subject: [PATCH 113/730] Allow `ChoiceField.choices` to be set dynamically (#5426) ## Description The `choices` field for the `ChoiceField` class should be able to be edited after `ChoiceField.__init__` is called. ``` field = ChoiceField(choices=[1,2]) field.choices = [1] # Should no longer allow `2` as a choice ``` Currently, you must update `choices`, `grouped_choices`, and `choice_strings_to_values` to achieve this. This P/R keeps `grouped_choices` and `choice_strings_to_values` in sync whenever the `choices` are edited. --- rest_framework/fields.py | 26 +++++++++++++++++--------- tests/test_fields.py | 13 +++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index d2079d5d6..63df452ce 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1337,18 +1337,10 @@ class ChoiceField(Field): html_cutoff_text = _('More than {count} items...') def __init__(self, choices, **kwargs): - self.grouped_choices = to_choices_dict(choices) - self.choices = flatten_choices_dict(self.grouped_choices) + self.choices = choices self.html_cutoff = kwargs.pop('html_cutoff', self.html_cutoff) self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text) - # Map the string representation of choices to the underlying value. - # 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() - } - self.allow_blank = kwargs.pop('allow_blank', False) super(ChoiceField, self).__init__(**kwargs) @@ -1377,6 +1369,22 @@ class ChoiceField(Field): cutoff_text=self.html_cutoff_text ) + def _get_choices(self): + return self._choices + + def _set_choices(self, choices): + self.grouped_choices = to_choices_dict(choices) + self._choices = flatten_choices_dict(self.grouped_choices) + + # Map the string representation of choices to the underlying value. + # 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() + } + + choices = property(_get_choices, _set_choices) + class MultipleChoiceField(ChoiceField): default_error_messages = { diff --git a/tests/test_fields.py b/tests/test_fields.py index 4173e6ab5..011fbe7de 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1425,6 +1425,19 @@ class TestChoiceField(FieldValues): assert items[9].value == 'boolean' + def test_edit_choices(self): + field = serializers.ChoiceField( + allow_null=True, + choices=[ + 1, 2, + ] + ) + field.choices = [1] + assert field.run_validation(1) is 1 + with pytest.raises(serializers.ValidationError) as exc_info: + field.run_validation(2) + assert exc_info.value.detail == ['"2" is not a valid choice.'] + class TestChoiceFieldWithType(FieldValues): """ From 89daaf6276805fbe9aef7524130b1a917da15366 Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Wed, 20 Sep 2017 12:05:04 +0200 Subject: [PATCH 114/730] Add the project layout to the quickstart to have a milestone for the project creation. (#5434) --- docs/tutorial/quickstart.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md index e8a4c8ef6..31361413e 100644 --- a/docs/tutorial/quickstart.md +++ b/docs/tutorial/quickstart.md @@ -24,6 +24,30 @@ Create a new Django project named `tutorial`, then start a new app called `quick django-admin.py startapp quickstart cd .. +The project layout should look like: + + $ pwd + /tutorial + $ find . + . + ./manage.py + ./tutorial + ./tutorial/__init__.py + ./tutorial/quickstart + ./tutorial/quickstart/__init__.py + ./tutorial/quickstart/admin.py + ./tutorial/quickstart/apps.py + ./tutorial/quickstart/migrations + ./tutorial/quickstart/migrations/__init__.py + ./tutorial/quickstart/models.py + ./tutorial/quickstart/tests.py + ./tutorial/quickstart/views.py + ./tutorial/settings.py + ./tutorial/urls.py + ./tutorial/wsgi.py + +It may look unusual that the application has been created within the project directory. Using the project's namespace avoids name clashes with external module (topic goes outside the scope of the quickstart). + Now sync your database for the first time: python manage.py migrate From 7d6d043531845c8fdfbec7e62d1fe52ca04a0acf Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 20 Sep 2017 12:15:15 +0200 Subject: [PATCH 115/730] Fix DateTimeField TZ handling (#5435) * Add failing TZ tests for DateTimeField - tests "current" timezone activation - tests output for non-UTC timezones * Update DateTimeField TZ aware/naive test output * Fix DateTimeField TZ handling * Add Release Note for BC change --- docs/topics/release-notes.md | 7 +++ requirements/requirements-optionals.txt | 1 + rest_framework/fields.py | 7 ++- tests/test_fields.py | 60 +++++++++++++++++++++++-- 4 files changed, 70 insertions(+), 5 deletions(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index b9fff2ce0..5744771b3 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -44,7 +44,11 @@ You can determine your currently installed version using `pip freeze`: * Fix `DjangoModelPermissions` to ensure user authentication before calling the view's `get_queryset()` method. As a side effect, this changes the order of the HTTP method permissions and authentication checks, and 405 responses will only be returned when authenticated. If you want to replicate the old behavior, see the PR for details. [#5376][gh5376] * Deprecated `exclude_from_schema` on `APIView` and `api_view` decorator. Set `schema = None` or `@schema(None)` as appropriate. [#5422][gh5422] +* Timezone-aware `DateTimeField`s now respect active or default) `timezone` during serialization, instead of always using UTC. + Resolves inconsistency whereby instances were serialised with supplied datetime for `create` but UTC for `retrieve`. [#3732][gh3732] + + **Possible backwards compatibility break** if you were relying on datetime strings being UTC. Have client interpret datetimes or [set default or active timezone (docs)][djangodocs-set-timezone] to UTC if needed. ### 3.6.4 @@ -1426,3 +1430,6 @@ For older release notes, [please see the version 2.x documentation][old-release- [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 diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt index e8ba50851..319b7547a 100644 --- a/requirements/requirements-optionals.txt +++ b/requirements/requirements-optionals.txt @@ -1,4 +1,5 @@ # Optional packages which may be used with REST framework. +pytz==2017.2 markdown==2.6.4 django-guardian==1.4.8 django-filter==1.0.4 diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 63df452ce..072bbf1b9 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1125,7 +1125,9 @@ class DateTimeField(Field): """ field_timezone = getattr(self, 'timezone', self.default_timezone()) - if (field_timezone is not None) and not timezone.is_aware(value): + if field_timezone is not None: + if timezone.is_aware(value): + return value.astimezone(field_timezone) try: return timezone.make_aware(value, field_timezone) except InvalidTimeError: @@ -1135,7 +1137,7 @@ class DateTimeField(Field): return value def default_timezone(self): - return timezone.get_default_timezone() if settings.USE_TZ else None + return timezone.get_current_timezone() if settings.USE_TZ else None def to_internal_value(self, value): input_formats = getattr(self, 'input_formats', api_settings.DATETIME_INPUT_FORMATS) @@ -1174,6 +1176,7 @@ class DateTimeField(Field): return value if output_format.lower() == ISO_8601: + value = self.enforce_timezone(value) value = value.isoformat() if value.endswith('+00:00'): value = value[:-6] + 'Z' diff --git a/tests/test_fields.py b/tests/test_fields.py index 011fbe7de..c2b053189 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -10,12 +10,17 @@ import pytest from django.http import QueryDict from django.test import TestCase, override_settings from django.utils import six -from django.utils.timezone import utc +from django.utils.timezone import activate, deactivate, utc import rest_framework from rest_framework import compat, serializers from rest_framework.fields import is_simple_callable +try: + import pytz +except ImportError: + pytz = None + try: import typings except ImportError: @@ -1168,7 +1173,7 @@ class TestDateTimeField(FieldValues): datetime.date(2001, 1, 1): ['Expected a datetime but got a date.'], } outputs = { - datetime.datetime(2001, 1, 1, 13, 00): '2001-01-01T13:00:00', + datetime.datetime(2001, 1, 1, 13, 00): '2001-01-01T13:00:00Z', datetime.datetime(2001, 1, 1, 13, 00, tzinfo=utc): '2001-01-01T13:00:00Z', '2001-01-01T00:00:00': '2001-01-01T00:00:00', six.text_type('2016-01-10T00:00:00'): '2016-01-10T00:00:00', @@ -1230,10 +1235,59 @@ class TestNaiveDateTimeField(FieldValues): '2001-01-01 13:00': datetime.datetime(2001, 1, 1, 13, 00), } invalid_inputs = {} - outputs = {} + outputs = { + datetime.datetime(2001, 1, 1, 13, 00): '2001-01-01T13:00:00', + datetime.datetime(2001, 1, 1, 13, 00, tzinfo=utc): '2001-01-01T13:00:00', + } field = serializers.DateTimeField(default_timezone=None) +@pytest.mark.skipif(pytz is None, reason='pytz not installed') +class TestTZWithDateTimeField(FieldValues): + """ + Valid and invalid values for `DateTimeField` when not using UTC as the timezone. + """ + @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') + + 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)), + } + cls.invalid_inputs = {} + cls.outputs = { + datetime.datetime(2016, 12, 19, 10): '2016-12-19T10:00:00+05:30', + datetime.datetime(2016, 12, 19, 4, 30, tzinfo=utc): '2016-12-19T10:00:00+05:30', + } + cls.field = serializers.DateTimeField(default_timezone=kolkata) + + +@pytest.mark.skipif(pytz is None, reason='pytz not installed') +@override_settings(TIME_ZONE='UTC', USE_TZ=True) +class TestDefaultTZDateTimeField(TestCase): + """ + Test the current/default timezone handling in `DateTimeField`. + """ + + @classmethod + def setup_class(cls): + cls.field = serializers.DateTimeField() + cls.kolkata = pytz.timezone('Asia/Kolkata') + + def test_default_timezone(self): + assert self.field.default_timezone() == utc + + def test_current_timezone(self): + assert self.field.default_timezone() == utc + activate(self.kolkata) + assert self.field.default_timezone() == self.kolkata + deactivate() + assert self.field.default_timezone() == utc + + class TestNaiveDayLightSavingTimeTimeZoneDateTimeField(FieldValues): """ Invalid values for `DateTimeField` with datetime in DST shift (non-existing or ambiguous) and timezone with DST. From cb6e7e0fdd3fe621926014d0a4612fc923c78ade Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 20 Sep 2017 14:23:34 +0200 Subject: [PATCH 116/730] Drop erroneous `)` in release notes --- docs/topics/release-notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 5744771b3..ef43df670 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -44,7 +44,7 @@ You can determine your currently installed version using `pip freeze`: * Fix `DjangoModelPermissions` to ensure user authentication before calling the view's `get_queryset()` method. As a side effect, this changes the order of the HTTP method permissions and authentication checks, and 405 responses will only be returned when authenticated. If you want to replicate the old behavior, see the PR for details. [#5376][gh5376] * Deprecated `exclude_from_schema` on `APIView` and `api_view` decorator. Set `schema = None` or `@schema(None)` as appropriate. [#5422][gh5422] -* Timezone-aware `DateTimeField`s now respect active or default) `timezone` during serialization, instead of always using UTC. +* Timezone-aware `DateTimeField`s now respect active or default `timezone` during serialization, instead of always using UTC. Resolves inconsistency whereby instances were serialised with supplied datetime for `create` but UTC for `retrieve`. [#3732][gh3732] From f6c19e5eacbdc8a132e996f66f019994f34fda70 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Fri, 14 Jul 2017 12:05:42 -0400 Subject: [PATCH 117/730] Remove DjangoFilterBackend and associated tests --- rest_framework/compat.py | 7 - rest_framework/filters.py | 41 +--- tests/models.py | 9 - tests/test_filters.py | 424 +------------------------------------- 4 files changed, 2 insertions(+), 479 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 168bccf83..528340d69 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -182,13 +182,6 @@ except ImportError: coreschema = None -# django-filter is optional -try: - import django_filters -except ImportError: - django_filters = None - - # django-crispy-forms is optional try: import crispy_forms diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 63ebf05ef..0473787bb 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -5,7 +5,6 @@ returned by list views. from __future__ import unicode_literals import operator -import warnings from functools import reduce from django.core.exceptions import ImproperlyConfigured @@ -18,7 +17,7 @@ from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _ from rest_framework.compat import ( - coreapi, coreschema, distinct, django_filters, guardian, template_render + coreapi, coreschema, distinct, guardian, template_render ) from rest_framework.settings import api_settings @@ -40,44 +39,6 @@ class BaseFilterBackend(object): return [] -if django_filters: - from django_filters.rest_framework.filterset import FilterSet as DFFilterSet - - class FilterSet(DFFilterSet): - def __init__(self, *args, **kwargs): - warnings.warn( - "The built in 'rest_framework.filters.FilterSet' is deprecated. " - "You should use 'django_filters.rest_framework.FilterSet' instead.", - DeprecationWarning, stacklevel=2 - ) - return super(FilterSet, self).__init__(*args, **kwargs) - - DFBase = django_filters.rest_framework.DjangoFilterBackend - -else: - def FilterSet(): - assert False, 'django-filter must be installed to use the `FilterSet` class' - - DFBase = BaseFilterBackend - - -class DjangoFilterBackend(DFBase): - """ - A filter backend that uses django-filter. - """ - def __new__(cls, *args, **kwargs): - assert django_filters, 'Using DjangoFilterBackend, but django-filter is not installed' - assert django_filters.VERSION >= (0, 15, 3), 'django-filter 0.15.3 and above is required' - - warnings.warn( - "The built in 'rest_framework.filters.DjangoFilterBackend' is deprecated. " - "You should use 'django_filters.rest_framework.DjangoFilterBackend' instead.", - DeprecationWarning, stacklevel=2 - ) - - return super(DjangoFilterBackend, cls).__new__(cls, *args, **kwargs) - - class SearchFilter(BaseFilterBackend): # The URL query parameter used for the search. search_param = api_settings.SEARCH_PARAM diff --git a/tests/models.py b/tests/models.py index 6c9dde8fa..e5d49a0a5 100644 --- a/tests/models.py +++ b/tests/models.py @@ -24,15 +24,6 @@ class BasicModel(RESTFrameworkModel): ) -class BaseFilterableItem(RESTFrameworkModel): - text = models.CharField(max_length=100) - - -class FilterableItem(BaseFilterableItem): - decimal = models.DecimalField(max_digits=4, decimal_places=2) - date = models.DateField() - - # Models for relations tests # ManyToMany class ManyToManyTarget(RESTFrameworkModel): diff --git a/tests/test_filters.py b/tests/test_filters.py index 6df0a3169..dc5b18068 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -2,125 +2,21 @@ from __future__ import unicode_literals import datetime import unittest -import warnings -from decimal import Decimal import django import pytest -from django.conf.urls import url from django.core.exceptions import ImproperlyConfigured from django.db import models from django.test import TestCase from django.test.utils import override_settings -from django.utils.dateparse import parse_date from django.utils.six.moves import reload_module -from rest_framework import filters, generics, serializers, status -from rest_framework.compat import django_filters, reverse +from rest_framework import filters, generics, serializers from rest_framework.test import APIRequestFactory -from .models import BaseFilterableItem, BasicModel, FilterableItem - factory = APIRequestFactory() -if django_filters: - class FilterableItemSerializer(serializers.ModelSerializer): - class Meta: - model = FilterableItem - fields = '__all__' - - # Basic filter on a list view. - class FilterFieldsRootView(generics.ListCreateAPIView): - queryset = FilterableItem.objects.all() - serializer_class = FilterableItemSerializer - filter_fields = ['decimal', 'date'] - filter_backends = (filters.DjangoFilterBackend,) - - # These class are used to test a filter class. - class SeveralFieldsFilter(django_filters.FilterSet): - text = django_filters.CharFilter(lookup_expr='icontains') - decimal = django_filters.NumberFilter(lookup_expr='lt') - date = django_filters.DateFilter(lookup_expr='gt') - - class Meta: - model = FilterableItem - fields = ['text', 'decimal', 'date'] - - class FilterClassRootView(generics.ListCreateAPIView): - queryset = FilterableItem.objects.all() - serializer_class = FilterableItemSerializer - filter_class = SeveralFieldsFilter - filter_backends = (filters.DjangoFilterBackend,) - - # These classes are used to test a misconfigured filter class. - class MisconfiguredFilter(django_filters.FilterSet): - text = django_filters.CharFilter(lookup_expr='icontains') - - class Meta: - model = BasicModel - fields = ['text'] - - class IncorrectlyConfiguredRootView(generics.ListCreateAPIView): - queryset = FilterableItem.objects.all() - serializer_class = FilterableItemSerializer - filter_class = MisconfiguredFilter - filter_backends = (filters.DjangoFilterBackend,) - - class FilterClassDetailView(generics.RetrieveAPIView): - queryset = FilterableItem.objects.all() - serializer_class = FilterableItemSerializer - filter_class = SeveralFieldsFilter - filter_backends = (filters.DjangoFilterBackend,) - - # These classes are used to test base model filter support - class BaseFilterableItemFilter(django_filters.FilterSet): - text = django_filters.CharFilter() - - class Meta: - model = BaseFilterableItem - fields = '__all__' - - # Test the same filter using the deprecated internal FilterSet class. - class BaseFilterableItemFilterWithProxy(filters.FilterSet): - text = django_filters.CharFilter() - - class Meta: - model = BaseFilterableItem - fields = '__all__' - - class BaseFilterableItemFilterRootView(generics.ListCreateAPIView): - queryset = FilterableItem.objects.all() - serializer_class = FilterableItemSerializer - filter_class = BaseFilterableItemFilter - filter_backends = (filters.DjangoFilterBackend,) - - class BaseFilterableItemFilterWithProxyRootView(BaseFilterableItemFilterRootView): - filter_class = BaseFilterableItemFilterWithProxy - - # Regression test for #814 - class FilterFieldsQuerysetView(generics.ListCreateAPIView): - queryset = FilterableItem.objects.all() - serializer_class = FilterableItemSerializer - filter_fields = ['decimal', 'date'] - filter_backends = (filters.DjangoFilterBackend,) - - class GetQuerysetView(generics.ListCreateAPIView): - serializer_class = FilterableItemSerializer - filter_class = SeveralFieldsFilter - filter_backends = (filters.DjangoFilterBackend,) - - def get_queryset(self): - return FilterableItem.objects.all() - - urlpatterns = [ - url(r'^(?P\d+)/$', FilterClassDetailView.as_view(), name='detail-view'), - url(r'^$', FilterClassRootView.as_view(), name='root-view'), - url(r'^get-queryset/$', GetQuerysetView.as_view(), - name='get-queryset-view'), - ] - - class BaseFilterTests(TestCase): def setUp(self): self.original_coreapi = filters.coreapi @@ -142,288 +38,6 @@ class BaseFilterTests(TestCase): assert self.filter_backend.get_schema_fields({}) == [] -class CommonFilteringTestCase(TestCase): - def _serialize_object(self, obj): - return {'id': obj.id, 'text': obj.text, 'decimal': str(obj.decimal), 'date': obj.date.isoformat()} - - def setUp(self): - """ - Create 10 FilterableItem instances. - """ - base_data = ('a', Decimal('0.25'), datetime.date(2012, 10, 8)) - for i in range(10): - text = chr(i + ord(base_data[0])) * 3 # Produces string 'aaa', 'bbb', etc. - decimal = base_data[1] + i - date = base_data[2] - datetime.timedelta(days=i * 2) - FilterableItem(text=text, decimal=decimal, date=date).save() - - self.objects = FilterableItem.objects - self.data = [ - self._serialize_object(obj) - for obj in self.objects.all() - ] - - -class IntegrationTestFiltering(CommonFilteringTestCase): - """ - Integration tests for filtered list views. - """ - - @unittest.skipUnless(django_filters, 'django-filter not installed') - def test_backend_deprecation(self): - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - - view = FilterFieldsRootView.as_view() - request = factory.get('/') - response = view(request).render() - - assert response.status_code == status.HTTP_200_OK - assert response.data == self.data - - self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) - self.assertIn("'rest_framework.filters.DjangoFilterBackend' is deprecated.", str(w[-1].message)) - - @unittest.skipUnless(django_filters, 'django-filter not installed') - def test_no_df_deprecation(self): - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - - import django_filters.rest_framework - - class DFFilterFieldsRootView(FilterFieldsRootView): - filter_backends = (django_filters.rest_framework.DjangoFilterBackend,) - - view = DFFilterFieldsRootView.as_view() - request = factory.get('/') - response = view(request).render() - - assert response.status_code == status.HTTP_200_OK - assert response.data == self.data - assert len(w) == 0 - - @unittest.skipUnless(django_filters, 'django-filter not installed') - def test_backend_mro(self): - class CustomBackend(filters.DjangoFilterBackend): - def filter_queryset(self, request, queryset, view): - assert False, "custom filter_queryset should run" - - class DFFilterFieldsRootView(FilterFieldsRootView): - filter_backends = (CustomBackend,) - - view = DFFilterFieldsRootView.as_view() - request = factory.get('/') - - with pytest.raises(AssertionError, message="custom filter_queryset should run"): - view(request).render() - - @unittest.skipUnless(django_filters, 'django-filter not installed') - def test_get_filtered_fields_root_view(self): - """ - GET requests to paginated ListCreateAPIView should return paginated results. - """ - view = FilterFieldsRootView.as_view() - - # Basic test with no filter. - request = factory.get('/') - response = view(request).render() - assert response.status_code == status.HTTP_200_OK - assert response.data == self.data - - # Tests that the decimal filter works. - search_decimal = Decimal('2.25') - request = factory.get('/', {'decimal': '%s' % search_decimal}) - response = view(request).render() - assert response.status_code == status.HTTP_200_OK - expected_data = [f for f in self.data if Decimal(f['decimal']) == search_decimal] - assert response.data == expected_data - - # Tests that the date filter works. - search_date = datetime.date(2012, 9, 22) - request = factory.get('/', {'date': '%s' % search_date}) # search_date str: '2012-09-22' - response = view(request).render() - assert response.status_code == status.HTTP_200_OK - expected_data = [f for f in self.data if parse_date(f['date']) == search_date] - assert response.data == expected_data - - @unittest.skipUnless(django_filters, 'django-filter not installed') - def test_filter_with_queryset(self): - """ - Regression test for #814. - """ - view = FilterFieldsQuerysetView.as_view() - - # Tests that the decimal filter works. - search_decimal = Decimal('2.25') - request = factory.get('/', {'decimal': '%s' % search_decimal}) - response = view(request).render() - assert response.status_code == status.HTTP_200_OK - expected_data = [f for f in self.data if Decimal(f['decimal']) == search_decimal] - assert response.data == expected_data - - @unittest.skipUnless(django_filters, 'django-filter not installed') - def test_filter_with_get_queryset_only(self): - """ - Regression test for #834. - """ - view = GetQuerysetView.as_view() - request = factory.get('/get-queryset/') - view(request).render() - # Used to raise "issubclass() arg 2 must be a class or tuple of classes" - # here when neither `model' nor `queryset' was specified. - - @unittest.skipUnless(django_filters, 'django-filter not installed') - def test_get_filtered_class_root_view(self): - """ - GET requests to filtered ListCreateAPIView that have a filter_class set - should return filtered results. - """ - view = FilterClassRootView.as_view() - - # Basic test with no filter. - request = factory.get('/') - response = view(request).render() - assert response.status_code == status.HTTP_200_OK - assert response.data == self.data - - # Tests that the decimal filter set with 'lt' in the filter class works. - search_decimal = Decimal('4.25') - request = factory.get('/', {'decimal': '%s' % search_decimal}) - response = view(request).render() - assert response.status_code == status.HTTP_200_OK - expected_data = [f for f in self.data if Decimal(f['decimal']) < search_decimal] - assert response.data == expected_data - - # Tests that the date filter set with 'gt' in the filter class works. - search_date = datetime.date(2012, 10, 2) - request = factory.get('/', {'date': '%s' % search_date}) # search_date str: '2012-10-02' - response = view(request).render() - assert response.status_code == status.HTTP_200_OK - expected_data = [f for f in self.data if parse_date(f['date']) > search_date] - assert response.data == expected_data - - # Tests that the text filter set with 'icontains' in the filter class works. - search_text = 'ff' - request = factory.get('/', {'text': '%s' % search_text}) - response = view(request).render() - assert response.status_code == status.HTTP_200_OK - expected_data = [f for f in self.data if search_text in f['text'].lower()] - assert response.data == expected_data - - # Tests that multiple filters works. - search_decimal = Decimal('5.25') - search_date = datetime.date(2012, 10, 2) - request = factory.get('/', { - 'decimal': '%s' % (search_decimal,), - 'date': '%s' % (search_date,) - }) - response = view(request).render() - assert response.status_code == status.HTTP_200_OK - expected_data = [f for f in self.data if parse_date(f['date']) > search_date and - Decimal(f['decimal']) < search_decimal] - assert response.data == expected_data - - @unittest.skipUnless(django_filters, 'django-filter not installed') - def test_incorrectly_configured_filter(self): - """ - An error should be displayed when the filter class is misconfigured. - """ - view = IncorrectlyConfiguredRootView.as_view() - - request = factory.get('/') - self.assertRaises(AssertionError, view, request) - - @unittest.skipUnless(django_filters, 'django-filter not installed') - def test_base_model_filter(self): - """ - The `get_filter_class` model checks should allow base model filters. - """ - view = BaseFilterableItemFilterRootView.as_view() - - request = factory.get('/?text=aaa') - response = view(request).render() - assert response.status_code == status.HTTP_200_OK - assert len(response.data) == 1 - - @unittest.skipUnless(django_filters, 'django-filter not installed') - def test_base_model_filter_with_proxy(self): - """ - The `get_filter_class` model checks should allow base model filters. - """ - view = BaseFilterableItemFilterWithProxyRootView.as_view() - - request = factory.get('/?text=aaa') - response = view(request).render() - assert response.status_code == status.HTTP_200_OK - assert len(response.data) == 1 - - @unittest.skipUnless(django_filters, 'django-filter not installed') - def test_unknown_filter(self): - """ - GET requests with filters that aren't configured should return 200. - """ - view = FilterFieldsRootView.as_view() - - search_integer = 10 - request = factory.get('/', {'integer': '%s' % search_integer}) - response = view(request).render() - assert response.status_code == status.HTTP_200_OK - - -@override_settings(ROOT_URLCONF='tests.test_filters') -class IntegrationTestDetailFiltering(CommonFilteringTestCase): - """ - Integration tests for filtered detail views. - """ - def _get_url(self, item): - return reverse('detail-view', kwargs=dict(pk=item.pk)) - - @unittest.skipUnless(django_filters, 'django-filter not installed') - def test_get_filtered_detail_view(self): - """ - GET requests to filtered RetrieveAPIView that have a filter_class set - should return filtered results. - """ - item = self.objects.all()[0] - data = self._serialize_object(item) - - # Basic test with no filter. - response = self.client.get(self._get_url(item)) - assert response.status_code == status.HTTP_200_OK - assert response.data == data - - # Tests that the decimal filter set that should fail. - search_decimal = Decimal('4.25') - high_item = self.objects.filter(decimal__gt=search_decimal)[0] - response = self.client.get( - '{url}'.format(url=self._get_url(high_item)), - {'decimal': '{param}'.format(param=search_decimal)}) - assert response.status_code == status.HTTP_404_NOT_FOUND - - # Tests that the decimal filter set that should succeed. - search_decimal = Decimal('4.25') - low_item = self.objects.filter(decimal__lt=search_decimal)[0] - low_item_data = self._serialize_object(low_item) - response = self.client.get( - '{url}'.format(url=self._get_url(low_item)), - {'decimal': '{param}'.format(param=search_decimal)}) - assert response.status_code == status.HTTP_200_OK - assert response.data == low_item_data - - # Tests that multiple filters works. - search_decimal = Decimal('5.25') - search_date = datetime.date(2012, 10, 2) - valid_item = self.objects.filter(decimal__lt=search_decimal, date__gt=search_date)[0] - valid_item_data = self._serialize_object(valid_item) - response = self.client.get( - '{url}'.format(url=self._get_url(valid_item)), { - 'decimal': '{decimal}'.format(decimal=search_decimal), - 'date': '{date}'.format(date=search_date) - }) - assert response.status_code == status.HTTP_200_OK - assert response.data == valid_item_data - - class SearchFilterModel(models.Model): title = models.CharField(max_length=20) text = models.CharField(max_length=100) @@ -720,42 +334,6 @@ class DjangoFilterOrderingSerializer(serializers.ModelSerializer): fields = '__all__' -class DjangoFilterOrderingTests(TestCase): - def setUp(self): - data = [{ - 'date': datetime.date(2012, 10, 8), - 'text': 'abc' - }, { - 'date': datetime.date(2013, 10, 8), - 'text': 'bcd' - }, { - 'date': datetime.date(2014, 10, 8), - 'text': 'cde' - }] - - for d in data: - DjangoFilterOrderingModel.objects.create(**d) - - @unittest.skipUnless(django_filters, 'django-filter not installed') - def test_default_ordering(self): - class DjangoFilterOrderingView(generics.ListAPIView): - serializer_class = DjangoFilterOrderingSerializer - queryset = DjangoFilterOrderingModel.objects.all() - filter_backends = (filters.DjangoFilterBackend,) - filter_fields = ['text'] - ordering = ('-date',) - - view = DjangoFilterOrderingView.as_view() - request = factory.get('/') - response = view(request) - - assert response.data == [ - {'id': 3, 'date': '2014-10-08', 'text': 'cde'}, - {'id': 2, 'date': '2013-10-08', 'text': 'bcd'}, - {'id': 1, 'date': '2012-10-08', 'text': 'abc'} - ] - - class OrderingFilterTests(TestCase): def setUp(self): # Sequence of title/text is: From b64f8066c06c3ad65e07e6741d8d7266d81ceba9 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Fri, 7 Jul 2017 12:46:17 -0400 Subject: [PATCH 118/730] Add json util wrapper, failing JSONField test --- rest_framework/utils/encoders.py | 2 +- rest_framework/utils/json.py | 33 ++++++++++++++++++++++++++++++++ tests/test_fields.py | 1 + tests/test_utils.py | 21 ++++++++++++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 rest_framework/utils/json.py diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index 8896e4f2c..a4fe8d0c4 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -1,7 +1,7 @@ """ Helper classes for parsers. """ -from __future__ import unicode_literals +from __future__ import absolute_import, unicode_literals import datetime import decimal diff --git a/rest_framework/utils/json.py b/rest_framework/utils/json.py new file mode 100644 index 000000000..cc7df542e --- /dev/null +++ b/rest_framework/utils/json.py @@ -0,0 +1,33 @@ + +from __future__ import absolute_import + +import functools +import json + + +def strict_constant(o): + raise ValueError('Out of range float values are not JSON compliant: ' + repr(o)) + + +@functools.wraps(json.dump) +def dump(*args, **kwargs): + kwargs.setdefault('allow_nan', False) + return json.dump(*args, **kwargs) + + +@functools.wraps(json.dumps) +def dumps(*args, **kwargs): + kwargs.setdefault('allow_nan', False) + return json.dumps(*args, **kwargs) + + +@functools.wraps(json.load) +def load(*args, **kwargs): + kwargs.setdefault('parse_constant', strict_constant) + return json.load(*args, **kwargs) + + +@functools.wraps(json.loads) +def loads(*args, **kwargs): + kwargs.setdefault('parse_constant', strict_constant) + return json.loads(*args, **kwargs) diff --git a/tests/test_fields.py b/tests/test_fields.py index c2b053189..c1b99818a 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1896,6 +1896,7 @@ class TestJSONField(FieldValues): ] invalid_inputs = [ ({'a': set()}, ['Value must be valid JSON.']), + ({'a': float('inf')}, ['Value must be valid JSON.']), ] outputs = [ ({ diff --git a/tests/test_utils.py b/tests/test_utils.py index 11c9e9bb0..9619776d6 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -9,6 +9,7 @@ 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 from rest_framework.utils.breadcrumbs import get_breadcrumbs from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet @@ -177,3 +178,23 @@ class ResolveModelWithPatchedDjangoTests(TestCase): 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 + """ + + def test_dumps(self): + with self.assertRaises(ValueError): + json.dumps(float('inf')) + + with self.assertRaises(ValueError): + json.dumps(float('nan')) + + def test_loads(self): + with self.assertRaises(ValueError): + json.loads("Infinity") + + with self.assertRaises(ValueError): + json.loads("NaN") From d740bae95a30d473e9de0019bc31475900caa435 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Fri, 7 Jul 2017 12:47:08 -0400 Subject: [PATCH 119/730] Update json imports --- rest_framework/fields.py | 3 +-- rest_framework/parsers.py | 2 +- rest_framework/renderers.py | 3 +-- rest_framework/utils/serializer_helpers.py | 2 +- tests/test_renderers.py | 2 +- tests/test_routers.py | 2 +- 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 072bbf1b9..e17a0bf9a 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -5,7 +5,6 @@ import copy import datetime import decimal import inspect -import json import re import uuid from collections import OrderedDict @@ -37,7 +36,7 @@ from rest_framework.compat import ( ) from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.settings import api_settings -from rest_framework.utils import html, humanize_datetime, representation +from rest_framework.utils import html, humanize_datetime, json, representation class empty: diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 0e40e1a7a..5ebaeca2e 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -7,7 +7,6 @@ on the request, such as form content or json encoded data. from __future__ import unicode_literals import codecs -import json from django.conf import settings from django.core.files.uploadhandler import StopFutureHandlers @@ -23,6 +22,7 @@ from django.utils.six.moves.urllib import parse as urlparse from rest_framework import renderers from rest_framework.exceptions import ParseError +from rest_framework.utils import json class DataAndFiles(object): diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 687cb0e1e..a87eb1889 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -9,7 +9,6 @@ REST framework also provides an HTML renderer that renders the browsable API. from __future__ import unicode_literals import base64 -import json from collections import OrderedDict from django import forms @@ -30,7 +29,7 @@ from rest_framework.compat import ( from rest_framework.exceptions import ParseError from rest_framework.request import is_form_media_type, override_method from rest_framework.settings import api_settings -from rest_framework.utils import encoders +from rest_framework.utils import encoders, json from rest_framework.utils.breadcrumbs import get_breadcrumbs from rest_framework.utils.field_mapping import ClassLookupDict diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index d1bba9666..da89d08ff 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -1,12 +1,12 @@ from __future__ import unicode_literals import collections -import json from collections import OrderedDict from django.utils.encoding import force_text from rest_framework.compat import unicode_to_repr +from rest_framework.utils import json class ReturnDict(OrderedDict): diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 1625b0b70..ccc910c63 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -import json import re from collections import MutableMapping, OrderedDict @@ -24,6 +23,7 @@ from rest_framework.request import Request from rest_framework.response import Response from rest_framework.settings import api_settings from rest_framework.test import APIRequestFactory +from rest_framework.utils import json from rest_framework.views import APIView DUMMYSTATUS = status.HTTP_200_OK diff --git a/tests/test_routers.py b/tests/test_routers.py index fee39b2b3..cbc7c0554 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -1,6 +1,5 @@ from __future__ import unicode_literals -import json from collections import namedtuple import pytest @@ -15,6 +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.utils import json factory = APIRequestFactory() From 8ab75a2f01df89ae14b21f44d3f76015e9191969 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 10 Jul 2017 15:23:12 -0400 Subject: [PATCH 120/730] Add 'STRICT_JSON' API setting. STRICT_JSON controls the renderer & parser behavior on whether or not to accept non-standard float values (NaN, Infinity). --- rest_framework/parsers.py | 5 ++++- rest_framework/renderers.py | 3 ++- rest_framework/settings.py | 1 + tests/test_parsers.py | 24 +++++++++++++++++++++--- tests/test_renderers.py | 13 +++++++++++++ tests/test_utils.py | 7 +++++++ 6 files changed, 48 insertions(+), 5 deletions(-) diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 5ebaeca2e..7a256276a 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -22,6 +22,7 @@ from django.utils.six.moves.urllib import parse as urlparse from rest_framework import renderers from rest_framework.exceptions import ParseError +from rest_framework.settings import api_settings from rest_framework.utils import json @@ -53,6 +54,7 @@ class JSONParser(BaseParser): """ media_type = 'application/json' renderer_class = renderers.JSONRenderer + strict = api_settings.STRICT_JSON def parse(self, stream, media_type=None, parser_context=None): """ @@ -63,7 +65,8 @@ class JSONParser(BaseParser): try: decoded_stream = codecs.getreader(encoding)(stream) - return json.load(decoded_stream) + parse_constant = json.strict_constant if self.strict else None + return json.load(decoded_stream, parse_constant=parse_constant) except ValueError as exc: raise ParseError('JSON parse error - %s' % six.text_type(exc)) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index a87eb1889..90f516b35 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -61,6 +61,7 @@ class JSONRenderer(BaseRenderer): encoder_class = encoders.JSONEncoder ensure_ascii = not api_settings.UNICODE_JSON compact = api_settings.COMPACT_JSON + strict = api_settings.STRICT_JSON # We don't set a charset because JSON is a binary encoding, # that can be encoded as utf-8, utf-16 or utf-32. @@ -101,7 +102,7 @@ class JSONRenderer(BaseRenderer): ret = json.dumps( data, cls=self.encoder_class, indent=indent, ensure_ascii=self.ensure_ascii, - separators=separators + allow_nan=not self.strict, separators=separators ) # On python 2.x json.dumps() returns bytestrings if ensure_ascii=True, diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 3f3c9110a..bc42c38a6 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -110,6 +110,7 @@ DEFAULTS = { # Encoding 'UNICODE_JSON': True, 'COMPACT_JSON': True, + 'STRICT_JSON': True, 'COERCE_DECIMAL_TO_STRING': True, 'UPLOADED_FILES_USE_URL': True, diff --git a/tests/test_parsers.py b/tests/test_parsers.py index 2499bfa3a..0a18aad1a 100644 --- a/tests/test_parsers.py +++ b/tests/test_parsers.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- - from __future__ import unicode_literals +import math + import pytest from django import forms from django.core.files.uploadhandler import ( @@ -9,7 +10,7 @@ from django.core.files.uploadhandler import ( ) from django.http.request import RawPostDataException from django.test import TestCase -from django.utils.six.moves import StringIO +from django.utils.six import BytesIO, StringIO from rest_framework.exceptions import ParseError from rest_framework.parsers import ( @@ -42,7 +43,6 @@ class TestFileUploadParser(TestCase): def setUp(self): class MockRequest(object): pass - from io import BytesIO self.stream = BytesIO( "Test text file".encode('utf-8') ) @@ -129,6 +129,24 @@ class TestFileUploadParser(TestCase): self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = disposition +class TestJSONParser(TestCase): + def bytes(self, value): + return BytesIO(value.encode('utf-8')) + + def test_float_strictness(self): + parser = JSONParser() + + # Default to strict + for value in ['Infinity', '-Infinity', 'NaN']: + with pytest.raises(ParseError): + parser.parse(self.bytes(value)) + + parser.strict = False + assert parser.parse(self.bytes('Infinity')) == float('inf') + assert parser.parse(self.bytes('-Infinity')) == float('-inf') + assert math.isnan(parser.parse(self.bytes('NaN'))) + + class TestPOSTAccessed(TestCase): def setUp(self): self.factory = APIRequestFactory() diff --git a/tests/test_renderers.py b/tests/test_renderers.py index ccc910c63..5f4aa51a3 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -358,6 +358,19 @@ class JSONRendererTests(TestCase): with self.assertRaises(TypeError): JSONRenderer().render(x) + def test_float_strictness(self): + renderer = JSONRenderer() + + # Default to strict + for value in [float('inf'), float('-inf'), float('nan')]: + with pytest.raises(ValueError): + renderer.render(value) + + renderer.strict = False + assert renderer.render(float('inf')) == b'Infinity' + assert renderer.render(float('-inf')) == b'-Infinity' + assert renderer.render(float('nan')) == b'NaN' + def test_without_content_type_args(self): """ Test basic JSON rendering. diff --git a/tests/test_utils.py b/tests/test_utils.py index 9619776d6..c5e6f26dc 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -198,3 +198,10 @@ class JsonFloatTests(TestCase): with self.assertRaises(ValueError): json.loads("NaN") + + +@override_settings(STRICT_JSON=False) +class NonStrictJsonFloatTests(JsonFloatTests): + """ + 'STRICT_JSON = False' should not somehow affect internal json behavior + """ From 901657e7e8fe460cf9f14b48b9bce70d3cd06b09 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 10 Jul 2017 17:22:55 -0400 Subject: [PATCH 121/730] Add banned imports to prevent standard json import --- requirements/requirements-codestyle.txt | 1 + rest_framework/utils/encoders.py | 2 +- rest_framework/utils/json.py | 2 +- setup.cfg | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/requirements/requirements-codestyle.txt b/requirements/requirements-codestyle.txt index 264416f5f..9bafbe391 100644 --- a/requirements/requirements-codestyle.txt +++ b/requirements/requirements-codestyle.txt @@ -1,5 +1,6 @@ # PEP8 code linting, which we run on all commits. flake8==2.4.0 +flake8-tidy-imports==1.1.0 pep8==1.5.7 # Sort and lint imports diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index a4fe8d0c4..d754e4465 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -5,7 +5,7 @@ from __future__ import absolute_import, unicode_literals import datetime import decimal -import json +import json # noqa import uuid from django.db.models.query import QuerySet diff --git a/rest_framework/utils/json.py b/rest_framework/utils/json.py index cc7df542e..ea5e22725 100644 --- a/rest_framework/utils/json.py +++ b/rest_framework/utils/json.py @@ -2,7 +2,7 @@ from __future__ import absolute_import import functools -import json +import json # noqa def strict_constant(o): diff --git a/setup.cfg b/setup.cfg index 509abd58b..9f6b5be77 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,3 +6,4 @@ license_file = LICENSE.md [flake8] ignore = E501 +banned-modules = json = use from rest_framework.utils import json! From c98223f2316984ac362ba06bc82d3e2ca454ede3 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 10 Jul 2017 18:51:44 -0400 Subject: [PATCH 122/730] Pass on invalid value (Inf, NaN) encoding in JSONBoundField --- rest_framework/utils/serializer_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index da89d08ff..fa79b6526 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -88,7 +88,7 @@ class JSONBoundField(BoundField): value = self.value try: value = json.dumps(self.value, sort_keys=True, indent=4) - except TypeError: + except (TypeError, ValueError): pass return self.__class__(self._field, value, self.errors, self._prefix) From 215248c0421f451c2b6e64c45f511da7e60eae78 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 10 Jul 2017 19:34:04 -0400 Subject: [PATCH 123/730] Add 'STRICT_JSON' docs --- docs/api-guide/settings.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index aaedd463e..5c9eaa12c 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -362,6 +362,14 @@ The default style is to return minified responses, in line with [Heroku's API de Default: `True` +#### STRICT_JSON + +When set to `True`, JSON rendering and parsing will only observe syntactically valid JSON, raising an exception for the extended float values (`nan`, `inf`, `-inf`) accepted by Python's `json` module. This is the recommended setting, as these values are not generally supported. e.g., neither Javascript's `JSON.Parse` nor PostgreSQL's JSON data type accept these values. + +When set to `False`, JSON rendering and parsing will be permissive. However, these values are still invalid and will need to be specially handled in your code. + +Default: `True` + #### COERCE_DECIMAL_TO_STRING When returning decimal objects in API representations that do not support a native decimal type, it is normally best to return the value as a string. This avoids the loss of precision that occurs with binary floating point implementations. From ea894cd90a7544b0507c5f94bb3eb3da25000ccf Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 11 Jul 2017 11:53:20 -0400 Subject: [PATCH 124/730] Add docstring to json wrapper module --- rest_framework/utils/json.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rest_framework/utils/json.py b/rest_framework/utils/json.py index ea5e22725..cb5572380 100644 --- a/rest_framework/utils/json.py +++ b/rest_framework/utils/json.py @@ -1,3 +1,10 @@ +""" +Wrapper for the builtin json module that ensures compliance with the JSON spec. + +REST framework should always import this wrapper module in order to maintain +spec-compliant encoding/decoding. Support for non-standard features should be +handled by users at the renderer and parser layer. +""" from __future__ import absolute_import From e29ad1e7b386f6a2f46b1e67560cdb93bdc050bf Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Fri, 22 Sep 2017 12:03:05 +0200 Subject: [PATCH 125/730] =?UTF-8?q?JSONEncoder:=20Don=E2=80=99t=20strip=20?= =?UTF-8?q?microseconds=20from=20`time`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #4749. This is the matching commit to the fix for `datetime` in #4256 --- rest_framework/utils/encoders.py | 2 -- tests/test_encoders.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index d754e4465..87df365e0 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -37,8 +37,6 @@ class JSONEncoder(json.JSONEncoder): if timezone and timezone.is_aware(obj): raise ValueError("JSON can't represent timezone-aware times.") representation = obj.isoformat() - if obj.microsecond: - representation = representation[:12] return representation elif isinstance(obj, datetime.timedelta): return six.text_type(total_seconds(obj)) diff --git a/tests/test_encoders.py b/tests/test_encoders.py index 8f8694c47..27136df87 100644 --- a/tests/test_encoders.py +++ b/tests/test_encoders.py @@ -44,7 +44,7 @@ class JSONEncoderTests(TestCase): Tests encoding a timezone """ current_time = datetime.now().time() - assert self.encoder.default(current_time) == current_time.isoformat()[:12] + assert self.encoder.default(current_time) == current_time.isoformat() def test_encode_time_tz(self): """ From aecca9d8e83ed42cf6d4b04de84d82dd185e2aa7 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Mon, 25 Sep 2017 11:04:23 +0200 Subject: [PATCH 126/730] Add note on force_authenticate + refresh_from_db MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …in case you’re reusing the same in-memory user whilst updating it in the DB. Closes #5016, closes #5066, closes #4102 --- docs/api-guide/testing.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md index 2b93080b3..caba5cea2 100644 --- a/docs/api-guide/testing.md +++ b/docs/api-guide/testing.md @@ -86,6 +86,10 @@ For example, when forcibly authenticating using a token, you might do something --- +**Note**: `force_authenticate` directly sets `request.user` to the in-memory `user` instance. If you are re-using the same `user` instance across multiple tests that update the saved `user` state, you may need to call [`refresh_from_db()`][refresh_from_db_docs] between tests. + +--- + **Note**: When using `APIRequestFactory`, the object that is returned is Django's standard `HttpRequest`, and not REST framework's `Request` object, which is only generated once the view is called. This means that setting attributes directly on the request object may not always have the effect you expect. For example, setting `.token` directly will have no effect, and setting `.user` directly will only work if session authentication is being used. @@ -378,3 +382,4 @@ 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 From 60b9e58a1241a8821c7020555104b365cd97a1ec Mon Sep 17 00:00:00 2001 From: Kris Dorosz Date: Wed, 5 Jul 2017 10:35:17 +0200 Subject: [PATCH 127/730] Add support for page_size parameter in CursorPaginator class --- rest_framework/pagination.py | 34 +++++++- tests/test_pagination.py | 162 +++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 1 deletion(-) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 61e0a80b0..2c3b0d2a5 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -482,6 +482,15 @@ class CursorPagination(BasePagination): ordering = '-created' template = 'rest_framework/pagination/previous_and_next.html' + # Client can control the page size using this query parameter. + # Default is 'None'. Set to eg 'page_size' to enable usage. + page_size_query_param = None + page_size_query_description = _('Number of results to return per page.') + + # Set to an integer to limit the maximum page size the client may request. + # Only relevant if 'page_size_query_param' has also been set. + max_page_size = None + # The offset in the cursor is used in situations where we have a # nearly-unique index. (Eg millisecond precision creation timestamps) # We guard against malicious users attempting to cause expensive database @@ -566,6 +575,16 @@ class CursorPagination(BasePagination): return self.page def get_page_size(self, request): + if self.page_size_query_param: + try: + return _positive_int( + request.query_params[self.page_size_query_param], + strict=True, + cutoff=self.max_page_size + ) + except (KeyError, ValueError): + pass + return self.page_size def get_next_link(self): @@ -779,7 +798,7 @@ class CursorPagination(BasePagination): def get_schema_fields(self, view): assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`' - return [ + fields = [ coreapi.Field( name=self.cursor_query_param, required=False, @@ -790,3 +809,16 @@ class CursorPagination(BasePagination): ) ) ] + if self.page_size_query_param is not None: + fields.append( + coreapi.Field( + name=self.page_size_query_param, + required=False, + location='query', + schema=coreschema.Integer( + title='Page size', + description=force_text(self.page_size_query_description) + ) + ) + ) + return fields diff --git a/tests/test_pagination.py b/tests/test_pagination.py index dd7f70330..d9ad9e6f6 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -633,6 +633,164 @@ class CursorPaginationTestsMixin: assert isinstance(self.pagination.to_html(), type('')) + def test_cursor_pagination_with_page_size(self): + (previous, current, next, previous_url, next_url) = self.get_pages('/?page_size=20') + + assert previous is None + assert current == [1, 1, 1, 1, 1, 1, 2, 3, 4, 4, 4, 4, 5, 6, 7, 7, 7, 7, 7, 7] + assert next == [7, 7, 7, 8, 9, 9, 9, 9, 9, 9] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + assert previous == [1, 1, 1, 1, 1, 1, 2, 3, 4, 4, 4, 4, 5, 6, 7, 7, 7, 7, 7, 7] + assert current == [7, 7, 7, 8, 9, 9, 9, 9, 9, 9] + assert next is None + + def test_cursor_pagination_with_page_size_over_limit(self): + (previous, current, next, previous_url, next_url) = self.get_pages('/?page_size=30') + + assert previous is None + assert current == [1, 1, 1, 1, 1, 1, 2, 3, 4, 4, 4, 4, 5, 6, 7, 7, 7, 7, 7, 7] + assert next == [7, 7, 7, 8, 9, 9, 9, 9, 9, 9] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + assert previous == [1, 1, 1, 1, 1, 1, 2, 3, 4, 4, 4, 4, 5, 6, 7, 7, 7, 7, 7, 7] + assert current == [7, 7, 7, 8, 9, 9, 9, 9, 9, 9] + assert next is None + + def test_cursor_pagination_with_page_size_zero(self): + (previous, current, next, previous_url, next_url) = self.get_pages('/?page_size=0') + + assert previous is None + assert current == [1, 1, 1, 1, 1] + assert next == [1, 2, 3, 4, 4] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [1, 1, 1, 1, 1] + assert current == [1, 2, 3, 4, 4] + assert next == [4, 4, 5, 6, 7] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [1, 2, 3, 4, 4] + assert current == [4, 4, 5, 6, 7] + assert next == [7, 7, 7, 7, 7] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [4, 4, 4, 5, 6] # Paging artifact + assert current == [7, 7, 7, 7, 7] + assert next == [7, 7, 7, 8, 9] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [7, 7, 7, 7, 7] + assert current == [7, 7, 7, 8, 9] + assert next == [9, 9, 9, 9, 9] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [7, 7, 7, 8, 9] + assert current == [9, 9, 9, 9, 9] + assert next is None + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous == [7, 7, 7, 7, 7] + assert current == [7, 7, 7, 8, 9] + assert next == [9, 9, 9, 9, 9] + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous == [4, 4, 5, 6, 7] + assert current == [7, 7, 7, 7, 7] + assert next == [8, 9, 9, 9, 9] # Paging artifact + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous == [1, 2, 3, 4, 4] + assert current == [4, 4, 5, 6, 7] + assert next == [7, 7, 7, 7, 7] + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous == [1, 1, 1, 1, 1] + assert current == [1, 2, 3, 4, 4] + assert next == [4, 4, 5, 6, 7] + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous is None + assert current == [1, 1, 1, 1, 1] + assert next == [1, 2, 3, 4, 4] + + def test_cursor_pagination_with_page_size_negative(self): + (previous, current, next, previous_url, next_url) = self.get_pages('/?page_size=-5') + + assert previous is None + assert current == [1, 1, 1, 1, 1] + assert next == [1, 2, 3, 4, 4] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [1, 1, 1, 1, 1] + assert current == [1, 2, 3, 4, 4] + assert next == [4, 4, 5, 6, 7] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [1, 2, 3, 4, 4] + assert current == [4, 4, 5, 6, 7] + assert next == [7, 7, 7, 7, 7] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [4, 4, 4, 5, 6] # Paging artifact + assert current == [7, 7, 7, 7, 7] + assert next == [7, 7, 7, 8, 9] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [7, 7, 7, 7, 7] + assert current == [7, 7, 7, 8, 9] + assert next == [9, 9, 9, 9, 9] + + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [7, 7, 7, 8, 9] + assert current == [9, 9, 9, 9, 9] + assert next is None + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous == [7, 7, 7, 7, 7] + assert current == [7, 7, 7, 8, 9] + assert next == [9, 9, 9, 9, 9] + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous == [4, 4, 5, 6, 7] + assert current == [7, 7, 7, 7, 7] + assert next == [8, 9, 9, 9, 9] # Paging artifact + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous == [1, 2, 3, 4, 4] + assert current == [4, 4, 5, 6, 7] + assert next == [7, 7, 7, 7, 7] + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous == [1, 1, 1, 1, 1] + assert current == [1, 2, 3, 4, 4] + assert next == [4, 4, 5, 6, 7] + + (previous, current, next, previous_url, next_url) = self.get_pages(previous_url) + + assert previous is None + assert current == [1, 1, 1, 1, 1] + assert next == [1, 2, 3, 4, 4] + class TestCursorPagination(CursorPaginationTestsMixin): """ @@ -671,6 +829,8 @@ class TestCursorPagination(CursorPaginationTestsMixin): class ExamplePagination(pagination.CursorPagination): page_size = 5 + page_size_query_param = 'page_size' + max_page_size = 20 ordering = 'created' self.pagination = ExamplePagination() @@ -727,6 +887,8 @@ class TestCursorPaginationWithValueQueryset(CursorPaginationTestsMixin, TestCase def setUp(self): class ExamplePagination(pagination.CursorPagination): page_size = 5 + page_size_query_param = 'page_size' + max_page_size = 20 ordering = 'created' self.pagination = ExamplePagination() From 11e585119691d851a6c206a38a07c6dafe8038a2 Mon Sep 17 00:00:00 2001 From: Paolo Melchiorre Date: Mon, 17 Jul 2017 12:02:30 +0200 Subject: [PATCH 128/730] Update pagination.md Fixed 2 missing spaces in Custom Pagination snippet --- 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 d767e45de..496886473 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -226,8 +226,8 @@ Suppose we want to replace the default pagination output style with a modified f def get_paginated_response(self, data): return Response({ 'links': { - 'next': self.get_next_link(), - 'previous': self.get_previous_link() + 'next': self.get_next_link(), + 'previous': self.get_previous_link() }, 'count': self.page.paginator.count, 'results': data From 107e8b3d23a933b8cdc1f14045d7d42742be53a9 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Mon, 25 Sep 2017 09:36:31 -0400 Subject: [PATCH 129/730] Make `DEFAULT_PAGINATION_CLASS` `None` by default. (#5170) * Changes to the paginator defaults and settings Require a default paginator be specified when using the page size setting. https://github.com/encode/django-rest-framework/issues/5168 * DRF-5168 import warnings missed this in last commit * Add a system checks file Add a check for pagination settings for the 3.7 upgrade cycle. * more compatible import approach * missing bactic * revised language and approach to import the system check Adds a rest framework app config. * Adjust doc wording --- docs/api-guide/pagination.md | 6 +++--- rest_framework/__init__.py | 2 ++ rest_framework/apps.py | 10 ++++++++++ rest_framework/checks.py | 18 ++++++++++++++++++ rest_framework/settings.py | 2 +- 5 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 rest_framework/apps.py create mode 100644 rest_framework/checks.py diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 496886473..7b5e5b3f6 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -21,14 +21,14 @@ Pagination can be turned off by setting the pagination class to `None`. ## Setting the pagination style -The default pagination style may be set globally, using the `DEFAULT_PAGINATION_CLASS` and `PAGE_SIZE` setting keys. For example, to use the built-in limit/offset pagination, you would do something like this: +The pagination style may be set globally, using the `DEFAULT_PAGINATION_CLASS` and `PAGE_SIZE` setting keys. For example, to use the built-in limit/offset pagination, you would do something like this: REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 100 } -Note that you need to set both the pagination class, and the page size that should be used. +Note that you need to set both the pagination class, and the page size that should be used. Both `DEFAULT_PAGINATION_CLASS` and `PAGE_SIZE` are `None` by default. You can also set the pagination class on an individual view by using the `pagination_class` attribute. Typically you'll want to use the same pagination style throughout your API, although you might want to vary individual aspects of the pagination, such as default or maximum page size, on a per-view basis. @@ -85,7 +85,7 @@ This pagination style accepts a single number page number in the request query p #### Setup -To enable the `PageNumberPagination` style globally, use the following configuration, modifying the `PAGE_SIZE` as desired: +To enable the `PageNumberPagination` style globally, use the following configuration, and set the `PAGE_SIZE` as desired: REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 9da9989e9..ae130a4d4 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -21,3 +21,5 @@ HTTP_HEADER_ENCODING = 'iso-8859-1' # Default datetime input and output formats ISO_8601 = 'iso-8601' + +default_app_config = 'rest_framework.apps.RestFrameworkConfig' diff --git a/rest_framework/apps.py b/rest_framework/apps.py new file mode 100644 index 000000000..f6013eb7e --- /dev/null +++ b/rest_framework/apps.py @@ -0,0 +1,10 @@ +from django.apps import AppConfig + + +class RestFrameworkConfig(AppConfig): + name = 'rest_framework' + verbose_name = "Django REST framework" + + def ready(self): + # Add System checks + from .checks import pagination_system_check # NOQA diff --git a/rest_framework/checks.py b/rest_framework/checks.py new file mode 100644 index 000000000..af6634d1e --- /dev/null +++ b/rest_framework/checks.py @@ -0,0 +1,18 @@ +from django.core.checks import Tags, Warning, register + + +@register(Tags.compatibility) +def pagination_system_check(app_configs, **kwargs): + errors = [] + # Use of default page size setting requires a default Paginator class + from rest_framework.settings import api_settings + if api_settings.PAGE_SIZE and not api_settings.DEFAULT_PAGINATION_CLASS: + errors.append( + Warning( + "You have specified a default PAGE_SIZE pagination rest_framework setting," + "without specifying also a DEFAULT_PAGINATION_CLASS.", + hint="The default for DEFAULT_PAGINATION_CLASS is None. " + "In previous versions this was PageNumberPagination", + ) + ) + return errors diff --git a/rest_framework/settings.py b/rest_framework/settings.py index bc42c38a6..86b577219 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -51,7 +51,7 @@ DEFAULTS = { 'DEFAULT_VERSIONING_CLASS': None, # Generic view behavior - 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', + 'DEFAULT_PAGINATION_CLASS': None, 'DEFAULT_FILTER_BACKENDS': (), # Throttling From 5333565fe668646b45cca86f0029e0ccd80555d9 Mon Sep 17 00:00:00 2001 From: Katharyn Garcia Date: Mon, 25 Sep 2017 16:17:25 +0200 Subject: [PATCH 130/730] allow custom authentication and permission classes for docs view --- rest_framework/documentation.py | 21 ++++++++++++++++++--- rest_framework/schemas/__init__.py | 8 +++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/rest_framework/documentation.py b/rest_framework/documentation.py index 48458e188..9f9c828bc 100644 --- a/rest_framework/documentation.py +++ b/rest_framework/documentation.py @@ -4,11 +4,14 @@ from rest_framework.renderers import ( CoreJSONRenderer, DocumentationRenderer, SchemaJSRenderer ) from rest_framework.schemas import SchemaGenerator, get_schema_view +from rest_framework.settings import api_settings def get_docs_view( title=None, description=None, schema_url=None, public=True, - patterns=None, generator_class=SchemaGenerator): + patterns=None, generator_class=SchemaGenerator, + authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES, + permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES): renderer_classes = [DocumentationRenderer, CoreJSONRenderer] return get_schema_view( @@ -19,12 +22,16 @@ def get_docs_view( public=public, patterns=patterns, generator_class=generator_class, + authentication_classes=authentication_classes, + permission_classes=permission_classes, ) def get_schemajs_view( title=None, description=None, schema_url=None, public=True, - patterns=None, generator_class=SchemaGenerator): + patterns=None, generator_class=SchemaGenerator, + authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES, + permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES): renderer_classes = [SchemaJSRenderer] return get_schema_view( @@ -35,12 +42,16 @@ def get_schemajs_view( public=public, patterns=patterns, generator_class=generator_class, + authentication_classes=authentication_classes, + permission_classes=permission_classes, ) def include_docs_urls( title=None, description=None, schema_url=None, public=True, - patterns=None, generator_class=SchemaGenerator): + patterns=None, generator_class=SchemaGenerator, + authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES, + permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES): docs_view = get_docs_view( title=title, description=description, @@ -48,6 +59,8 @@ def include_docs_urls( public=public, patterns=patterns, generator_class=generator_class, + authentication_classes=authentication_classes, + permission_classes=permission_classes, ) schema_js_view = get_schemajs_view( title=title, @@ -56,6 +69,8 @@ def include_docs_urls( public=public, patterns=patterns, generator_class=generator_class, + authentication_classes=authentication_classes, + permission_classes=permission_classes, ) urls = [ url(r'^$', docs_view, name='docs-index'), diff --git a/rest_framework/schemas/__init__.py b/rest_framework/schemas/__init__.py index fc551640e..1af0b9fc5 100644 --- a/rest_framework/schemas/__init__.py +++ b/rest_framework/schemas/__init__.py @@ -20,13 +20,17 @@ basic use-cases: Other access should target the submodules directly """ +from rest_framework.settings import api_settings + from .generators import SchemaGenerator from .inspectors import AutoSchema, ManualSchema # noqa def get_schema_view( title=None, url=None, description=None, urlconf=None, renderer_classes=None, - public=False, patterns=None, generator_class=SchemaGenerator): + public=False, patterns=None, generator_class=SchemaGenerator, + authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES, + permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES): """ Return a schema view. """ @@ -40,4 +44,6 @@ def get_schema_view( renderer_classes=renderer_classes, schema_generator=generator, public=public, + authentication_classes=authentication_classes, + permission_classes=permission_classes, ) From 1bcee8c60c4429e1f841a32fcfe45a37f52fb0f8 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Mon, 25 Sep 2017 16:25:40 +0200 Subject: [PATCH 131/730] Document extra parameters to `get_schema_view` --- docs/api-guide/schemas.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index fc848199c..2cb29d5f8 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -334,6 +334,15 @@ to be exposed in the schema: May be used to specify a `SchemaGenerator` subclass to be passed to the `SchemaView`. +#### `authentication_classes` + +May be used to specify the list of authentication classes that will apply to the schema endpoint. +Defaults to `settings.DEFAULT_AUTHENTICATION_CLASSES` + +#### `permission_classes` + +May be used to specify the list of permission classes that will apply to the schema endpoint. +Defaults to `settings.DEFAULT_PERMISSION_CLASSES` ## Using an explicit schema view From bf0fbd5df174faf9e5399263c01d21e634470acf Mon Sep 17 00:00:00 2001 From: Sigve Sebastian Farstad Date: Mon, 25 Sep 2017 18:28:36 +0200 Subject: [PATCH 132/730] Catch APIException in doc generation (#5443) The documentation generator calls view.get_serializer() in order to inspect it for documentation generation. However, if get_serializer() throws an APIException (e.g. PermissionDenied), it doesn't get caught at the call site, but instead propagates up and aborts the entire view. With the try/except in this commit, the documentation generator instead gratiously ignores that particular view and moves on to the next one instead. Practical concequences of this commit is that the docs no longer break if any view's get_serializer(..) throws an APIException. --- rest_framework/schemas/inspectors.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index a91205cde..7f2c1cc71 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -4,13 +4,14 @@ inspectors.py # Per-endpoint view introspection See schemas.__init__.py for package overview. """ import re +import warnings from collections import OrderedDict from django.db import models from django.utils.encoding import force_text, smart_text from django.utils.translation import ugettext_lazy as _ -from rest_framework import serializers +from rest_framework import exceptions, serializers from rest_framework.compat import coreapi, coreschema, uritemplate, urlparse from rest_framework.settings import api_settings from rest_framework.utils import formatting @@ -285,7 +286,14 @@ class AutoSchema(ViewInspector): if not hasattr(view, 'get_serializer'): return [] - serializer = view.get_serializer() + try: + serializer = view.get_serializer() + except exceptions.APIException: + serializer = None + warnings.warn('{}.get_serializer() raised an exception during ' + 'schema generation. Serializer fields will not be ' + 'generated for {} {}.'.format( + type(view), method, path)) if isinstance(serializer, serializers.ListSerializer): return [ From 50acb9b2feb2fc3815e06cb881f81d3e16ff4864 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 25 Sep 2017 15:09:54 -0400 Subject: [PATCH 133/730] Fix warning in AutoSchema.get_serializer_fields() (#5451) --- rest_framework/schemas/inspectors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index 7f2c1cc71..bdd720938 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -292,8 +292,8 @@ class AutoSchema(ViewInspector): serializer = None warnings.warn('{}.get_serializer() raised an exception during ' 'schema generation. Serializer fields will not be ' - 'generated for {} {}.'.format( - type(view), method, path)) + 'generated for {} {}.' + .format(view.__class__.__name__, method, path)) if isinstance(serializer, serializers.ListSerializer): return [ From 607e4edca77441f057ce40cd90175b1b5700f316 Mon Sep 17 00:00:00 2001 From: John Eskew Date: Tue, 26 Sep 2017 04:02:20 -0400 Subject: [PATCH 134/730] Defer translated string evaluation on validators. (#5452) * Customize validators to defer error string evaluation. * Add docstring for `CustomValidatorMessage` --- rest_framework/compat.py | 31 +++++++++++++++-- rest_framework/fields.py | 72 ++++++++++++++++++++++++++++------------ 2 files changed, 79 insertions(+), 24 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 528340d69..e0f718ced 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -11,13 +11,18 @@ import inspect import django from django.apps import apps from django.conf import settings -from django.core.exceptions import ImproperlyConfigured +from django.core.exceptions import ImproperlyConfigured, ValidationError +from django.core.validators import \ + MaxLengthValidator as DjangoMaxLengthValidator +from django.core.validators import MaxValueValidator as DjangoMaxValueValidator +from django.core.validators import \ + MinLengthValidator as DjangoMinLengthValidator +from django.core.validators import MinValueValidator as DjangoMinValueValidator from django.db import connection, models, transaction from django.template import Context, RequestContext, Template from django.utils import six from django.views.generic import View - try: from django.urls import ( NoReverseMatch, RegexURLPattern, RegexURLResolver, ResolverMatch, Resolver404, get_script_prefix, reverse, reverse_lazy, resolve @@ -293,6 +298,28 @@ try: except ImportError: DecimalValidator = None +class CustomValidatorMessage(object): + """ + We need to avoid evaluation of `lazy` translated `message` in `django.core.validators.BaseValidator.__init__`. + https://github.com/django/django/blob/75ed5900321d170debef4ac452b8b3cf8a1c2384/django/core/validators.py#L297 + + Ref: https://github.com/encode/django-rest-framework/pull/5452 + """ + def __init__(self, *args, **kwargs): + self.message = kwargs.pop('message', self.message) + super(CustomValidatorMessage, self).__init__(*args, **kwargs) + +class MinValueValidator(CustomValidatorMessage, DjangoMinValueValidator): + pass + +class MaxValueValidator(CustomValidatorMessage, DjangoMaxValueValidator): + pass + +class MinLengthValidator(CustomValidatorMessage, DjangoMinLengthValidator): + pass + +class MaxLengthValidator(CustomValidatorMessage, DjangoMaxLengthValidator): + pass def set_rollback(): if hasattr(transaction, 'set_rollback'): diff --git a/rest_framework/fields.py b/rest_framework/fields.py index e17a0bf9a..1bcdf763d 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -13,8 +13,7 @@ from django.conf import settings from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ObjectDoesNotExist from django.core.validators import ( - EmailValidator, MaxLengthValidator, MaxValueValidator, MinLengthValidator, - MinValueValidator, RegexValidator, URLValidator, ip_address_validators + EmailValidator, RegexValidator, URLValidator, ip_address_validators ) from django.forms import FilePathField as DjangoFilePathField from django.forms import ImageField as DjangoImageField @@ -25,14 +24,16 @@ from django.utils.dateparse import ( from django.utils.duration import duration_string from django.utils.encoding import is_protected_type, smart_text from django.utils.formats import localize_input, sanitize_separators +from django.utils.functional import lazy from django.utils.ipv6 import clean_ipv6_address from django.utils.timezone import utc from django.utils.translation import ugettext_lazy as _ from rest_framework import ISO_8601 from rest_framework.compat import ( - InvalidTimeError, get_remote_field, unicode_repr, unicode_to_repr, - value_from_object + InvalidTimeError, MaxLengthValidator, MaxValueValidator, + MinLengthValidator, MinValueValidator, get_remote_field, unicode_repr, + unicode_to_repr, value_from_object ) from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.settings import api_settings @@ -750,11 +751,17 @@ class CharField(Field): self.min_length = kwargs.pop('min_length', None) super(CharField, self).__init__(**kwargs) if self.max_length is not None: - message = self.error_messages['max_length'].format(max_length=self.max_length) - self.validators.append(MaxLengthValidator(self.max_length, message=message)) + message = lazy( + self.error_messages['max_length'].format, + six.text_type)(max_length=self.max_length) + self.validators.append( + MaxLengthValidator(self.max_length, message=message)) if self.min_length is not None: - message = self.error_messages['min_length'].format(min_length=self.min_length) - self.validators.append(MinLengthValidator(self.min_length, message=message)) + message = lazy( + self.error_messages['min_length'].format, + six.text_type)(min_length=self.min_length) + self.validators.append( + MinLengthValidator(self.min_length, message=message)) def run_validation(self, data=empty): # Test for the empty string here so that it does not get validated, @@ -909,11 +916,17 @@ class IntegerField(Field): self.min_value = kwargs.pop('min_value', None) super(IntegerField, self).__init__(**kwargs) if self.max_value is not None: - message = self.error_messages['max_value'].format(max_value=self.max_value) - self.validators.append(MaxValueValidator(self.max_value, message=message)) + message = lazy( + self.error_messages['max_value'].format, + six.text_type)(max_value=self.max_value) + self.validators.append( + MaxValueValidator(self.max_value, message=message)) if self.min_value is not None: - message = self.error_messages['min_value'].format(min_value=self.min_value) - self.validators.append(MinValueValidator(self.min_value, message=message)) + message = lazy( + self.error_messages['min_value'].format, + six.text_type)(min_value=self.min_value) + self.validators.append( + MinValueValidator(self.min_value, message=message)) def to_internal_value(self, data): if isinstance(data, six.text_type) and len(data) > self.MAX_STRING_LENGTH: @@ -943,11 +956,17 @@ class FloatField(Field): self.min_value = kwargs.pop('min_value', None) super(FloatField, self).__init__(**kwargs) if self.max_value is not None: - message = self.error_messages['max_value'].format(max_value=self.max_value) - self.validators.append(MaxValueValidator(self.max_value, message=message)) + message = lazy( + self.error_messages['max_value'].format, + six.text_type)(max_value=self.max_value) + self.validators.append( + MaxValueValidator(self.max_value, message=message)) if self.min_value is not None: - message = self.error_messages['min_value'].format(min_value=self.min_value) - self.validators.append(MinValueValidator(self.min_value, message=message)) + message = lazy( + self.error_messages['min_value'].format, + six.text_type)(min_value=self.min_value) + self.validators.append( + MinValueValidator(self.min_value, message=message)) def to_internal_value(self, data): @@ -996,11 +1015,17 @@ class DecimalField(Field): super(DecimalField, self).__init__(**kwargs) if self.max_value is not None: - message = self.error_messages['max_value'].format(max_value=self.max_value) - self.validators.append(MaxValueValidator(self.max_value, message=message)) + message = lazy( + self.error_messages['max_value'].format, + six.text_type)(max_value=self.max_value) + self.validators.append( + MaxValueValidator(self.max_value, message=message)) if self.min_value is not None: - message = self.error_messages['min_value'].format(min_value=self.min_value) - self.validators.append(MinValueValidator(self.min_value, message=message)) + message = lazy( + self.error_messages['min_value'].format, + six.text_type)(min_value=self.min_value) + self.validators.append( + MinValueValidator(self.min_value, message=message)) def to_internal_value(self, data): """ @@ -1797,8 +1822,11 @@ class ModelField(Field): max_length = kwargs.pop('max_length', None) super(ModelField, self).__init__(**kwargs) if max_length is not None: - message = self.error_messages['max_length'].format(max_length=max_length) - self.validators.append(MaxLengthValidator(max_length, message=message)) + message = lazy( + self.error_messages['max_length'].format, + six.text_type)(max_length=self.max_length) + self.validators.append( + MaxLengthValidator(self.max_length, message=message)) def to_internal_value(self, data): rel = get_remote_field(self.model_field, default=None) From ab7e5c4551b828415db192ed8e1d8c272ae60d11 Mon Sep 17 00:00:00 2001 From: Rokker Ruslan Date: Tue, 26 Sep 2017 11:24:30 +0300 Subject: [PATCH 135/730] Added default value for 'detail' param into 'ValidationError' exception (#5342) --- rest_framework/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index 7cc3ba049..12fd41931 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -123,7 +123,7 @@ class ValidationError(APIException): default_detail = _('Invalid input.') default_code = 'invalid' - def __init__(self, detail, code=None): + def __init__(self, detail=None, code=None): if detail is None: detail = self.default_detail if code is None: From b1c6ea124069c413e9d1ad5d38f8597df82ff0fb Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 27 Sep 2017 09:13:10 +0200 Subject: [PATCH 136/730] Adjust schema get_filter_fields rules to match framework (#5454) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #5237 Generics/ModelViewset performs filtering on: list, retrieve, put, patch and destroy (plus method equivalents). i.e. on list plus anything that calls `get_object`. This PR makes schema generation follow that. It adds `AutoSchema._allows_filters()` which can be overridden in subclasses. I’ve made this initially “private” so we can make quick changes if needs be in a 3.7.1 etc. --- rest_framework/schemas/inspectors.py | 31 +++++++++++++++++++++------- tests/test_schemas.py | 18 ++++++++++------ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index bdd720938..30f639f5b 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -337,18 +337,33 @@ class AutoSchema(ViewInspector): paginator = view.pagination_class() return paginator.get_schema_fields(view) + def _allows_filters(self, path, method): + """ + Determine whether to include filter Fields in schema. + + Default implementation looks for ModelViewSet or GenericAPIView + actions/methods that cause filtering on the default implementation. + + Override to adjust behaviour for your view. + + Note: Introduced in v3.7: Initially "private" (i.e. with leading underscore) + to allow changes based on user experience. + """ + if getattr(self.view, 'filter_backends', None) is None: + return False + + if hasattr(self.view, 'action'): + return self.view.action in ["list", "retrieve", "update", "partial_update", "destroy"] + + return method.lower in ["get", "put", "patch", "delete"] + def get_filter_fields(self, path, method): - view = self.view - - if not is_list_view(path, method, view): - return [] - - if not getattr(view, 'filter_backends', None): + if not self._allows_filters(path, method): return [] fields = [] - for filter_backend in view.filter_backends: - fields += filter_backend().get_schema_fields(view) + for filter_backend in self.view.filter_backends: + fields += filter_backend().get_schema_fields(self.view) return fields def get_encoding(self, path, method): diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 184401a86..07c49b71d 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -139,7 +139,8 @@ class TestRouterGeneratedSchema(TestCase): url='/example/{id}/', action='get', fields=[ - coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + coreapi.Field('id', required=True, location='path', schema=coreschema.String()), + coreapi.Field('ordering', required=False, location='query', schema=coreschema.String(title='Ordering', description='Which field to use when ordering the results.')) ] ) } @@ -179,7 +180,8 @@ class TestRouterGeneratedSchema(TestCase): url='/example/{id}/', action='get', fields=[ - coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + coreapi.Field('id', required=True, location='path', schema=coreschema.String()), + coreapi.Field('ordering', required=False, location='query', schema=coreschema.String(title='Ordering', description='Which field to use when ordering the results.')) ] ), 'custom_action': coreapi.Link( @@ -225,7 +227,8 @@ class TestRouterGeneratedSchema(TestCase): fields=[ coreapi.Field('id', required=True, location='path', schema=coreschema.String()), coreapi.Field('a', required=True, location='form', schema=coreschema.String(title='A', description=('A field description'))), - coreapi.Field('b', required=False, location='form', schema=coreschema.String(title='B')) + coreapi.Field('b', required=False, location='form', schema=coreschema.String(title='B')), + coreapi.Field('ordering', required=False, location='query', schema=coreschema.String(title='Ordering', description='Which field to use when ordering the results.')) ] ), 'partial_update': coreapi.Link( @@ -235,14 +238,16 @@ class TestRouterGeneratedSchema(TestCase): fields=[ coreapi.Field('id', required=True, location='path', schema=coreschema.String()), coreapi.Field('a', required=False, location='form', schema=coreschema.String(title='A', description='A field description')), - coreapi.Field('b', required=False, location='form', schema=coreschema.String(title='B')) + coreapi.Field('b', required=False, location='form', schema=coreschema.String(title='B')), + coreapi.Field('ordering', required=False, location='query', schema=coreschema.String(title='Ordering', description='Which field to use when ordering the results.')) ] ), 'delete': coreapi.Link( url='/example/{id}/', action='delete', fields=[ - coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + coreapi.Field('id', required=True, location='path', schema=coreschema.String()), + coreapi.Field('ordering', required=False, location='query', schema=coreschema.String(title='Ordering', description='Which field to use when ordering the results.')) ] ) } @@ -450,7 +455,8 @@ class TestSchemaGeneratorWithMethodLimitedViewSets(TestCase): url='/example1/{id}/', action='get', fields=[ - coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + coreapi.Field('id', required=True, location='path', schema=coreschema.String()), + coreapi.Field('ordering', required=False, location='query', schema=coreschema.String(title='Ordering', description='Which field to use when ordering the results.')) ] ) } From 296904099f1f87657e064d5915737cbb59fde079 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 27 Sep 2017 10:29:48 +0200 Subject: [PATCH 137/730] Update test matrix: Add Django 2.0, drop 1.8 (#5457) * Add Django 2.0 to tox/travis. Updated requirements * Drop Django 1.8 & 1.9 --- .travis.yml | 10 ++++++---- requirements/requirements-codestyle.txt | 4 ++-- requirements/requirements-documentation.txt | 2 +- requirements/requirements-optionals.txt | 4 ++-- requirements/requirements-packaging.txt | 4 ++-- requirements/requirements-testing.txt | 4 ++-- tox.ini | 10 ++++------ 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7a91af809..6a3ce8c49 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,10 +8,9 @@ python: sudo: false env: - - DJANGO=1.8 - - DJANGO=1.9 - DJANGO=1.10 - DJANGO=1.11 + - DJANGO=2.0 - DJANGO=master matrix: @@ -21,8 +20,8 @@ matrix: env: DJANGO=master - python: "3.6" env: DJANGO=1.11 - - python: "3.3" - env: DJANGO=1.8 + - python: "3.6" + env: DJANGO=2.0 - python: "2.7" env: TOXENV="lint" - python: "2.7" @@ -30,11 +29,14 @@ matrix: exclude: - python: "2.7" env: DJANGO=master + - python: "2.7" + env: DJANGO=2.0 - python: "3.4" env: DJANGO=master allow_failures: - env: DJANGO=master + - env: DJANGO=2.0 install: - pip install tox tox-travis diff --git a/requirements/requirements-codestyle.txt b/requirements/requirements-codestyle.txt index 9bafbe391..a64cfa29f 100644 --- a/requirements/requirements-codestyle.txt +++ b/requirements/requirements-codestyle.txt @@ -1,7 +1,7 @@ # PEP8 code linting, which we run on all commits. -flake8==2.4.0 +flake8==3.4.1 flake8-tidy-imports==1.1.0 -pep8==1.5.7 +pep8==1.7.0 # Sort and lint imports isort==4.2.5 diff --git a/requirements/requirements-documentation.txt b/requirements/requirements-documentation.txt index 81aa70456..012ec99f1 100644 --- a/requirements/requirements-documentation.txt +++ b/requirements/requirements-documentation.txt @@ -1,2 +1,2 @@ # MkDocs to build our documentation. -mkdocs==0.16.2 +mkdocs==0.16.3 diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt index 319b7547a..67525bebc 100644 --- a/requirements/requirements-optionals.txt +++ b/requirements/requirements-optionals.txt @@ -1,7 +1,7 @@ # Optional packages which may be used with REST framework. pytz==2017.2 markdown==2.6.4 -django-guardian==1.4.8 +django-guardian==1.4.9 django-filter==1.0.4 -coreapi==2.2.4 +coreapi==2.3.1 coreschema==0.0.4 diff --git a/requirements/requirements-packaging.txt b/requirements/requirements-packaging.txt index f930bfa96..f12d4af0e 100644 --- a/requirements/requirements-packaging.txt +++ b/requirements/requirements-packaging.txt @@ -1,8 +1,8 @@ # Wheel for PyPI installs. -wheel==0.29.0 +wheel==0.30.0 # Twine for secured PyPI uploads. -twine==1.6.5 +twine==1.9.1 # Transifex client for managing translation resources. transifex-client==0.11 diff --git a/requirements/requirements-testing.txt b/requirements/requirements-testing.txt index b9e168442..515cff78d 100644 --- a/requirements/requirements-testing.txt +++ b/requirements/requirements-testing.txt @@ -1,4 +1,4 @@ # PyTest for running the tests. -pytest==3.0.5 +pytest==3.2.2 pytest-django==3.1.2 -pytest-cov==2.4.0 +pytest-cov==2.5.1 diff --git a/tox.ini b/tox.ini index 698a0b745..9c91a4920 100644 --- a/tox.ini +++ b/tox.ini @@ -3,18 +3,17 @@ addopts=--tb=short [tox] envlist = - {py27,py33,py34,py35}-django18, - {py27,py34,py35}-django{19,110}, + {py27,py34,py35}-django110, {py27,py34,py35,py36}-django111, + {py34,py35,py36}-django20, {py35,py36}-djangomaster lint,docs [travis:env] DJANGO = - 1.8: django18 - 1.9: django19 1.10: django110 1.11: django111 + 2.0: django20 master: djangomaster [testenv] @@ -23,10 +22,9 @@ setenv = PYTHONDONTWRITEBYTECODE=1 PYTHONWARNINGS=once deps = - django18: Django>=1.8,<1.9 - django19: Django>=1.9,<1.10 django110: Django>=1.10,<1.11 django111: Django>=1.11,<2.0 + django20: Django>=2.0a1,<2.1 djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From 760268ade2e29a60b151cde15a1a79685970e68a Mon Sep 17 00:00:00 2001 From: Thierry Bastian Date: Wed, 27 Sep 2017 10:51:45 +0200 Subject: [PATCH 138/730] Fixed a deprecation warning (#5058) --- rest_framework/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 1bcdf763d..7a79ae93c 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1831,7 +1831,7 @@ class ModelField(Field): def to_internal_value(self, data): rel = get_remote_field(self.model_field, default=None) if rel is not None: - return rel.to._meta.get_field(rel.field_name).to_python(data) + return rel.model._meta.get_field(rel.field_name).to_python(data) return self.model_field.to_python(data) def get_attribute(self, obj): From 5c2290d97380ca15005f7d42ffceae04dc817dba Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 27 Sep 2017 12:23:54 +0200 Subject: [PATCH 139/730] Add note on not using floats with CursorPagination (#5459) Closes #5160, closes #5164. --- docs/api-guide/pagination.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 7b5e5b3f6..b28f1616d 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -179,6 +179,10 @@ Proper usage of cursor pagination should have an ordering field that satisfies t * Should be an unchanging value, such as a timestamp, slug, or other field that is only set once, on creation. * Should be unique, or nearly unique. Millisecond precision timestamps are a good example. This implementation of cursor pagination uses a smart "position plus offset" style that allows it to properly support not-strictly-unique values as the ordering. * Should be a non-nullable value that can be coerced to a string. +* Should not be a float. Precision errors easily lead to incorrect results. + Hint: use decimals instead. + (If you already have a float field and must paginate on that, an + [example `CursorPagination` subclass that uses decimals to limit precision is available here][float_cursor_pagination_example].) * The field should have a database index. Using an ordering field that does not satisfy these constraints will generally still work, but you'll be losing some of the benefits of cursor pagination. @@ -317,3 +321,4 @@ The [`django-rest-framework-link-header-pagination` package][drf-link-header-pag [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 +[float_cursor_pagination_example]: https://gist.github.com/keturn/8bc88525a183fd41c73ffb729b8865be#file-fpcursorpagination-py From 018e43e908e912b5e99dcc648b37a68ee59a58ed Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Fri, 29 Sep 2017 10:42:24 -0400 Subject: [PATCH 140/730] Remove old django-filter templates (#5465) --- .../templates/rest_framework/filters/django_filter.html | 6 ------ .../rest_framework/filters/django_filter_crispyforms.html | 5 ----- 2 files changed, 11 deletions(-) delete mode 100644 rest_framework/templates/rest_framework/filters/django_filter.html delete mode 100644 rest_framework/templates/rest_framework/filters/django_filter_crispyforms.html diff --git a/rest_framework/templates/rest_framework/filters/django_filter.html b/rest_framework/templates/rest_framework/filters/django_filter.html deleted file mode 100644 index b116e3531..000000000 --- a/rest_framework/templates/rest_framework/filters/django_filter.html +++ /dev/null @@ -1,6 +0,0 @@ -{% load i18n %} -

{% trans "Field filters" %}

- - {{ filter.form.as_p }} - - diff --git a/rest_framework/templates/rest_framework/filters/django_filter_crispyforms.html b/rest_framework/templates/rest_framework/filters/django_filter_crispyforms.html deleted file mode 100644 index 171767c08..000000000 --- a/rest_framework/templates/rest_framework/filters/django_filter_crispyforms.html +++ /dev/null @@ -1,5 +0,0 @@ -{% load crispy_forms_tags %} -{% load i18n %} - -

{% trans "Field filters" %}

-{% crispy filter.form %} From efc427dfc8144855f28a20888bbe11b83cbfa08f Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Mon, 2 Oct 2017 08:59:53 +0200 Subject: [PATCH 141/730] Reuse 'apply_markdown' function in 'render_markdown' templatetag func (#5469) * reused 'apply_markdown' function in 'render_markdown' templatetag function * typo fixed --- rest_framework/templatetags/rest_framework.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index a2ee5ccdd..264b6444c 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -11,7 +11,8 @@ from django.utils.html import escape, format_html, smart_urlquote from django.utils.safestring import SafeData, mark_safe from rest_framework.compat import ( - NoReverseMatch, markdown, pygments_highlight, reverse, template_render + NoReverseMatch, apply_markdown, pygments_highlight, reverse, + template_render ) from rest_framework.renderers import HTMLFormRenderer from rest_framework.utils.urls import replace_query_param @@ -68,9 +69,9 @@ def form_for_link(link): @register.simple_tag def render_markdown(markdown_text): - if not markdown: + if apply_markdown is None: return markdown_text - return mark_safe(markdown.markdown(markdown_text)) + return mark_safe(apply_markdown(markdown_text)) @register.simple_tag From e6193cfd9e6391c635368b9c71ed545c8fc024d1 Mon Sep 17 00:00:00 2001 From: Shreyans Sheth Date: Mon, 2 Oct 2017 12:34:55 +0530 Subject: [PATCH 142/730] Added Response import in Code Snippet (#5468) Added `from rest_framework.response import Response` in the viewset code snippet example --- docs/tutorial/6-viewsets-and-routers.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md index 6189c7771..252021e39 100644 --- a/docs/tutorial/6-viewsets-and-routers.md +++ b/docs/tutorial/6-viewsets-and-routers.md @@ -26,6 +26,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.response import Response class SnippetViewSet(viewsets.ModelViewSet): """ From 62ecbf2817e5dbce8bd67f4b8ba257bb4c4eec8c Mon Sep 17 00:00:00 2001 From: Lim H Date: Mon, 2 Oct 2017 10:16:33 +0100 Subject: [PATCH 143/730] Add drf-openapi (#5470) * Add DRF OpenAPI as a 3rd party tool for DRF doc * Add image * Add third party packages section to schema doc * Add DRF OpenAPI reference --- docs/api-guide/schemas.md | 11 +++++++++++ docs/img/drf-openapi.png | Bin 0 -> 75416 bytes docs/topics/documenting-your-api.md | 19 +++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 docs/img/drf-openapi.png diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 2cb29d5f8..51a6c00c6 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -765,10 +765,21 @@ Valid only if a `location="body"` field is included on the `Link`. A short description of the meaning and intended usage of the input field. +--- + +# Third party packages + +## DRF OpenAPI + +[DRF OpenAPI][drf-openapi] renders the schema generated by Django Rest Framework +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 [open-api]: https://openapis.org/ +[drf-openapi]: https://github.com/limdauto/drf_openapi [json-hyperschema]: http://json-schema.org/latest/json-schema-hypermedia.html [api-blueprint]: https://apiblueprint.org/ [static-files]: https://docs.djangoproject.com/en/stable/howto/static-files/ diff --git a/docs/img/drf-openapi.png b/docs/img/drf-openapi.png new file mode 100644 index 0000000000000000000000000000000000000000..7a37e19d6136301b5ff43bea160db7e32d3a9b96 GIT binary patch literal 75416 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 jem7vu7tb~Bf{=`4mg7QE_2QS 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 literal 0 HcmV?d00001 diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md index da1dbe358..b68eb64ec 100644 --- a/docs/topics/documenting-your-api.md +++ b/docs/topics/documenting-your-api.md @@ -75,6 +75,23 @@ When using viewsets, you should use the relevant action names as delimiters. There are a number of mature third-party packages for providing API documentation. +#### DRF OpenAPI + +[DRF OpenAPI][drf-openapi] bridges the gap between OpenAPI specification and tool chain with the schema exposed +out-of-the-box by Django Rest Framework. Its goals are: + + * To be dropped into any existing DRF project without any code change necessary. + * Provide clear disctinction between request schema and response schema. + * Provide a versioning mechanism for each schema. Support defining schema by version range syntax, e.g. >1.0, <=2.0 + * Support multiple response codes, not just 200 + * All this information should be bound to view methods, not view classes. + +It also tries to stay current with the maturing schema generation mechanism provided by DRF. + +![Screenshot - DRF OpenAPI][image-drf-openapi] + +--- + #### DRF Docs [DRF Docs][drfdocs-repo] allows you to document Web APIs made with Django REST Framework and it is authored by Emmanouil Konstantinidis. It's made to work out of the box and its setup should not take more than a couple of minutes. Complete documentation can be found on the [website][drfdocs-website] while there is also a [demo][drfdocs-demo] available for people to see what it looks like. **Live API Endpoints** allow you to utilize the endpoints from within the documentation in a neat way. @@ -197,6 +214,8 @@ In this approach, rather than documenting the available API endpoints up front, To implement a hypermedia API you'll need to decide on an appropriate media type for the API, and implement a custom renderer and parser for that media type. The [REST, Hypermedia & HATEOAS][hypermedia-docs] section of the documentation includes pointers to background reading, as well as links to various hypermedia formats. [cite]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven +[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-demo]: http://demo.drfdocs.com/ From 063534ae50d3d15a307205c91ed5a25974b0a1f4 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Mon, 2 Oct 2017 11:44:29 +0200 Subject: [PATCH 144/730] Docstrings highlighting with pygments (#5462) * add 'docstrings-with-pygments' feature without packages checks and tests * move syntax_highlight doc filter in compatibility module and define it conditionally * typo fixed * add test for optional code highlight ('pygments' and 'markdown' packages must be installed) --- rest_framework/compat.py | 33 +++++++++++++++++++++++++ tests/test_description.py | 52 ++++++++++++++++++++++++++++++++++----- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index e0f718ced..3b341a656 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -244,6 +244,7 @@ try: md = markdown.Markdown( extensions=extensions, extension_configs=extension_configs ) + md_filter_add_syntax_highlight(md) return md.convert(text) except ImportError: apply_markdown = None @@ -273,6 +274,38 @@ except ImportError: def pygments_css(style): return None +if markdown is not None and pygments is not None: + # starting from this blogpost and modified to support current markdown extensions API + # https://zerokspot.com/weblog/2008/06/18/syntax-highlighting-in-markdown-with-pygments/ + + from markdown.preprocessors import Preprocessor + import re + + class CodeBlockPreprocessor(Preprocessor): + pattern = re.compile( + r'^\s*@@ (.+?) @@\s*(.+?)^\s*@@', re.M|re.S) + + formatter = HtmlFormatter() + + def run(self, lines): + def repl(m): + try: + lexer = get_lexer_by_name(m.group(1)) + except (ValueError, NameError): + lexer = TextLexer() + code = m.group(2).replace('\t',' ') + code = pygments.highlight(code, lexer, self.formatter) + code = code.replace('\n\n', '\n \n').replace('\n', '
').replace('\\@','@') + return '\n\n%s\n\n' % code + ret = self.pattern.sub(repl, "\n".join(lines)) + return ret.split("\n") + + def md_filter_add_syntax_highlight(md): + md.preprocessors.add('highlight', CodeBlockPreprocessor(), "_begin") + return True +else: + def md_filter_add_syntax_highlight(md): + return False try: import pytz diff --git a/tests/test_description.py b/tests/test_description.py index 4df14ac55..a97550ed8 100644 --- a/tests/test_description.py +++ b/tests/test_description.py @@ -24,11 +24,36 @@ another header indented -# hash style header #""" +# hash style header # + +@@ json @@ +[{ + "alpha": 1, + "beta: "this is a string" +}] +@@""" # If markdown is installed we also test it's working # (and that our wrapped forces '=' to h2 and '-' to h3) +MARKED_DOWN_HILITE = """ +
[{
"alpha": 1,
\ + "beta: "this\ + is a \ +string"
}]
+ +


""" + +MARKED_DOWN_NOT_HILITE = """ +

@@ json @@ +[{ + "alpha": 1, + "beta: "this is a string" +}] +@@

""" + # We support markdown < 2.1 and markdown >= 2.1 MARKED_DOWN_lt_21 = """

an example docstring

From 73203e6b5920dcbe78e3309b7bf2803eb56db536 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Thu, 25 Jan 2018 03:40:49 -0500 Subject: [PATCH 294/730] 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 | 52 ++++++--- rest_framework/routers.py | 146 ++++++++++++------------ 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, 333 insertions(+), 206 deletions(-) diff --git a/docs/api-guide/metadata.md b/docs/api-guide/metadata.md index d5c3c1a67..affeea61c 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 e4415df15..09e06ff1d 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 27fb1d780..503459a96 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. +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. -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. - -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 128175ef5..2c45b70af 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 7d87c0212..9452b4947 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 2f93fdd97..62afa0597 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -130,29 +130,49 @@ def schema(view_inspector): return decorator +def action(methods=None, detail=None, url_path=None, url_name=None, **kwargs): + """ + Mark a ViewSet method as a routable action. + + Set the `detail` boolean to determine if this action should apply to + instance/detail requests or collection/list requests. + """ + 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 = 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. """ - methods = ['get'] if (methods is None) else methods - - def decorator(func): - func.bind_to_methods = methods - func.detail = True - func.kwargs = kwargs - return func - return decorator + 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 f4d2fab38..9007788f8 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -16,6 +16,7 @@ For example, you might have a `urls.py` that looks something like this: 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.schemas.views import SchemaView 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 @@ class SimpleRouter(BaseRouter): # 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)])) + extra_actions = viewset.get_extra_actions() - # 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)) + # 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)) - 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, - )) + # 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] - return ret - - ret = [] + 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 @@ class SimpleRouter(BaseRouter): 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 4ee7cdaf8..9a85049bc 100644 --- a/rest_framework/viewsets.py +++ b/rest_framework/viewsets.py @@ -19,6 +19,7 @@ automatically. 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 import generics, mixins, views 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 @@ class ViewSetMixin(object): # 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 @@ class ViewSetMixin(object): 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 @@ class ViewSetMixin(object): 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 6331742db..674990730 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 @@ class DecoratorTestCase(TestCase): 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 55ccc647b..36255f48f 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -11,7 +11,7 @@ from django.urls import resolve 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 @@ class EmptyPrefixViewSet(viewsets.ModelViewSet): 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 @@ class TestActionKeywordArgs(TestCase): 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 @@ class TestActionKeywordArgs(TestCase): 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 @@ class TestViewInitkwargs(URLPatternsTestCase, TestCase): 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 34cb20798..1cbee0695 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -10,9 +10,7 @@ from rest_framework import ( 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 @@ class TestURLNamingCollisions(TestCase): 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 @@ class TestURLNamingCollisions(TestCase): 'detail_export': coreapi.Link( url='/from-routercollision/detail/export/', action='get', - description=desc) + description=desc_0) }, 'detail_0': coreapi.Link( url='/from-routercollision/detail/', action='get', - description=desc + description=desc_1 ) } ) @@ -1046,7 +1047,7 @@ def test_head_and_options_methods_are_excluded(): 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 beff42cb8..25feb0f37 100644 --- a/tests/test_viewsets.py +++ b/tests/test_viewsets.py @@ -3,7 +3,7 @@ from django.db import models 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 @@ class ActionViewSet(GenericViewSet): 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 @@ class InitializeViewSetsTestCase(TestCase): 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 295/730] 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 2c45b70af..820fa731b 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 296/730] 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 019b48b3d..1de669b42 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 de011cd09..0f588c2d7 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 14007aa52..5d4d258be 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 8015bd7f0..1155e7ece 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 de704ab51..814910344 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 8ef880cbc..a6938fc45 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 631ad71ba..ba4891708 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 297/730] 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 403c2b865..78fb3f83e 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 298/730] 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 8e62c70f1..c106e536d 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 299/730] 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 d885ba643..492872ae5 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -14,6 +14,7 @@ from django.utils.translation import ugettext_lazy as _ 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 @@ class ErrorDetail(six.text_type): 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 176aeb174..006191a49 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -53,6 +53,33 @@ class ExceptionTestCase(TestCase): '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 300/730] 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 a0a286b22..bb17a3a11 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("../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(../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 301/730] 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 f9ee7fb53..1f51517db 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -8,10 +8,8 @@ from django.core.exceptions import PermissionDenied 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 302/730] 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 3284c9e1b..eb2799859 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 303/730] 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 19976b00b..9cc6ccee0 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 304/730] 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 413b9c0ac..8ef783683 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 1cbee0695..1abe5d2a1 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -23,7 +23,7 @@ from rest_framework.utils import formatting 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 @@ class TestSchemaGeneratorWithRestrictedViewSets(TestCase): 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(r'^example/?$', ForeignKeySourceView.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='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 305/730] 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 8ef783683..86c6cb71a 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 1abe5d2a1..267c44585 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 @@ class ExampleViewSet(ModelViewSet): """ 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 @@ class TestRouterGeneratedSchema(TestCase): 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='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='post', From 878fe895dcd35297be798083a704ee74dd8709ba Mon Sep 17 00:00:00 2001 From: "Fraire, Santiago" Date: Fri, 5 Jan 2018 13:57:52 +0100 Subject: [PATCH 306/730] 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 0704118bd..e33023c40 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(r'^api-token-auth/', CustomAuthToken.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 307/730] 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 eb2799859..aaefe3db8 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 6254d2f7f..6eed3782e 100644 --- a/rest_framework/authtoken/views.py +++ b/rest_framework/authtoken/views.py @@ -3,6 +3,9 @@ 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 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 86c6cb71a..171b88b0b 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 @@ class ManualSchema(ViewInspector): 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 @@ class ManualSchema(ViewInspector): 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 308/730] 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 7e4daf274..9d4f73d30 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -278,10 +278,11 @@ class Request(object): 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 8c680baa0..83d295a12 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -13,6 +13,7 @@ from django.contrib.auth.middleware import AuthenticationMiddleware 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 @@ class MockView(APIView): 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 @@ class FileUploadView(APIView): urlpatterns = [ url(r'^$', MockView.as_view()), + url(r'^echo/$', EchoView.as_view()), url(r'^upload/$', FileUploadView.as_view()) ] @@ -271,24 +278,64 @@ class TestSecure(TestCase): 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 309/730] 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 6eed3782e..ff53c01ba 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 310/730] 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 72ce56d26..e0db14d44 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 311/730] 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 72cbeab91..f5fc214cd 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 312/730] 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 82113bd8a..000000000 --- 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(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../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 be9574d70..000000000 --- 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 da4170647..000000000 --- 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("