From ea810001602bbf8fa615097abffa372a0b29a4f7 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Wed, 8 Nov 2017 21:30:33 -0800 Subject: [PATCH 001/585] Remove ulrparse compatability shim; use six instead The urlparse shim in compat.py duplicates Django's bundled six. Can rely on upstream instead of duplicating their works. Unifies shim with other files already using six. --- rest_framework/compat.py | 6 ------ rest_framework/schemas/inspectors.py | 3 ++- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 456c8b20d1..03cae21878 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -54,12 +54,6 @@ def make_url_resolver(regex, urlpatterns): return RegexURLResolver(regex, urlpatterns) -try: - import urlparse # Python 2.x -except ImportError: - import urllib.parse as urlparse - - def unicode_repr(instance): # Get the repr of an instance, but ensure it is a unicode string # on both python 3 (already the case) and 2 (not the case). diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index f112e5eee6..bae4d38ede 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -9,10 +9,11 @@ from django.db import models from django.utils.encoding import force_text, smart_text +from django.utils.six.moves.urllib import parse as urlparse from django.utils.translation import ugettext_lazy as _ from rest_framework import exceptions, serializers -from rest_framework.compat import coreapi, coreschema, uritemplate, urlparse +from rest_framework.compat import coreapi, coreschema, uritemplate from rest_framework.settings import api_settings from rest_framework.utils import formatting From f8e8381c009bc9c8651037eb130e57ab7daee5bf Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Thu, 9 Nov 2017 00:03:48 -0800 Subject: [PATCH 002/585] Drop compat wrapper for TimeDelta.total_seconds() (#5577) TimeDelta.total_seconds() was introduced in Python 2.7 and 3.2. It is available on all supported Python versions. https://docs.python.org/2/library/datetime.html#datetime.timedelta.total_seconds https://docs.python.org/3/library/datetime.html#datetime.timedelta.total_seconds --- rest_framework/compat.py | 8 -------- rest_framework/utils/encoders.py | 4 ++-- tests/test_compat.py | 9 --------- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 03cae21878..67531948ee 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -78,14 +78,6 @@ def unicode_http_header(value): return value -def total_seconds(timedelta): - # TimeDelta.total_seconds() is only available in Python 2.7 - if hasattr(timedelta, 'total_seconds'): - return timedelta.total_seconds() - else: - return (timedelta.days * 86400.0) + float(timedelta.seconds) + (timedelta.microseconds / 1000000.0) - - def distinct(queryset, base): if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle": # distinct analogue for Oracle users diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index 87df365e0b..7518d4ffd3 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -13,7 +13,7 @@ from django.utils.encoding import force_text from django.utils.functional import Promise -from rest_framework.compat import coreapi, total_seconds +from rest_framework.compat import coreapi class JSONEncoder(json.JSONEncoder): @@ -39,7 +39,7 @@ def default(self, obj): representation = obj.isoformat() return representation elif isinstance(obj, datetime.timedelta): - return six.text_type(total_seconds(obj)) + return six.text_type(obj.total_seconds()) elif isinstance(obj, decimal.Decimal): # Serializers will coerce decimals to strings by default. return float(obj) diff --git a/tests/test_compat.py b/tests/test_compat.py index aa11076177..842cb8ef81 100644 --- a/tests/test_compat.py +++ b/tests/test_compat.py @@ -13,15 +13,6 @@ def tearDown(self): compat.django.VERSION = self.original_django_version compat.transaction = self.original_transaction - def test_total_seconds(self): - class MockTimedelta(object): - days = 1 - seconds = 1 - microseconds = 100 - timedelta = MockTimedelta() - expected = (timedelta.days * 86400.0) + float(timedelta.seconds) + (timedelta.microseconds / 1000000.0) - assert compat.total_seconds(timedelta) == expected - def test_set_rollback_for_transaction_in_managed_mode(self): class MockTransaction(object): called_rollback = False From f9c67f04d408365704cbb98590cc7348a9f07120 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Thu, 9 Nov 2017 11:57:53 -0800 Subject: [PATCH 003/585] Clean up all whitespace throughout project (#5578) * Remove trailing whitespace from lines * Remove trailing nad leading whitespace from files Allows for cleaner diffs in future changes. For editors that automatically clean up whitespace on save, will avoid unrelated line changes in diffs. --- .editorconfig | 7 +++++++ .gitignore | 1 + .tx/config | 1 - docs/api-guide/caching.md | 8 ++++---- docs/api-guide/generic-views.md | 6 +++--- docs/api-guide/permissions.md | 6 +++--- docs/api-guide/routers.md | 12 ++++++------ docs/api-guide/schemas.md | 1 - docs/api-guide/validators.md | 2 +- docs/topics/3.7-announcement.md | 1 - docs/topics/browser-enhancements.md | 2 +- docs/topics/html-and-forms.md | 4 ++-- docs/topics/jobs.md | 2 +- rest_framework/compat.py | 1 - rest_framework/static/rest_framework/css/default.css | 1 - .../docs/fonts/fontawesome-webfont.svg | 2 +- .../docs/fonts/glyphicons-halflings-regular.svg | 2 +- .../fonts/glyphicons-halflings-regular.svg | 2 +- .../templates/rest_framework/docs/error.html | 4 ---- .../rest_framework/docs/langs/javascript-intro.html | 1 - tests/importable/__init__.py | 1 - tests/importable/test_installed.py | 1 - 22 files changed, 32 insertions(+), 36 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..f999431de5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.gitignore b/.gitignore index 41768084c5..70b55a0944 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ MANIFEST coverage.* +!.editorconfig !.gitignore !.travis.yml !.isort.cfg diff --git a/.tx/config b/.tx/config index dea9db7c93..e151a7e6ff 100644 --- a/.tx/config +++ b/.tx/config @@ -7,4 +7,3 @@ file_filter = rest_framework/locale//LC_MESSAGES/django.po source_file = rest_framework/locale/en_US/LC_MESSAGES/django.po source_lang = en_US type = PO - diff --git a/docs/api-guide/caching.md b/docs/api-guide/caching.md index 289b5a2b2f..ed3f62c21e 100644 --- a/docs/api-guide/caching.md +++ b/docs/api-guide/caching.md @@ -1,6 +1,6 @@ # Caching -> A certain woman had a very sharp conciousness but almost no +> A certain woman had a very sharp conciousness but almost no > memory ... She remembered enough to work, and she worked hard. > - Lydia Davis @@ -22,9 +22,9 @@ from rest_framework.views import APIView from rest_framework import viewsets class UserViewSet(viewsets.Viewset): - + # Cache requested url for each user for 2 hours - @method_decorator(cache_page(60*60*2)) + @method_decorator(cache_page(60*60*2)) @method_decorator(vary_on_cookie) def list(self, request, format=None): content = { @@ -35,7 +35,7 @@ class UserViewSet(viewsets.Viewset): class PostView(APIView): # Cache page for the requested url - @method_decorator(cache_page(60*60*2)) + @method_decorator(cache_page(60*60*2)) def get(self, request, format=None): content = { 'title': 'Post title', diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 381f1fe73e..a0ed7bdead 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -113,11 +113,11 @@ For example: Note that if your API doesn't include any object level permissions, you may optionally exclude the `self.check_object_permissions`, and simply return the object from the `get_object_or_404` lookup. -#### `filter_queryset(self, queryset)` +#### `filter_queryset(self, queryset)` -Given a queryset, filter it with whichever filter backends are in use, returning a new queryset. +Given a queryset, filter it with whichever filter backends are in use, returning a new queryset. -For example: +For example: def filter_queryset(self, queryset): filter_backends = (CategoryFilter,) diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index ef9ce3abd3..3b89e9141d 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -197,15 +197,15 @@ If you need to test if a request is a read operation or a write operation, you s --- Custom permissions will raise a `PermissionDenied` exception if the test fails. To change the error message associated with the exception, implement a `message` attribute directly on your custom permission. Otherwise the `default_detail` attribute from `PermissionDenied` will be used. - + from rest_framework import permissions class CustomerAccessPermission(permissions.BasePermission): message = 'Adding customers not allowed.' - + def has_permission(self, request, view): ... - + ## Examples The following is an example of a permission class that checks the incoming request's IP address against a blacklist, and denies the request if the IP has been blacklisted. diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md index 0c6aab152c..84ca82a3f7 100644 --- a/docs/api-guide/routers.md +++ b/docs/api-guide/routers.md @@ -58,11 +58,11 @@ For example, you can append `router.urls` to a list of existing views… router = routers.SimpleRouter() router.register(r'users', UserViewSet) router.register(r'accounts', AccountViewSet) - + urlpatterns = [ url(https://github.com/r'%5Eforgot-password/),%20ForgotPasswordFormView.as_view()), ] - + urlpatterns += router.urls Alternatively you can use Django's `include` function, like so… @@ -106,10 +106,10 @@ For example, if you want to change the URL for our custom action to `^users/{pk} from myapp.permissions import IsAdminOrIsSelf from rest_framework.decorators import detail_route - + class UserViewSet(ModelViewSet): ... - + @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_path='change-password') def set_password(self, request, pk=None): ... @@ -124,10 +124,10 @@ For example, if you want to change the name of our custom action to `'user-chang 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): ... diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 29b779d50f..22894a9782 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -784,4 +784,3 @@ in [OpenAPI][open-api] format. [api-blueprint]: https://apiblueprint.org/ [static-files]: https://docs.djangoproject.com/en/stable/howto/static-files/ [named-arguments]: https://docs.djangoproject.com/en/stable/topics/http/urls/#named-groups - \ No newline at end of file diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index 0e58c6fff7..fe492c0a71 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -84,7 +84,7 @@ It has two required arguments, and a single optional `messages` argument: The validator should be applied to *serializer classes*, like so: from rest_framework.validators import UniqueTogetherValidator - + class ExampleSerializer(serializers.Serializer): # ... class Meta: diff --git a/docs/topics/3.7-announcement.md b/docs/topics/3.7-announcement.md index 709580a4b2..a916f67b40 100644 --- a/docs/topics/3.7-announcement.md +++ b/docs/topics/3.7-announcement.md @@ -1,4 +1,3 @@ - {% endif %} {% endblock %} {% endblock %} From 15024f3f07b9311887ad6f5afb366f6c032c0486 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 14 Nov 2017 03:55:59 -0500 Subject: [PATCH 008/585] Remove set_rollback() from compat (#5591) * Remove Django 1.6 transaction compat * Move set_rollback from compat => views --- rest_framework/compat.py | 21 ++----------------- rest_framework/views.py | 9 ++++++-- tests/test_compat.py | 44 ---------------------------------------- 3 files changed, 9 insertions(+), 65 deletions(-) delete mode 100644 tests/test_compat.py diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 75a840ad59..5009ffee19 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -12,7 +12,7 @@ from django.conf import settings from django.core import validators from django.core.exceptions import ImproperlyConfigured -from django.db import connection, models, transaction +from django.db import models from django.utils import six from django.views.generic import View @@ -250,7 +250,7 @@ def md_filter_add_syntax_highlight(md): # pytz is required from Django 1.11. Remove when dropping Django 1.10 support. try: - import pytz # noqa + import pytz # noqa from pytz.exceptions import InvalidTimeError except ImportError: InvalidTimeError = Exception @@ -297,23 +297,6 @@ class MaxLengthValidator(CustomValidatorMessage, validators.MaxLengthValidator): pass -def set_rollback(): - if hasattr(transaction, 'set_rollback'): - if connection.settings_dict.get('ATOMIC_REQUESTS', False): - # If running in >=1.6 then mark a rollback as required, - # and allow it to be handled by Django. - if connection.in_atomic_block: - transaction.set_rollback(True) - elif transaction.is_managed(): - # Otherwise handle it explicitly if in managed mode. - if transaction.is_dirty(): - transaction.rollback() - transaction.leave_transaction_management() - else: - # transaction not managed - pass - - def authenticate(request=None, **credentials): from django.contrib.auth import authenticate if django.VERSION < (1, 11): diff --git a/rest_framework/views.py b/rest_framework/views.py index dfed158887..3140bb9a3f 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -5,7 +5,7 @@ from django.conf import settings from django.core.exceptions import PermissionDenied -from django.db import models +from django.db import connection, models, transaction from django.http import Http404 from django.http.response import HttpResponseBase from django.utils import six @@ -16,7 +16,6 @@ from django.views.generic import View 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 @@ -55,6 +54,12 @@ def get_view_description(view_cls, html=False): return description +def set_rollback(): + atomic_requests = connection.settings_dict.get('ATOMIC_REQUESTS', False) + if atomic_requests and connection.in_atomic_block: + transaction.set_rollback(True) + + def exception_handler(exc, context): """ Returns the response that should be used for any given exception. diff --git a/tests/test_compat.py b/tests/test_compat.py deleted file mode 100644 index 842cb8ef81..0000000000 --- a/tests/test_compat.py +++ /dev/null @@ -1,44 +0,0 @@ -from django.test import TestCase - -from rest_framework import compat - - -class CompatTests(TestCase): - - def setUp(self): - self.original_django_version = compat.django.VERSION - self.original_transaction = compat.transaction - - def tearDown(self): - compat.django.VERSION = self.original_django_version - compat.transaction = self.original_transaction - - def test_set_rollback_for_transaction_in_managed_mode(self): - class MockTransaction(object): - called_rollback = False - called_leave_transaction_management = False - - def is_managed(self): - return True - - def is_dirty(self): - return True - - def rollback(self): - self.called_rollback = True - - def leave_transaction_management(self): - self.called_leave_transaction_management = True - - dirty_mock_transaction = MockTransaction() - compat.transaction = dirty_mock_transaction - compat.set_rollback() - assert dirty_mock_transaction.called_rollback is True - assert dirty_mock_transaction.called_leave_transaction_management is True - - clean_mock_transaction = MockTransaction() - clean_mock_transaction.is_dirty = lambda: False - compat.transaction = clean_mock_transaction - compat.set_rollback() - assert clean_mock_transaction.called_rollback is False - assert clean_mock_transaction.called_leave_transaction_management is True From 9f66e8baddd9d8106a121b159356422086e7d90c Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 15 Nov 2017 14:58:37 -0500 Subject: [PATCH 009/585] Fix request body/POST access (#5590) * Modernize middleware tests * Added a failing test for #5582 * Set data ref on underlying django request --- rest_framework/request.py | 5 ++-- tests/test_middleware.py | 60 +++++++++++++++++++++++++++++++++------ 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/rest_framework/request.py b/rest_framework/request.py index 4f413e03f6..f9503cd593 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -250,9 +250,10 @@ def _load_data_and_files(self): else: self._full_data = self._data - # copy files refs to the underlying request so that closable + # copy data & files refs to the underlying request so that closable # objects are handled appropriately. - self._request._files = self._files + self._request._post = self.POST + self._request._files = self.FILES def _load_stream(self): """ diff --git a/tests/test_middleware.py b/tests/test_middleware.py index a9f620c0e3..9df7d8e3e6 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -1,34 +1,76 @@ from django.conf.urls import url from django.contrib.auth.models import User +from django.http import HttpRequest from django.test import override_settings from rest_framework.authentication import TokenAuthentication from rest_framework.authtoken.models import Token +from rest_framework.request import is_form_media_type +from rest_framework.response import Response from rest_framework.test import APITestCase from rest_framework.views import APIView + +class PostView(APIView): + def post(self, request): + return Response(data=request.data, status=200) + + urlpatterns = [ - url(https://github.com/r'%5E),%20APIView.as_view(authentication_classes=(TokenAuthentication,))), + url(https://github.com/r'%5Eauth),%20APIView.as_view(authentication_classes=(TokenAuthentication,))), + url(https://github.com/r'%5Epost),%20PostView.as_view()), ] -class MyMiddleware(object): +class RequestUserMiddleware(object): + def __init__(self, get_response): + self.get_response = get_response - def process_response(self, request, response): + def __call__(self, request): + response = self.get_response(request) assert hasattr(request, 'user'), '`user` is not set on request' - assert request.user.is_authenticated(), '`user` is not authenticated' + assert request.user.is_authenticated, '`user` is not authenticated' + + return response + + +class RequestPOSTMiddleware(object): + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + assert isinstance(request, HttpRequest) + + # Parse body with underlying Django request + request.body + + # Process request with DRF view + response = self.get_response(request) + + # Ensure request.POST is set as appropriate + if is_form_media_type(request.content_type): + assert request.POST == {'foo': ['bar']} + else: + assert request.POST == {} + return response @override_settings(ROOT_URLCONF='tests.test_middleware') class TestMiddleware(APITestCase): + + @override_settings(MIDDLEWARE=('tests.test_middleware.RequestUserMiddleware',)) def test_middleware_can_access_user_when_processing_response(self): user = User.objects.create_user('john', 'john@example.com', 'password') key = 'abcd1234' Token.objects.create(key=key, user=user) - with self.settings( - MIDDLEWARE_CLASSES=('tests.test_middleware.MyMiddleware',) - ): - auth = 'Token ' + key - self.client.get('/', HTTP_AUTHORIZATION=auth) + self.client.get('/auth', HTTP_AUTHORIZATION='Token %s' % key) + + @override_settings(MIDDLEWARE=('tests.test_middleware.RequestPOSTMiddleware',)) + def test_middleware_can_access_request_post_when_processing_response(self): + response = self.client.post('/post', {'foo': 'bar'}) + assert response.status_code == 200 + + response = self.client.post('/post', {'foo': 'bar'}, format='json') + assert response.status_code == 200 From 25319984274e799ab557dc96a2f6563d4d2c07a7 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 20 Nov 2017 02:58:29 -0500 Subject: [PATCH 010/585] Rename test to reference correct issue (#5610) --- 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 df88393569..23c6ec2c10 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -515,7 +515,7 @@ def test_validation_success(self): assert serializer.errors == {} -class Test2505Regression: +class Test2555Regression: def test_serializer_context(self): class NestedSerializer(serializers.Serializer): def __init__(self, *args, **kwargs): From 20954469b2938f2e701f11e0398815c557bafa67 Mon Sep 17 00:00:00 2001 From: Alexei Znamensky Date: Mon, 20 Nov 2017 21:07:36 +1300 Subject: [PATCH 011/585] Fix in documentation (#5611) - model serializers now must provide either "fields" or "exclude" as attribute --- docs/api-guide/serializers.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 0e235c88d8..021ef1c389 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -493,6 +493,8 @@ The names in the `fields` and `exclude` attributes will normally map to model fi Alternatively names in the `fields` options can map to properties or methods which take no arguments that exist on the model class. +Since version 3.3.0, it is **mandatory** to provide one of the attributes `fields` or `exclude`. + ## Specifying nested serialization The default `ModelSerializer` uses primary keys for relationships, but you can also easily generate nested representations using the `depth` option: From 9c11077cf63283fcebb939d58202d4841c942eb8 Mon Sep 17 00:00:00 2001 From: bartkim0426 Date: Mon, 20 Nov 2017 17:08:16 +0900 Subject: [PATCH 012/585] Fix in documentation (#5612) - typo in serialization document: 'intead' => 'instead' --- 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 021ef1c389..ee6e416078 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -1011,7 +1011,7 @@ Takes the object instance that requires serialization, and should return a primi Takes the unvalidated incoming data as input and should return the validated data that will be made available as `serializer.validated_data`. The return value will also be passed to the `.create()` or `.update()` methods if `.save()` is called on the serializer class. -If any of the validation fails, then the method should raise a `serializers.ValidationError(errors)`. The `errors` argument should be a dictionary mapping field names (or `settings.NON_FIELD_ERRORS_KEY`) to a list of error messages. If you don't need to alter deserialization behavior and instead want to provide object-level validation, it's recommended that you intead override the [`.validate()`](#object-level-validation) method. +If any of the validation fails, then the method should raise a `serializers.ValidationError(errors)`. The `errors` argument should be a dictionary mapping field names (or `settings.NON_FIELD_ERRORS_KEY`) to a list of error messages. If you don't need to alter deserialization behavior and instead want to provide object-level validation, it's recommended that you instead override the [`.validate()`](#object-level-validation) method. The `data` argument passed to this method will normally be the value of `request.data`, so the datatype it provides will depend on the parser classes you have configured for your API. From ff556a91fdd62b389b9b0b6c353d2161263346bc Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Mon, 20 Nov 2017 00:35:54 -0800 Subject: [PATCH 013/585] Remove references to unsupported Django versions in docs and code (#5602) Per the trove classifiers, DRF only supports Django versions 1.10+. Can drop documentation, code comments, and workarounds for older Django versions. --- docs/api-guide/fields.md | 4 +--- docs/index.md | 4 ++-- docs/tutorial/1-serialization.md | 2 -- docs/tutorial/4-authentication-and-permissions.md | 5 ++--- rest_framework/authtoken/serializers.py | 12 ++++-------- rest_framework/renderers.py | 2 +- rest_framework/urls.py | 5 ++--- rest_framework/utils/model_meta.py | 10 ++-------- tests/test_atomic_requests.py | 5 ++--- tests/test_fields.py | 6 ------ tests/test_filters.py | 3 --- 11 files changed, 16 insertions(+), 42 deletions(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 64014b56ed..d209a945bc 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -356,8 +356,6 @@ Corresponds to `django.db.models.fields.DurationField` The `validated_data` for these fields will contain a `datetime.timedelta` instance. The representation is a string following this format `'[DD] [HH:[MM:]]ss[.uuuuuu]'`. -**Note:** This field is only available with Django versions >= 1.8. - **Signature:** `DurationField()` --- @@ -681,4 +679,4 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide [django-rest-framework-gis]: https://github.com/djangonauts/django-rest-framework-gis [django-rest-framework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore [django-hstore]: https://github.com/djangonauts/django-hstore -[python-decimal-rounding-modes]: https://docs.python.org/3/library/decimal.html#rounding-modes \ No newline at end of file +[python-decimal-rounding-modes]: https://docs.python.org/3/library/decimal.html#rounding-modes diff --git a/docs/index.md b/docs/index.md index a902ed3af2..0e747463bc 100644 --- a/docs/index.md +++ b/docs/index.md @@ -120,10 +120,10 @@ If you're intending to use the browsable API you'll probably also want to add RE urlpatterns = [ ... - url(https://github.com/r'%5Eapi-auth/',%20include('rest_framework.urls',%20namespace='rest_framework')) + url(https://github.com/r'%5Eapi-auth/',%20include('rest_framework.urls')) ] -Note that the URL path can be whatever you want, but you must include `'rest_framework.urls'` with the `'rest_framework'` namespace. You may leave out the namespace in Django 1.9+, and REST framework will set it for you. +Note that the URL path can be whatever you want. ## Example diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index 5587978161..a834c8dbbd 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -48,8 +48,6 @@ We'll need to add our new `snippets` app and the `rest_framework` app to `INSTAL 'snippets.apps.SnippetsConfig', ) -Please note that if you're using Django <1.9, you need to replace `snippets.apps.SnippetsConfig` with `snippets`. - Okay, we're ready to roll. ## Creating a model to work with diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md index b43fabfac1..72cf64e378 100644 --- a/docs/tutorial/4-authentication-and-permissions.md +++ b/docs/tutorial/4-authentication-and-permissions.md @@ -142,11 +142,10 @@ Add the following import at the top of the file: And, at the end of the file, add a pattern to include the login and logout views for the browsable API. urlpatterns += [ - url(r'^api-auth/', include('rest_framework.urls', - namespace='rest_framework')), + url(https://github.com/r'%5Eapi-auth/',%20include('rest_framework.urls'), ] -The `r'^api-auth/'` part of pattern can actually be whatever URL you want to use. The only restriction is that the included urls must use the `'rest_framework'` namespace. In Django 1.9+, REST framework will set the namespace, so you may leave it out. +The `r'^api-auth/'` part of pattern can actually be whatever URL you want to use. Now if you open up the browser again and refresh the page you'll see a 'Login' link in the top right of the page. If you log in as one of the users you created earlier, you'll be able to create code snippets again. diff --git a/rest_framework/authtoken/serializers.py b/rest_framework/authtoken/serializers.py index 301b6a0cb2..01d2d40b92 100644 --- a/rest_framework/authtoken/serializers.py +++ b/rest_framework/authtoken/serializers.py @@ -20,14 +20,10 @@ def validate(self, attrs): user = authenticate(request=self.context.get('request'), username=username, password=password) - if user: - # From Django 1.10 onwards the `authenticate` call simply - # returns `None` for is_active=False users. - # (Assuming the default `ModelBackend` authentication backend.) - if not user.is_active: - msg = _('User account is disabled.') - raise serializers.ValidationError(msg, code='authorization') - else: + # The authenticate call simply returns None for is_active=False + # users. (Assuming the default ModelBackend authentication + # backend.) + if not user: msg = _('Unable to log in with provided credentials.') raise serializers.ValidationError(msg, code='authorization') else: diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 3298294ce2..bbefb46248 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -666,7 +666,7 @@ def get_context(self, data, accepted_media_type, renderer_context): paginator = None csrf_cookie_name = settings.CSRF_COOKIE_NAME - csrf_header_name = getattr(settings, 'CSRF_HEADER_NAME', 'HTTP_X_CSRFToken') # Fallback for Django 1.8 + csrf_header_name = settings.CSRF_HEADER_NAME if csrf_header_name.startswith('HTTP_'): csrf_header_name = csrf_header_name[5:] csrf_header_name = csrf_header_name.replace('_', '-') diff --git a/rest_framework/urls.py b/rest_framework/urls.py index 10cc5def0c..80fce5dc45 100644 --- a/rest_framework/urls.py +++ b/rest_framework/urls.py @@ -6,11 +6,10 @@ urlpatterns = [ ... - url(https://github.com/r'%5Eauth/',%20include('rest_framework.urls',%20namespace='rest_framework')) + url(https://github.com/r'%5Eauth/',%20include('rest_framework.urls')) ] -In Django versions older than 1.9, the urls must be namespaced as 'rest_framework', -and you should make sure your authentication settings include `SessionAuthentication`. +You should make sure your authentication settings include `SessionAuthentication`. """ from __future__ import unicode_literals diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index f0ae02bb2e..4cc93b8ef5 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -105,18 +105,13 @@ def _get_reverse_relationships(opts): """ Returns an `OrderedDict` of field names to `RelationInfo`. """ - # Note that we have a hack here to handle internal API differences for - # this internal API across Django 1.7 -> Django 1.8. - # See: https://code.djangoproject.com/ticket/24208 - reverse_relations = OrderedDict() all_related_objects = [r for r in opts.related_objects if not r.field.many_to_many] for relation in all_related_objects: accessor_name = relation.get_accessor_name() - related = getattr(relation, 'related_model', relation.model) reverse_relations[accessor_name] = RelationInfo( model_field=None, - related_model=related, + related_model=relation.related_model, to_many=relation.field.remote_field.multiple, to_field=_get_to_field(relation.field), has_through_model=False, @@ -127,10 +122,9 @@ def _get_reverse_relationships(opts): all_related_many_to_many_objects = [r for r in opts.related_objects if r.field.many_to_many] for relation in all_related_many_to_many_objects: accessor_name = relation.get_accessor_name() - related = getattr(relation, 'related_model', relation.model) reverse_relations[accessor_name] = RelationInfo( model_field=None, - related_model=related, + related_model=relation.related_model, to_many=True, # manytomany do not have to_fields to_field=None, diff --git a/tests/test_atomic_requests.py b/tests/test_atomic_requests.py index f925ce3d3c..697c549dea 100644 --- a/tests/test_atomic_requests.py +++ b/tests/test_atomic_requests.py @@ -120,13 +120,12 @@ def test_api_exception_rollback_transaction(self): Transaction is rollbacked by our transaction atomic block. """ request = factory.post('/') - num_queries = (4 if getattr(connection.features, - 'can_release_savepoints', False) else 3) + num_queries = 4 if connection.features.can_release_savepoints else 3 with self.assertNumQueries(num_queries): # 1 - begin savepoint # 2 - insert # 3 - rollback savepoint - # 4 - release savepoint (django>=1.8 only) + # 4 - release savepoint with transaction.atomic(): response = self.view(request) assert transaction.get_rollback() diff --git a/tests/test_fields.py b/tests/test_fields.py index 101d3b26d9..fc9ce192ad 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -5,7 +5,6 @@ import uuid from decimal import ROUND_DOWN, ROUND_UP, Decimal -import django import pytest from django.http import QueryDict from django.test import TestCase, override_settings @@ -1197,11 +1196,6 @@ class TestDateTimeField(FieldValues): field = serializers.DateTimeField(default_timezone=utc) -if django.VERSION[:2] <= (1, 8): - # Doesn't raise an error on earlier versions of Django - TestDateTimeField.invalid_inputs.pop('2018-08-16 22:00-24:00') - - class TestCustomInputFormatDateTimeField(FieldValues): """ Valid and invalid values for `DateTimeField` with a custom input format. diff --git a/tests/test_filters.py b/tests/test_filters.py index 970f6bdfcf..f9e068fec7 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -1,9 +1,7 @@ from __future__ import unicode_literals import datetime -import unittest -import django import pytest from django.core.exceptions import ImproperlyConfigured from django.db import models @@ -291,7 +289,6 @@ def setUpTestData(cls): Entry.objects.create(blog=b2, headline='Something unrelated', pub_date=datetime.date(1979, 1, 1)) Entry.objects.create(blog=b2, headline='Retrospective on Lennon', pub_date=datetime.date(1990, 6, 1)) - @unittest.skipIf(django.VERSION < (1, 9), "Django 1.8 does not support transforms") def test_multiple_filter_conditions(self): class SearchListView(generics.ListAPIView): queryset = Blog.objects.all() From a3df1c119967e04fd57495ebbb4645b02883fc4e Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 20 Nov 2017 03:51:16 -0500 Subject: [PATCH 014/585] Test Serializer exclude for declared fields (#5599) * Test current behavior of exclude+declared field * Assert declared fields are not present in exclude --- rest_framework/serializers.py | 11 +++++++++++ tests/test_model_serializer.py | 16 ++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 994d0273fb..0952e190c6 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1102,6 +1102,17 @@ def get_field_names(self, declared_fields, info): if exclude is not None: # If `Meta.exclude` is included, then remove those fields. for field_name in exclude: + assert field_name not in self._declared_fields, ( + "Cannot both declare the field '{field_name}' and include " + "it in the {serializer_class} 'exclude' option. Remove the " + "field or, if inherited from a parent serializer, disable " + "with `{field_name} = None`." + .format( + field_name=field_name, + serializer_class=self.__class__.__name__ + ) + ) + assert field_name in fields, ( "The field '{field_name}' was included on serializer " "{serializer_class} in the 'exclude' option, but does " diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 203e1fe7f4..98586b971d 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -900,6 +900,22 @@ class Meta: "Cannot set both 'fields' and 'exclude' options on serializer ExampleSerializer." ) + def test_declared_fields_with_exclude_option(self): + class ExampleSerializer(serializers.ModelSerializer): + text = serializers.CharField() + + class Meta: + model = MetaClassTestModel + exclude = ('text',) + + expected = ( + "Cannot both declare the field 'text' and include it in the " + "ExampleSerializer 'exclude' option. Remove the field or, if " + "inherited from a parent serializer, disable with `text = None`." + ) + with self.assertRaisesMessage(AssertionError, expected): + ExampleSerializer().fields + class Issue2704TestCase(TestCase): def test_queryset_all(self): From 134a6f66f92dc4e6ae390f166c6cead8a8d8c13b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Bielawski?= Date: Wed, 22 Nov 2017 05:11:59 +0000 Subject: [PATCH 015/585] Fixed schema generation for filter backends (#5613) --- rest_framework/schemas/inspectors.py | 2 +- tests/test_schemas.py | 48 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index bae4d38ede..80dc492684 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -368,7 +368,7 @@ def _allows_filters(self, path, method): if hasattr(self.view, 'action'): return self.view.action in ["list", "retrieve", "update", "partial_update", "destroy"] - return method.lower in ["get", "put", "patch", "delete"] + return method.lower() in ["get", "put", "patch", "delete"] def get_filter_fields(self, path, method): if not self._allows_filters(path, method): diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 1a84dfc892..56692d4f59 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -951,3 +951,51 @@ def custom_action(self, request, pk): assert inspector.should_include_endpoint(path, callback) assert inspector.get_allowed_methods(callback) == ["GET"] + + +class TestAutoSchemaAllowsFilters(object): + class MockAPIView(APIView): + filter_backends = [filters.OrderingFilter] + + def _test(self, method): + view = self.MockAPIView() + fields = view.schema.get_filter_fields('', method) + field_names = [f.name for f in fields] + + return 'ordering' in field_names + + def test_get(self): + assert self._test('get') + + def test_GET(self): + assert self._test('GET') + + def test_put(self): + assert self._test('put') + + def test_PUT(self): + assert self._test('PUT') + + def test_patch(self): + assert self._test('patch') + + def test_PATCH(self): + assert self._test('PATCH') + + def test_delete(self): + assert self._test('delete') + + def test_DELETE(self): + assert self._test('DELETE') + + def test_post(self): + assert not self._test('post') + + def test_POST(self): + assert not self._test('POST') + + def test_foo(self): + assert not self._test('foo') + + def test_FOO(self): + assert not self._test('FOO') From ae88f5c55bc8d7b273e0619354120c5c8f17202e Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 22 Nov 2017 04:36:34 -0500 Subject: [PATCH 016/585] Minor cleanup for ModelSerializer tests (#5598) * Replace assertRaises with assertRaisesMessage * Remove outdated implicit Meta.fields test * Simplify parent declared field test --- tests/test_model_serializer.py | 69 ++++++++-------------------------- 1 file changed, 16 insertions(+), 53 deletions(-) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 98586b971d..c2c7fb61ec 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -123,10 +123,10 @@ class Meta: 'non_model_field': 'bar', }) serializer.is_valid() - with self.assertRaises(TypeError) as excinfo: - serializer.save() + msginitial = 'Got a `TypeError` when calling `OneFieldModel.objects.create()`.' - assert str(excinfo.exception).startswith(msginitial) + with self.assertRaisesMessage(TypeError, msginitial): + serializer.save() def test_abstract_model(self): """ @@ -147,10 +147,10 @@ class Meta: serializer = TestSerializer(data={ 'afield': 'foo', }) - with self.assertRaises(ValueError) as excinfo: - serializer.is_valid() + msginitial = 'Cannot use ModelSerializer with Abstract Models.' - assert str(excinfo.exception).startswith(msginitial) + with self.assertRaisesMessage(ValueError, msginitial): + serializer.is_valid() class TestRegularFieldMappings(TestCase): @@ -294,10 +294,9 @@ class Meta: model = RegularFieldsModel fields = ('auto_field', 'invalid') - with self.assertRaises(ImproperlyConfigured) as excinfo: - TestSerializer().fields expected = 'Field name `invalid` is not valid for model `RegularFieldsModel`.' - assert str(excinfo.exception) == expected + with self.assertRaisesMessage(ImproperlyConfigured, expected): + TestSerializer().fields def test_missing_field(self): """ @@ -311,13 +310,12 @@ class Meta: model = RegularFieldsModel fields = ('auto_field',) - with self.assertRaises(AssertionError) as excinfo: - TestSerializer().fields expected = ( "The field 'missing' was declared on serializer TestSerializer, " "but has not been included in the 'fields' option." ) - assert str(excinfo.exception) == expected + with self.assertRaisesMessage(AssertionError, expected): + TestSerializer().fields def test_missing_superclass_field(self): """ @@ -327,13 +325,7 @@ def test_missing_superclass_field(self): class TestSerializer(serializers.ModelSerializer): missing = serializers.ReadOnlyField() - class Meta: - model = RegularFieldsModel - fields = '__all__' - class ChildSerializer(TestSerializer): - missing = serializers.ReadOnlyField() - class Meta: model = RegularFieldsModel fields = ('auto_field',) @@ -348,22 +340,6 @@ class Meta: ExampleSerializer() - def test_fields_and_exclude_behavior(self): - class ImplicitFieldsSerializer(serializers.ModelSerializer): - class Meta: - model = RegularFieldsModel - fields = '__all__' - - class ExplicitFieldsSerializer(serializers.ModelSerializer): - class Meta: - model = RegularFieldsModel - fields = '__all__' - - implicit = ImplicitFieldsSerializer() - explicit = ExplicitFieldsSerializer() - - assert implicit.data == explicit.data - class TestDurationFieldMapping(TestCase): def test_duration_field(self): @@ -862,28 +838,20 @@ class Meta: model = MetaClassTestModel fields = 'text' - with self.assertRaises(TypeError) as result: + msginitial = "The `fields` option must be a list or tuple" + with self.assertRaisesMessage(TypeError, msginitial): ExampleSerializer().fields - exception = result.exception - assert str(exception).startswith( - "The `fields` option must be a list or tuple" - ) - def test_meta_class_exclude_option(self): class ExampleSerializer(serializers.ModelSerializer): class Meta: model = MetaClassTestModel exclude = 'text' - with self.assertRaises(TypeError) as result: + msginitial = "The `exclude` option must be a list or tuple" + with self.assertRaisesMessage(TypeError, msginitial): ExampleSerializer().fields - exception = result.exception - assert str(exception).startswith( - "The `exclude` option must be a list or tuple" - ) - def test_meta_class_fields_and_exclude_options(self): class ExampleSerializer(serializers.ModelSerializer): class Meta: @@ -891,15 +859,10 @@ class Meta: fields = ('text',) exclude = ('text',) - with self.assertRaises(AssertionError) as result: + msginitial = "Cannot set both 'fields' and 'exclude' options on serializer ExampleSerializer." + with self.assertRaisesMessage(AssertionError, msginitial): ExampleSerializer().fields - exception = result.exception - self.assertEqual( - str(exception), - "Cannot set both 'fields' and 'exclude' options on serializer ExampleSerializer." - ) - def test_declared_fields_with_exclude_option(self): class ExampleSerializer(serializers.ModelSerializer): text = serializers.CharField() From 1a667f420d9f78f2a9189763981bf65b8e967fb3 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 22 Nov 2017 05:42:59 -0500 Subject: [PATCH 017/585] Reimplement request attribute access w/ __getattr__ (#5617) * Add tests for proxying WSGIRequest attributes in Request. * Add request attribute exception test * Reimplement request attribute access --- rest_framework/request.py | 12 +++--------- tests/test_request.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/rest_framework/request.py b/rest_framework/request.py index f9503cd593..9446910399 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -10,8 +10,6 @@ """ from __future__ import unicode_literals -import sys - from django.conf import settings from django.http import QueryDict from django.http.multipartparser import parse_header @@ -373,19 +371,15 @@ def _not_authenticated(self): else: self.auth = None - def __getattribute__(self, attr): + def __getattr__(self, attr): """ If an attribute does not exist on this instance, then we also attempt to proxy it to the underlying HttpRequest object. """ try: - return super(Request, self).__getattribute__(attr) + return getattr(self._request, attr) except AttributeError: - info = sys.exc_info() - try: - return getattr(self._request, attr) - except AttributeError: - six.reraise(info[0], info[1], info[2].tb_next) + return self.__getattribute__(attr) @property def DATA(self): diff --git a/tests/test_request.py b/tests/test_request.py index a87060df10..cbadb14678 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -249,3 +249,26 @@ def test_default_secure_false(self): def test_default_secure_true(self): request = Request(factory.get('/', secure=True)) assert request.scheme == 'https' + + +class TestWSGIRequestProxy(TestCase): + def test_attribute_access(self): + wsgi_request = factory.get('/') + request = Request(wsgi_request) + + inner_sentinel = object() + wsgi_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): + # ensure the exception message is not for the underlying WSGIRequest + wsgi_request = factory.get('/') + request = Request(wsgi_request) + + message = "'Request' object has no attribute 'inner_property'" + with self.assertRaisesMessage(AttributeError, message): + request.inner_property From d71bd57b645190cf3edb8772f8886272c30c2a6b Mon Sep 17 00:00:00 2001 From: Sander Steffann Date: Wed, 22 Nov 2017 15:47:03 +0100 Subject: [PATCH 018/585] SchemaJSRenderer renders invalid Javascript (#5607) * SchemaJSRenderer renders invalid Javascript Under Py3 the base64.b64encode() method returns a binary object, which gets rendered as `b'...'` in schema.js. This results in the output becoming: var coreJSON = window.atob('b'eyJf...''); which is invalid Javascript. Because base64 only uses ASCII characters it is safe to decode('ascii') it. Under Py2 this will result in a unicode object, which is fine. Under Py3 it results in a string, which is also fine. This solves the problem and results in a working schema.js output. * Add regression test for #5608 * Add regression test for #5608 * Apparently the linter on Travis wants the imports in a different order than on my box... --- rest_framework/renderers.py | 2 +- tests/test_renderers.py | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index bbefb46248..80a22dee59 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -852,7 +852,7 @@ class SchemaJSRenderer(BaseRenderer): def render(self, data, accepted_media_type=None, renderer_context=None): codec = coreapi.codecs.CoreJSONCodec() - schema = base64.b64encode(codec.encode(data)) + schema = base64.b64encode(codec.encode(data)).decode('ascii') template = loader.get_template(self.template) context = {'schema': mark_safe(schema)} diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 54b3ce9645..ba8400c065 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -18,7 +18,7 @@ from rest_framework import permissions, serializers, status from rest_framework.renderers import ( AdminRenderer, BaseRenderer, BrowsableAPIRenderer, DocumentationRenderer, - HTMLFormRenderer, JSONRenderer, StaticHTMLRenderer + HTMLFormRenderer, JSONRenderer, SchemaJSRenderer, StaticHTMLRenderer ) from rest_framework.request import Request from rest_framework.response import Response @@ -736,3 +736,20 @@ def test_document_with_link_named_data(self): html = renderer.render(document, accepted_media_type="text/html", renderer_context={"request": request}) assert '

Data Endpoint API

' in html + + +class TestSchemaJSRenderer(TestCase): + + def test_schemajs_output(self): + """ + Test output of the SchemaJS renderer as per #5608. Django 2.0 on Py3 prints binary data as b'xyz' in templates, + and the base64 encoding used by SchemaJSRenderer outputs base64 as binary. Test fix. + """ + factory = APIRequestFactory() + request = factory.get('/') + + renderer = SchemaJSRenderer() + + output = renderer.render('data', renderer_context={"request": request}) + assert "'ImRhdGEi'" in output + assert "'b'ImRhdGEi''" not in output From 1922dc6e73ecc926a1cd5b0972691e186d614ed3 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 22 Nov 2017 23:16:07 -0500 Subject: [PATCH 019/585] Update Django to 2.0rc1 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 7b78c0ba34..e2c7c2feb4 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ setenv = deps = django110: Django>=1.10,<1.11 django111: Django>=1.11,<2.0 - django20: Django>=2.0b1,<2.1 + django20: Django>=2.0rc1,<2.1 djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From bc28b0c74f435a2c9e3a724d552f32845cfb9c70 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 22 Nov 2017 23:16:23 -0500 Subject: [PATCH 020/585] Remove django 2.0 from allowable failures in CI --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 05309cd976..9c9ad7a4ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,6 @@ matrix: allow_failures: - env: DJANGO=master - - env: DJANGO=2.0 install: - pip install tox tox-travis From 7a533cd8b8be156824ca8b1a8a1d540019ba4642 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 22 Nov 2017 23:21:00 -0500 Subject: [PATCH 021/585] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3501745551..ceac9e57c3 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ There is a live example API for testing purposes, [available here][sandbox]. # Requirements * Python (2.7, 3.4, 3.5, 3.6) -* Django (1.10, 1.11, 2.0 alpha) +* Django (1.10, 1.11, 2.0rc1) # Installation From a91361dd2fb9e7705a925e52e26b71ffcb13bb30 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Thu, 23 Nov 2017 02:57:31 -0500 Subject: [PATCH 022/585] Perform type check on passed request argument (#5618) * Add test for wrapped request instance * Add 'request' argument type check to Request init * Fix metadata tests' request object --- rest_framework/request.py | 8 +++++++- tests/test_metadata.py | 3 +-- tests/test_request.py | 12 ++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/rest_framework/request.py b/rest_framework/request.py index 9446910399..5c9cb6136a 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -11,7 +11,7 @@ from __future__ import unicode_literals from django.conf import settings -from django.http import QueryDict +from django.http import HttpRequest, QueryDict from django.http.multipartparser import parse_header from django.http.request import RawPostDataException from django.utils import six @@ -132,6 +132,12 @@ class Request(object): def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None): + assert isinstance(request, HttpRequest), ( + 'The `request` argument must be an instance of ' + '`django.http.HttpRequest`, not `{}.{}`.' + .format(request.__class__.__module__, request.__class__.__name__) + ) + self._request = request self.parsers = parsers or () self.authenticators = authenticators or () diff --git a/tests/test_metadata.py b/tests/test_metadata.py index a9d2dc0c96..b9db36e81d 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -9,12 +9,11 @@ exceptions, metadata, serializers, status, versioning, views ) from rest_framework.renderers import BrowsableAPIRenderer -from rest_framework.request import Request from rest_framework.test import APIRequestFactory from .models import BasicModel -request = Request(APIRequestFactory().options('/')) +request = APIRequestFactory().options('/') class TestMetadata: diff --git a/tests/test_request.py b/tests/test_request.py index cbadb14678..c14233a6fc 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -25,6 +25,18 @@ factory = APIRequestFactory() +class TestInitializer(TestCase): + def test_request_type(self): + request = Request(factory.get('/')) + + message = ( + 'The `request` argument must be an instance of ' + '`django.http.HttpRequest`, not `rest_framework.request.Request`.' + ) + with self.assertRaisesMessage(AssertionError, message): + Request(request) + + class PlainTextParser(BaseParser): media_type = 'text/plain' From c63e35cb09170e1166896ad31a1bb9a121aaa2f8 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Thu, 23 Nov 2017 02:58:04 -0500 Subject: [PATCH 023/585] Fix AttributeError hiding on request authenticators (#5600) * Update assertion style in user logout test * Apply middlewares to django request object * Fix test for request auth hiding AttributeErrors * Re-raise/wrap auth attribute errors * Fix test for py2k * Add docs for WrappedAttributeError --- docs/api-guide/authentication.md | 6 +++++ docs/api-guide/requests.md | 4 +++ rest_framework/request.py | 32 ++++++++++++++++++++--- tests/test_request.py | 44 +++++++++++++++++++------------- 4 files changed, 64 insertions(+), 22 deletions(-) diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index a7a24029b1..63a789dfca 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -291,6 +291,12 @@ You *may* also override the `.authenticate_header(self, request)` method. If im If the `.authenticate_header()` method is not overridden, the authentication scheme will return `HTTP 403 Forbidden` responses when an unauthenticated request is denied access. +--- + +**Note:** When your custom authenticator is invoked by the request object's `.user` or `.auth` properties, you may see an `AttributeError` re-raised as a `WrappedAttributeError`. This is necessary to prevent the original exception from being suppressed by the outer property access. Python will not recognize that the `AttributeError` orginates from your custom authenticator and will instead assume that the request object does not have a `.user` or `.auth` property. These errors should be fixed or otherwise handled by your authenticator. + +--- + ## Example The following example will authenticate any incoming request as the user given by the username in a custom request header named 'X_USERNAME'. diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index 393b1ab9ac..83a38d1c3e 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -90,6 +90,10 @@ You won't typically need to access this property. --- +**Note:** You may see a `WrappedAttributeError` raised when calling the `.user` or `.auth` properties. These errors originate from an authenticator as a standard `AttributeError`, however it's necessary that they be re-raised as a different exception type in order to prevent them from being suppressed by the outer property access. Python will not recognize that the `AttributeError` orginates from the authenticator and will instaed assume that the request object does not have a `.user` or `.auth` property. The authenticator will need to be fixed. + +--- + # Browser enhancements REST framework supports a few browser enhancements such as browser-based `PUT`, `PATCH` and `DELETE` forms. diff --git a/rest_framework/request.py b/rest_framework/request.py index 5c9cb6136a..7e4daf2749 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -10,6 +10,9 @@ """ from __future__ import unicode_literals +import sys +from contextlib import contextmanager + from django.conf import settings from django.http import HttpRequest, QueryDict from django.http.multipartparser import parse_header @@ -59,6 +62,24 @@ def __exit__(self, *args, **kwarg): self.view.action = self.action +class WrappedAttributeError(Exception): + pass + + +@contextmanager +def wrap_attributeerrors(): + """ + Used to re-raise AttributeErrors caught during authentication, preventing + these errors from otherwise being handled by the attribute access protocol. + """ + try: + yield + except AttributeError: + info = sys.exc_info() + exc = WrappedAttributeError(str(info[1])) + six.reraise(type(exc), exc, info[2]) + + class Empty(object): """ Placeholder for unset attributes. @@ -197,7 +218,8 @@ def user(self): by the authentication classes provided to the request. """ if not hasattr(self, '_user'): - self._authenticate() + with wrap_attributeerrors(): + self._authenticate() return self._user @user.setter @@ -220,7 +242,8 @@ def auth(self): request, such as an authentication token. """ if not hasattr(self, '_auth'): - self._authenticate() + with wrap_attributeerrors(): + self._authenticate() return self._auth @auth.setter @@ -239,7 +262,8 @@ def successful_authenticator(self): to authenticate the request, or `None`. """ if not hasattr(self, '_authenticator'): - self._authenticate() + with wrap_attributeerrors(): + self._authenticate() return self._authenticator def _load_data_and_files(self): @@ -322,7 +346,7 @@ def _parse(self): try: parsed = parser.parse(stream, media_type, self.parser_context) - except: + except Exception: # If we get an exception during parsing, fill in empty data and # re-raise. Ensures we don't simply repeat the error when # attempting to render the browsable renderer response, or when diff --git a/tests/test_request.py b/tests/test_request.py index c14233a6fc..76589a4401 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -6,8 +6,10 @@ import os.path import tempfile +import pytest from django.conf.urls import url from django.contrib.auth import authenticate, login, logout +from django.contrib.auth.middleware import AuthenticationMiddleware from django.contrib.auth.models import User from django.contrib.sessions.middleware import SessionMiddleware from django.core.files.uploadedfile import SimpleUploadedFile @@ -17,7 +19,7 @@ from rest_framework import status from rest_framework.authentication import SessionAuthentication from rest_framework.parsers import BaseParser, FormParser, MultiPartParser -from rest_framework.request import Request +from rest_framework.request import Request, WrappedAttributeError from rest_framework.response import Response from rest_framework.test import APIClient, APIRequestFactory from rest_framework.views import APIView @@ -197,7 +199,8 @@ def setUp(self): # available to login and logout functions self.wrapped_request = factory.get('/') self.request = Request(self.wrapped_request) - SessionMiddleware().process_request(self.request) + SessionMiddleware().process_request(self.wrapped_request) + AuthenticationMiddleware().process_request(self.wrapped_request) User.objects.create_user('ringo', 'starr@thebeatles.com', 'yellow') self.user = authenticate(username='ringo', password='yellow') @@ -212,9 +215,9 @@ def test_user_can_login(self): def test_user_can_logout(self): self.request.user = self.user - self.assertFalse(self.request.user.is_anonymous) + assert not self.request.user.is_anonymous logout(self.request) - self.assertTrue(self.request.user.is_anonymous) + assert self.request.user.is_anonymous def test_logged_in_user_is_set_on_wrapped_request(self): login(self.request, self.user) @@ -227,22 +230,27 @@ def test_calling_user_fails_when_attribute_error_is_raised(self): """ class AuthRaisesAttributeError(object): def authenticate(self, request): - import rest_framework - rest_framework.MISSPELLED_NAME_THAT_DOESNT_EXIST + self.MISSPELLED_NAME_THAT_DOESNT_EXIST - self.request = Request(factory.get('/'), authenticators=(AuthRaisesAttributeError(),)) - SessionMiddleware().process_request(self.request) + request = Request(self.wrapped_request, authenticators=(AuthRaisesAttributeError(),)) - login(self.request, self.user) - try: - self.request.user - except AttributeError as error: - assert str(error) in ( - "'module' object has no attribute 'MISSPELLED_NAME_THAT_DOESNT_EXIST'", # Python < 3.5 - "module 'rest_framework' has no attribute 'MISSPELLED_NAME_THAT_DOESNT_EXIST'", # Python >= 3.5 - ) - else: - assert False, 'AttributeError not raised' + # The middleware processes the underlying Django request, sets anonymous user + assert self.wrapped_request.user.is_anonymous + + # The DRF request object does not have a user and should run authenticators + expected = r"no attribute 'MISSPELLED_NAME_THAT_DOESNT_EXIST'" + with pytest.raises(WrappedAttributeError, match=expected): + request.user + + # python 2 hasattr fails for *any* exception, not just AttributeError + if six.PY2: + return + + with pytest.raises(WrappedAttributeError, match=expected): + hasattr(request, 'user') + + with pytest.raises(WrappedAttributeError, match=expected): + login(request, self.user) class TestAuthSetter(TestCase): From eb61eb2b86798ef4c1b1630b86fbb11d00ab55ef Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Fri, 24 Nov 2017 22:38:07 -0500 Subject: [PATCH 024/585] Update testing requirements --- requirements/requirements-codestyle.txt | 6 +++--- requirements/requirements-optionals.txt | 2 +- requirements/requirements-testing.txt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements/requirements-codestyle.txt b/requirements/requirements-codestyle.txt index a64cfa29f7..1a51179f8d 100644 --- a/requirements/requirements-codestyle.txt +++ b/requirements/requirements-codestyle.txt @@ -1,7 +1,7 @@ # PEP8 code linting, which we run on all commits. -flake8==3.4.1 +flake8==3.5.0 flake8-tidy-imports==1.1.0 -pep8==1.7.0 +pycodestyle==2.3.1 # Sort and lint imports -isort==4.2.5 +isort==4.2.15 diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt index 67525bebc7..f27178a3ff 100644 --- a/requirements/requirements-optionals.txt +++ b/requirements/requirements-optionals.txt @@ -2,6 +2,6 @@ pytz==2017.2 markdown==2.6.4 django-guardian==1.4.9 -django-filter==1.0.4 +django-filter==1.1.0 coreapi==2.3.1 coreschema==0.0.4 diff --git a/requirements/requirements-testing.txt b/requirements/requirements-testing.txt index 515cff78dc..72ce56d26a 100644 --- a/requirements/requirements-testing.txt +++ b/requirements/requirements-testing.txt @@ -1,4 +1,4 @@ # PyTest for running the tests. -pytest==3.2.2 +pytest==3.2.5 pytest-django==3.1.2 pytest-cov==2.5.1 From 7b58a2c124f6c7f0e2f50d97d47d7b2b07cbc8e4 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Sat, 25 Nov 2017 21:06:13 -0500 Subject: [PATCH 025/585] Fix bare except statements --- rest_framework/schemas/inspectors.py | 2 +- rest_framework/utils/encoders.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index 80dc492684..47f5b9e13e 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -260,7 +260,7 @@ def get_path_fields(self, path, method): # Attempt to infer a field description if possible. try: model_field = model._meta.get_field(variable) - except: + except Exception: model_field = None if model_field is not None and model_field.verbose_name: diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index 7518d4ffd3..148a2c9c0d 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -61,7 +61,7 @@ def default(self, obj): elif hasattr(obj, '__getitem__'): try: return dict(obj) - except: + except Exception: pass elif hasattr(obj, '__iter__'): return tuple(item for item in obj) From 5c19652080e872b6dc13d6576e9a43ee821ae122 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Sat, 25 Nov 2017 21:10:30 -0500 Subject: [PATCH 026/585] Fix whitespace in imports --- rest_framework/templatetags/rest_framework.py | 1 + rest_framework/validators.py | 1 - tests/importable/test_installed.py | 1 + tests/test_description.py | 2 +- tests/test_one_to_one_with_inheritance.py | 1 - 5 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index bc38c0a343..a331339a6a 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -10,6 +10,7 @@ from django.utils.encoding import force_text, iri_to_uri from django.utils.html import escape, format_html, smart_urlquote from django.utils.safestring import SafeData, mark_safe + from rest_framework.compat import apply_markdown, pygments_highlight from rest_framework.renderers import HTMLFormRenderer from rest_framework.utils.urls import replace_query_param diff --git a/rest_framework/validators.py b/rest_framework/validators.py index 7f77407116..2ea3e5ac15 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -19,7 +19,6 @@ # Robust filter and exist implementations. Ensures that queryset.exists() for # an invalid value returns `False`, rather than raising an error. # Refs https://github.com/encode/django-rest-framework/issues/3381 - def qs_exists(queryset): try: return queryset.exists() diff --git a/tests/importable/test_installed.py b/tests/importable/test_installed.py index fff51fd8f9..072d3b2e43 100644 --- a/tests/importable/test_installed.py +++ b/tests/importable/test_installed.py @@ -1,4 +1,5 @@ from django.conf import settings + from tests import importable diff --git a/tests/test_description.py b/tests/test_description.py index b3e8b0f8bd..6ccbec882f 100644 --- a/tests/test_description.py +++ b/tests/test_description.py @@ -33,9 +33,9 @@ }] ```""" + # If markdown is installed we also test it's working # (and that our wrapped forces '=' to h2 and '-' to h3) - MARKED_DOWN_HILITE = """
[{
"alpha" Date: Sat, 25 Nov 2017 21:19:55 -0500 Subject: [PATCH 027/585] Add _pytest to .isort.cfg --- .isort.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.isort.cfg b/.isort.cfg index 4d4a6a509d..fd9c67a97c 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -3,5 +3,5 @@ skip=.tox atomic=true multi_line_output=5 known_standard_library=types -known_third_party=pytest,django +known_third_party=pytest,_pytest,django known_first_party=rest_framework From 224d8cfb9da89454ea71821454680ee3e8569028 Mon Sep 17 00:00:00 2001 From: Akshar Raaj Date: Mon, 27 Nov 2017 13:38:18 +0530 Subject: [PATCH 028/585] Serializer._declared_fields enable modifying fields on a serializer instance without affecting every other serializer instance. --- rest_framework/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 0952e190c6..fa6a6f0bbf 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -384,7 +384,7 @@ def get_fields(self): """ # Every new serializer is created with a clone of the field instances. # This allows users to dynamically modify the fields on a serializer - # instance without affecting every other serializer class. + # instance without affecting every other serializer instance. return copy.deepcopy(self._declared_fields) def get_validators(self): From abef84fb600ef714befde42243317ef767ef0ba8 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 27 Nov 2017 05:28:25 -0500 Subject: [PATCH 029/585] Fix packaging (#5624) * Packaging should use manifest * Add schema.js template to MANIFEST --- MANIFEST.in | 2 +- setup.py | 31 +++---------------------------- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 15bfe4caac..48ec57edf7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,6 @@ include README.md include LICENSE.md recursive-include rest_framework/static *.js *.css *.png *.eot *.svg *.ttf *.woff -recursive-include rest_framework/templates *.html +recursive-include rest_framework/templates *.html schema.js global-exclude __pycache__ global-exclude *.py[co] diff --git a/setup.py b/setup.py index 3854130528..736df7b129 100755 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ import sys from io import open -from setuptools import setup +from setuptools import setup, find_packages try: from pypandoc import convert @@ -28,31 +28,6 @@ def get_version(package): return re.search("__version__ = ['\"]([^'\"]+)['\"]", init_py).group(1) -def get_packages(package): - """ - Return root package and all sub-packages. - """ - return [dirpath - for dirpath, dirnames, filenames in os.walk(package) - if os.path.exists(os.path.join(dirpath, '__init__.py'))] - - -def get_package_data(package): - """ - Return all files under the root package, that are not in a - package themselves. - """ - walk = [(dirpath.replace(package + os.sep, '', 1), filenames) - for dirpath, dirnames, filenames in os.walk(package) - if not os.path.exists(os.path.join(dirpath, '__init__.py'))] - - filepaths = [] - for base, filenames in walk: - filepaths.extend([os.path.join(base, filename) - for filename in filenames]) - return {package: filepaths} - - version = get_version('rest_framework') @@ -84,8 +59,8 @@ def get_package_data(package): long_description=read_md('README.md'), author='Tom Christie', author_email='tom@tomchristie.com', # SEE NOTE BELOW (*) - packages=get_packages('rest_framework'), - package_data=get_package_data('rest_framework'), + packages=find_packages(exclude=['tests*']), + include_package_data=True, install_requires=[], zip_safe=False, classifiers=[ From fc6b192b708c67b3c2ff2da5e249502e33afdb60 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 27 Nov 2017 05:32:17 -0500 Subject: [PATCH 030/585] Fix readme, add to CI (#5625) * Add readme build to CI * Fix pandoc import in setup.py * Replace sponsor HTML with markdown This loses the image centering, but can be converted to RST. * Fix README RST conversion - Links do not render correctly inside italics - Add hr between image caption and section, fixes markup on older versions of pandoc. --- .travis.yml | 4 ++++ README.md | 30 +++++++++++++++++-------- requirements/requirements-packaging.txt | 3 +++ setup.py | 4 ++-- tox.ini | 9 ++++++-- 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9c9ad7a4ee..c5a15140d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,10 @@ matrix: - { python: "3.6", env: DJANGO=2.0 } - { python: "2.7", env: TOXENV=lint } - { python: "2.7", env: TOXENV=docs } + - python: "2.7" + env: TOXENV=readme + addons: + apt_packages: pandoc exclude: - { python: "2.7", env: DJANGO=master } - { python: "2.7", env: DJANGO=2.0 } diff --git a/README.md b/README.md index ceac9e57c3..c19105bc72 100644 --- a/README.md +++ b/README.md @@ -15,20 +15,18 @@ Full documentation for the project is available at [http://www.django-rest-frame REST framework is a *collaboratively funded project*. If you use REST framework commercially we strongly encourage you to invest in its -continued development by **[signing up for a paid plan][funding]**. +continued development by [signing up for a paid plan][funding]. The initial aim is to provide a single full-time position on REST framework. *Every single sign-up makes a significant impact towards making that possible.* -

- - - - - -

+[![][rover-img]][rover-url] +[![][sentry-img]][sentry-url] +[![][stream-img]][stream-url] +[![][machinalis-img]][machinalis-url] +[![][rollbar-img]][rollbar-url] -*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [Machinalis](https://hello.machinalis.co.uk/), and [Rollbar](https://rollbar.com/).* +Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover][rover-url], [Sentry][sentry-url], [Stream][stream-url], [Machinalis][machinalis-url], and [Rollbar][rollbar-url]. --- @@ -50,6 +48,8 @@ There is a live example API for testing purposes, [available here][sandbox]. ![Screenshot][image] +---- + # Requirements * Python (2.7, 3.4, 3.5, 3.6) @@ -188,6 +188,18 @@ Send a description of the issue via email to [rest-framework-security@googlegrou [funding]: https://fund.django-rest-framework.org/topics/funding/ [sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors +[rover-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/rover-readme.png +[sentry-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/sentry-readme.png +[stream-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/stream-readme.png +[machinalis-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/machinalis-readme.png +[rollbar-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/rollbar-readme.png + +[rover-url]: http://jobs.rover.com/ +[sentry-url]: https://getsentry.com/welcome/ +[stream-url]: https://getstream.io/try-the-api/?utm_source=drf&utm_medium=banner&utm_campaign=drf +[machinalis-url]: https://hello.machinalis.co.uk/ +[rollbar-url]: https://rollbar.com/ + [oauth1-section]: http://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth [oauth2-section]: http://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit [serializer-section]: http://www.django-rest-framework.org/api-guide/serializers/#serializers diff --git a/requirements/requirements-packaging.txt b/requirements/requirements-packaging.txt index f12d4af0ed..76b15f8320 100644 --- a/requirements/requirements-packaging.txt +++ b/requirements/requirements-packaging.txt @@ -9,3 +9,6 @@ transifex-client==0.11 # Pandoc to have a nice pypi page pypandoc + +# readme_renderer to check readme syntax +readme_renderer diff --git a/setup.py b/setup.py index 736df7b129..54acad01ba 100755 --- a/setup.py +++ b/setup.py @@ -9,10 +9,10 @@ from setuptools import setup, find_packages try: - from pypandoc import convert + from pypandoc import convert_file def read_md(f): - return convert(f, 'rst') + return convert_file(f, 'rst') except ImportError: print("warning: pypandoc module not found, could not convert Markdown to RST") diff --git a/tox.ini b/tox.ini index e2c7c2feb4..eea6bbf37f 100644 --- a/tox.ini +++ b/tox.ini @@ -6,8 +6,8 @@ envlist = {py27,py34,py35}-django110, {py27,py34,py35,py36}-django111, {py34,py35,py36}-django20, - {py35,py36}-djangomaster - lint,docs + {py35,py36}-djangomaster, + lint,docs,readme, [travis:env] DJANGO = @@ -42,3 +42,8 @@ commands = mkdocs build deps = -rrequirements/requirements-testing.txt -rrequirements/requirements-documentation.txt + +[testenv:readme] +commands = ./setup.py check -rs +deps = + -rrequirements/requirements-packaging.txt From 743fc247eba50e96ffda36825f49b0d529d8c29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padilla?= Date: Mon, 27 Nov 2017 05:34:17 -0500 Subject: [PATCH 031/585] Update tutorial (#5622) * Use createsuperuser email and username flags * Only remove db.sqlite3 * Remove global permission class This interferes with Core API schema endpoint * Add default pagination class * Specify changes made in snippets/urls.py * Auth urls were already set in tutorial/urls.py * Specify changes made in snippets/urls.py * Use the suggested admin username from quickstart * Move global pagination setting away from quickstart section --- docs/tutorial/2-requests-and-responses.md | 2 +- docs/tutorial/3-class-based-views.md | 2 +- docs/tutorial/4-authentication-and-permissions.md | 6 +++--- docs/tutorial/5-relationships-and-hyperlinked-apis.md | 11 +++-------- docs/tutorial/6-viewsets-and-routers.md | 10 ++++------ docs/tutorial/quickstart.md | 11 ++--------- 6 files changed, 14 insertions(+), 28 deletions(-) diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index e55ffa7090..7fb1b2f2e2 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -106,7 +106,7 @@ and def snippet_detail(request, pk, format=None): -Now update the `urls.py` file slightly, to append a set of `format_suffix_patterns` in addition to the existing URLs. +Now update the `snippets/urls.py` file slightly, to append a set of `format_suffix_patterns` in addition to the existing URLs. from django.conf.urls import url from rest_framework.urlpatterns import format_suffix_patterns diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md index 5a8cdd46ab..5c3b979296 100644 --- a/docs/tutorial/3-class-based-views.md +++ b/docs/tutorial/3-class-based-views.md @@ -62,7 +62,7 @@ So far, so good. It looks pretty similar to the previous case, but we've got be That's looking good. Again, it's still pretty similar to the function based view right now. -We'll also need to refactor our `urls.py` slightly now that we're using class-based views. +We'll also need to refactor our `snippets/urls.py` slightly now that we're using class-based views. from django.conf.urls import url from rest_framework.urlpatterns import format_suffix_patterns diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md index 72cf64e378..6e5b077ce5 100644 --- a/docs/tutorial/4-authentication-and-permissions.md +++ b/docs/tutorial/4-authentication-and-permissions.md @@ -43,7 +43,7 @@ And now we can add a `.save()` method to our model class: When that's all done we'll need to update our database tables. Normally we'd create a database migration in order to do that, but for the purposes of this tutorial, let's just delete the database and start again. - rm -f tmp.db db.sqlite3 + rm -f db.sqlite3 rm -r snippets/migrations python manage.py makemigrations snippets python manage.py migrate @@ -205,11 +205,11 @@ If we try to create a snippet without authenticating, we'll get an error: We can make a successful request by including the username and password of one of the users we created earlier. - http -a tom:password123 POST http://127.0.0.1:8000/snippets/ code="print 789" + http -a admin:password123 POST http://127.0.0.1:8000/snippets/ code="print 789" { "id": 1, - "owner": "tom", + "owner": "admin", "title": "foo", "code": "print 789", "linenos": false, diff --git a/docs/tutorial/5-relationships-and-hyperlinked-apis.md b/docs/tutorial/5-relationships-and-hyperlinked-apis.md index 9fd61b4149..1e4da788ea 100644 --- a/docs/tutorial/5-relationships-and-hyperlinked-apis.md +++ b/docs/tutorial/5-relationships-and-hyperlinked-apis.md @@ -130,23 +130,18 @@ After adding all those names into our URLconf, our final `snippets/urls.py` file name='user-detail') ]) - # Login and logout views for the browsable API - urlpatterns += [ - url(r'^api-auth/', include('rest_framework.urls', - namespace='rest_framework')), - ] - ## Adding pagination The list views for users and code snippets could end up returning quite a lot of instances, so really we'd like to make sure we paginate the results, and allow the API client to step through each of the individual pages. -We can change the default list style to use pagination, by modifying our `tutorial/settings.py` file slightly. Add the following setting: +We can change the default list style to use pagination, by modifying our `tutorial/settings.py` file slightly. Add the following setting: REST_FRAMEWORK = { + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10 } -Note that settings in REST framework are all namespaced into a single dictionary setting, named 'REST_FRAMEWORK', which helps keep them well separated from your other project settings. +Note that settings in REST framework are all namespaced into a single dictionary setting, named `REST_FRAMEWORK`, which helps keep them well separated from your other project settings. We could also customize the pagination style if we needed too, but in this case we'll just stick with the default. diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md index 252021e395..7d87c02129 100644 --- a/docs/tutorial/6-viewsets-and-routers.md +++ b/docs/tutorial/6-viewsets-and-routers.md @@ -61,7 +61,7 @@ The URLs for custom actions by default depend on the method name itself. If you The handler methods only get bound to the actions when we define the URLConf. To see what's going on under the hood let's first explicitly create a set of views from our ViewSets. -In the `urls.py` file we bind our `ViewSet` classes into a set of concrete views. +In the `snippets/urls.py` file we bind our `ViewSet` classes into a set of concrete views. from snippets.views import SnippetViewSet, UserViewSet, api_root from rest_framework import renderers @@ -103,11 +103,11 @@ Now that we've bound our resources into concrete views, we can register the view Because we're using `ViewSet` classes rather than `View` classes, we actually don't need to design the URL conf ourselves. The conventions for wiring up resources into views and urls can be handled automatically, using a `Router` class. All we need to do is register the appropriate view sets with a router, and let it do the rest. -Here's our re-wired `urls.py` file. +Here's our re-wired `snippets/urls.py` file. from django.conf.urls import url, include - from snippets import views from rest_framework.routers import DefaultRouter + from snippets import views # Create a router and register our viewsets with it. router = DefaultRouter() @@ -115,10 +115,8 @@ Here's our re-wired `urls.py` file. router.register(r'users', views.UserViewSet) # The API URLs are now determined automatically by the router. - # Additionally, we include the login URLs for the browsable API. urlpatterns = [ - url(https://github.com/r'%5E',%20include(router.urls)), - url(https://github.com/r'%5Eapi-auth/',%20include('rest_framework.urls',%20namespace='rest_framework')) + url(https://github.com/r'%5E',%20include(router.urls)) ] Registering the viewsets with the router is similar to providing a urlpattern. We include two arguments - the URL prefix for the views, and the viewset itself. diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md index 31361413e8..ab789518d0 100644 --- a/docs/tutorial/quickstart.md +++ b/docs/tutorial/quickstart.md @@ -54,7 +54,7 @@ Now sync your database for the first time: We'll also create an initial user named `admin` with a password of `password123`. We'll authenticate as that user later in our example. - python manage.py createsuperuser + python manage.py createsuperuser --email admin@example.com --username admin Once you've set up a database and initial user created and ready to go, open up the app's directory and we'll get coding... @@ -134,20 +134,13 @@ Finally, we're including default login and logout views for use with the browsab ## Settings -We'd also like to set a few global settings. We'd like to turn on pagination, and we want our API to only be accessible to admin users. The settings module will be in `tutorial/settings.py` +Add `'rest_framework'` to `INSTALLED_APPS`. The settings module will be in `tutorial/settings.py` INSTALLED_APPS = ( ... 'rest_framework', ) - REST_FRAMEWORK = { - 'DEFAULT_PERMISSION_CLASSES': [ - 'rest_framework.permissions.IsAdminUser', - ], - 'PAGE_SIZE': 10 - } - Okay, we're done. --- From 97f7a82b372d22e588c8771398971ecdd517cdb7 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Tue, 28 Nov 2017 16:46:34 +0100 Subject: [PATCH 032/585] Correct typos Closes #5634 --- 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 9ac3ab1005..44d8c7a120 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -43,14 +43,14 @@ You can determine your currently installed version using `pip freeze`: ### 3.7.3 -**Date**: [6th Novemember 2017][3.7.3-milestone] +**Date**: [6th November 2017][3.7.3-milestone] * Fix `AppRegistryNotReady` error from contrib.auth view imports [#5567][gh5567] ### 3.7.2 -**Date**: [6th Novemember 2017][3.7.2-milestone] +**Date**: [6th November 2017][3.7.2-milestone] * Fixed Django 2.1 compatibility due to removal of django.contrib.auth.login()/logout() views. [#5510][gh5510] * Add missing import for TextLexer. [#5512][gh5512] From 905a5579dfaa717ad4210ddd285267bb9bbb9a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B4mulo=20Oliveira?= Date: Fri, 1 Dec 2017 06:54:25 -0200 Subject: [PATCH 033/585] Non-required fields with 'allow_null=True' should not imply a default value (#5639) Ref #5518. --- rest_framework/fields.py | 4 ++-- tests/test_serializer.py | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 55c2fe48e9..a710df7b48 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -442,10 +442,10 @@ def get_attribute(self, instance): except (KeyError, AttributeError) as exc: if self.default is not empty: return self.get_default() - if self.allow_null: - return None if not self.required: raise SkipField() + if self.allow_null: + return None msg = ( 'Got {exc_type} when attempting to get a value for field ' '`{field}` on serializer `{serializer}`.\nThe serializer ' diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 23c6ec2c10..0c30deb916 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -372,6 +372,14 @@ def create(self, validated_data): serializer.save() assert serializer.data == {'included': 'abc'} + def test_not_required_output_for_allow_null_field(self): + class ExampleSerializer(serializers.Serializer): + omitted = serializers.CharField(required=False, allow_null=True) + included = serializers.CharField() + + serializer = ExampleSerializer({'included': 'abc'}) + assert 'omitted' not in serializer.data + class TestDefaultOutput: def setup(self): From 5f42cb70270d698df57df679726964e7fad11f45 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Fri, 1 Dec 2017 04:07:33 -0500 Subject: [PATCH 034/585] Add allow_null serialization output note (#5641) --- docs/api-guide/fields.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index d209a945bc..8d61f2cbcc 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -45,6 +45,8 @@ Defaults to `True`. Normally an error will be raised if `None` is passed to a serializer field. Set this keyword argument to `True` if `None` should be considered a valid value. +Note that setting this argument to `True` will imply a default value of `null` for serialization output, but does imply a default for input deserialization. + Defaults to `False` ### `default` From 101d3d24f701e52e1b4a0e1c5a0124482ff26f4c Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Sun, 3 Dec 2017 10:33:26 -0800 Subject: [PATCH 035/585] Update to use the Django 2.0 release in tox.ini Was previously using a release candidate as the minimum version. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index eea6bbf37f..e2d7aa4488 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ setenv = deps = django110: Django>=1.10,<1.11 django111: Django>=1.11,<2.0 - django20: Django>=2.0rc1,<2.1 + django20: Django>=2.0,<2.1 djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From daba5e9ba5cea03d56e76baa4bf05d67b7ac6cea Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 4 Dec 2017 02:39:55 -0500 Subject: [PATCH 036/585] Fix Serializer.data when provided invalid 'data' (#5646) * Test serializer/API renderer for invalid datatype * Fix Serializer.data with invalid input datatype --- rest_framework/serializers.py | 4 +++ tests/browsable_api/test_form_rendering.py | 30 ++++++++++++++++++++++ tests/test_serializer.py | 9 +++++++ 3 files changed, 43 insertions(+) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index fa6a6f0bbf..e2ea0d7440 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -398,6 +398,10 @@ def get_validators(self): def get_initial(self): if hasattr(self, 'initial_data'): + # initial_data may not be a valid type + if not isinstance(self.initial_data, Mapping): + return OrderedDict() + return OrderedDict([ (field_name, field.get_value(self.initial_data)) for field_name, field in self.fields.items() diff --git a/tests/browsable_api/test_form_rendering.py b/tests/browsable_api/test_form_rendering.py index 47186c783e..d8378a2cab 100644 --- a/tests/browsable_api/test_form_rendering.py +++ b/tests/browsable_api/test_form_rendering.py @@ -14,6 +14,10 @@ class Meta: fields = '__all__' +class StandardPostView(generics.CreateAPIView): + serializer_class = BasicSerializer + + class ManyPostView(generics.GenericAPIView): queryset = BasicModel.objects.all() serializer_class = BasicSerializer @@ -24,6 +28,32 @@ def post(self, request, *args, **kwargs): return Response(serializer.data, status.HTTP_200_OK) +class TestPostingListData(TestCase): + """ + POSTing a list of data to a regular view should not cause the browsable + API to fail during rendering. + + Regression test for https://github.com/encode/django-rest-framework/issues/5637 + """ + + def test_json_response(self): + # sanity check for non-browsable API responses + view = StandardPostView.as_view() + request = factory.post('/', [{}], format='json') + response = view(request).render() + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertTrue('non_field_errors' in response.data) + + def test_browsable_api(self): + view = StandardPostView.as_view() + request = factory.post('/?format=api', [{}], format='json') + response = view(request).render() + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertTrue('non_field_errors' in response.data) + + class TestManyPostView(TestCase): def setUp(self): """ diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 0c30deb916..da1394515c 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -77,14 +77,23 @@ def test_valid_serializer(self): serializer = self.Serializer(data={'char': 'abc', 'integer': 123}) assert serializer.is_valid() assert serializer.validated_data == {'char': 'abc', 'integer': 123} + assert serializer.data == {'char': 'abc', 'integer': 123} assert serializer.errors == {} def test_invalid_serializer(self): serializer = self.Serializer(data={'char': 'abc'}) assert not serializer.is_valid() assert serializer.validated_data == {} + assert serializer.data == {'char': 'abc'} assert serializer.errors == {'integer': ['This field is required.']} + def test_invalid_datatype(self): + serializer = self.Serializer(data=[{'char': 'abc'}]) + assert not serializer.is_valid() + assert serializer.validated_data == {} + assert serializer.data == {} + assert serializer.errors == {'non_field_errors': ['Invalid data. Expected a dictionary, but got list.']} + def test_partial_validation(self): serializer = self.Serializer(data={'char': 'abc'}, partial=True) assert serializer.is_valid() From a0cdba627767ec489fbc683483a8904bdfe606a9 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Mon, 4 Dec 2017 09:07:43 +0100 Subject: [PATCH 037/585] Extract method for `manual_fields` processing (#5633) * Extract method for `manual_fields` processing Allows reuse of logic to replace Field instances in a field list by `Field.name`. Adds a utility function for the logic plus a wrapper method on `AutoSchema`. Closes #5632 * Manual fields suggestions (#2) * Use OrderedDict in inspectors * Move empty check to 'update_fields()' * Make 'update_fields()' an AutoSchema staticmethod * Add 'AutoSchema.get_manual_fields()' * Conform '.get_manual_fields()' to other methods * Add test for update_fields * Make sure `manual_fields` is a list. (As documented to be) * Add docs for new AutoSchema methods. * `get_manual_fields` * `update_fields` * Add release notes for PR. --- docs/api-guide/schemas.md | 25 +++++++++++++++++ docs/topics/release-notes.md | 19 +++++++++++++ rest_framework/schemas/inspectors.py | 35 +++++++++++++++++++----- tests/test_schemas.py | 40 ++++++++++++++++++++++++++-- 4 files changed, 111 insertions(+), 8 deletions(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 22894a9782..2b83e0671b 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -603,6 +603,31 @@ Return a list of `coreapi.Link()` instances, as returned by the `get_schema_fiel Return a list of `coreapi.Link()` instances, as returned by the `get_schema_fields()` method of any filter classes used by the view. +### get_manual_fields(self, path, method) + +Return a list of `coreapi.Field()` instances to be added to or replace generated fields. Defaults to (optional) `manual_fields` passed to `AutoSchema` constructor. + +May be overridden to customise manual fields by `path` or `method`. For example, a per-method adjustment may look like this: + +```python +def get_manual_fields(self, path, method): + """Example adding per-method fields.""" + + extra_fields = [] + if method=='GET': + extra_fields = # ... list of extra fields for GET ... + if method=='POST': + extra_fields = # ... list of extra fields for POST ... + + manual_fields = super().get_manual_fields() + return manual_fields + extra_fields +``` + +### update_fields(fields, update_with) + +Utility `staticmethod`. Encapsulates logic to add or replace fields from a list +by `Field.name`. May be overridden to adjust replacement criteria. + ## ManualSchema diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 44d8c7a120..2f2cdf1a15 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,25 @@ You can determine your currently installed version using `pip freeze`: ## 3.7.x series +### 3.7.4 + +**Date**: UNRELEASED + +* Extract method for `manual_fields` processing [#5633][gh5633] + + Allows for easier customisation of `manual_fields` processing, for example + to provide per-method manual fields. `AutoSchema` adds `get_manual_fields`, + as the intended override point, and a utility method `update_fields`, to + handle by-name field replacement from a list, which, in general, you are not + expected to override. + + Note: `AutoSchema.__init__` now ensures `manual_fields` is a list. + Previously may have been stored internally as `None`. + + +[gh5633]: https://github.com/encode/django-rest-framework/issues/5633 + + ### 3.7.3 diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index 47f5b9e13e..008d7c0910 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -172,7 +172,8 @@ def __init__(self, manual_fields=None): * `manual_fields`: list of `coreapi.Field` instances that will be added to auto-generated fields, overwriting on `Field.name` """ - + if manual_fields is None: + manual_fields = [] self._manual_fields = manual_fields def get_link(self, path, method, base_url): @@ -181,11 +182,8 @@ def get_link(self, path, method, base_url): 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()) + manual_fields = self.get_manual_fields(path, method) + fields = self.update_fields(fields, manual_fields) if fields and any([field.location in ('form', 'body') for field in fields]): encoding = self.get_encoding(path, method) @@ -379,6 +377,31 @@ def get_filter_fields(self, path, method): fields += filter_backend().get_schema_fields(self.view) return fields + def get_manual_fields(self, path, method): + return self._manual_fields + + @staticmethod + def update_fields(fields, update_with): + """ + Update list of coreapi.Field instances, overwriting on `Field.name`. + + Utility function to handle replacing coreapi.Field fields + from a list by name. Used to handle `manual_fields`. + + Parameters: + + * `fields`: list of `coreapi.Field` instances to update + * `update_with: list of `coreapi.Field` instances to add or replace. + """ + if not update_with: + return fields + + by_name = OrderedDict((f.name, f) for f in fields) + for f in update_with: + by_name[f.name] = f + fields = list(by_name.values()) + return fields + def get_encoding(self, path, method): """ Return the 'encoding' parameter to use for a given endpoint. diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 56692d4f59..ba561a9597 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -516,7 +516,7 @@ def test_4605_regression(self): assert prefix == '/' -class TestDescriptor(TestCase): +class TestAutoSchema(TestCase): def test_apiview_schema_descriptor(self): view = APIView() @@ -528,7 +528,43 @@ def test_get_link_requires_instance(self): with pytest.raises(AssertionError): descriptor.get_link(None, None, None) # ???: Do the dummy arguments require a tighter assert? - def test_manual_fields(self): + def test_update_fields(self): + """ + That updating fields by-name helper is correct + + Recall: `update_fields(fields, update_with)` + """ + schema = AutoSchema() + fields = [] + + # Adds a field... + fields = schema.update_fields(fields, [ + coreapi.Field( + "my_field", + required=True, + location="path", + schema=coreschema.String() + ), + ]) + + assert len(fields) == 1 + assert fields[0].name == "my_field" + + # Replaces a field... + fields = schema.update_fields(fields, [ + coreapi.Field( + "my_field", + required=False, + location="path", + schema=coreschema.String() + ), + ]) + + assert len(fields) == 1 + assert fields[0].required is False + + def test_get_manual_fields(self): + """That get_manual_fields is applied during get_link""" class CustomView(APIView): schema = AutoSchema(manual_fields=[ From c7df69ab7703c7ed345a445fe1b16ac52c40f026 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Mon, 4 Dec 2017 10:52:59 +0100 Subject: [PATCH 038/585] Note AutoSchema limitations on bare APIView (#5649) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AutoSchema uses GenericAPIView hooks to introspect. If these are not present it’s results will be limited. Note this. Closes #5121 --- docs/api-guide/schemas.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 2b83e0671b..9234212ea8 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -167,6 +167,18 @@ appropriate Core API `Link` object for the view, request method and path: (In compiling the schema, `SchemaGenerator` calls `view.schema.get_link()` for each view, allowed method and path.) +--- + +**Note**: For basic `APIView` subclasses, default introspection is essentially +limited to the URL kwarg path parameters. For `GenericAPIView` +subclasses, which includes all the provided class based views, `AutoSchema` will +attempt to introspect serialiser, pagination and filter fields, as well as +provide richer path field descriptions. (The key hooks here are the relevant +`GenericAPIView` attributes and methods: `get_serializer`, `pagination_class`, +`filter_backends` and so on.) + +--- + To customise the `Link` generation you may: * Instantiate `AutoSchema` on your view with the `manual_fields` kwarg: From 7855d3bd8b607ed8aed1a2266b35e3e3ef265288 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 4 Dec 2017 05:55:49 -0500 Subject: [PATCH 039/585] Add '.basename' and '.reverse_action()' to ViewSet (#5648) * Router sets 'basename' on ViewSet * Add 'ViewSet.reverse_action()' method * Test router setting initkwargs --- docs/api-guide/viewsets.md | 15 +++++++ rest_framework/routers.py | 7 +++- rest_framework/viewsets.py | 16 ++++++- tests/test_routers.py | 16 +++++++ tests/test_viewsets.py | 86 +++++++++++++++++++++++++++++++++++++- 5 files changed, 137 insertions(+), 3 deletions(-) diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index a8fc6afef0..04ce7357d6 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -159,6 +159,21 @@ These decorators will route `GET` requests by default, but may also accept other The two new actions will then be available at the urls `^users/{pk}/set_password/$` and `^users/{pk}/unset_password/$` +## 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. + +Note that the `basename` is provided by the router during `ViewSet` registration. If you are not using a router, then you must provide the `basename` argument to the `.as_view()` method. + +Using the example from the previous section: + +```python +>>> view.reverse_action('set-password', args=['1']) +'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. + --- # API Reference diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 2010a11384..f4d2fab383 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -278,7 +278,12 @@ def get_urls(self): if not prefix and regex[:2] == '^/': regex = '^' + regex[2:] - view = viewset.as_view(mapping, **route.initkwargs) + initkwargs = route.initkwargs.copy() + initkwargs.update({ + 'basename': basename, + }) + + view = viewset.as_view(mapping, **initkwargs) name = route.name.format(basename=basename) ret.append(url(https://github.com/regex,%20view,%20name=name)) diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py index 7f3c550a9e..4ee7cdaf86 100644 --- a/rest_framework/viewsets.py +++ b/rest_framework/viewsets.py @@ -24,6 +24,7 @@ from django.views.decorators.csrf import csrf_exempt from rest_framework import generics, mixins, views +from rest_framework.reverse import reverse class ViewSetMixin(object): @@ -46,10 +47,14 @@ def as_view(cls, actions=None, **initkwargs): instantiated view, we need to totally reimplement `.as_view`, and slightly modify the view function that is created and returned. """ - # The suffix initkwarg is reserved for identifying the viewset type + # The suffix initkwarg is reserved for displaying the viewset type. # eg. 'List' or 'Instance'. cls.suffix = 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 + # actions must not be empty if not actions: raise TypeError("The `actions` argument must be provided when " @@ -121,6 +126,15 @@ def initialize_request(self, request, *args, **kwargs): self.action = self.action_map.get(method) return request + def reverse_action(self, url_name, *args, **kwargs): + """ + Reverse the action for the given `url_name`. + """ + url_name = '%s-%s' % (self.basename, url_name) + kwargs.setdefault('request', self.request) + + return reverse(url_name, *args, **kwargs) + class ViewSet(ViewSetMixin, views.APIView): """ diff --git a/tests/test_routers.py b/tests/test_routers.py index 07a57fe55b..5a1cfe8f40 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -7,6 +7,7 @@ from django.core.exceptions import ImproperlyConfigured from django.db import models from django.test import TestCase, override_settings +from django.urls import resolve from rest_framework import permissions, serializers, viewsets from rest_framework.compat import get_regex_pattern @@ -435,3 +436,18 @@ def test_regex_url_path_detail(self): 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} + + +@override_settings(ROOT_URLCONF='tests.test_routers') +class TestViewInitkwargs(TestCase): + def test_suffix(self): + match = resolve('/example/notes/') + initkwargs = match.func.initkwargs + + assert initkwargs['suffix'] == 'List' + + def test_basename(self): + match = resolve('/example/notes/') + initkwargs = match.func.initkwargs + + assert initkwargs['basename'] == 'routertestmodel' diff --git a/tests/test_viewsets.py b/tests/test_viewsets.py index 846a368074..beff42cb89 100644 --- a/tests/test_viewsets.py +++ b/tests/test_viewsets.py @@ -1,7 +1,11 @@ -from django.test import TestCase +from django.conf.urls import include, url +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.response import Response +from rest_framework.routers import SimpleRouter from rest_framework.test import APIRequestFactory from rest_framework.viewsets import GenericViewSet @@ -22,6 +26,46 @@ def dummy(self, request, *args, **kwargs): return Response({'view': self}) +class Action(models.Model): + pass + + +class ActionViewSet(GenericViewSet): + queryset = Action.objects.all() + + def list(self, request, *args, **kwargs): + pass + + def retrieve(self, request, *args, **kwargs): + pass + + @list_route() + def list_action(self, request, *args, **kwargs): + pass + + @list_route(url_name='list-custom') + def custom_list_action(self, request, *args, **kwargs): + pass + + @detail_route() + def detail_action(self, request, *args, **kwargs): + pass + + @detail_route(url_name='detail-custom') + def custom_detail_action(self, request, *args, **kwargs): + pass + + +router = SimpleRouter() +router.register(r'actions', ActionViewSet) +router.register(r'actions-alt', ActionViewSet, base_name='actions-alt') + + +urlpatterns = [ + url(https://github.com/r'%5Eapi/',%20include(router.urls)), +] + + class InitializeViewSetsTestCase(TestCase): def test_initialize_view_set_with_actions(self): request = factory.get('/', '', content_type='application/json') @@ -65,3 +109,43 @@ def test_args_kwargs_request_action_map_on_self(self): for attribute in ('args', 'kwargs', 'request', 'action_map'): self.assertNotIn(attribute, dir(bare_view)) self.assertIn(attribute, dir(view)) + + +@override_settings(ROOT_URLCONF='tests.test_viewsets') +class ReverseActionTests(TestCase): + def test_default_basename(self): + view = ActionViewSet() + view.basename = router.get_default_base_name(ActionViewSet) + view.request = None + + assert view.reverse_action('list') == '/api/actions/' + assert view.reverse_action('list-action') == '/api/actions/list_action/' + assert view.reverse_action('list-custom') == '/api/actions/custom_list_action/' + + assert view.reverse_action('detail', args=['1']) == '/api/actions/1/' + assert view.reverse_action('detail-action', args=['1']) == '/api/actions/1/detail_action/' + assert view.reverse_action('detail-custom', args=['1']) == '/api/actions/1/custom_detail_action/' + + def test_custom_basename(self): + view = ActionViewSet() + view.basename = 'actions-alt' + view.request = None + + assert view.reverse_action('list') == '/api/actions-alt/' + assert view.reverse_action('list-action') == '/api/actions-alt/list_action/' + assert view.reverse_action('list-custom') == '/api/actions-alt/custom_list_action/' + + assert view.reverse_action('detail', args=['1']) == '/api/actions-alt/1/' + assert view.reverse_action('detail-action', args=['1']) == '/api/actions-alt/1/detail_action/' + assert view.reverse_action('detail-custom', args=['1']) == '/api/actions-alt/1/custom_detail_action/' + + def test_request_passing(self): + view = ActionViewSet() + view.basename = router.get_default_base_name(ActionViewSet) + view.request = factory.get('/') + + # Passing the view's request object should result in an absolute URL. + assert view.reverse_action('list') == 'http://testserver/api/actions/' + + # Users should be able to explicitly not pass the view's request. + assert view.reverse_action('list', request=None) == '/api/actions/' From 01587b9eb17bf68c716e84e616202d6e4ccbaecf Mon Sep 17 00:00:00 2001 From: Hang Park Date: Mon, 4 Dec 2017 21:00:03 +0900 Subject: [PATCH 040/585] Typos in serializers documentation (#5652) Fixes #5651. Change `update()` to `.update()` in serializers documentation to get a consistency with `.create()`. --- docs/api-guide/serializers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index ee6e416078..89196949d7 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -73,7 +73,7 @@ Deserialization is similar. First we parse a stream into Python native datatypes ## Saving instances -If we want to be able to return complete object instances based on the validated data we need to implement one or both of the `.create()` and `update()` methods. For example: +If we want to be able to return complete object instances based on the validated data we need to implement one or both of the `.create()` and `.update()` methods. For example: class CommentSerializer(serializers.Serializer): email = serializers.EmailField() @@ -325,7 +325,7 @@ For updates you'll want to think carefully about how to handle updates to relati * Ignore the data and leave the instance as it is. * Raise a validation error. -Here's an example for an `update()` method on our previous `UserSerializer` class. +Here's an example for an `.update()` method on our previous `UserSerializer` class. def update(self, instance, validated_data): profile_data = validated_data.pop('profile') From 1692feb535472cb78c49328613a14aef48f77787 Mon Sep 17 00:00:00 2001 From: Anna Ossowski Date: Wed, 6 Dec 2017 03:10:41 +0100 Subject: [PATCH 041/585] Updated monthly report link --- docs/topics/funding.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/funding.md b/docs/topics/funding.md index f9c31e79a3..8b0735099c 100644 --- a/docs/topics/funding.md +++ b/docs/topics/funding.md @@ -340,7 +340,7 @@ For further enquires please contact From 4a200d5e66e093ae6f8c6e94eb749ed588849ce0 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Thu, 14 Dec 2017 10:33:48 +0100 Subject: [PATCH 042/585] Fix `override_settings` compat (#5668) * Add test checking override_settings compat * Refresh APISettings, rather than replace Fix suggested by @daggaz https://github.com/encode/django-rest-framework/issues/2466#issuecomment-344297213 --- rest_framework/settings.py | 14 +++++++++++--- tests/test_settings.py | 21 +++++++++++++++++++-- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/rest_framework/settings.py b/rest_framework/settings.py index d9e3a29428..478a5229f5 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -200,6 +200,7 @@ def __init__(self, user_settings=None, defaults=None, import_strings=None): self._user_settings = self.__check_user_settings(user_settings) self.defaults = defaults or DEFAULTS self.import_strings = import_strings or IMPORT_STRINGS + self._cached_attrs = set() @property def user_settings(self): @@ -223,6 +224,7 @@ def __getattr__(self, attr): val = perform_import(val, attr) # Cache the result + self._cached_attrs.add(attr) setattr(self, attr, val) return val @@ -233,15 +235,21 @@ def __check_user_settings(self, user_settings): raise RuntimeError("The '%s' setting has been removed. Please refer to '%s' for available settings." % (setting, SETTINGS_DOC)) return user_settings + def reload(self): + for attr in self._cached_attrs: + delattr(self, attr) + self._cached_attrs.clear() + if hasattr(self, '_user_settings'): + delattr(self, '_user_settings') + api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) def reload_api_settings(*args, **kwargs): - global api_settings - setting, value = kwargs['setting'], kwargs['value'] + setting = kwargs['setting'] if setting == 'REST_FRAMEWORK': - api_settings = APISettings(value, DEFAULTS, IMPORT_STRINGS) + api_settings.reload() setting_changed.connect(reload_api_settings) diff --git a/tests/test_settings.py b/tests/test_settings.py index 9ba552d281..51e9751b25 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -1,8 +1,8 @@ from __future__ import unicode_literals -from django.test import TestCase +from django.test import TestCase, override_settings -from rest_framework.settings import APISettings +from rest_framework.settings import APISettings, api_settings class TestSettings(TestCase): @@ -28,6 +28,23 @@ def test_warning_raised_on_removed_setting(self): 'MAX_PAGINATE_BY': 100 }) + def test_compatibility_with_override_settings(self): + """ + Ref #5658 & #2466: Documented usage of api_settings + is bound at import time: + + from rest_framework.settings import api_settings + + setting_changed signal hook must ensure bound instance + is refreshed. + """ + assert api_settings.PAGE_SIZE is None, "Checking a known default should be None" + + with override_settings(REST_FRAMEWORK={'PAGE_SIZE': 10}): + assert api_settings.PAGE_SIZE == 10, "Setting should have been updated" + + assert api_settings.PAGE_SIZE is None, "Setting should have been restored" + class TestSettingTypes(TestCase): def test_settings_consistently_coerced_to_list(self): From 791539acecb801f3830948dea727f459cbd4b122 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Thu, 14 Dec 2017 11:24:21 +0100 Subject: [PATCH 043/585] Add DEFAULT_SCHEMA_CLASS setting (#5658) * Add test for new setting * Add DefaultSchema utility * Add new setting to docs --- docs/api-guide/settings.md | 6 ++++++ rest_framework/schemas/__init__.py | 2 +- rest_framework/schemas/inspectors.py | 11 +++++++++++ rest_framework/settings.py | 4 ++++ rest_framework/views.py | 4 ++-- tests/test_schemas.py | 17 +++++++++++++++++ 6 files changed, 41 insertions(+), 3 deletions(-) diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 5c9eaa12c2..a8abd2a63b 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -94,6 +94,12 @@ A content negotiation class, that determines how a renderer is selected for the Default: `'rest_framework.negotiation.DefaultContentNegotiation'` +#### DEFAULT_SCHEMA_CLASS + +A view inspector class that will be used for schema generation. + +Default: `'rest_framework.schemas.AutoSchema'` + --- ## Generic view settings diff --git a/rest_framework/schemas/__init__.py b/rest_framework/schemas/__init__.py index 1af0b9fc55..ba0ec65369 100644 --- a/rest_framework/schemas/__init__.py +++ b/rest_framework/schemas/__init__.py @@ -23,7 +23,7 @@ from rest_framework.settings import api_settings from .generators import SchemaGenerator -from .inspectors import AutoSchema, ManualSchema # noqa +from .inspectors import AutoSchema, DefaultSchema, ManualSchema # noqa def get_schema_view( diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index 008d7c0910..b2a5320bd2 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ inspectors.py # Per-endpoint view introspection @@ -456,3 +457,13 @@ def get_link(self, path, method, base_url): ) return self._link + + +class DefaultSchema(object): + """Allows overriding AutoSchema using DEFAULT_SCHEMA_CLASS setting""" + def __get__(self, instance, owner): + inspector_class = api_settings.DEFAULT_SCHEMA_CLASS + assert issubclass(inspector_class, ViewInspector), "DEFAULT_SCHEMA_CLASS must be set to a ViewInspector (usually an AutoSchema) subclass" + inspector = inspector_class() + inspector.view = instance + return inspector diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 478a5229f5..db92b7a7b6 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -55,6 +55,9 @@ 'DEFAULT_PAGINATION_CLASS': None, 'DEFAULT_FILTER_BACKENDS': (), + # Schema + 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema', + # Throttling 'DEFAULT_THROTTLE_RATES': { 'user': None, @@ -140,6 +143,7 @@ 'DEFAULT_VERSIONING_CLASS', 'DEFAULT_PAGINATION_CLASS', 'DEFAULT_FILTER_BACKENDS', + 'DEFAULT_SCHEMA_CLASS', 'EXCEPTION_HANDLER', 'TEST_REQUEST_RENDERER_CLASSES', 'UNAUTHENTICATED_USER', diff --git a/rest_framework/views.py b/rest_framework/views.py index 3140bb9a3f..f9ee7fb539 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -18,7 +18,7 @@ from rest_framework import exceptions, status from rest_framework.request import Request from rest_framework.response import Response -from rest_framework.schemas import AutoSchema +from rest_framework.schemas import DefaultSchema from rest_framework.settings import api_settings from rest_framework.utils import formatting @@ -117,7 +117,7 @@ class APIView(View): # Allow dependency injection of other settings to make testing easier. settings = api_settings - schema = AutoSchema() + schema = DefaultSchema() @classmethod def as_view(cls, **initkwargs): diff --git a/tests/test_schemas.py b/tests/test_schemas.py index ba561a9597..fa91bac038 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -516,6 +516,11 @@ def test_4605_regression(self): assert prefix == '/' +class CustomViewInspector(AutoSchema): + """A dummy AutoSchema subclass""" + pass + + class TestAutoSchema(TestCase): def test_apiview_schema_descriptor(self): @@ -523,6 +528,18 @@ def test_apiview_schema_descriptor(self): assert hasattr(view, 'schema') assert isinstance(view.schema, AutoSchema) + def test_set_custom_inspector_class_on_view(self): + class CustomView(APIView): + schema = CustomViewInspector() + + view = CustomView() + assert isinstance(view.schema, CustomViewInspector) + + def test_set_custom_inspector_class_via_settings(self): + with override_settings(REST_FRAMEWORK={'DEFAULT_SCHEMA_CLASS': 'tests.test_schemas.CustomViewInspector'}): + view = APIView() + assert isinstance(view.schema, CustomViewInspector) + def test_get_link_requires_instance(self): descriptor = APIView.schema # Accessed from class with pytest.raises(AssertionError): From 2359d3981b0480fc850a784499a870353a7f2b8c Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Thu, 14 Dec 2017 11:39:54 +0100 Subject: [PATCH 044/585] Add docs note re generated BooleanField being `required=False` (#5665) * Note that BooleanField default is required=False Closes #5664 --- docs/api-guide/fields.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 8d61f2cbcc..a8a1865a49 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -124,6 +124,8 @@ A boolean representation. When using HTML encoded form input be aware that omitting a value will always be treated as setting a field to `False`, even if it has a `default=True` option specified. This is because HTML checkbox inputs represent the unchecked state by omitting the value, so REST framework treats omission as if it is an empty checkbox input. +Note that default `BooleanField` instances will be generated with a `required=False` option (since Django `models.BooleanField` is always `blank=True`). If you want to change this behaviour explicitly declare the `BooleanField` on the serializer class. + Corresponds to `django.db.models.fields.BooleanField`. **Signature:** `BooleanField()` From d12005cf902e3969b5002593eab83bb63feda840 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Thu, 14 Dec 2017 05:48:03 -0500 Subject: [PATCH 045/585] Add 'dist' build (#5656) --- .travis.yml | 11 ++++++++++- runtests.py | 16 ++++++++++++++-- tox.ini | 10 +++++++++- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index c5a15140d6..f503eb5e17 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,10 +22,19 @@ matrix: - { python: "3.6", env: DJANGO=2.0 } - { python: "2.7", env: TOXENV=lint } - { python: "2.7", env: TOXENV=docs } - - python: "2.7" + + - python: "3.6" + env: TOXENV=dist + script: + - python setup.py bdist_wheel + - tox --installpkg ./dist/djangorestframework-*.whl + - tox # test sdist + + - python: "3.6" env: TOXENV=readme addons: apt_packages: pandoc + exclude: - { python: "2.7", env: DJANGO=master } - { python: "2.7", env: DJANGO=2.0 } diff --git a/runtests.py b/runtests.py index 5e8460c854..a8f818ad73 100755 --- a/runtests.py +++ b/runtests.py @@ -16,8 +16,6 @@ ISORT_ARGS = ['--recursive', '--check-only', '-o' 'uritemplate', '-p', 'tests', 'rest_framework', 'tests'] -sys.path.append(os.path.dirname(__file__)) - def exit_on_failure(ret, message=None): if ret: @@ -84,6 +82,20 @@ def is_class(string): run_flake8 = False run_isort = False + try: + # Remove the package root directory from `sys.path`, ensuring that rest_framework + # is imported from the installed site packages. Used for testing the distribution + sys.argv.remove('--no-pkgroot') + except ValueError: + pass + else: + sys.path.pop(0) + + # import rest_framework before pytest re-adds the package root directory. + import rest_framework + package_dir = os.path.join(os.getcwd(), 'rest_framework') + assert not rest_framework.__file__.startswith(package_dir) + if len(sys.argv) > 1: pytest_args = sys.argv[1:] first_arg = pytest_args[0] diff --git a/tox.ini b/tox.ini index e2d7aa4488..f04e6ba939 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,7 @@ envlist = {py27,py34,py35,py36}-django111, {py34,py35,py36}-django20, {py35,py36}-djangomaster, - lint,docs,readme, + dist,lint,docs,readme, [travis:env] DJANGO = @@ -18,6 +18,7 @@ DJANGO = [testenv] commands = ./runtests.py --fast {posargs} --coverage -rw +envdir = {toxworkdir}/venvs/{envname} setenv = PYTHONDONTWRITEBYTECODE=1 PYTHONWARNINGS=once @@ -29,6 +30,13 @@ deps = -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt +[testenv:dist] +commands = ./runtests.py --fast {posargs} --no-pkgroot -rw +deps = + django + -rrequirements/requirements-testing.txt + -rrequirements/requirements-optionals.txt + [testenv:lint] basepython = python2.7 commands = ./runtests.py --lintonly From 21a9740156eed349b908057856aef9acd0e620a9 Mon Sep 17 00:00:00 2001 From: Pavlin Gergov Date: Tue, 19 Dec 2017 09:45:33 +0200 Subject: [PATCH 046/585] Fix typo in docstring (#5678) --- rest_framework/throttling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/throttling.py b/rest_framework/throttling.py index 4224315668..834ced148e 100644 --- a/rest_framework/throttling.py +++ b/rest_framework/throttling.py @@ -54,7 +54,7 @@ class SimpleRateThrottle(BaseThrottle): A simple cache implementation, that only requires `.get_cache_key()` to be overridden. - The rate (requests / seconds) is set by a `throttle` attribute on the View + The rate (requests / seconds) is set by a `rate` attribute on the View class. The attribute is a string of the form 'number_of_requests/period'. Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day') From cc25f57f7b88ad22a9b4d29fa53063c85bdca5d9 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Tue, 19 Dec 2017 10:31:20 +0100 Subject: [PATCH 047/585] Add UNAUTHENTICATED_USER = None note (#5679) When removing authentication entirely you cannot import `django.contrib.auth.models.AnonymousUser` Closes #3494 --- docs/api-guide/settings.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index a8abd2a63b..caa0653894 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -202,6 +202,8 @@ Default: `'version'` #### UNAUTHENTICATED_USER The class that should be used to initialize `request.user` for unauthenticated requests. +(If removing authentication entirely, e.g. by removing `django.contrib.auth` from +`INSTALLED_APPS`, set `UNAUTHENTICATED_USER` to `None`.) Default: `django.contrib.auth.models.AnonymousUser` From 6560f44912de615a0dfc2e6ee0f8ab42767d3a95 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Tue, 19 Dec 2017 12:05:46 +0100 Subject: [PATCH 048/585] =?UTF-8?q?Update=20OPTIONS=20example=20from=20?= =?UTF-8?q?=E2=80=9CDocumenting=20Your=20API=E2=80=9D=20(#5680)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #3489 * Updates example to post-3.0 API, using metadata class * Adds link to metadata docs. --- docs/topics/documenting-your-api.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md index 425b02eb89..ae592e6369 100644 --- a/docs/topics/documenting-your-api.md +++ b/docs/topics/documenting-your-api.md @@ -254,16 +254,19 @@ REST framework APIs also support programmatically accessible descriptions, using When using the generic views, any `OPTIONS` requests will additionally respond with metadata regarding any `POST` or `PUT` actions available, describing which fields are on the serializer. -You can modify the response behavior to `OPTIONS` requests by overriding the `metadata` view method. For example: +You can modify the response behavior to `OPTIONS` requests by overriding the `options` view method and/or by providing a custom Metadata class. For example: - def metadata(self, request): + def options(self, request, *args, **kwargs): """ Don't include the view description in OPTIONS responses. """ - data = super(ExampleView, self).metadata(request) + meta = self.metadata_class() + data = meta.determine_metadata(request, self) data.pop('description') return data +See [the Metadata docs][metadata-docs] for more details. + --- ## The hypermedia approach @@ -292,3 +295,4 @@ To implement a hypermedia API you'll need to decide on an appropriate media type [image-apiary]: ../img/apiary.png [image-self-describing-api]: ../img/self-describing.png [schemas-examples]: ../api-guide/schemas/#example +[metadata-docs]: ../api-guide/metadata/ From 43c2c91dde3be7128df01b53e114d65d8e23438d Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Tue, 19 Dec 2017 12:05:59 +0100 Subject: [PATCH 049/585] Add note on object permissions for FBVs (#5681) Closes #3269 --- 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 3b89e9141d..0a3d0ef657 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -192,7 +192,7 @@ If you need to test if a request is a read operation or a write operation, you s --- -**Note**: The instance-level `has_object_permission` method will only be called if the view-level `has_permission` checks have already passed. Also note that in order for the instance-level checks to run, the view code should explicitly call `.check_object_permissions(request, obj)`. If you are using the generic views then this will be handled for you by default. +**Note**: The instance-level `has_object_permission` method will only be called if the view-level `has_permission` checks have already passed. Also note that in order for the instance-level checks to run, the view code should explicitly call `.check_object_permissions(request, obj)`. If you are using the generic views then this will be handled for you by default. (Function-based views will need to check object permissions explicitly, raising `PermissionDenied` on failure.) --- From b3a0b271cd21365d8167c9b581c7f7e7deab85d2 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Tue, 19 Dec 2017 12:06:24 +0100 Subject: [PATCH 050/585] Add example to to_representation docs (#5682) Closes #5425 as per https://github.com/encode/django-rest-framework/issues/5425#issuecomment-341063819 --- docs/api-guide/serializers.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 89196949d7..85682838b4 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -1007,6 +1007,14 @@ The signatures for these methods are as follows: Takes the object instance that requires serialization, and should return a primitive representation. Typically this means returning a structure of built-in Python datatypes. The exact types that can be handled will depend on the render classes you have configured for your API. +May be overridden in order modify the representation style. For example: + + def to_representation(self, instance): + """Convert `username` to lowercase.""" + ret = super().to_representation(instance) + ret['username'] = ret['username'].lower() + return ret + #### ``.to_internal_value(self, data)`` Takes the unvalidated incoming data as input and should return the validated data that will be made available as `serializer.validated_data`. The return value will also be passed to the `.create()` or `.update()` methods if `.save()` is called on the serializer class. From e87fcbb99d38fab6be973843abe9f43d33cbd399 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Tue, 19 Dec 2017 12:06:41 +0100 Subject: [PATCH 051/585] Add link to Classy DRF in docs (#5683) Closes #5636 as per https://github.com/encode/django-rest-framework/issues/5636#issuecomment-348143281 --- docs/api-guide/views.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 2f9f51cf92..017bcad0c7 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -42,6 +42,13 @@ For example: usernames = [user.username for user in User.objects.all()] return Response(usernames) +--- + +**Note**: The full methods, attributes on, and relations between Django REST Framework's `APIView`, `GenericAPIView`, various `Mixins`, and `Viewsets` can be initially complex. In addition to the documentation here, the [Classy Django REST Framework][classy-drf] resource provides a browsable reference, with full methods and attributes, for each of Django REST Framework's class-based views. + +--- + + ## API policy attributes The following attributes control the pluggable aspects of API views. @@ -212,3 +219,5 @@ You may pass `None` in order to exclude the view from schema generation. [settings]: settings.md [throttling]: throttling.md [schemas]: schemas.md +[classy-drf]: http://www.cdrf.co + From 4bcbf691cf312a4e41b8c20568cbf6e8cff4bd00 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Tue, 19 Dec 2017 12:06:57 +0100 Subject: [PATCH 052/585] Document ViewSet.action (#5685) Closes #2941 Provides example of adjusting permission by action. --- docs/api-guide/viewsets.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index 04ce7357d6..27fb1d7805 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -70,9 +70,10 @@ There are two main advantages of using a `ViewSet` class over using a `View` cla Both of these come with a trade-off. Using regular views and URL confs is more explicit and gives you more control. ViewSets are helpful if you want to get up and running quickly, or when you have a large API and you want to enforce a consistent URL configuration throughout. -## Marking extra actions for routing -The default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style operations, as shown below: +## ViewSet actions + +The default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style actions, as shown below: class UserViewSet(viewsets.ViewSet): """ @@ -101,6 +102,23 @@ 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. + +For example, you could restrict permissions to everything except the `list` action similar to this: + + def get_permissions(self): + """ + Instantiates and returns the list of permissions that this view requires. + """ + if self.action == 'list': + permission_classes = [IsAuthenticated] + else: + permission_classes = [IsAdmin] + return [permission() for permission in permission_classes] + +## Marking extra actions for routing + If you have ad-hoc methods that you need to be routed to, you can mark them as requiring routing using the `@detail_route` or `@list_route` decorators. The `@detail_route` decorator contains `pk` in its URL pattern and is intended for methods which require a single instance. The `@list_route` decorator is intended for methods which operate on a list of objects. From ea0b3b32ad49d9ac34af8caf49c81b9d8f231b83 Mon Sep 17 00:00:00 2001 From: Jimmy Merrild Krag Date: Tue, 19 Dec 2017 15:03:47 +0100 Subject: [PATCH 053/585] Fix typo (#5687) Fixed `include_docs_url` to be `include_docs_urls` --- docs/topics/documenting-your-api.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md index ae592e6369..b38d301a66 100644 --- a/docs/topics/documenting-your-api.md +++ b/docs/topics/documenting-your-api.md @@ -93,11 +93,11 @@ When using viewsets, you should use the relevant action names as delimiters. ### `documentation` API Reference -The `rest_framework.documentation` module provides three helper functions to help configure the interactive API documentation, `include_docs_url` (usage shown above), `get_docs_view` and `get_schemajs_view`. +The `rest_framework.documentation` module provides three helper functions to help configure the interactive API documentation, `include_docs_urls` (usage shown above), `get_docs_view` and `get_schemajs_view`. - `include_docs_url` employs `get_docs_view` and `get_schemajs_view` to generate the url patterns for the documentation page and JavaScript resource that exposes the API schema respectively. They expose the following options for customisation. (`get_docs_view` and `get_schemajs_view` ultimately call `rest_frameworks.schemas.get_schema_view()`, see the Schemas docs for more options there.) + `include_docs_urls` employs `get_docs_view` and `get_schemajs_view` to generate the url patterns for the documentation page and JavaScript resource that exposes the API schema respectively. They expose the following options for customisation. (`get_docs_view` and `get_schemajs_view` ultimately call `rest_frameworks.schemas.get_schema_view()`, see the Schemas docs for more options there.) -#### `include_docs_url` +#### `include_docs_urls` * `title`: Default `None`. May be used to provide a descriptive title for the schema definition. * `description`: Default `None`. May be used to provide a description for the schema definition. From d38b94fd74572ab7e5d741e1ff056bebe9f4c783 Mon Sep 17 00:00:00 2001 From: Tilmann Becker Date: Wed, 20 Dec 2017 09:10:28 +0100 Subject: [PATCH 054/585] Fix URL pattern parsing in schema generation (#5689) * Fix url parsing in schema generation - Call `str(pattern)` to get non-escaped route - Strip converters from path to comply with uritemplate format. Background: https://github.com/encode/django-rest-framework/issues/5675#issuecomment-352829363 Fixes #5675 --- rest_framework/compat.py | 10 ++++- rest_framework/schemas/generators.py | 10 ++++- tests/test_schemas.py | 55 +++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 5009ffee19..9502c245f5 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -32,7 +32,7 @@ def get_regex_pattern(urlpattern): if hasattr(urlpattern, 'pattern'): # Django 2.0 - return urlpattern.pattern.regex.pattern + return str(urlpattern.pattern) else: # Django < 2.0 return urlpattern.regex.pattern @@ -255,6 +255,14 @@ def md_filter_add_syntax_highlight(md): except ImportError: InvalidTimeError = Exception +# Django 1.x url routing syntax. Remove when dropping Django 1.11 support. +try: + from django.urls import include, path, re_path # noqa +except ImportError: + from django.conf.urls import include, url # noqa + path = None + re_path = url + # `separators` argument to `json.dumps()` differs between 2.x and 3.x # See: http://bugs.python.org/issue22767 diff --git a/rest_framework/schemas/generators.py b/rest_framework/schemas/generators.py index 2fe4927d83..6f5c044756 100644 --- a/rest_framework/schemas/generators.py +++ b/rest_framework/schemas/generators.py @@ -3,6 +3,7 @@ See schemas.__init__.py for package overview. """ +import re import warnings from collections import Counter, OrderedDict from importlib import import_module @@ -135,6 +136,11 @@ def endpoint_ordering(endpoint): return (path, method_priority) +_PATH_PARAMETER_COMPONENT_RE = re.compile( + r'<(?:(?P[^>:]+):)?(?P\w+)>' +) + + class EndpointEnumerator(object): """ A class to determine the available API endpoints that a project exposes. @@ -189,7 +195,9 @@ def get_path_from_regex(self, path_regex): Given a URL conf regex, return a URI template string. """ path = simplify_regex(path_regex) - path = path.replace('<', '{').replace('>', '}') + + # Strip Django 2.0 convertors as they are incompatible with uritemplate format + path = re.sub(_PATH_PARAMETER_COMPONENT_RE, r'{\g}', path) return path def should_include_endpoint(self, path, callback): diff --git a/tests/test_schemas.py b/tests/test_schemas.py index fa91bac038..34cb20798a 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -9,7 +9,7 @@ from rest_framework import ( filters, generics, pagination, permissions, serializers ) -from rest_framework.compat import coreapi, coreschema, get_regex_pattern +from rest_framework.compat import coreapi, coreschema, get_regex_pattern, path from rest_framework.decorators import ( api_view, detail_route, list_route, schema ) @@ -361,6 +361,59 @@ def test_schema_for_regular_views(self): assert schema == expected +@unittest.skipUnless(coreapi, 'coreapi is not installed') +@unittest.skipUnless(path, 'needs Django 2') +class TestSchemaGeneratorDjango2(TestCase): + def setUp(self): + self.patterns = [ + path('example/', ExampleListView.as_view()), + path('example//', ExampleDetailView.as_view()), + path('example//sub/', ExampleDetailView.as_view()), + ] + + def test_schema_for_regular_views(self): + """ + Ensure that schema generation works for APIView classes. + """ + generator = SchemaGenerator(title='Example API', patterns=self.patterns) + schema = generator.get_schema() + expected = coreapi.Document( + url='', + title='Example API', + content={ + 'example': { + 'create': coreapi.Link( + url='/example/', + action='https://github.com/post', + fields=[] + ), + 'list': coreapi.Link( + url='/example/', + action='https://github.com/get', + fields=[] + ), + 'read': coreapi.Link( + url='/example/{id}/', + action='https://github.com/get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ), + 'sub': { + 'list': coreapi.Link( + url='/example/{id}/sub/', + action='https://github.com/get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()) + ] + ) + } + } + } + ) + assert schema == expected + + @unittest.skipUnless(coreapi, 'coreapi is not installed') class TestSchemaGeneratorNotAtRoot(TestCase): def setUp(self): From cf3929d88dc3d8f311acf4a1179160db4511f658 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 20 Dec 2017 10:04:12 +0100 Subject: [PATCH 055/585] =?UTF-8?q?Add=20example=20using=20`source=3D?= =?UTF-8?q?=E2=80=98*=E2=80=99`=20to=20custom=20field=20docs.=20(#5688)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add example using `source=‘*’` to custom field docs. * Add nested serialiser example Closes #2032 closes #3066 --- docs/api-guide/fields.md | 135 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index a8a1865a49..6bf0ed15ea 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -561,6 +561,8 @@ Note that the `WritableField` class that was present in version 2.x no longer ex ## Examples +### A Basic Custom Field + Let's look at an example of serializing a class that represents an RGB color value: class Color(object): @@ -600,7 +602,7 @@ As an example, let's create a field that can be used to represent the class name """ return obj.__class__.__name__ -#### Raising validation errors +### Raising validation errors Our `ColorField` class above currently does not perform any data validation. To indicate invalid data, we should raise a `serializers.ValidationError`, like so: @@ -646,6 +648,137 @@ The `.fail()` method is a shortcut for raising `ValidationError` that takes a me This style keeps your error messages cleaner and more separated from your code, and should be preferred. +### Using `source='*'` + +Here we'll take an example of a _flat_ `DataPoint` model with `x_coordinate` and `y_coordinate` attributes. + + class DataPoint(models.Model): + label = models.CharField(max_length=50) + x_coordinate = models.SmallIntegerField() + y_coordinate = models.SmallIntegerField() + +Using a custom field and `source='*'` we can provide a nested representation of +the coordinate pair: + + class CoordinateField(serializers.Field): + + def to_representation(self, obj): + ret = { + "x": obj.x_coordinate, + "y": obj.y_coordinate + } + return ret + + def to_internal_value(self, data): + ret = { + "x_coordinate": data["x"], + "y_coordinate": data["y"], + } + return ret + + + class DataPointSerializer(serializers.ModelSerializer): + coordinates = CoordinateField(source='*') + + class Meta: + model = DataPoint + fields = ['label', 'coordinates'] + +Note that this example doesn't handle validation. Partly for that reason, in a +real project, the coordinate nesting might be better handled with a nested serialiser +using `source='*'`, with two `IntegerField` instances, each with their own `source` +pointing to the relevant field. + +The key points from the example, though, are: + +* `to_representation` is passed the entire `DataPoint` object and must map from that +to the desired output. + + >>> instance = DataPoint(label='Example', x_coordinate=1, y_coordinate=2) + >>> out_serializer = DataPointSerializer(instance) + >>> out_serializer.data + ReturnDict([('label', 'testing'), ('coordinates', {'x': 1, 'y': 2})]) + +* Unless our field is to be read-only, `to_internal_value` must map back to a dict +suitable for updating our target object. With `source='*'`, the return from +`to_internal_value` will update the root validated data dictionary, rather than a single key. + + >>> data = { + ... "label": "Second Example", + ... "coordinates": { + ... "x": 3, + ... "y": 4, + ... } + ... } + >>> in_serializer = DataPointSerializer(data=data) + >>> in_serializer.is_valid() + True + >>> in_serializer.validated_data + OrderedDict([('label', 'Second Example'), + ('y_coordinate', 4), + ('x_coordinate', 3)]) + +For completeness lets do the same thing again but with the nested serialiser +approach suggested above: + + class NestedCoordinateSerializer(serializers.Serializer): + x = serializers.IntegerField(source='x_coordinate') + y = serializers.IntegerField(source='y_coordinate') + + + class DataPointSerializer(serializers.ModelSerializer): + coordinates = NestedCoordinateSerializer(source='*') + + class Meta: + model = DataPoint + fields = ['label', 'coordinates'] + +Here the mapping between the target and source attribute pairs (`x` and +`x_coordinate`, `y` and `y_coordinate`) is handled in the `IntegerField` +declarations. It's our `NestedCoordinateSerializer` that takes `source='*'`. + +Our new `DataPointSerializer` exhibits the same behaviour as the custom field +approach. + +Serialising: + + >>> out_serializer = DataPointSerializer(instance) + >>> out_serializer.data + ReturnDict([('label', 'testing'), + ('coordinates', OrderedDict([('x', 1), ('y', 2)]))]) + +Deserialising: + + >>> in_serializer = DataPointSerializer(data=data) + >>> in_serializer.is_valid() + True + >>> in_serializer.validated_data + OrderedDict([('label', 'still testing'), + ('x_coordinate', 3), + ('y_coordinate', 4)]) + +But we also get the built-in validation for free: + + >>> invalid_data = { + ... "label": "still testing", + ... "coordinates": { + ... "x": 'a', + ... "y": 'b', + ... } + ... } + >>> invalid_serializer = DataPointSerializer(data=invalid_data) + >>> invalid_serializer.is_valid() + False + >>> invalid_serializer.errors + ReturnDict([('coordinates', + {'x': ['A valid integer is required.'], + 'y': ['A valid integer is required.']})]) + +For this reason, the nested serialiser approach would be the first to try. You +would use the custom field approach when the nested serialiser becomes infeasible +or overly complex. + + # Third party packages The following third party packages are also available. From 6de12e574e4ec059664c0bc14f8ba1a1b1de4087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristi=20V=C3=AEjdea?= Date: Wed, 20 Dec 2017 13:17:54 +0100 Subject: [PATCH 056/585] Fix format_suffix_patterns behavior with Django 2 path() routes (#5691) * Add failing test for #5672 * Add get_original_route to complement get_regex_pattern * [WIP] Fix path handling * needs more tests * maybe needs some refactoring * Add django 2 variant for all tests and fix trailing slash bug * Add more combinations to mixed path test --- rest_framework/compat.py | 32 ++++- rest_framework/schemas/generators.py | 4 +- rest_framework/urlpatterns.py | 68 +++++++++- tests/test_urlpatterns.py | 182 ++++++++++++++++++++++----- 4 files changed, 247 insertions(+), 39 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 9502c245f5..9870fe77e2 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -29,7 +29,11 @@ ) -def get_regex_pattern(urlpattern): +def get_original_route(urlpattern): + """ + Get the original route/regex that was typed in by the user into the path(), re_path() or url() directive. This + is in contrast with get_regex_pattern below, which for RoutePattern returns the raw regex generated from the path(). + """ if hasattr(urlpattern, 'pattern'): # Django 2.0 return str(urlpattern.pattern) @@ -38,6 +42,29 @@ def get_regex_pattern(urlpattern): return urlpattern.regex.pattern +def get_regex_pattern(urlpattern): + """ + Get the raw regex out of the urlpattern's RegexPattern or RoutePattern. This is always a regular expression, + unlike get_original_route above. + """ + if hasattr(urlpattern, 'pattern'): + # Django 2.0 + return urlpattern.pattern.regex.pattern + else: + # Django < 2.0 + return urlpattern.regex.pattern + + +def is_route_pattern(urlpattern): + if hasattr(urlpattern, 'pattern'): + # Django 2.0 + from django.urls.resolvers import RoutePattern + return isinstance(urlpattern.pattern, RoutePattern) + else: + # Django < 2.0 + return False + + def make_url_resolver(regex, urlpatterns): try: # Django 2.0 @@ -257,10 +284,11 @@ def md_filter_add_syntax_highlight(md): # Django 1.x url routing syntax. Remove when dropping Django 1.11 support. try: - from django.urls import include, path, re_path # noqa + from django.urls import include, path, re_path, register_converter # noqa except ImportError: from django.conf.urls import include, url # noqa path = None + register_converter = None re_path = url diff --git a/rest_framework/schemas/generators.py b/rest_framework/schemas/generators.py index 6f5c044756..10af6ee04d 100644 --- a/rest_framework/schemas/generators.py +++ b/rest_framework/schemas/generators.py @@ -16,7 +16,7 @@ from rest_framework import exceptions from rest_framework.compat import ( - URLPattern, URLResolver, coreapi, coreschema, get_regex_pattern + URLPattern, URLResolver, coreapi, coreschema, get_original_route ) from rest_framework.request import clone_request from rest_framework.settings import api_settings @@ -170,7 +170,7 @@ def get_api_endpoints(self, patterns=None, prefix=''): api_endpoints = [] for pattern in patterns: - path_regex = prefix + get_regex_pattern(pattern) + path_regex = prefix + get_original_route(pattern) if isinstance(pattern, URLPattern): path = self.get_path_from_regex(path_regex) callback = pattern.callback diff --git a/rest_framework/urlpatterns.py b/rest_framework/urlpatterns.py index 4aabc7f144..ab3a74978f 100644 --- a/rest_framework/urlpatterns.py +++ b/rest_framework/urlpatterns.py @@ -2,11 +2,39 @@ from django.conf.urls import include, url -from rest_framework.compat import URLResolver, get_regex_pattern +from rest_framework.compat import ( + URLResolver, get_regex_pattern, is_route_pattern, path, register_converter +) from rest_framework.settings import api_settings -def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required): +def _get_format_path_converter(suffix_kwarg, allowed): + if allowed: + if len(allowed) == 1: + allowed_pattern = allowed[0] + else: + allowed_pattern = '(?:%s)' % '|'.join(allowed) + suffix_pattern = r"\.%s/?" % allowed_pattern + else: + suffix_pattern = r"\.[a-z0-9]+/?" + + class FormatSuffixConverter: + regex = suffix_pattern + + def to_python(self, value): + return value.strip('./') + + def to_url(https://github.com/self,%20value): + return '.' + value + '/' + + converter_name = 'drf_format_suffix' + if allowed: + converter_name += '_' + '_'.join(allowed) + + return converter_name, FormatSuffixConverter + + +def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_route=None): ret = [] for urlpattern in urlpatterns: if isinstance(urlpattern, URLResolver): @@ -18,8 +46,18 @@ def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required): # Add in the included patterns, after applying the suffixes patterns = apply_suffix_patterns(urlpattern.url_patterns, suffix_pattern, - suffix_required) - ret.append(url(https://github.com/regex,%20include((patterns,%20app_name), namespace), kwargs)) + suffix_required, + suffix_route) + + # if the original pattern was a RoutePattern we need to preserve it + if is_route_pattern(urlpattern): + assert path is not None + route = str(urlpattern.pattern) + new_pattern = path(route, include((patterns, app_name), namespace), kwargs) + else: + new_pattern = url(https://github.com/regex,%20include((patterns,%20app_name), namespace), kwargs) + + ret.append(new_pattern) else: # Regular URL pattern regex = get_regex_pattern(urlpattern).rstrip('$').rstrip('/') + suffix_pattern @@ -29,7 +67,17 @@ def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required): # Add in both the existing and the new urlpattern if not suffix_required: ret.append(urlpattern) - ret.append(url(https://github.com/regex,%20view,%20kwargs,%20name)) + + # if the original pattern was a RoutePattern we need to preserve it + if is_route_pattern(urlpattern): + assert path is not None + assert suffix_route is not None + route = str(urlpattern.pattern).rstrip('$').rstrip('/') + suffix_route + new_pattern = path(route, view, kwargs, name) + else: + new_pattern = url(https://github.com/regex,%20view,%20kwargs,%20name) + + ret.append(new_pattern) return ret @@ -60,4 +108,12 @@ def format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None): else: suffix_pattern = r'\.(?P<%s>[a-z0-9]+)/?$' % suffix_kwarg - return apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required) + if path and register_converter: + converter_name, suffix_converter = _get_format_path_converter(suffix_kwarg, allowed) + register_converter(suffix_converter, converter_name) + + suffix_route = '<%s:%s>' % (converter_name, suffix_kwarg) + else: + suffix_route = None + + return apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_route) diff --git a/tests/test_urlpatterns.py b/tests/test_urlpatterns.py index e844651411..59ba395d29 100644 --- a/tests/test_urlpatterns.py +++ b/tests/test_urlpatterns.py @@ -1,12 +1,13 @@ from __future__ import unicode_literals +import unittest from collections import namedtuple from django.conf.urls import include, url from django.test import TestCase from django.urls import Resolver404 -from rest_framework.compat import make_url_resolver +from rest_framework.compat import make_url_resolver, path, re_path from rest_framework.test import APIRequestFactory from rest_framework.urlpatterns import format_suffix_patterns @@ -23,52 +24,58 @@ class FormatSuffixTests(TestCase): Tests `format_suffix_patterns` against different URLPatterns to ensure the URLs still resolve properly, including any captured parameters. """ - def _resolve_urlpatterns(self, urlpatterns, test_paths): + def _resolve_urlpatterns(self, urlpatterns, test_paths, allowed=None): factory = APIRequestFactory() try: - urlpatterns = format_suffix_patterns(urlpatterns) + urlpatterns = format_suffix_patterns(urlpatterns, allowed=allowed) except Exception: self.fail("Failed to apply `format_suffix_patterns` on the supplied urlpatterns") resolver = make_url_resolver(r'^/', urlpatterns) for test_path in test_paths: + try: + test_path, expected_resolved = test_path + except (TypeError, ValueError): + expected_resolved = True + request = factory.get(test_path.path) try: callback, callback_args, callback_kwargs = resolver.resolve(request.path_info) + except Resolver404: + callback, callback_args, callback_kwargs = (None, None, None) + if expected_resolved: + raise except Exception: self.fail("Failed to resolve URL: %s" % request.path_info) + + if not expected_resolved: + assert callback is None + continue + assert callback_args == test_path.args assert callback_kwargs == test_path.kwargs - def test_trailing_slash(self): - factory = APIRequestFactory() - urlpatterns = format_suffix_patterns([ - url(https://github.com/r'%5Etest/),%20dummy_view), - ]) - resolver = make_url_resolver(r'^/', urlpatterns) - + def _test_trailing_slash(self, urlpatterns): test_paths = [ (URLTestPath('/test.api', (), {'format': 'api'}), True), (URLTestPath('/test/.api', (), {'format': 'api'}), False), (URLTestPath('/test.api/', (), {'format': 'api'}), True), ] + self._resolve_urlpatterns(urlpatterns, test_paths) - for test_path, expected_resolved in test_paths: - request = factory.get(test_path.path) - try: - callback, callback_args, callback_kwargs = resolver.resolve(request.path_info) - except Resolver404: - callback, callback_args, callback_kwargs = (None, None, None) - if not expected_resolved: - assert callback is None - continue - - assert callback_args == test_path.args - assert callback_kwargs == test_path.kwargs + def test_trailing_slash(self): + urlpatterns = [ + url(https://github.com/r'%5Etest/),%20dummy_view), + ] + self._test_trailing_slash(urlpatterns) - def test_format_suffix(self): + @unittest.skipUnless(path, 'needs Django 2') + def test_trailing_slash_django2(self): urlpatterns = [ - url(https://github.com/r'%5Etest),%20dummy_view), + path('test/', dummy_view), ] + self._test_trailing_slash(urlpatterns) + + def _test_format_suffix(self, urlpatterns): test_paths = [ URLTestPath('/test', (), {}), URLTestPath('/test.api', (), {'format': 'api'}), @@ -76,10 +83,36 @@ def test_format_suffix(self): ] self._resolve_urlpatterns(urlpatterns, test_paths) - def test_default_args(self): + def test_format_suffix(self): urlpatterns = [ - url(https://github.com/r'%5Etest),%20dummy_view,%20%7B'foo':%20'bar'%7D), + url(https://github.com/r'%5Etest),%20dummy_view), + ] + self._test_format_suffix(urlpatterns) + + @unittest.skipUnless(path, 'needs Django 2') + def test_format_suffix_django2(self): + urlpatterns = [ + path('test', dummy_view), ] + self._test_format_suffix(urlpatterns) + + @unittest.skipUnless(path, 'needs Django 2') + def test_format_suffix_django2_args(self): + urlpatterns = [ + path('convtest/', dummy_view), + re_path(r'^retest/(?P[0-9]+)$', dummy_view), + ] + test_paths = [ + URLTestPath('/convtest/42', (), {'pk': 42}), + URLTestPath('/convtest/42.api', (), {'pk': 42, 'format': 'api'}), + URLTestPath('/convtest/42.asdf', (), {'pk': 42, 'format': 'asdf'}), + URLTestPath('/retest/42', (), {'pk': '42'}), + URLTestPath('/retest/42.api', (), {'pk': '42', 'format': 'api'}), + URLTestPath('/retest/42.asdf', (), {'pk': '42', 'format': 'asdf'}), + ] + self._resolve_urlpatterns(urlpatterns, test_paths) + + def _test_default_args(self, urlpatterns): test_paths = [ URLTestPath('/test', (), {'foo': 'bar', }), URLTestPath('/test.api', (), {'foo': 'bar', 'format': 'api'}), @@ -87,6 +120,27 @@ def test_default_args(self): ] self._resolve_urlpatterns(urlpatterns, test_paths) + def test_default_args(self): + urlpatterns = [ + url(https://github.com/r'%5Etest),%20dummy_view,%20%7B'foo':%20'bar'%7D), + ] + self._test_default_args(urlpatterns) + + @unittest.skipUnless(path, 'needs Django 2') + def test_default_args_django2(self): + urlpatterns = [ + path('test', dummy_view, {'foo': 'bar'}), + ] + self._test_default_args(urlpatterns) + + def _test_included_urls(self, urlpatterns): + test_paths = [ + URLTestPath('/test/path', (), {'foo': 'bar', }), + URLTestPath('/test/path.api', (), {'foo': 'bar', 'format': 'api'}), + URLTestPath('/test/path.asdf', (), {'foo': 'bar', 'format': 'asdf'}), + ] + self._resolve_urlpatterns(urlpatterns, test_paths) + def test_included_urls(self): nested_patterns = [ url(https://github.com/r'%5Epath),%20dummy_view) @@ -94,9 +148,79 @@ def test_included_urls(self): urlpatterns = [ url(https://github.com/r'%5Etest/',%20include(nested_patterns), {'foo': 'bar'}), ] + self._test_included_urls(urlpatterns) + + @unittest.skipUnless(path, 'needs Django 2') + def test_included_urls_django2(self): + nested_patterns = [ + path('path', dummy_view) + ] + urlpatterns = [ + path('test/', include(nested_patterns), {'foo': 'bar'}), + ] + self._test_included_urls(urlpatterns) + + @unittest.skipUnless(path, 'needs Django 2') + def test_included_urls_django2_mixed(self): + nested_patterns = [ + path('path', dummy_view) + ] + urlpatterns = [ + url('https://github.com/%5Etest/',%20include(nested_patterns), {'foo': 'bar'}), + ] + self._test_included_urls(urlpatterns) + + @unittest.skipUnless(path, 'needs Django 2') + def test_included_urls_django2_mixed_args(self): + nested_patterns = [ + path('path/', dummy_view), + url('https://github.com/^url/(?P%3Cchild%3E[0-9]+)$', dummy_view) + ] + urlpatterns = [ + url('https://github.com/^purl/(?P%3Cparent%3E[0-9]+)/', include(nested_patterns), {'foo': 'bar'}), + path('ppath//', include(nested_patterns), {'foo': 'bar'}), + ] test_paths = [ - URLTestPath('/test/path', (), {'foo': 'bar', }), - URLTestPath('/test/path.api', (), {'foo': 'bar', 'format': 'api'}), - URLTestPath('/test/path.asdf', (), {'foo': 'bar', 'format': 'asdf'}), + # parent url() nesting child path() + URLTestPath('/purl/87/path/42', (), {'parent': '87', 'child': 42, 'foo': 'bar', }), + URLTestPath('/purl/87/path/42.api', (), {'parent': '87', 'child': 42, 'foo': 'bar', 'format': 'api'}), + URLTestPath('/purl/87/path/42.asdf', (), {'parent': '87', 'child': 42, 'foo': 'bar', 'format': 'asdf'}), + + # parent path() nesting child url() + URLTestPath('/ppath/87/url/42', (), {'parent': 87, 'child': '42', 'foo': 'bar', }), + URLTestPath('/ppath/87/url/42.api', (), {'parent': 87, 'child': '42', 'foo': 'bar', 'format': 'api'}), + URLTestPath('/ppath/87/url/42.asdf', (), {'parent': 87, 'child': '42', 'foo': 'bar', 'format': 'asdf'}), + + # parent path() nesting child path() + URLTestPath('/ppath/87/path/42', (), {'parent': 87, 'child': 42, 'foo': 'bar', }), + URLTestPath('/ppath/87/path/42.api', (), {'parent': 87, 'child': 42, 'foo': 'bar', 'format': 'api'}), + URLTestPath('/ppath/87/path/42.asdf', (), {'parent': 87, 'child': 42, 'foo': 'bar', 'format': 'asdf'}), + + # parent url() nesting child url() + URLTestPath('/purl/87/url/42', (), {'parent': '87', 'child': '42', 'foo': 'bar', }), + URLTestPath('/purl/87/url/42.api', (), {'parent': '87', 'child': '42', 'foo': 'bar', 'format': 'api'}), + URLTestPath('/purl/87/url/42.asdf', (), {'parent': '87', 'child': '42', 'foo': 'bar', 'format': 'asdf'}), ] self._resolve_urlpatterns(urlpatterns, test_paths) + + def _test_allowed_formats(self, urlpatterns): + allowed_formats = ['good', 'ugly'] + test_paths = [ + (URLTestPath('/test.good/', (), {'format': 'good'}), True), + (URLTestPath('/test.bad', (), {}), False), + (URLTestPath('/test.ugly', (), {'format': 'ugly'}), True), + ] + self._resolve_urlpatterns(urlpatterns, test_paths, allowed=allowed_formats) + + def test_allowed_formats(self): + urlpatterns = [ + url('https://github.com/%5Etest),%20dummy_view), + ] + self._test_allowed_formats(urlpatterns) + + @unittest.skipUnless(path, 'needs Django 2') + def test_allowed_formats_django2(self): + urlpatterns = [ + path('test', dummy_view), + ] + self._test_allowed_formats(urlpatterns) From a81e60ff390b9b6788b2cac24a01ee4dd2dcffd2 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 20 Dec 2017 15:28:31 +0100 Subject: [PATCH 057/585] Version 3.7.4 Release (#5650) * Update version for 3.7.4 Release * Add release notes to 01587b9eb17bf68c7 * Django 2.0 is now final. * Add trove classifer for Django 2.0 * Finalise release notes for v3.7.4 * Set release date: December 20, 2017 * Update Transifex * Add release note for #5691 * Move Issue links to bottom --- README.md | 2 +- docs/topics/release-notes.md | 105 +++++++++++++++++- rest_framework/__init__.py | 2 +- .../locale/nb/LC_MESSAGES/django.mo | Bin 9803 -> 10358 bytes .../locale/nb/LC_MESSAGES/django.po | 33 +++--- .../locale/pt_BR/LC_MESSAGES/django.mo | Bin 10238 -> 10845 bytes .../locale/pt_BR/LC_MESSAGES/django.po | 29 ++--- setup.py | 1 + 8 files changed, 134 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index c19105bc72..92aad74995 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ There is a live example API for testing purposes, [available here][sandbox]. # Requirements * Python (2.7, 3.4, 3.5, 3.6) -* Django (1.10, 1.11, 2.0rc1) +* Django (1.10, 1.11, 2.0) # Installation diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 2f2cdf1a15..e6c9f78f21 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -42,9 +42,9 @@ You can determine your currently installed version using `pip freeze`: ### 3.7.4 -**Date**: UNRELEASED +**Date**: [20th December 2017][3.7.4-milestone] -* Extract method for `manual_fields` processing [#5633][gh5633] +* Schema: Extract method for `manual_fields` processing [#5633][gh5633] Allows for easier customisation of `manual_fields` processing, for example to provide per-method manual fields. `AutoSchema` adds `get_manual_fields`, @@ -54,10 +54,51 @@ You can determine your currently installed version using `pip freeze`: Note: `AutoSchema.__init__` now ensures `manual_fields` is a list. Previously may have been stored internally as `None`. - - -[gh5633]: https://github.com/encode/django-rest-framework/issues/5633 - +* Remove ulrparse compatability shim; use six instead [#5579][gh5579] +* Drop compat wrapper for `TimeDelta.total_seconds()` [#5577][gh5577] +* Clean up all whitespace throughout project [#5578][gh5578] +* Compat cleanup [#5581][gh5581] +* Add pygments CSS block in browsable API views [#5584][gh5584] [#5587][gh5587] +* Remove `set_rollback()` from compat [#5591][gh5591] +* Fix request body/POST access [#5590][gh5590] +* Rename test to reference correct issue [#5610][gh5610] +* Documentation Fixes [#5611][gh5611] [#5612][gh5612] +* Remove references to unsupported Django versions in docs and code [#5602][gh5602] +* Test Serializer exclude for declared fields [#5599][gh5599] +* Fixed schema generation for filter backends [#5613][gh5613] +* Minor cleanup for ModelSerializer tests [#5598][gh5598] +* Reimplement request attribute access w/ `__getattr__` [#5617][gh5617] +* Fixed SchemaJSRenderer renders invalid Javascript [#5607][gh5607] +* Make Django 2.0 support official/explicit [#5619][gh5619] +* Perform type check on passed request argument [#5618][gh5618] +* Fix AttributeError hiding on request authenticators [#5600][gh5600] +* Update test requirements [#5626][gh5626] +* Docs: `Serializer._declared_fields` enable modifying fields on a serializer [#5629][gh5629] +* Fix packaging [#5624][gh5624] +* Fix readme rendering for PyPI, add readme build to CI [#5625][gh5625] +* Update tutorial [#5622][gh5622] +* Non-required fields with `allow_null=True` should not imply a default value [#5639][gh5639] +* Docs: Add `allow_null` serialization output note [#5641][gh5641] +* Update to use the Django 2.0 release in tox.ini [#5645][gh5645] +* Fix `Serializer.data` for Browsable API rendering when provided invalid `data` [#5646][gh5646] +* Docs: Note AutoSchema limitations on bare APIView [#5649][gh5649] +* Add `.basename` and `.reverse_action()` to ViewSet [#5648][gh5648] +* Docs: Fix typos in serializers documentation [#5652][gh5652] +* Fix `override_settings` compat [#5668][gh5668] +* Add DEFAULT_SCHEMA_CLASS setting [#5658][gh5658] +* Add docs note re generated BooleanField being `required=False` [#5665][gh5665] +* Add 'dist' build [#5656][gh5656] +* Fix typo in docstring [#5678][gh5678] +* Docs: Add `UNAUTHENTICATED_USER = None` note [#5679][gh5679] +* Update OPTIONS example from “Documenting Your API” [#5680][gh5680] +* Docs: Add note on object permissions for FBVs [#5681][gh5681] +* Docs: Add example to `to_representation` docs [#5682][gh5682] +* Add link to Classy DRF in docs [#5683][gh5683] +* Document ViewSet.action [#5685][gh5685] +* Fix schema docs typo [#5687][gh5687] +* Fix URL pattern parsing in schema generation [#5689][gh5689] +* Add example using `source=‘*’` to custom field docs. [#5688][gh5688] +* Fix format_suffix_patterns behavior with Django 2 path() routes [#5691][gh5691] ### 3.7.3 @@ -882,6 +923,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.7.1-milestone]: https://github.com/encode/django-rest-framework/milestone/58?closed=1 [3.7.2-milestone]: https://github.com/encode/django-rest-framework/milestone/59?closed=1 [3.7.3-milestone]: https://github.com/encode/django-rest-framework/milestone/60?closed=1 +[3.7.4-milestone]: https://github.com/encode/django-rest-framework/milestone/62?closed=1 @@ -1640,3 +1682,54 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh5567]: https://github.com/encode/django-rest-framework/issues/5567 + + +[gh5691]: https://github.com/encode/django-rest-framework/issues/5691 +[gh5688]: https://github.com/encode/django-rest-framework/issues/5688 +[gh5689]: https://github.com/encode/django-rest-framework/issues/5689 +[gh5687]: https://github.com/encode/django-rest-framework/issues/5687 +[gh5685]: https://github.com/encode/django-rest-framework/issues/5685 +[gh5683]: https://github.com/encode/django-rest-framework/issues/5683 +[gh5682]: https://github.com/encode/django-rest-framework/issues/5682 +[gh5681]: https://github.com/encode/django-rest-framework/issues/5681 +[gh5680]: https://github.com/encode/django-rest-framework/issues/5680 +[gh5679]: https://github.com/encode/django-rest-framework/issues/5679 +[gh5678]: https://github.com/encode/django-rest-framework/issues/5678 +[gh5656]: https://github.com/encode/django-rest-framework/issues/5656 +[gh5665]: https://github.com/encode/django-rest-framework/issues/5665 +[gh5658]: https://github.com/encode/django-rest-framework/issues/5658 +[gh5668]: https://github.com/encode/django-rest-framework/issues/5668 +[gh5652]: https://github.com/encode/django-rest-framework/issues/5652 +[gh5648]: https://github.com/encode/django-rest-framework/issues/5648 +[gh5649]: https://github.com/encode/django-rest-framework/issues/5649 +[gh5646]: https://github.com/encode/django-rest-framework/issues/5646 +[gh5645]: https://github.com/encode/django-rest-framework/issues/5645 +[gh5641]: https://github.com/encode/django-rest-framework/issues/5641 +[gh5639]: https://github.com/encode/django-rest-framework/issues/5639 +[gh5622]: https://github.com/encode/django-rest-framework/issues/5622 +[gh5625]: https://github.com/encode/django-rest-framework/issues/5625 +[gh5624]: https://github.com/encode/django-rest-framework/issues/5624 +[gh5629]: https://github.com/encode/django-rest-framework/issues/5629 +[gh5626]: https://github.com/encode/django-rest-framework/issues/5626 +[gh5600]: https://github.com/encode/django-rest-framework/issues/5600 +[gh5618]: https://github.com/encode/django-rest-framework/issues/5618 +[gh5619]: https://github.com/encode/django-rest-framework/issues/5619 +[gh5607]: https://github.com/encode/django-rest-framework/issues/5607 +[gh5617]: https://github.com/encode/django-rest-framework/issues/5617 +[gh5598]: https://github.com/encode/django-rest-framework/issues/5598 +[gh5613]: https://github.com/encode/django-rest-framework/issues/5613 +[gh5599]: https://github.com/encode/django-rest-framework/issues/5599 +[gh5602]: https://github.com/encode/django-rest-framework/issues/5602 +[gh5612]: https://github.com/encode/django-rest-framework/issues/5612 +[gh5611]: https://github.com/encode/django-rest-framework/issues/5611 +[gh5610]: https://github.com/encode/django-rest-framework/issues/5610 +[gh5590]: https://github.com/encode/django-rest-framework/issues/5590 +[gh5591]: https://github.com/encode/django-rest-framework/issues/5591 +[gh5587]: https://github.com/encode/django-rest-framework/issues/5587 +[gh5584]: https://github.com/encode/django-rest-framework/issues/5584 +[gh5581]: https://github.com/encode/django-rest-framework/issues/5581 +[gh5578]: https://github.com/encode/django-rest-framework/issues/5578 +[gh5577]: https://github.com/encode/django-rest-framework/issues/5577 +[gh5579]: https://github.com/encode/django-rest-framework/issues/5579 +[gh5633]: https://github.com/encode/django-rest-framework/issues/5633 + diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index e1e55c612d..8c30544075 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '3.7.3' +__version__ = '3.7.4' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2017 Tom Christie' diff --git a/rest_framework/locale/nb/LC_MESSAGES/django.mo b/rest_framework/locale/nb/LC_MESSAGES/django.mo index 18cc4bc8d98712a02b5ee5832dafdd3b7001880f..536cb327207b74f233a113299d3f24ee6262205f 100644 GIT binary patch delta 2662 zcmYk-Yitx%7{>9_ZRwVBE0@+5X*&h0P-qL30xj3JT#8-G#TKMOaqLdprR{FBvrq|! z6%9rplE{RpsKFaiKlrJLO439n$OlCdyu@fi#F(gw5Mlx*NYwu`yQn9;^E+qQJ#*f3 z-kp9Hy15~Fc~aIfL+d7{5ur?D4&vk4TxhT67*mF?p@nC$9Dl+R%pGk^A*nk30rXdSYzt3 z1NHyMFoYLy22LGk%p_cnTHr?1{itu!n4@$w;c2YJ-;sZ2=J?b~7osv=j(SiS7vl+B zf!|^i7EUmx3b!G*%wg2$j^j-H619`Ju>wo-cn;4u>*&nIEvN~e!FBjPF2jt8sR>u$ zRK~68aRimgW2nqtMz+*^m>%Cw|9=tbXL5fTY9|`F=;+!osR>;=_u&yNz|&~q1)Pl6 zFbe|=I29AbYMg|WfoV+Jf_&7pq2}p9mC-)D3lF5nub>isb28_zv;LYJ%Aj~k>Vx&D z@g`I;#c&}WMxF5|>G3U80`u}yp9`VIxEHm+gXr%Fj$(WpwV(j2n~kFj*#CKSYPqo$ zdr+Bvj79iq+8S78IdvOkag!)}yMHNdn7iCnQwiQ=19>SG4f?D`rs4X5%Hq$W=wNo{y zM7nV~K93}jG~dus)t6ev@XIi*xD-24XY>+kC(ffL_zn4Kn_~901}jkC@5D9uEH>a* zsGng0tDtMjQRB6!`8MM;o^STj(HCCuZy56(>I;8h5R3SYah#?MS*xi-HpOg3{c47g zIy5Jc(=(@#dNyC6itb0$f`36JbOUviWvn8|^UZ8JnxF=3 zXMYCu`AptUdT=Q+x2Z!VupMhLiHq<(T!2?m2~4LVlNOzobo2!qRTNKQ6OQ00%pgte zL=b1;1XPXGVJCKDHl9Q3(R_}2z|W|f7)9ZfU><4#)tH0fne4wh1Kh|!6<|G~?Hfbf zX(~i*nI5X9N$)h(fSNMp#QdiEX(5=_Zx3<3pHMP7!U{qQZP58|q@$)4ul9SXzgXMo zKSHb}bZ#m>H5C!>5&v(*8e}*9wu);T(M9Mj*i5LcPvO77l&qR|QEj`07D|)dh9xw??kKZv3Wq4sD+Q z+g0_dsrViwLK@K45fl7g>LsGC_h)dbcPV%-$7+l9IZ@N#wT>(D-py$UHpLw~;e>~; z=IqSQw0oT4(1f9ks`gDGYtT;gR#~fJj%!6@32VSkboE+xbdR;$iMx?l)QZ{zjyq^~ zIo@-5`QF*Qs>~K=&+zrUu^BF`+{}$Q7ZzpfP%G?04K#r*_y%sk%h-iw`DS%E zg8Z|ST=cs$xEkkCI~OQ0TZ5IDz^zz^Dcr2BK1-t=uOZJ^bD>!Y_MrNEQ7f24t;qF# z$G?9Gwc-U-NJ6({p0C7e`W+a-hta_&uozEcDn#R58tkr}$2;*eBuw@z>cxMfUXV{G z3s+(kYyEyVCg}H}LispqqBE%9{owciMCC?=(VMZMnEh7_C;S_)p;kVR`r&WrV8OD? z#1g0^+KYGK>sXJoxC;Zz&2Gg9e5ZV$!v^lZjdKCB?@=K-7iIqyqj{#e3jad&W2CfJ z+=UJfV>v#CRroS$#h+p~E?_Sc)C!*UeII-1U%^hSD$9g)3>C5iSb;}SJ8=rNfYc=#9W?$$ z&9I%fD?6V?^3mSI^*Dpt@&MbQ9f+e|umc0IwH? zLq*{u(Y<`?L4}t`y!e9+P*a6mbULZ%Q&I_1@1yd$dRy3zT5+7J!bW&W%Y40ZukQv_ z2sj|UkomVgydWxLo zsyJ&$b;zh_w-lOgs&-ICU(f&JE8aI2eQMXs5RD}D9;y;arJt&hsN_?XCyIF+RV6!v z_aDt5?Hzt!r>x5M3|6BVEu$`>D!E#zN;Xbh@2dbWn|ERd^?u!;I8+@_N~DDQO87_j zM9xt9=bVWww=uRnJykdqa0`m+(#fK{Kze8Kn?W}xGVNZBB-|y@E$&$Ksc@p+Nwl^# zwYUq>Xvx6%, 2017 # Petter Kjelkenes , 2015 # Thomas Bruun , 2017 msgid "" @@ -10,8 +11,8 @@ msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-07-12 16:13+0100\n" -"PO-Revision-Date: 2017-11-01 09:58+0000\n" -"Last-Translator: Thomas Bruun \n" +"PO-Revision-Date: 2017-11-28 15:25+0000\n" +"Last-Translator: Håken Lid \n" "Language-Team: Norwegian Bokmål (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/nb/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,7 +26,7 @@ msgstr "Ugyldig basic header. Ingen legitimasjon gitt." #: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." -msgstr "Ugylid basic header. Legitimasjonsstreng bør ikke inneholde mellomrom." +msgstr "Ugyldig basic header. Legitimasjonsstreng bør ikke inneholde mellomrom." #: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." @@ -58,35 +59,35 @@ msgstr "Ugyldig token." #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "Auth Token" #: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "Nøkkel" #: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "Bruker" #: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "Opprettet" #: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "Token" #: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "Tokener" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "Brukernavn" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "Passord" #: authtoken/serializers.py:20 msgid "User account is disabled." @@ -98,7 +99,7 @@ msgstr "Kan ikke logge inn med gitt legitimasjon." #: authtoken/serializers.py:26 msgid "Must include \"username\" and \"password\"." -msgstr "Må inkludere \"username\" og \"password\"." +msgstr "Må inneholde \"username\" og \"password\"." #: exceptions.py:49 msgid "A server error occurred." @@ -316,15 +317,15 @@ msgstr "Send inn" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "stigende" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "synkende" #: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "Ugyldig side" #: pagination.py:427 msgid "Invalid cursor" @@ -426,7 +427,7 @@ msgstr "Ugyldig versjon i URL-banen." #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "Ugyldig versjon i URL. Passer ikke med noen eksisterende versjon." #: versioning.py:147 msgid "Invalid version in hostname." diff --git a/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo b/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo index 482c07cdb98db3dec8f311ac8f3b150e21096025..2740e8c13ab167fca747b6f2a8835e6583e61962 100644 GIT binary patch delta 2737 zcmZA2TWl0n9LMp~mR^7Y)b|_k5!{4* z@Hw20ui-R2jrsT!HW-sMH>fmmV;WD(I4;KXSclntjaiG0I0p~oJiLO-uz#U3)!2&q z{1Arl5{|{;{frreHK-R{iMsCwCXG2rMID~OO8gV~Gn4wK-?R!f;~LaJ5qu1fVJ%+8 zMl2a%OgU~qZkc_k=MLi}{1TPP`#1$h7BLRvn#+?r z(~DS&pJx0KHPgG9b`H~-NxKZ$zh(t0Gp%?n%NPeWKqH-K?|4{+hj0vjftuhSNh%Yl z6fzBcFbnyvS&MVfMa|#@>dkN9LcERI{gtFgd*vlm`*UooNI0l|@u;;wr3QXJ~i6f=t$&Ld~EDm8st{mW@hxupH~Se*oFo<_hXXf5ypp1Lxyl zD?OnVxSaN$v`KT7N;NkojW&i2Z^Eb2baX%IG{)_6V6!@al;dvGb1 z@zYR$?a1WK?o9g)R6i$i9Q)5)rm}_`caX`M(75yi+fbY45Ng09s7#zfop@iNGIa|{ zo+;r&wX2X-Fg9`qnHNwm@;Yiy9KqiB8Rjy+xlBa^e1mLga}$-qf6>Cc3F-DkR3^$% zYiJ`$F&9t+{(#KE+(S)d6u;?NSd%e^`u+)2M!vo3GSY*Z`7fve|H9cgkTmtdrYYpVFO`+t$U?23_5#O2 za36xAboVBvrJKQ*Gu2>*s#g*$eNeRFwA*>VpghTSDWS>e1sN_lH|n+iD`}|cjWuhP z>BI)&8DcS^b*ms$bhdMj1pgzNa6F;1kZ2*ogqHJhLPfh>MVV8UR7gZnp4R%W$TYOu z8#8qawG>+CY+?(cHC{_-ZB=pzofWmjdO}6%)*iBn2$4^yXqg8Fwe-b-UPN*e7wys~ z2)2P~CbYJj39XfW89IpAQ^EPL099%T9WW1Q=$lyd1mDNreXN$|_&8DrD`j6Yb^JLeGg?u9vVn>_oWTvfbU*PA3+RdamW#9Zr0Q z9d`T!Ma6!1QF+cXXLss$QDIiT9gpwwVv*F{fgfZSriTo!asTZh7yQ=Z%7Qg@l1W( z3i~x9%lw-qt$B^Hs2%bA(1a5IcxkWHrqcQOMcrL(QP;Mj?#}M6?a_#xdeb_cRU5WD zqHeoqMQn>~m0Qj9Wre+1jGTC`hOu17ZSU-`gM{hRi1$#=ipC7{Lt|#=Hn!V4om6tn zoUGi{Ja4By9((Qjv#y*VZn>FF1sP7Nr20V}D zSbB$91Upd!8bb9?I^ROw?+dKgRBuozpre`(CS}d28Fr!`G>(mU1lw^2JF#TGSq+XM zfA$6+`rRR1ffrFL7brAaiRGBYjaY+e?A20#LZub|Lawof1!haJ8`T~~&0qpGBhUG% z>;Dcl z!b*%`!nM0FNxKiVmHSWw{RZ{BpIrNIRBjaU^aflVW&O2>_(Q<#GV1yhG1gyueU)KqM0uog8MdQl zupO6U+Vy{g8t@cqAOW7%g;7ce{helSgCgv$Hafj6-m z+j#l_zJyAiY19@3OR@uAf|_}|^J&!ehcSiMkaV);B!srC0jsbTTW}|8BIzSkHc`3k zOq6D`ejk!gb`mwyZ&44-k7t*%6?Ma>P&soDoADfO#n3Xd2k;TpeU2h?u+Lq48o54g zKU1mTiwNt^7;G)-#zRQH*nZR&dC02UY1E3GM%~q5bzc_$Q, 2015 # Filipe Rinaldi , 2015 # Hugo Leonardo Chalhoub Mendonça , 2015 +# Jonatas Baldin , 2017 msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-07-12 16:13+0100\n" -"PO-Revision-Date: 2017-08-03 14:58+0000\n" -"Last-Translator: Thomas Christie \n" +"PO-Revision-Date: 2017-12-06 09:53+0000\n" +"Last-Translator: Jonatas Baldin \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -60,35 +61,35 @@ msgstr "Token inválido." #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "Token de autenticação" #: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "Chave" #: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "Usuário" #: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "Criado" #: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "Token" #: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "Tokens" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "Nome do usuário" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "Senha" #: authtoken/serializers.py:20 msgid "User account is disabled." @@ -318,15 +319,15 @@ msgstr "Enviar" #: filters.py:336 msgid "ascending" -msgstr "" +msgstr "ascendente" #: filters.py:337 msgid "descending" -msgstr "" +msgstr "descendente" #: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "Página inválida." #: pagination.py:427 msgid "Invalid cursor" @@ -428,7 +429,7 @@ msgstr "Versão inválida no caminho de URL." #: versioning.py:115 msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" +msgstr "Versão inválida no caminho da URL. Não corresponde a nenhuma versão do namespace." #: versioning.py:147 msgid "Invalid version in hostname." diff --git a/setup.py b/setup.py index 54acad01ba..9223facc95 100755 --- a/setup.py +++ b/setup.py @@ -69,6 +69,7 @@ def get_version(package): 'Framework :: Django', 'Framework :: Django :: 1.10', 'Framework :: Django :: 1.11', + 'Framework :: Django :: 2.0', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', From 5e21bf852b3b00e62d6c8d8b1ef86d06de7f78a4 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Wed, 20 Dec 2017 21:27:22 +0100 Subject: [PATCH 058/585] Bumped Django 2.0 version in requirements list. --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 0e747463bc..0b544e5bfa 100644 --- a/docs/index.md +++ b/docs/index.md @@ -87,7 +87,7 @@ continued development by **[signing up for a paid plan][funding]**. REST framework requires the following: * Python (2.7, 3.2, 3.3, 3.4, 3.5, 3.6) -* Django (1.10, 1.11, 2.0 alpha) +* Django (1.10, 1.11, 2.0) The following packages are optional: From 37cfe903385bc1c0b9de2674ad3b2d8682f93e53 Mon Sep 17 00:00:00 2001 From: Matthias Runge Date: Thu, 21 Dec 2017 11:00:43 +0100 Subject: [PATCH 059/585] Add locale files back to installed files (#5696) Resolves https://github.com/encode/django-rest-framework/issues/5695 --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 48ec57edf7..3c6c45e523 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,5 +2,6 @@ include README.md include LICENSE.md recursive-include rest_framework/static *.js *.css *.png *.eot *.svg *.ttf *.woff recursive-include rest_framework/templates *.html schema.js +recursice-include rest_framework/locale *.mo global-exclude __pycache__ global-exclude *.py[co] From df469260e148e569843541f8de7dca121fff6357 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 21 Dec 2017 10:03:03 +0000 Subject: [PATCH 060/585] Include woff2 in packaging. Closes #5692 --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 3c6c45e523..789f2bb167 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,6 @@ include README.md include LICENSE.md -recursive-include rest_framework/static *.js *.css *.png *.eot *.svg *.ttf *.woff +recursive-include rest_framework/static *.js *.css *.png *.eot *.svg *.ttf *.woff *.woff2 recursive-include rest_framework/templates *.html schema.js recursice-include rest_framework/locale *.mo global-exclude __pycache__ From 3a22b1d1f0c8df2924dc35bb45a1f5b01a973c11 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 21 Dec 2017 10:03:41 +0000 Subject: [PATCH 061/585] Version 3.7.5 --- rest_framework/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 8c30544075..492ecad159 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '3.7.4' +__version__ = '3.7.5' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2017 Tom Christie' From 0d96be9266488d0f7de0dcffad1092316d98e4bf Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 21 Dec 2017 10:13:18 +0000 Subject: [PATCH 062/585] Add 3.7.5 release notes --- docs/topics/release-notes.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index e6c9f78f21..52a7e6fbd0 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -42,6 +42,13 @@ You can determine your currently installed version using `pip freeze`: ### 3.7.4 +**Date**: [21st December 2017][3.7.5-milestone] + +* Add missing *.woff2 font files to packaging. [#5692][gh5692] +* Add missing *.mo locale files to packaging. [#5695][gh5695], [#5696][gh5696] + +### 3.7.4 + **Date**: [20th December 2017][3.7.4-milestone] * Schema: Extract method for `manual_fields` processing [#5633][gh5633] @@ -924,7 +931,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.7.2-milestone]: https://github.com/encode/django-rest-framework/milestone/59?closed=1 [3.7.3-milestone]: https://github.com/encode/django-rest-framework/milestone/60?closed=1 [3.7.4-milestone]: https://github.com/encode/django-rest-framework/milestone/62?closed=1 - +[3.7.5-milestone]: https://github.com/encode/django-rest-framework/milestone/63?closed=1 [gh2013]: https://github.com/encode/django-rest-framework/issues/2013 @@ -1733,3 +1740,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh5579]: https://github.com/encode/django-rest-framework/issues/5579 [gh5633]: https://github.com/encode/django-rest-framework/issues/5633 + +[gh5692]: https://github.com/encode/django-rest-framework/issues/5692 +[gh5695]: https://github.com/encode/django-rest-framework/issues/5695 +[gh5696]: https://github.com/encode/django-rest-framework/issues/5696 From 65791d8c79bcf53ea578dad0a1dc78d17a39346c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 21 Dec 2017 10:17:59 +0000 Subject: [PATCH 063/585] Version 3.7.5. Add missing .ico to packaging. --- MANIFEST.in | 2 +- docs/topics/release-notes.md | 9 ++++++++- rest_framework/__init__.py | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 789f2bb167..78f9928979 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,6 @@ include README.md include LICENSE.md -recursive-include rest_framework/static *.js *.css *.png *.eot *.svg *.ttf *.woff *.woff2 +recursive-include rest_framework/static *.js *.css *.png *.ico *.eot *.svg *.ttf *.woff *.woff2 recursive-include rest_framework/templates *.html schema.js recursice-include rest_framework/locale *.mo global-exclude __pycache__ diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 52a7e6fbd0..7b6cad95e9 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,7 +40,13 @@ You can determine your currently installed version using `pip freeze`: ## 3.7.x series -### 3.7.4 +### 3.7.6 + +**Date**: [21st December 2017][3.7.6-milestone] + +* Add missing *.ico icon files to packaging. + +### 3.7.5 **Date**: [21st December 2017][3.7.5-milestone] @@ -932,6 +938,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.7.3-milestone]: https://github.com/encode/django-rest-framework/milestone/60?closed=1 [3.7.4-milestone]: https://github.com/encode/django-rest-framework/milestone/62?closed=1 [3.7.5-milestone]: https://github.com/encode/django-rest-framework/milestone/63?closed=1 +[3.7.6-milestone]: https://github.com/encode/django-rest-framework/milestone/64?closed=1 [gh2013]: https://github.com/encode/django-rest-framework/issues/2013 diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 492ecad159..665d9ea41b 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '3.7.5' +__version__ = '3.7.6' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2017 Tom Christie' From 67964a1bf4af3a9f37df03b419e8e7ff06b750ac Mon Sep 17 00:00:00 2001 From: Laurent Paoletti Date: Thu, 21 Dec 2017 12:12:08 +0100 Subject: [PATCH 064/585] Fix typo to bring locale back to installed files --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 78f9928979..6f7cb8f13e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,6 +2,6 @@ include README.md include LICENSE.md recursive-include rest_framework/static *.js *.css *.png *.ico *.eot *.svg *.ttf *.woff *.woff2 recursive-include rest_framework/templates *.html schema.js -recursice-include rest_framework/locale *.mo +recursive-include rest_framework/locale *.mo global-exclude __pycache__ global-exclude *.py[co] From 955a6b902b0838cd4f4471dc5fab6b334f36615b Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Thu, 21 Dec 2017 12:35:37 +0100 Subject: [PATCH 065/585] Add 3.7.7 release notes --- docs/topics/release-notes.md | 8 ++++++++ rest_framework/__init__.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 7b6cad95e9..4d668254c9 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,12 @@ You can determine your currently installed version using `pip freeze`: ## 3.7.x series +### 3.7.7 + +**Date**: [21st December 2017][3.7.7-milestone] + +* Fix typo to include *.mo locale files to packaging. [#5697][gh5697], [#5695][gh5695] + ### 3.7.6 **Date**: [21st December 2017][3.7.6-milestone] @@ -939,6 +945,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.7.4-milestone]: https://github.com/encode/django-rest-framework/milestone/62?closed=1 [3.7.5-milestone]: https://github.com/encode/django-rest-framework/milestone/63?closed=1 [3.7.6-milestone]: https://github.com/encode/django-rest-framework/milestone/64?closed=1 +[3.7.7-milestone]: https://github.com/encode/django-rest-framework/milestone/65?closed=1 [gh2013]: https://github.com/encode/django-rest-framework/issues/2013 @@ -1751,3 +1758,4 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh5692]: https://github.com/encode/django-rest-framework/issues/5692 [gh5695]: https://github.com/encode/django-rest-framework/issues/5695 [gh5696]: https://github.com/encode/django-rest-framework/issues/5696 +[gh5697]: https://github.com/encode/django-rest-framework/issues/5697 diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 665d9ea41b..5d16490e85 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '3.7.6' +__version__ = '3.7.7' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2017 Tom Christie' From 832267e945d65b97802b84fe70ef1e156ecb8ab1 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Fri, 22 Dec 2017 07:28:35 +0000 Subject: [PATCH 066/585] Fix formatting of the 3.7.4 release note (#5704) --- docs/topics/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 4d668254c9..244adef0b3 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -73,6 +73,7 @@ You can determine your currently installed version using `pip freeze`: Note: `AutoSchema.__init__` now ensures `manual_fields` is a list. Previously may have been stored internally as `None`. + * Remove ulrparse compatability shim; use six instead [#5579][gh5579] * Drop compat wrapper for `TimeDelta.total_seconds()` [#5577][gh5577] * Clean up all whitespace throughout project [#5578][gh5578] From 5fc35eb7eb1ce8ca91fa6364978b5e423b80a332 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Fri, 22 Dec 2017 11:52:51 -0500 Subject: [PATCH 067/585] Add missing word in 'Field.allow_null' docs --- docs/api-guide/fields.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 6bf0ed15ea..a2f10ed19e 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -45,7 +45,7 @@ Defaults to `True`. Normally an error will be raised if `None` is passed to a serializer field. Set this keyword argument to `True` if `None` should be considered a valid value. -Note that setting this argument to `True` will imply a default value of `null` for serialization output, but does imply a default for input deserialization. +Note that setting this argument to `True` will imply a default value of `null` for serialization output, but does not imply a default for input deserialization. Defaults to `False` From 6ce60cd594ce3ddb14f08fb69f16b1901f1624ea Mon Sep 17 00:00:00 2001 From: Mariano Baragiola Date: Wed, 27 Dec 2017 10:17:10 -0300 Subject: [PATCH 068/585] Update writable nested serializers docs --- docs/api-guide/serializers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 85682838b4..059e873914 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -352,7 +352,7 @@ Here's an example for an `.update()` method on our previous `UserSerializer` cla Because the behavior of nested creates and updates can be ambiguous, and may require complex dependencies between related models, REST framework 3 requires you to always write these methods explicitly. The default `ModelSerializer` `.create()` and `.update()` methods do not include support for writable nested representations. -It is possible that a third party package, providing automatic support some kinds of automatic writable nested representations may be released alongside the 3.1 release. +There are however, third-party packages available such as [DRF Writable Nested][route-decorators] that support automatic writable nested representations. #### Handling saving related instances in model manager classes From be0a57b0f12ad09d773cd9a898ddccd60324a608 Mon Sep 17 00:00:00 2001 From: Mariano Baragiola Date: Wed, 27 Dec 2017 10:28:59 -0300 Subject: [PATCH 069/585] FIX link --- docs/api-guide/serializers.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 059e873914..140de0fe2e 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -352,7 +352,7 @@ Here's an example for an `.update()` method on our previous `UserSerializer` cla Because the behavior of nested creates and updates can be ambiguous, and may require complex dependencies between related models, REST framework 3 requires you to always write these methods explicitly. The default `ModelSerializer` `.create()` and `.update()` methods do not include support for writable nested representations. -There are however, third-party packages available such as [DRF Writable Nested][route-decorators] that support automatic writable nested representations. +There are however, third-party packages available such as [DRF Writable Nested][thirdparty-writable-nested] that support automatic writable nested representations. #### Handling saving related instances in model manager classes @@ -1173,6 +1173,7 @@ The [drf-writable-nested][drf-writable-nested] package provides writable nested [relations]: relations.md [model-managers]: https://docs.djangoproject.com/en/stable/topics/db/managers/ [encapsulation-blogpost]: http://www.dabapps.com/blog/django-models-and-encapsulation/ +[thirdparty-writable-nested]: serializers.md#drf-writable-nested [django-rest-marshmallow]: http://tomchristie.github.io/django-rest-marshmallow/ [marshmallow]: https://marshmallow.readthedocs.io/en/latest/ [serpy]: https://github.com/clarkduvall/serpy From 88c75ba808c5b0ac830efdf36117297e350f0925 Mon Sep 17 00:00:00 2001 From: guntanis Date: Thu, 28 Dec 2017 02:01:01 -0800 Subject: [PATCH 070/585] Fixed "typo" in example. Fixing code "typo" in example. In the original file, line 145 reads: url(https://github.com/r'%5Eapi-auth/',%20include('rest_framework.urls'), It's missing the closing parenthesis. --- docs/tutorial/4-authentication-and-permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md index 6e5b077ce5..af9a7c0ffd 100644 --- a/docs/tutorial/4-authentication-and-permissions.md +++ b/docs/tutorial/4-authentication-and-permissions.md @@ -142,7 +142,7 @@ Add the following import at the top of the file: And, at the end of the file, add a pattern to include the login and logout views for the browsable API. urlpatterns += [ - url(https://github.com/r'%5Eapi-auth/',%20include('rest_framework.urls'), + url(https://github.com/r'%5Eapi-auth/',%20include('rest_framework.urls')), ] The `r'^api-auth/'` part of pattern can actually be whatever URL you want to use. From 6bd773e7f8bf3535ee51e67708f4396c00f78a1d Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 2 Jan 2018 04:45:59 -0500 Subject: [PATCH 071/585] Improve composite field child errors (#5655) * Fixup DictField test descriptions * Nest ListField/DictField errors under the idx/key * Add nested ListField/DictField tests --- rest_framework/fields.py | 37 +++++++++++++++++++++++++++----- tests/test_fields.py | 46 +++++++++++++++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index a710df7b48..9b88784c83 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1626,7 +1626,7 @@ def to_internal_value(self, data): self.fail('not_a_list', input_type=type(data).__name__) if not self.allow_empty and len(data) == 0: self.fail('empty') - return [self.child.run_validation(item) for item in data] + return self.run_child_validation(data) def to_representation(self, data): """ @@ -1634,6 +1634,20 @@ def to_representation(self, data): """ return [self.child.to_representation(item) if item is not None else None for item in data] + def run_child_validation(self, data): + result = [] + errors = OrderedDict() + + for idx, item in enumerate(data): + try: + result.append(self.child.run_validation(item)) + except ValidationError as e: + errors[idx] = e.detail + + if not errors: + return result + raise ValidationError(errors) + class DictField(Field): child = _UnvalidatedField() @@ -1669,10 +1683,7 @@ def to_internal_value(self, data): data = html.parse_html_dict(data) if not isinstance(data, dict): self.fail('not_a_dict', input_type=type(data).__name__) - return { - six.text_type(key): self.child.run_validation(value) - for key, value in data.items() - } + return self.run_child_validation(data) def to_representation(self, value): """ @@ -1683,6 +1694,22 @@ def to_representation(self, value): for key, val in value.items() } + def run_child_validation(self, data): + result = {} + errors = OrderedDict() + + for key, value in data.items(): + key = six.text_type(key) + + try: + result[key] = self.child.run_validation(value) + except ValidationError as e: + errors[key] = e.detail + + if not errors: + return result + raise ValidationError(errors) + class JSONField(Field): default_error_messages = { diff --git a/tests/test_fields.py b/tests/test_fields.py index fc9ce192ad..bc11cd133a 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1767,7 +1767,7 @@ class TestListField(FieldValues): ] invalid_inputs = [ ('not a list', ['Expected a list of items but got type "str".']), - ([1, 2, 'error'], ['A valid integer is required.']), + ([1, 2, 'error', 'error'], {2: ['A valid integer is required.'], 3: ['A valid integer is required.']}), ({'one': 'two'}, ['Expected a list of items but got type "dict".']) ] outputs = [ @@ -1794,6 +1794,25 @@ def test_collection_types_are_invalid_input(self): assert exc_info.value.detail == ['Expected a list of items but got type "dict".'] +class TestNestedListField(FieldValues): + """ + Values for nested `ListField` with IntegerField as child. + """ + valid_inputs = [ + ([[1, 2], [3]], [[1, 2], [3]]), + ([[]], [[]]) + ] + invalid_inputs = [ + (['not a list'], {0: ['Expected a list of items but got type "str".']}), + ([[1, 2, 'error'], ['error']], {0: {2: ['A valid integer is required.']}, 1: {0: ['A valid integer is required.']}}), + ([{'one': 'two'}], {0: ['Expected a list of items but got type "dict".']}) + ] + outputs = [ + ([[1, 2], [3]], [[1, 2], [3]]), + ] + field = serializers.ListField(child=serializers.ListField(child=serializers.IntegerField())) + + class TestEmptyListField(FieldValues): """ Values for `ListField` with allow_empty=False flag. @@ -1834,13 +1853,13 @@ class TestUnvalidatedListField(FieldValues): class TestDictField(FieldValues): """ - Values for `ListField` with CharField as child. + Values for `DictField` with CharField as child. """ valid_inputs = [ ({'a': 1, 'b': '2', 3: 3}, {'a': '1', 'b': '2', '3': '3'}), ] invalid_inputs = [ - ({'a': 1, 'b': None}, ['This field may not be null.']), + ({'a': 1, 'b': None, 'c': None}, {'b': ['This field may not be null.'], 'c': ['This field may not be null.']}), ('not a dict', ['Expected a dictionary of items but got type "str".']), ] outputs = [ @@ -1866,9 +1885,26 @@ def test_allow_null(self): assert output is None +class TestNestedDictField(FieldValues): + """ + Values for nested `DictField` with CharField as child. + """ + valid_inputs = [ + ({0: {'a': 1, 'b': '2'}, 1: {3: 3}}, {'0': {'a': '1', 'b': '2'}, '1': {'3': '3'}}), + ] + invalid_inputs = [ + ({0: {'a': 1, 'b': None}, 1: {'c': None}}, {'0': {'b': ['This field may not be null.']}, '1': {'c': ['This field may not be null.']}}), + ({0: 'not a dict'}, {'0': ['Expected a dictionary of items but got type "str".']}), + ] + outputs = [ + ({0: {'a': 1, 'b': '2'}, 1: {3: 3}}, {'0': {'a': '1', 'b': '2'}, '1': {'3': '3'}}), + ] + field = serializers.DictField(child=serializers.DictField(child=serializers.CharField())) + + class TestDictFieldWithNullChild(FieldValues): """ - Values for `ListField` with allow_null CharField as child. + Values for `DictField` with allow_null CharField as child. """ valid_inputs = [ ({'a': None, 'b': '2', 3: 3}, {'a': None, 'b': '2', '3': '3'}), @@ -1883,7 +1919,7 @@ class TestDictFieldWithNullChild(FieldValues): class TestUnvalidatedDictField(FieldValues): """ - Values for `ListField` with no `child` argument. + Values for `DictField` with no `child` argument. """ valid_inputs = [ ({'a': 1, 'b': [4, 5, 6], 1: 123}, {'a': 1, 'b': [4, 5, 6], '1': 123}), From b7ed6459276f6416a155f1733abee7d1ce4ec7d2 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 2 Jan 2018 04:50:49 -0500 Subject: [PATCH 072/585] Disable HTML inputs for dict/list fields (#5702) --- rest_framework/renderers.py | 6 ++++++ .../rest_framework/horizontal/dict_field.html | 11 +++++++++++ .../rest_framework/horizontal/list_field.html | 11 +++++++++++ .../templates/rest_framework/inline/dict_field.html | 9 +++++++++ .../templates/rest_framework/inline/list_field.html | 9 +++++++++ .../templates/rest_framework/vertical/dict_field.html | 7 +++++++ .../templates/rest_framework/vertical/list_field.html | 7 +++++++ 7 files changed, 60 insertions(+) create mode 100644 rest_framework/templates/rest_framework/horizontal/dict_field.html create mode 100644 rest_framework/templates/rest_framework/horizontal/list_field.html create mode 100644 rest_framework/templates/rest_framework/inline/dict_field.html create mode 100644 rest_framework/templates/rest_framework/inline/list_field.html create mode 100644 rest_framework/templates/rest_framework/vertical/dict_field.html create mode 100644 rest_framework/templates/rest_framework/vertical/list_field.html diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 80a22dee59..f071e7b4d3 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -320,6 +320,12 @@ class HTMLFormRenderer(BaseRenderer): serializers.ListSerializer: { 'base_template': 'list_fieldset.html' }, + serializers.ListField: { + 'base_template': 'list_field.html' + }, + serializers.DictField: { + 'base_template': 'dict_field.html' + }, serializers.FilePathField: { 'base_template': 'select.html', }, diff --git a/rest_framework/templates/rest_framework/horizontal/dict_field.html b/rest_framework/templates/rest_framework/horizontal/dict_field.html new file mode 100644 index 0000000000..7c7414bc4b --- /dev/null +++ b/rest_framework/templates/rest_framework/horizontal/dict_field.html @@ -0,0 +1,11 @@ +
+ {% if field.label %} + + {% endif %} + +
+

Dictionaries are not currently supported in HTML input.

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

Lists are not currently supported in HTML input.

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

Dictionaries are not currently supported in HTML input.

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

Lists are not currently supported in HTML input.

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

Dictionaries are not currently supported in HTML input.

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

Lists are not currently supported in HTML input.

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

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

    - {% if 'shell' in langs %}{% include "rest_framework/docs/langs/shell-intro.html" %}{% endif %} - {% if 'python' in langs %}{% include "rest_framework/docs/langs/python-intro.html" %}{% endif %} - {% if 'javascript' in langs %}{% include "rest_framework/docs/langs/javascript-intro.html" %}{% endif %} + {% for html in lang_intro_htmls %} + {% include html %} + {% endfor %}
    {% if document|data %} diff --git a/rest_framework/templates/rest_framework/docs/index.html b/rest_framework/templates/rest_framework/docs/index.html index 9b7bb85d0e..de704ab518 100644 --- a/rest_framework/templates/rest_framework/docs/index.html +++ b/rest_framework/templates/rest_framework/docs/index.html @@ -51,6 +51,7 @@ $('#auth-control').children().removeClass('active'); $('#auth-control').find("[data-auth='session']").closest('li').addClass('active'); {% endif %} + $('pre.highlight').filter('[data-language="{{ langs | first }}"]').removeClass('hide'); diff --git a/rest_framework/templates/rest_framework/docs/langs/shell-intro.html b/rest_framework/templates/rest_framework/docs/langs/shell-intro.html index fa77bce4a1..2320ddfa94 100644 --- a/rest_framework/templates/rest_framework/docs/langs/shell-intro.html +++ b/rest_framework/templates/rest_framework/docs/langs/shell-intro.html @@ -1,3 +1,3 @@ {% load rest_framework %} -
    {% code bash %}# Install the command line client
    +
    {% code bash %}# Install the command line client
     $ pip install coreapi-cli{% endcode %}
    diff --git a/rest_framework/templates/rest_framework/docs/langs/shell.html b/rest_framework/templates/rest_framework/docs/langs/shell.html index d2744920c6..24137e4ae1 100644 --- a/rest_framework/templates/rest_framework/docs/langs/shell.html +++ b/rest_framework/templates/rest_framework/docs/langs/shell.html @@ -1,5 +1,5 @@ {% load rest_framework %} -
    {% code bash %}# Load the schema document
    +
    {% code bash %}# Load the schema document
     $ coreapi get {{ document.url }}{% if schema_format %} --format {{ schema_format }}{% endif %}
     
     # Interact with the API endpoint
    diff --git a/rest_framework/templates/rest_framework/docs/link.html b/rest_framework/templates/rest_framework/docs/link.html
    index fc2320a5f1..c40a44733f 100644
    --- a/rest_framework/templates/rest_framework/docs/link.html
    +++ b/rest_framework/templates/rest_framework/docs/link.html
    @@ -93,9 +93,9 @@ 

    Request Body

    - {% if 'shell' in langs %}{% include "rest_framework/docs/langs/shell.html" %}{% endif %} - {% if 'python' in langs %}{% include "rest_framework/docs/langs/python.html" %}{% endif %} - {% if 'javascript' in langs %}{% include "rest_framework/docs/langs/javascript.html" %}{% endif %} + {% for html in lang_htmls %} + {% include html %} + {% endfor %}
    diff --git a/rest_framework/templates/rest_framework/docs/sidebar.html b/rest_framework/templates/rest_framework/docs/sidebar.html index d23787025a..c318789f68 100644 --- a/rest_framework/templates/rest_framework/docs/sidebar.html +++ b/rest_framework/templates/rest_framework/docs/sidebar.html @@ -31,12 +31,12 @@

    {{ document.title }}

    From 73203e6b5920dcbe78e3309b7bf2803eb56db536 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Thu, 25 Jan 2018 03:40:49 -0500 Subject: [PATCH 090/585] Rework dynamic list/detail actions (#5705) * Merge list/detail route decorators into 'action' * Merge dynamic routes, add 'detail' attribute * Add 'ViewSet.get_extra_actions()' * Refactor dynamic route checking & collection * Refactor dynamic route generation * Add 'ViewSet.detail' initkwarg * Fixup schema test * Add release notes for dynamic action changes * Replace list/detail route decorators in tests * Convert tabs to spaces in router docs * Update docs * Make 'detail' a required argument of 'action' * Improve router docs --- docs/api-guide/metadata.md | 2 +- docs/api-guide/routers.md | 96 ++++++--------- docs/api-guide/viewsets.md | 46 ++++--- docs/topics/release-notes.md | 29 +++++ docs/tutorial/6-viewsets-and-routers.md | 10 +- rest_framework/decorators.py | 42 +++++-- rest_framework/routers.py | 152 ++++++++++++------------ rest_framework/viewsets.py | 18 ++- tests/test_decorators.py | 53 ++++++++- tests/test_routers.py | 40 ++++--- tests/test_schemas.py | 27 +++-- tests/test_viewsets.py | 20 +++- 12 files changed, 331 insertions(+), 204 deletions(-) diff --git a/docs/api-guide/metadata.md b/docs/api-guide/metadata.md index d5c3c1a67e..affeea61c9 100644 --- a/docs/api-guide/metadata.md +++ b/docs/api-guide/metadata.md @@ -67,7 +67,7 @@ If you have specific requirements for creating schema endpoints that are accesse For example, the following additional route could be used on a viewset to provide a linkable schema endpoint. - @list_route(methods=['GET']) + @action(methods=['GET'], detail=False) def schema(self, request): meta = self.metadata_class() data = meta.determine_metadata(request, self) diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md index e4415df155..09e06ff1db 100644 --- a/docs/api-guide/routers.md +++ b/docs/api-guide/routers.md @@ -81,81 +81,62 @@ Router URL patterns can also be namespaces. If using namespacing with hyperlinked serializers you'll also need to ensure that any `view_name` parameters on the serializers correctly reflect the namespace. In the example above you'd need to include a parameter such as `view_name='api:user-detail'` for serializer fields hyperlinked to the user detail view. -### Extra link and actions +### Routing for extra actions -Any methods on the viewset decorated with `@detail_route` or `@list_route` will also be routed. -For example, given a method like this on the `UserViewSet` class: +A viewset may [mark extra actions for routing][route-decorators] by decorating a method with the `@action` decorator. These extra actions will be included in the generated routes. For example, given the `set_password` method on the `UserViewSet` class: from myapp.permissions import IsAdminOrIsSelf - from rest_framework.decorators import detail_route + from rest_framework.decorators import action class UserViewSet(ModelViewSet): ... - @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf]) + @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf]) def set_password(self, request, pk=None): ... -The following URL pattern would additionally be generated: +The following route would be generated: -* URL pattern: `^users/{pk}/set_password/$` Name: `'user-set-password'` +* URL pattern: `^users/{pk}/set_password/$` +* URL name: `'user-set-password'` -If you do not want to use the default URL generated for your custom action, you can instead use the url_path parameter to customize it. +By default, the URL pattern is based on the method name, and the URL name is the combination of the `ViewSet.basename` and the hyphenated method name. +If you don't want to use the defaults for either of these values, you can instead provide the `url_path` and `url_name` arguments to the `@action` decorator. For example, if you want to change the URL for our custom action to `^users/{pk}/change-password/$`, you could write: from myapp.permissions import IsAdminOrIsSelf - from rest_framework.decorators import detail_route + from rest_framework.decorators import action class UserViewSet(ModelViewSet): ... - @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_path='change-password') + @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf], + url_path='change-password', url_name='change_password') def set_password(self, request, pk=None): ... The above example would now generate the following URL pattern: -* URL pattern: `^users/{pk}/change-password/$` Name: `'user-change-password'` - -In the case you do not want to use the default name generated for your custom action, you can use the url_name parameter to customize it. - -For example, if you want to change the name of our custom action to `'user-change-password'`, you could write: - - from myapp.permissions import IsAdminOrIsSelf - from rest_framework.decorators import detail_route - - class UserViewSet(ModelViewSet): - ... - - @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_name='change-password') - def set_password(self, request, pk=None): - ... - -The above example would now generate the following URL pattern: - -* URL pattern: `^users/{pk}/set_password/$` Name: `'user-change-password'` - -You can also use url_path and url_name parameters together to obtain extra control on URL generation for custom views. - -For more information see the viewset documentation on [marking extra actions for routing][route-decorators]. +* URL path: `^users/{pk}/change-password/$` +* URL name: `'user-change_password'` # API Guide ## SimpleRouter -This router includes routes for the standard set of `list`, `create`, `retrieve`, `update`, `partial_update` and `destroy` actions. The viewset can also mark additional methods to be routed, using the `@detail_route` or `@list_route` decorators. +This router includes routes for the standard set of `list`, `create`, `retrieve`, `update`, `partial_update` and `destroy` actions. The viewset can also mark additional methods to be routed, using the `@action` decorator. - + - +
    URL StyleHTTP MethodActionURL Name
    {prefix}/GETlist{basename}-list
    POSTcreate
    {prefix}/{methodname}/GET, or as specified by `methods` argument`@list_route` decorated method{basename}-{methodname}
    {prefix}/{url_path}/GET, or as specified by `methods` argument`@action(detail=False)` decorated method{basename}-{url_name}
    {prefix}/{lookup}/GETretrieve{basename}-detail
    PUTupdate
    PATCHpartial_update
    DELETEdestroy
    {prefix}/{lookup}/{methodname}/GET, or as specified by `methods` argument`@detail_route` decorated method{basename}-{methodname}
    {prefix}/{lookup}/{url_path}/GET, or as specified by `methods` argument`@action(detail=True)` decorated method{basename}-{url_name}
    By default the URLs created by `SimpleRouter` are appended with a trailing slash. @@ -180,12 +161,12 @@ This router is similar to `SimpleRouter` as above, but additionally includes a d [.format]GETautomatically generated root viewapi-root {prefix}/[.format]GETlist{basename}-list POSTcreate - {prefix}/{methodname}/[.format]GET, or as specified by `methods` argument`@list_route` decorated method{basename}-{methodname} + {prefix}/{url_path}/[.format]GET, or as specified by `methods` argument`@action(detail=False)` decorated method{basename}-{url_name} {prefix}/{lookup}/[.format]GETretrieve{basename}-detail PUTupdate PATCHpartial_update DELETEdestroy - {prefix}/{lookup}/{methodname}/[.format]GET, or as specified by `methods` argument`@detail_route` decorated method{basename}-{methodname} + {prefix}/{lookup}/{url_path}/[.format]GET, or as specified by `methods` argument`@action(detail=True)` decorated method{basename}-{url_name} As with `SimpleRouter` the trailing slashes on the URL routes can be removed by setting the `trailing_slash` argument to `False` when instantiating the router. @@ -212,18 +193,18 @@ The arguments to the `Route` named tuple are: * `{basename}` - The base to use for the URL names that are created. -**initkwargs**: A dictionary of any additional arguments that should be passed when instantiating the view. Note that the `suffix` argument is reserved for identifying the viewset type, used when generating the view name and breadcrumb links. +**initkwargs**: A dictionary of any additional arguments that should be passed when instantiating the view. Note that the `detail`, `basename`, and `suffix` arguments are reserved for viewset introspection and are also used by the browsable API to generate the view name and breadcrumb links. ## Customizing dynamic routes -You can also customize how the `@list_route` and `@detail_route` decorators are routed. -To route either or both of these decorators, include a `DynamicListRoute` and/or `DynamicDetailRoute` named tuple in the `.routes` list. +You can also customize how the `@action` decorator is routed. Include the `DynamicRoute` named tuple in the `.routes` list, setting the `detail` argument as appropriate for the list-based and detail-based routes. In addition to `detail`, the arguments to `DynamicRoute` are: -The arguments to `DynamicListRoute` and `DynamicDetailRoute` are: +**url**: A string representing the URL to be routed. May include the same format strings as `Route`, and additionally accepts the `{url_path}` format string. -**url**: A string representing the URL to be routed. May include the same format strings as `Route`, and additionally accepts the `{methodname}` and `{methodnamehyphen}` format strings. +**name**: The name of the URL as used in `reverse` calls. May include the following format strings: -**name**: The name of the URL as used in `reverse` calls. May include the following format strings: `{basename}`, `{methodname}` and `{methodnamehyphen}`. +* `{basename}` - The base to use for the URL names that are created. +* `{url_name}` - The `url_name` provided to the `@action`. **initkwargs**: A dictionary of any additional arguments that should be passed when instantiating the view. @@ -231,7 +212,7 @@ The arguments to `DynamicListRoute` and `DynamicDetailRoute` are: The following example will only route to the `list` and `retrieve` actions, and does not use the trailing slash convention. - from rest_framework.routers import Route, DynamicDetailRoute, SimpleRouter + from rest_framework.routers import Route, DynamicRoute, SimpleRouter class CustomReadOnlyRouter(SimpleRouter): """ @@ -239,22 +220,23 @@ The following example will only route to the `list` and `retrieve` actions, and """ routes = [ Route( - url=r'^{prefix}$', - mapping={'get': 'list'}, - name='{basename}-list', - initkwargs={'suffix': 'List'} + url=r'^{prefix}$', + mapping={'get': 'list'}, + name='{basename}-list', + initkwargs={'suffix': 'List'} ), Route( - url=r'^{prefix}/{lookup}$', + url=r'^{prefix}/{lookup}$', mapping={'get': 'retrieve'}, name='{basename}-detail', initkwargs={'suffix': 'Detail'} ), - DynamicDetailRoute( - url=r'^{prefix}/{lookup}/{methodnamehyphen}$', - name='{basename}-{methodnamehyphen}', - initkwargs={} - ) + DynamicRoute( + url=r'^{prefix}/{lookup}/{url_path}$', + name='{basename}-{url_name}', + detail=True, + initkwargs={} + ) ] Let's take a look at the routes our `CustomReadOnlyRouter` would generate for a simple viewset. @@ -269,7 +251,7 @@ Let's take a look at the routes our `CustomReadOnlyRouter` would generate for a serializer_class = UserSerializer lookup_field = 'username' - @detail_route() + @action(detail=True) def group_names(self, request, pk=None): """ Returns a list of all the group names that the given @@ -283,7 +265,7 @@ Let's take a look at the routes our `CustomReadOnlyRouter` would generate for a router = CustomReadOnlyRouter() router.register('users', UserViewSet) - urlpatterns = router.urls + urlpatterns = router.urls The following mappings would be generated... diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index 27fb1d7805..503459a963 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -102,10 +102,16 @@ The default routers included with REST framework will provide routes for a stand def destroy(self, request, pk=None): pass -During dispatch the name of the current action is available via the `.action` attribute. -You may inspect `.action` to adjust behaviour based on the current action. +## Introspecting ViewSet actions -For example, you could restrict permissions to everything except the `list` action similar to this: +During dispatch, the following attributes are available on the `ViewSet`. + +* `basename` - the base to use for the URL names that are created. +* `action` - the name of the current action (e.g., `list`, `create`). +* `detail` - boolean indicating if the current action is configured for a list or detail view. +* `suffix` - the display suffix for the viewset type - mirrors the `detail` attribute. + +You may inspect these attributes to adjust behaviour based on the current action. For example, you could restrict permissions to everything except the `list` action similar to this: def get_permissions(self): """ @@ -119,16 +125,13 @@ For example, you could restrict permissions to everything except the `list` acti ## Marking extra actions for routing -If you have ad-hoc methods that you need to be routed to, you can mark them as requiring routing using the `@detail_route` or `@list_route` decorators. - -The `@detail_route` decorator contains `pk` in its URL pattern and is intended for methods which require a single instance. The `@list_route` decorator is intended for methods which operate on a list of objects. +If you have ad-hoc methods that should be routable, you can mark them as such with the `@action` decorator. Like regular actions, extra actions may be intended for either a list of objects, or a single instance. To indicate this, set the `detail` argument to `True` or `False`. The router will configure its URL patterns accordingly. e.g., the `DefaultRouter` will configure detail actions to contain `pk` in their URL patterns. -For example: +A more complete example of extra actions: from django.contrib.auth.models import User - from rest_framework import status - from rest_framework import viewsets - from rest_framework.decorators import detail_route, list_route + from rest_framework import status, viewsets + from rest_framework.decorators import action from rest_framework.response import Response from myapp.serializers import UserSerializer, PasswordSerializer @@ -139,7 +142,7 @@ For example: queryset = User.objects.all() serializer_class = UserSerializer - @detail_route(methods=['post']) + @action(methods=['post'], detail=True) def set_password(self, request, pk=None): user = self.get_object() serializer = PasswordSerializer(data=request.data) @@ -151,7 +154,7 @@ For example: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - @list_route() + @action(detail=False) def recent_users(self, request): recent_users = User.objects.all().order('-last_login') @@ -163,20 +166,22 @@ For example: serializer = self.get_serializer(recent_users, many=True) return Response(serializer.data) -The decorators can additionally take extra arguments that will be set for the routed view only. For example... +The decorator can additionally take extra arguments that will be set for the routed view only. For example: - @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf]) + @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf]) def set_password(self, request, pk=None): ... -These decorators will route `GET` requests by default, but may also accept other HTTP methods, by using the `methods` argument. For example: +These decorator will route `GET` requests by default, but may also accept other HTTP methods by setting the `methods` argument. For example: - @detail_route(methods=['post', 'delete']) + @action(methods=['post', 'delete'], detail=True) def unset_password(self, request, pk=None): ... The two new actions will then be available at the urls `^users/{pk}/set_password/$` and `^users/{pk}/unset_password/$` +To view all extra actions, call the `.get_extra_actions()` method. + ## Reversing action URLs If you need to get the URL of an action, use the `.reverse_action()` method. This is a convenience wrapper for `reverse()`, automatically passing the view's `request` object and prepending the `url_name` with the `.basename` attribute. @@ -190,7 +195,14 @@ Using the example from the previous section: 'http://localhost:8000/api/users/1/set_password' ``` -The `url_name` argument should match the same argument to the `@list_route` and `@detail_route` decorators. Additionally, this can be used to reverse the default `list` and `detail` routes. +Alternatively, you can use the `url_name` attribute set by the `@action` decorator. + +```python +>>> view.reverse_action(view.set_password.url_name, args=['1']) +'http://localhost:8000/api/users/1/set_password' +``` + +The `url_name` argument for `.reverse_action()` should match the same argument to the `@action` decorator. Additionally, this method can be used to reverse the default actions, such as `list` and `create`. --- diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 128175ef5e..2c45b70afd 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -38,6 +38,31 @@ You can determine your currently installed version using `pip freeze`: --- +## 3.8.x series + +### 3.8.0 + +**Date**: [unreleased][3.8.0-milestone] + +* Refactor dynamic route generation and improve viewset action introspectibility. [#5705][gh5705] + + `ViewSet`s have been provided with new attributes and methods that allow + it to introspect its set of actions and the details of the current action. + + * Merged `list_route` and `detail_route` into a single `action` decorator. + * Get all extra actions on a `ViewSet` with `.get_extra_actions()`. + * Extra actions now set the `url_name` and `url_path` on the decorated method. + * Enable action url reversing through `.reverse_action()` method (added in 3.7.4) + * Example reverse call: `self.reverse_action(self.custom_action.url_name)` + * Add `detail` initkwarg to indicate if the current action is operating on a + collection or a single instance. + + Additional changes: + + * Deprecated `list_route` & `detail_route` in favor of `action` decorator with `detail` boolean. + * Deprecated dynamic list/detail route variants in favor of `DynamicRoute` with `detail` boolean. + * Refactored the router's dynamic route generation. + ## 3.7.x series ### 3.7.7 @@ -940,6 +965,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.7.5-milestone]: https://github.com/encode/django-rest-framework/milestone/63?closed=1 [3.7.6-milestone]: https://github.com/encode/django-rest-framework/milestone/64?closed=1 [3.7.7-milestone]: https://github.com/encode/django-rest-framework/milestone/65?closed=1 +[3.8.0-milestone]: https://github.com/encode/django-rest-framework/milestone/61?closed=1 [gh2013]: https://github.com/encode/django-rest-framework/issues/2013 @@ -1750,3 +1776,6 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh5695]: https://github.com/encode/django-rest-framework/issues/5695 [gh5696]: https://github.com/encode/django-rest-framework/issues/5696 [gh5697]: https://github.com/encode/django-rest-framework/issues/5697 + + +[gh5705]: https://github.com/encode/django-rest-framework/issues/5705 diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md index 7d87c02129..9452b49472 100644 --- a/docs/tutorial/6-viewsets-and-routers.md +++ b/docs/tutorial/6-viewsets-and-routers.md @@ -25,7 +25,7 @@ Here we've used the `ReadOnlyModelViewSet` class to automatically provide the de Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighlight` view classes. We can remove the three views, and again replace them with a single class. - from rest_framework.decorators import detail_route + from rest_framework.decorators import action from rest_framework.response import Response class SnippetViewSet(viewsets.ModelViewSet): @@ -40,7 +40,7 @@ Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighl permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,) - @detail_route(renderer_classes=[renderers.StaticHTMLRenderer]) + @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer]) def highlight(self, request, *args, **kwargs): snippet = self.get_object() return Response(snippet.highlighted) @@ -50,11 +50,11 @@ Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighl This time we've used the `ModelViewSet` class in order to get the complete set of default read and write operations. -Notice that we've also used the `@detail_route` decorator to create a custom action, named `highlight`. This decorator can be used to add any custom endpoints that don't fit into the standard `create`/`update`/`delete` style. +Notice that we've also used the `@action` decorator to create a custom action, named `highlight`. This decorator can be used to add any custom endpoints that don't fit into the standard `create`/`update`/`delete` style. -Custom actions which use the `@detail_route` decorator will respond to `GET` requests by default. We can use the `methods` argument if we wanted an action that responded to `POST` requests. +Custom actions which use the `@action` decorator will respond to `GET` requests by default. We can use the `methods` argument if we wanted an action that responded to `POST` requests. -The URLs for custom actions by default depend on the method name itself. If you want to change the way url should be constructed, you can include url_path as a decorator keyword argument. +The URLs for custom actions by default depend on the method name itself. If you want to change the way url should be constructed, you can include `url_path` as a decorator keyword argument. ## Binding ViewSets to URLs explicitly diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index 2f93fdd976..62afa05979 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -130,29 +130,49 @@ def decorator(func): return decorator -def detail_route(methods=None, **kwargs): +def action(methods=None, detail=None, url_path=None, url_name=None, **kwargs): """ - Used to mark a method on a ViewSet that should be routed for detail requests. + Mark a ViewSet method as a routable action. + + Set the `detail` boolean to determine if this action should apply to + instance/detail requests or collection/list requests. """ methods = ['get'] if (methods is None) else methods + methods = [method.lower() for method in methods] + + assert detail is not None, ( + "@action() missing required argument: 'detail'" + ) def decorator(func): func.bind_to_methods = methods - func.detail = True + func.detail = detail + func.url_path = url_path or func.__name__ + func.url_name = url_name or func.__name__.replace('_', '-') func.kwargs = kwargs return func return decorator +def detail_route(methods=None, **kwargs): + """ + Used to mark a method on a ViewSet that should be routed for detail requests. + """ + warnings.warn( + "`detail_route` is pending deprecation and will be removed in 3.10 in favor of " + "`action`, which accepts a `detail` bool. Use `@action(detail=True)` instead.", + PendingDeprecationWarning, stacklevel=2 + ) + return action(methods, detail=True, **kwargs) + + def list_route(methods=None, **kwargs): """ Used to mark a method on a ViewSet that should be routed for list requests. """ - methods = ['get'] if (methods is None) else methods - - def decorator(func): - func.bind_to_methods = methods - func.detail = False - func.kwargs = kwargs - return func - return decorator + warnings.warn( + "`list_route` is pending deprecation and will be removed in 3.10 in favor of " + "`action`, which accepts a `detail` bool. Use `@action(detail=False)` instead.", + PendingDeprecationWarning, stacklevel=2 + ) + return action(methods, detail=False, **kwargs) diff --git a/rest_framework/routers.py b/rest_framework/routers.py index f4d2fab383..9007788f85 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -16,6 +16,7 @@ from __future__ import unicode_literals import itertools +import warnings from collections import OrderedDict, namedtuple from django.conf.urls import url @@ -30,9 +31,30 @@ from rest_framework.settings import api_settings from rest_framework.urlpatterns import format_suffix_patterns -Route = namedtuple('Route', ['url', 'mapping', 'name', 'initkwargs']) -DynamicDetailRoute = namedtuple('DynamicDetailRoute', ['url', 'name', 'initkwargs']) -DynamicListRoute = namedtuple('DynamicListRoute', ['url', 'name', 'initkwargs']) +Route = namedtuple('Route', ['url', 'mapping', 'name', 'detail', 'initkwargs']) +DynamicRoute = namedtuple('DynamicRoute', ['url', 'name', 'detail', 'initkwargs']) + + +class DynamicDetailRoute(object): + def __new__(cls, url, name, initkwargs): + warnings.warn( + "`DynamicDetailRoute` is pending deprecation and will be removed in 3.10 " + "in favor of `DynamicRoute`, which accepts a `detail` boolean. Use " + "`DynamicRoute(url, name, True, initkwargs)` instead.", + PendingDeprecationWarning, stacklevel=2 + ) + return DynamicRoute(url, name, True, initkwargs) + + +class DynamicListRoute(object): + def __new__(cls, url, name, initkwargs): + warnings.warn( + "`DynamicListRoute` is pending deprecation and will be removed in 3.10 in " + "favor of `DynamicRoute`, which accepts a `detail` boolean. Use " + "`DynamicRoute(url, name, False, initkwargs)` instead.", + PendingDeprecationWarning, stacklevel=2 + ) + return DynamicRoute(url, name, False, initkwargs) def escape_curly_brackets(url_path): @@ -44,18 +66,6 @@ def escape_curly_brackets(url_path): return url_path -def replace_methodname(format_string, methodname): - """ - Partially format a format_string, swapping out any - '{methodname}' or '{methodnamehyphen}' components. - """ - methodnamehyphen = methodname.replace('_', '-') - ret = format_string - ret = ret.replace('{methodname}', methodname) - ret = ret.replace('{methodnamehyphen}', methodnamehyphen) - return ret - - def flatten(list_of_lists): """ Takes an iterable of iterables, returns a single iterable containing all items @@ -103,14 +113,15 @@ class SimpleRouter(BaseRouter): 'post': 'create' }, name='{basename}-list', + detail=False, initkwargs={'suffix': 'List'} ), - # Dynamically generated list routes. - # Generated using @list_route decorator - # on methods of the viewset. - DynamicListRoute( - url=r'^{prefix}/{methodname}{trailing_slash}$', - name='{basename}-{methodnamehyphen}', + # Dynamically generated list routes. Generated using + # @action(detail=False) decorator on methods of the viewset. + DynamicRoute( + url=r'^{prefix}/{url_path}{trailing_slash}$', + name='{basename}-{url_name}', + detail=False, initkwargs={} ), # Detail route. @@ -123,13 +134,15 @@ class SimpleRouter(BaseRouter): 'delete': 'destroy' }, name='{basename}-detail', + detail=True, initkwargs={'suffix': 'Instance'} ), - # Dynamically generated detail routes. - # Generated using @detail_route decorator on methods of the viewset. - DynamicDetailRoute( - url=r'^{prefix}/{lookup}/{methodname}{trailing_slash}$', - name='{basename}-{methodnamehyphen}', + # Dynamically generated detail routes. Generated using + # @action(detail=True) decorator on methods of the viewset. + DynamicRoute( + url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$', + name='{basename}-{url_name}', + detail=True, initkwargs={} ), ] @@ -160,57 +173,47 @@ def get_routes(self, viewset): # converting to list as iterables are good for one pass, known host needs to be checked again and again for # different functions. known_actions = list(flatten([route.mapping.values() for route in self.routes if isinstance(route, Route)])) - - # Determine any `@detail_route` or `@list_route` decorated methods on the viewset - detail_routes = [] - list_routes = [] - for methodname in dir(viewset): - attr = getattr(viewset, methodname) - httpmethods = getattr(attr, 'bind_to_methods', None) - detail = getattr(attr, 'detail', True) - if httpmethods: - # checking method names against the known actions list - if methodname in known_actions: - raise ImproperlyConfigured('Cannot use @detail_route or @list_route ' - 'decorators on method "%s" ' - 'as it is an existing route' % methodname) - httpmethods = [method.lower() for method in httpmethods] - if detail: - detail_routes.append((httpmethods, methodname)) - else: - list_routes.append((httpmethods, methodname)) - - def _get_dynamic_routes(route, dynamic_routes): - ret = [] - for httpmethods, methodname in dynamic_routes: - method_kwargs = getattr(viewset, methodname).kwargs - initkwargs = route.initkwargs.copy() - initkwargs.update(method_kwargs) - url_path = initkwargs.pop("url_path", None) or methodname - url_path = escape_curly_brackets(url_path) - url_name = initkwargs.pop("url_name", None) or url_path - ret.append(Route( - url=replace_methodname(route.url, url_path), - mapping={httpmethod: methodname for httpmethod in httpmethods}, - name=replace_methodname(route.name, url_name), - initkwargs=initkwargs, - )) - - return ret - - ret = [] + extra_actions = viewset.get_extra_actions() + + # checking action names against the known actions list + not_allowed = [ + action.__name__ for action in extra_actions + if action.__name__ in known_actions + ] + if not_allowed: + msg = ('Cannot use the @action decorator on the following ' + 'methods, as they are existing routes: %s') + raise ImproperlyConfigured(msg % ', '.join(not_allowed)) + + # partition detail and list actions + detail_actions = [action for action in extra_actions if action.detail] + list_actions = [action for action in extra_actions if not action.detail] + + routes = [] for route in self.routes: - if isinstance(route, DynamicDetailRoute): - # Dynamic detail routes (@detail_route decorator) - ret += _get_dynamic_routes(route, detail_routes) - elif isinstance(route, DynamicListRoute): - # Dynamic list routes (@list_route decorator) - ret += _get_dynamic_routes(route, list_routes) + if isinstance(route, DynamicRoute) and route.detail: + routes += [self._get_dynamic_route(route, action) for action in detail_actions] + elif isinstance(route, DynamicRoute) and not route.detail: + routes += [self._get_dynamic_route(route, action) for action in list_actions] else: - # Standard route - ret.append(route) + routes.append(route) - return ret + return routes + + def _get_dynamic_route(self, route, action): + initkwargs = route.initkwargs.copy() + initkwargs.update(action.kwargs) + + url_path = escape_curly_brackets(action.url_path) + + return Route( + url=route.url.replace('{url_path}', url_path), + mapping={http_method: action.__name__ + for http_method in action.bind_to_methods}, + name=route.name.replace('{url_name}', action.url_name), + detail=route.detail, + initkwargs=initkwargs, + ) def get_method_map(self, viewset, method_map): """ @@ -281,6 +284,7 @@ def get_urls(self): initkwargs = route.initkwargs.copy() initkwargs.update({ 'basename': basename, + 'detail': route.detail, }) view = viewset.as_view(mapping, **initkwargs) diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py index 4ee7cdaf86..9a85049bcc 100644 --- a/rest_framework/viewsets.py +++ b/rest_framework/viewsets.py @@ -19,6 +19,7 @@ from __future__ import unicode_literals from functools import update_wrapper +from inspect import getmembers from django.utils.decorators import classonlymethod from django.views.decorators.csrf import csrf_exempt @@ -27,6 +28,10 @@ from rest_framework.reverse import reverse +def _is_extra_action(attr): + return hasattr(attr, 'bind_to_methods') + + class ViewSetMixin(object): """ This is the magic. @@ -51,6 +56,9 @@ def as_view(cls, actions=None, **initkwargs): # eg. 'List' or 'Instance'. cls.suffix = None + # The detail initkwarg is reserved for introspecting the viewset type. + cls.detail = None + # Setting a basename allows a view to reverse its action urls. This # value is provided by the router through the initkwargs. cls.basename = None @@ -112,8 +120,7 @@ def view(request, *args, **kwargs): def initialize_request(self, request, *args, **kwargs): """ - Set the `.action` attribute on the view, - depending on the request method. + Set the `.action` attribute on the view, depending on the request method. """ request = super(ViewSetMixin, self).initialize_request(request, *args, **kwargs) method = request.method.lower() @@ -135,6 +142,13 @@ def reverse_action(self, url_name, *args, **kwargs): return reverse(url_name, *args, **kwargs) + @classmethod + def get_extra_actions(cls): + """ + Get the methods that are marked as an extra ViewSet `@action`. + """ + return [method for _, method in getmembers(cls, _is_extra_action)] + class ViewSet(ViewSetMixin, views.APIView): """ diff --git a/tests/test_decorators.py b/tests/test_decorators.py index 6331742db2..674990730c 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -1,12 +1,14 @@ from __future__ import unicode_literals +import pytest from django.test import TestCase from rest_framework import status from rest_framework.authentication import BasicAuthentication from rest_framework.decorators import ( - api_view, authentication_classes, parser_classes, permission_classes, - renderer_classes, schema, throttle_classes + action, api_view, authentication_classes, detail_route, list_route, + parser_classes, permission_classes, renderer_classes, schema, + throttle_classes ) from rest_framework.parsers import JSONParser from rest_framework.permissions import IsAuthenticated @@ -166,3 +168,50 @@ def view(request): return Response({}) assert isinstance(view.cls.schema, CustomSchema) + + +class ActionDecoratorTestCase(TestCase): + + def test_defaults(self): + @action(detail=True) + def test_action(request): + pass + + assert test_action.bind_to_methods == ['get'] + assert test_action.detail is True + assert test_action.url_path == 'test_action' + assert test_action.url_name == 'test-action' + + def test_detail_required(self): + with pytest.raises(AssertionError) as excinfo: + @action() + def test_action(request): + pass + + assert str(excinfo.value) == "@action() missing required argument: 'detail'" + + def test_detail_route_deprecation(self): + with pytest.warns(PendingDeprecationWarning) as record: + @detail_route() + def view(request): + pass + + assert len(record) == 1 + assert str(record[0].message) == ( + "`detail_route` is pending deprecation and will be removed in " + "3.10 in favor of `action`, which accepts a `detail` bool. Use " + "`@action(detail=True)` instead." + ) + + def test_list_route_deprecation(self): + with pytest.warns(PendingDeprecationWarning) as record: + @list_route() + def view(request): + pass + + assert len(record) == 1 + assert str(record[0].message) == ( + "`list_route` is pending deprecation and will be removed in " + "3.10 in favor of `action`, which accepts a `detail` bool. Use " + "`@action(detail=False)` instead." + ) diff --git a/tests/test_routers.py b/tests/test_routers.py index 55ccc647b3..36255f48f4 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -11,7 +11,7 @@ from rest_framework import permissions, serializers, viewsets from rest_framework.compat import get_regex_pattern -from rest_framework.decorators import detail_route, list_route +from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.routers import DefaultRouter, SimpleRouter from rest_framework.test import APIRequestFactory, URLPatternsTestCase @@ -67,12 +67,12 @@ def get_object(self, *args, **kwargs): class RegexUrlPathViewSet(viewsets.ViewSet): - @list_route(url_path='list/(?P[0-9]{4})') + @action(detail=False, url_path='list/(?P[0-9]{4})') def regex_url_path_list(self, request, *args, **kwargs): kwarg = self.kwargs.get('kwarg', '') return Response({'kwarg': kwarg}) - @detail_route(url_path='detail/(?P[0-9]{4})') + @action(detail=True, url_path='detail/(?P[0-9]{4})') def regex_url_path_detail(self, request, *args, **kwargs): pk = self.kwargs.get('pk', '') kwarg = self.kwargs.get('kwarg', '') @@ -99,23 +99,23 @@ class BasicViewSet(viewsets.ViewSet): def list(self, request, *args, **kwargs): return Response({'method': 'list'}) - @detail_route(methods=['post']) + @action(methods=['post'], detail=True) def action1(self, request, *args, **kwargs): return Response({'method': 'action1'}) - @detail_route(methods=['post']) + @action(methods=['post'], detail=True) def action2(self, request, *args, **kwargs): return Response({'method': 'action2'}) - @detail_route(methods=['post', 'delete']) + @action(methods=['post', 'delete'], detail=True) def action3(self, request, *args, **kwargs): return Response({'method': 'action2'}) - @detail_route() + @action(detail=True) def link1(self, request, *args, **kwargs): return Response({'method': 'link1'}) - @detail_route() + @action(detail=True) def link2(self, request, *args, **kwargs): return Response({'method': 'link2'}) @@ -297,7 +297,7 @@ def setUp(self): class TestViewSet(viewsets.ModelViewSet): permission_classes = [] - @detail_route(methods=['post'], permission_classes=[permissions.AllowAny]) + @action(methods=['post'], detail=True, permission_classes=[permissions.AllowAny]) def custom(self, request, *args, **kwargs): return Response({ 'permission_classes': self.permission_classes @@ -315,14 +315,14 @@ def test_action_kwargs(self): class TestActionAppliedToExistingRoute(TestCase): """ - Ensure `@detail_route` decorator raises an except when applied + Ensure `@action` decorator raises an except when applied to an existing route """ def test_exception_raised_when_action_applied_to_existing_route(self): class TestViewSet(viewsets.ModelViewSet): - @detail_route(methods=['post']) + @action(methods=['post'], detail=True) def retrieve(self, request, *args, **kwargs): return Response({ 'hello': 'world' @@ -339,27 +339,27 @@ class DynamicListAndDetailViewSet(viewsets.ViewSet): def list(self, request, *args, **kwargs): return Response({'method': 'list'}) - @list_route(methods=['post']) + @action(methods=['post'], detail=False) def list_route_post(self, request, *args, **kwargs): return Response({'method': 'action1'}) - @detail_route(methods=['post']) + @action(methods=['post'], detail=True) def detail_route_post(self, request, *args, **kwargs): return Response({'method': 'action2'}) - @list_route() + @action(detail=False) def list_route_get(self, request, *args, **kwargs): return Response({'method': 'link1'}) - @detail_route() + @action(detail=True) def detail_route_get(self, request, *args, **kwargs): return Response({'method': 'link2'}) - @list_route(url_path="list_custom-route") + @action(detail=False, url_path="list_custom-route") def list_custom_route_get(self, request, *args, **kwargs): return Response({'method': 'link1'}) - @detail_route(url_path="detail_custom-route") + @action(detail=True, url_path="detail_custom-route") def detail_custom_route_get(self, request, *args, **kwargs): return Response({'method': 'link2'}) @@ -455,6 +455,12 @@ def test_suffix(self): assert initkwargs['suffix'] == 'List' + def test_detail(self): + match = resolve('/example/notes/') + initkwargs = match.func.initkwargs + + assert not initkwargs['detail'] + def test_basename(self): match = resolve('/example/notes/') initkwargs = match.func.initkwargs diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 34cb20798a..1cbee0695c 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -10,9 +10,7 @@ filters, generics, pagination, permissions, serializers ) from rest_framework.compat import coreapi, coreschema, get_regex_pattern, path -from rest_framework.decorators import ( - api_view, detail_route, list_route, schema -) +from rest_framework.decorators import action, api_view, schema from rest_framework.request import Request from rest_framework.routers import DefaultRouter, SimpleRouter from rest_framework.schemas import ( @@ -67,25 +65,25 @@ class ExampleViewSet(ModelViewSet): filter_backends = [filters.OrderingFilter] serializer_class = ExampleSerializer - @detail_route(methods=['post'], serializer_class=AnotherSerializer) + @action(methods=['post'], detail=True, serializer_class=AnotherSerializer) def custom_action(self, request, pk): """ A description of custom action. """ return super(ExampleSerializer, self).retrieve(self, request) - @detail_route(methods=['post'], serializer_class=AnotherSerializerWithListFields) + @action(methods=['post'], detail=True, serializer_class=AnotherSerializerWithListFields) def custom_action_with_list_fields(self, request, pk): """ A custom action using both list field and list serializer in the serializer. """ return super(ExampleSerializer, self).retrieve(self, request) - @list_route() + @action(detail=False) def custom_list_action(self, request): return super(ExampleViewSet, self).list(self, request) - @list_route(methods=['post', 'get'], serializer_class=EmptySerializer) + @action(methods=['post', 'get'], detail=False, serializer_class=EmptySerializer) def custom_list_action_multiple_methods(self, request): return super(ExampleViewSet, self).list(self, request) @@ -865,11 +863,11 @@ class NamingCollisionViewSet(GenericViewSet): """ permision_class = () - @list_route() + @action(detail=False) def detail(self, request): return {} - @list_route(url_path='detail/export') + @action(detail=False, url_path='detail/export') def detail_export(self, request): return {} @@ -949,7 +947,10 @@ def test_from_router(self): generator = SchemaGenerator(title='Naming Colisions', patterns=patterns) schema = generator.get_schema() - desc = schema['detail_0'].description # not important here + + # not important here + desc_0 = schema['detail']['detail_export'].description + desc_1 = schema['detail_0'].description expected = coreapi.Document( url='', @@ -959,12 +960,12 @@ def test_from_router(self): 'detail_export': coreapi.Link( url='/from-routercollision/detail/export/', action='https://github.com/get', - description=desc) + description=desc_0) }, 'detail_0': coreapi.Link( url='/from-routercollision/detail/', action='https://github.com/get', - description=desc + description=desc_1 ) } ) @@ -1046,7 +1047,7 @@ def options(self, request, *args, **kwargs): class AViewSet(ModelViewSet): - @detail_route(methods=['options', 'get']) + @action(methods=['options', 'get'], detail=True) def custom_action(self, request, pk): pass diff --git a/tests/test_viewsets.py b/tests/test_viewsets.py index beff42cb89..25feb0f372 100644 --- a/tests/test_viewsets.py +++ b/tests/test_viewsets.py @@ -3,7 +3,7 @@ from django.test import TestCase, override_settings from rest_framework import status -from rest_framework.decorators import detail_route, list_route +from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.routers import SimpleRouter from rest_framework.test import APIRequestFactory @@ -39,19 +39,19 @@ def list(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs): pass - @list_route() + @action(detail=False) def list_action(self, request, *args, **kwargs): pass - @list_route(url_name='list-custom') + @action(detail=False, url_name='list-custom') def custom_list_action(self, request, *args, **kwargs): pass - @detail_route() + @action(detail=True) def detail_action(self, request, *args, **kwargs): pass - @detail_route(url_name='detail-custom') + @action(detail=True, url_name='detail-custom') def custom_detail_action(self, request, *args, **kwargs): pass @@ -111,6 +111,16 @@ def test_args_kwargs_request_action_map_on_self(self): self.assertIn(attribute, dir(view)) +class GetExtraActionTests(TestCase): + + def test_extra_actions(self): + view = ActionViewSet() + actual = [action.__name__ for action in view.get_extra_actions()] + expected = ['custom_detail_action', 'custom_list_action', 'detail_action', 'list_action'] + + self.assertEqual(actual, expected) + + @override_settings(ROOT_URLCONF='tests.test_viewsets') class ReverseActionTests(TestCase): def test_default_basename(self): From 16645885007cf02b9085766185764ab4f95c8142 Mon Sep 17 00:00:00 2001 From: Aseem Shrey Date: Sun, 21 Jan 2018 20:22:21 +0530 Subject: [PATCH 091/585] Updated docs to use `pip show` Show the current DRF version using `pip show` Closes #5757 --- docs/topics/release-notes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 2c45b70afd..820fa731b3 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -32,9 +32,9 @@ To upgrade Django REST framework to the latest version, use pip: pip install -U djangorestframework -You can determine your currently installed version using `pip freeze`: +You can determine your currently installed version using `pip show`: - pip freeze | grep djangorestframework + pip show djangorestframework --- From 052a20cd7b9a8410a147d4ef2ab5291ab0eb1d04 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Thu, 25 Jan 2018 21:43:55 -0800 Subject: [PATCH 092/585] Load 'static' instead of 'staticfiles' in templates (#5773) --- docs/topics/api-clients.md | 2 +- rest_framework/templates/rest_framework/admin.html | 2 +- rest_framework/templates/rest_framework/base.html | 2 +- rest_framework/templates/rest_framework/docs/error.html | 2 +- rest_framework/templates/rest_framework/docs/index.html | 2 +- .../templates/rest_framework/docs/langs/javascript-intro.html | 2 +- rest_framework/templates/rest_framework/login_base.html | 1 - 7 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/topics/api-clients.md b/docs/topics/api-clients.md index 019b48b3dc..1de669b42c 100644 --- a/docs/topics/api-clients.md +++ b/docs/topics/api-clients.md @@ -395,7 +395,7 @@ Once the API documentation URLs are installed, you'll be able to include both th /static/rest_framework/js/coreapi-0.1.1.js /docs/schema.js --> - {% load staticfiles %} + {% load static %} diff --git a/rest_framework/templates/rest_framework/admin.html b/rest_framework/templates/rest_framework/admin.html index de011cd09a..0f588c2d73 100644 --- a/rest_framework/templates/rest_framework/admin.html +++ b/rest_framework/templates/rest_framework/admin.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} {% load i18n %} {% load rest_framework %} diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index 14007aa52e..5d4d258bef 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} {% load i18n %} {% load rest_framework %} diff --git a/rest_framework/templates/rest_framework/docs/error.html b/rest_framework/templates/rest_framework/docs/error.html index 8015bd7f02..1155e7ece5 100644 --- a/rest_framework/templates/rest_framework/docs/error.html +++ b/rest_framework/templates/rest_framework/docs/error.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} diff --git a/rest_framework/templates/rest_framework/docs/index.html b/rest_framework/templates/rest_framework/docs/index.html index de704ab518..8149103448 100644 --- a/rest_framework/templates/rest_framework/docs/index.html +++ b/rest_framework/templates/rest_framework/docs/index.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} diff --git a/rest_framework/templates/rest_framework/docs/langs/javascript-intro.html b/rest_framework/templates/rest_framework/docs/langs/javascript-intro.html index 8ef880cbc8..a6938fc45d 100644 --- a/rest_framework/templates/rest_framework/docs/langs/javascript-intro.html +++ b/rest_framework/templates/rest_framework/docs/langs/javascript-intro.html @@ -1,5 +1,5 @@ {% load rest_framework %} -{% load staticfiles %} +{% load static %}
    {% code html %}
     
     {% endcode %}
    diff --git a/rest_framework/templates/rest_framework/login_base.html b/rest_framework/templates/rest_framework/login_base.html index 631ad71bad..ba48917088 100644 --- a/rest_framework/templates/rest_framework/login_base.html +++ b/rest_framework/templates/rest_framework/login_base.html @@ -1,5 +1,4 @@ {% extends "rest_framework/base.html" %} -{% load staticfiles %} {% load rest_framework %} {% block body %} From 3e5d3752e77af14acc009743c8648d06f09246e6 Mon Sep 17 00:00:00 2001 From: Max Goodridge Date: Mon, 29 Jan 2018 07:41:55 +0000 Subject: [PATCH 093/585] Fixed a typo (#5783) --- docs/api-guide/fields.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 403c2b8655..78fb3f83eb 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -801,7 +801,7 @@ The [drf-compound-fields][drf-compound-fields] package provides "compound" seria The [drf-extra-fields][drf-extra-fields] package provides extra serializer fields for REST framework, including `Base64ImageField` and `PointField` classes. -## djangrestframework-recursive +## djangorestframework-recursive the [djangorestframework-recursive][djangorestframework-recursive] package provides a `RecursiveField` for serializing and deserializing recursive structures From 2677f59d5d1cb1b0170eabb516478180da8aa610 Mon Sep 17 00:00:00 2001 From: Matt Prahl Date: Mon, 29 Jan 2018 09:33:14 -0500 Subject: [PATCH 094/585] Refer to "NamespaceVersioning" instead of "NamespacedVersioning" in the documentation (#5754) --- docs/api-guide/versioning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/versioning.md b/docs/api-guide/versioning.md index 8e62c70f17..c106e536d2 100644 --- a/docs/api-guide/versioning.md +++ b/docs/api-guide/versioning.md @@ -37,7 +37,7 @@ The `reverse` function included by REST framework ties in with the versioning sc The above function will apply any URL transformations appropriate to the request version. For example: -* If `NamespacedVersioning` was being used, and the API version was 'v1', then the URL lookup used would be `'v1:bookings-list'`, which might resolve to a URL like `http://example.org/v1/bookings/`. +* If `NamespaceVersioning` was being used, and the API version was 'v1', then the URL lookup used would be `'v1:bookings-list'`, which might resolve to a URL like `http://example.org/v1/bookings/`. * If `QueryParameterVersioning` was being used, and the API version was `1.0`, then the returned URL might be something like `http://example.org/bookings/?version=1.0` #### Versioned APIs and hyperlinked serializers From 769bc1336fd5d6a7fcf10d8be3b374c3e7a21bb3 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 30 Jan 2018 08:45:09 +0100 Subject: [PATCH 095/585] ErrorDetail: add __eq__/__ne__ and __repr__ (#5787) This adds `__eq__` to handle `code` in comparisons. When comparing an ErrorDetail to a string (missing `code` there) the ErrorDetail's `code` is ignored, but otherwise it is taken into account. --- rest_framework/exceptions.py | 17 +++++++++++++++++ tests/test_exceptions.py | 27 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index d885ba6432..492872ae5b 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -14,6 +14,7 @@ from django.utils.translation import ungettext from rest_framework import status +from rest_framework.compat import unicode_to_repr from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList @@ -73,6 +74,22 @@ def __new__(cls, string, code=None): self.code = code return self + def __eq__(self, other): + r = super(ErrorDetail, self).__eq__(other) + try: + return r and self.code == other.code + except AttributeError: + return r + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return unicode_to_repr('ErrorDetail(string=%r, code=%r)' % ( + six.text_type(self), + self.code, + )) + class APIException(Exception): """ diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 176aeb1746..006191a491 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -53,6 +53,33 @@ def test_get_full_details_with_throttling(self): 'code': 'throttled'} +class ErrorDetailTests(TestCase): + + def test_eq(self): + assert ErrorDetail('msg') == ErrorDetail('msg') + assert ErrorDetail('msg', 'code') == ErrorDetail('msg', code='code') + + assert ErrorDetail('msg') == 'msg' + assert ErrorDetail('msg', 'code') == 'msg' + + def test_ne(self): + assert ErrorDetail('msg1') != ErrorDetail('msg2') + assert ErrorDetail('msg') != ErrorDetail('msg', code='invalid') + + assert ErrorDetail('msg1') != 'msg2' + assert ErrorDetail('msg1', 'code') != 'msg2' + + def test_repr(self): + assert repr(ErrorDetail('msg1')) == \ + 'ErrorDetail(string={!r}, code=None)'.format('msg1') + assert repr(ErrorDetail('msg1', 'code')) == \ + 'ErrorDetail(string={!r}, code={!r})'.format('msg1', 'code') + + def test_str(self): + assert str(ErrorDetail('msg1')) == 'msg1' + assert str(ErrorDetail('msg1', 'code')) == 'msg1' + + class TranslationTests(TestCase): @translation.override('fr') From ece4171ae40e0956c9d69e317fee1f96c427b113 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 30 Jan 2018 03:08:06 -0500 Subject: [PATCH 096/585] Replace `background-attachment: fixed` in docs (#5777) Fixed backgrounds have performance issues on large displays. --- docs_theme/css/default.css | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/docs_theme/css/default.css b/docs_theme/css/default.css index a0a286b226..bb17a3a115 100644 --- a/docs_theme/css/default.css +++ b/docs_theme/css/default.css @@ -160,9 +160,19 @@ body, .navbar .navbar-inner .container-fluid{ margin: 0 auto; } -body{ - background: url("https://github.com/img/grid.png") repeat-x; - background-attachment: fixed; +/* Replacement for `body { background-attachment: fixed; }`, which + has performance issues when scrolling on large displays. */ +body::before { + content: ' '; + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; + background-color: #f8f8f8; + background: url(https://github.com/img/grid.png) repeat-x; + will-change: transform; + z-index: -1; } From df77f7bb9d40f83d92848fd0afd5c61e281eeb48 Mon Sep 17 00:00:00 2001 From: Si Feng Date: Tue, 30 Jan 2018 14:10:02 -0800 Subject: [PATCH 097/585] Make 404 & 403 responses consistent with `exceptions.APIException` output (#5763) --- rest_framework/views.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/rest_framework/views.py b/rest_framework/views.py index f9ee7fb539..1f51517db3 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -8,10 +8,8 @@ from django.db import connection, models, transaction from django.http import Http404 from django.http.response import HttpResponseBase -from django.utils import six from django.utils.cache import cc_delim_re, patch_vary_headers from django.utils.encoding import smart_text -from django.utils.translation import ugettext_lazy as _ from django.views.decorators.csrf import csrf_exempt from django.views.generic import View @@ -70,6 +68,11 @@ def exception_handler(exc, context): Any unhandled exceptions may return `None`, which will cause a 500 error to be raised. """ + if isinstance(exc, Http404): + exc = exceptions.NotFound() + elif isinstance(exc, PermissionDenied): + exc = exceptions.PermissionDenied() + if isinstance(exc, exceptions.APIException): headers = {} if getattr(exc, 'auth_header', None): @@ -85,20 +88,6 @@ def exception_handler(exc, context): set_rollback() return Response(data, status=exc.status_code, headers=headers) - elif isinstance(exc, Http404): - msg = _('Not found.') - data = {'detail': six.text_type(msg)} - - set_rollback() - return Response(data, status=status.HTTP_404_NOT_FOUND) - - elif isinstance(exc, PermissionDenied): - msg = _('Permission denied.') - data = {'detail': six.text_type(msg)} - - set_rollback() - return Response(data, status=status.HTTP_403_FORBIDDEN) - return None From 2fa04caf7c2482f7e8bbf69052ad48af304da9a2 Mon Sep 17 00:00:00 2001 From: Veli-Matti Helke Date: Wed, 31 Jan 2018 15:25:57 +0200 Subject: [PATCH 098/585] small fix to API documentation: schemas (#5796) adding missing parameters to get_manual_fields() --- docs/api-guide/schemas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 3284c9e1b0..eb27998597 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -631,7 +631,7 @@ def get_manual_fields(self, path, method): if method=='POST': extra_fields = # ... list of extra fields for POST ... - manual_fields = super().get_manual_fields() + manual_fields = super().get_manual_fields(path, method) return manual_fields + extra_fields ``` From 3c7b3ac6df1deadee57ec9c01ffd58d804559389 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Thu, 1 Feb 2018 16:02:29 +0100 Subject: [PATCH 099/585] Updated step 1 of contributing guide (#5799) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create a fork, then clone it. * Link to GitHub’s How-To. --- docs/topics/contributing.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/topics/contributing.md b/docs/topics/contributing.md index 19976b00b7..9cc6ccee08 100644 --- a/docs/topics/contributing.md +++ b/docs/topics/contributing.md @@ -48,9 +48,15 @@ Getting involved in triaging incoming issues is a good way to start contributing # Development -To start developing on Django REST framework, clone the repo: +To start developing on Django REST framework, first create a Fork from the +[Django REST Framework repo][repo] on GitHub. - git clone git@github.com:encode/django-rest-framework.git +Then clone your fork. The clone command will look like this, with your GitHub +username instead of YOUR-USERNAME: + + git clone https://github.com/YOUR-USERNAME/Spoon-Knife + +See GitHub's [_Fork a Repo_][how-to-fork] Guide for more help. Changes should broadly follow the [PEP 8][pep-8] style conventions, and we recommend you set up your editor to automatically indicate non-conforming styles. @@ -210,3 +216,5 @@ If you want to draw attention to a note or warning, use a pair of enclosing line [markdown]: https://daringfireball.net/projects/markdown/basics [docs]: https://github.com/encode/django-rest-framework/tree/master/docs [mou]: http://mouapp.com/ +[repo]: https://github.com/encode/django-rest-framework +[how-to-fork]: https://help.github.com/articles/fork-a-repo/ From 27f32faee4dd96c5baee920d3a8f42ab4364782b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jeremy=20Lain=C3=A9?= Date: Thu, 1 Feb 2018 16:14:35 +0100 Subject: [PATCH 100/585] Fix schema generation for PrimaryKeyRelatedField (#5764) By default all subclasses of RelatedField are output as string fields in the schema, which works well for StringRelatedField, SlugRelatedField or HyperlinkedRelatedField. Handle the common case of a PrimaryKeyRelatedField pointing to an AutoField. --- rest_framework/schemas/inspectors.py | 8 +++++ tests/test_schemas.py | 47 +++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index 413b9c0ac8..8ef783683d 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -50,6 +50,14 @@ def field_to_schema(field): title=title, description=description ) + elif isinstance(field, serializers.PrimaryKeyRelatedField): + schema_cls = coreschema.String + model = getattr(field.queryset, 'model', None) + if model is not None: + model_field = model._meta.pk + if isinstance(model_field, models.AutoField): + schema_cls = coreschema.Integer + return schema_cls(title=title, description=description) elif isinstance(field, serializers.RelatedField): return coreschema.String(title=title, description=description) elif isinstance(field, serializers.MultipleChoiceField): diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 1cbee0695c..1abe5d2a16 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -23,7 +23,7 @@ from rest_framework.views import APIView from rest_framework.viewsets import GenericViewSet, ModelViewSet -from .models import BasicModel +from .models import BasicModel, ForeignKeySource factory = APIRequestFactory() @@ -556,6 +556,51 @@ def test_schema_for_regular_views(self): assert schema == expected +class ForeignKeySourceSerializer(serializers.ModelSerializer): + class Meta: + model = ForeignKeySource + fields = ('id', 'name', 'target') + + +class ForeignKeySourceView(generics.CreateAPIView): + queryset = ForeignKeySource.objects.all() + serializer_class = ForeignKeySourceSerializer + + +@unittest.skipUnless(coreapi, 'coreapi is not installed') +class TestSchemaGeneratorWithForeignKey(TestCase): + def setUp(self): + self.patterns = [ + url(https://github.com/r'^example/?$%27,%20ForeignKeySourceView.as_view()), + ] + + def test_schema_for_regular_views(self): + """ + Ensure that AutoField foreign keys are output as Integer. + """ + generator = SchemaGenerator(title='Example API', patterns=self.patterns) + schema = generator.get_schema() + + expected = coreapi.Document( + url='', + title='Example API', + content={ + 'example': { + 'create': coreapi.Link( + url='/example/', + action='https://github.com/post', + encoding='application/json', + fields=[ + coreapi.Field('name', required=True, location='form', schema=coreschema.String(title='Name')), + coreapi.Field('target', required=True, location='form', schema=coreschema.Integer(description='Target', title='Target')), + ] + ) + } + } + ) + assert schema == expected + + @unittest.skipUnless(coreapi, 'coreapi is not installed') class Test4605Regression(TestCase): def test_4605_regression(self): From a8d129b7da847ab12a1ecaeefa54c0d6d330184e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jeremy=20Lain=C3=A9?= Date: Tue, 23 Jan 2018 21:06:24 +0100 Subject: [PATCH 101/585] Represent serializer DictField as an Object in schema DictFields were incorrectly being output as String in the schema. This pull request outputs an Object instead and adds a unit test. Update s/detail_route/action/ after rebase --- rest_framework/schemas/inspectors.py | 5 +++++ tests/test_schemas.py | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index 8ef783683d..86c6cb71a9 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -34,6 +34,11 @@ def field_to_schema(field): title=title, description=description ) + elif isinstance(field, serializers.DictField): + return coreschema.Object( + title=title, + description=description + ) elif isinstance(field, serializers.Serializer): return coreschema.Object( properties=OrderedDict([ diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 1abe5d2a16..267c445850 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -49,6 +49,10 @@ class ExampleSerializer(serializers.Serializer): hidden = serializers.HiddenField(default='hello') +class AnotherSerializerWithDictField(serializers.Serializer): + a = serializers.DictField() + + class AnotherSerializerWithListFields(serializers.Serializer): a = serializers.ListField(child=serializers.IntegerField()) b = serializers.ListSerializer(child=serializers.CharField()) @@ -72,6 +76,13 @@ def custom_action(self, request, pk): """ return super(ExampleSerializer, self).retrieve(self, request) + @action(methods=['post'], detail=True, serializer_class=AnotherSerializerWithDictField) + def custom_action_with_dict_field(self, request, pk): + """ + A custom action using a dict field in the serializer. + """ + return super(ExampleSerializer, self).retrieve(self, request) + @action(methods=['post'], detail=True, serializer_class=AnotherSerializerWithListFields) def custom_action_with_list_fields(self, request, pk): """ @@ -198,6 +209,16 @@ def test_authenticated_request(self): coreapi.Field('d', required=False, location='form', schema=coreschema.String(title='D')), ] ), + 'custom_action_with_dict_field': coreapi.Link( + url='/example/{id}/custom_action_with_dict_field/', + action='https://github.com/post', + encoding='application/json', + description='A custom action using a dict field in the serializer.', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()), + coreapi.Field('a', required=True, location='form', schema=coreschema.Object(title='A')), + ] + ), 'custom_action_with_list_fields': coreapi.Link( url='/example/{id}/custom_action_with_list_fields/', action='https://github.com/post', From 878fe895dcd35297be798083a704ee74dd8709ba Mon Sep 17 00:00:00 2001 From: "Fraire, Santiago" Date: Fri, 5 Jan 2018 13:57:52 +0100 Subject: [PATCH 102/585] Docs: Added example reimplementing ObtainAuthToken Closes #5802 --- docs/api-guide/authentication.md | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 0704118bdc..e33023c40d 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -205,11 +205,39 @@ The `obtain_auth_token` view will return a JSON response when valid `username` a { 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' } -Note that the default `obtain_auth_token` view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. If you need a customized version of the `obtain_auth_token` view, you can do so by overriding the `ObtainAuthToken` view class, and using that in your url conf instead. +Note that the default `obtain_auth_token` view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. By default there are no permissions or throttling applied to the `obtain_auth_token` view. If you do wish to apply throttling you'll need to override the view class, and include them using the `throttle_classes` attribute. +If you need a customized version of the `obtain_auth_token` view, you can do so by subclassing the `ObtainAuthToken` view class, and using that in your url conf instead. + +For example, you may return additional user information beyond the `token` value: + + from rest_framework.authtoken.views import ObtainAuthToken + from rest_framework.authtoken.models import Token + from rest_framework.response import Response + + class CustomAuthToken(ObtainAuthToken): + + def post(self, request, *args, **kwargs): + serializer = self.serializer_class(data=request.data, + context={'request': request}) + serializer.is_valid(raise_exception=True) + user = serializer.validated_data['user'] + token, created = Token.objects.get_or_create(user=user) + return Response({ + 'token': token.key, + 'user_id': user.pk, + 'email': user.email + }) + +And in your `urls.py`: + + urlpatterns += [ + url(https://github.com/r'%5Eapi-token-auth/',%20CustomAuthToken.as_view()) + ] + ##### With Django admin From 0d5a3a00b0465a27ecfa6947478cbd57ac2432f2 Mon Sep 17 00:00:00 2001 From: Paulo Scardine Date: Sun, 17 Dec 2017 02:11:15 -0200 Subject: [PATCH 103/585] Add schema to ObtainAuthToken Add encoding parameter to ManualSchema Closes #5676 * Fixed lint errors * Added docs for ManualSchema encoding parameter --- docs/api-guide/schemas.md | 2 ++ rest_framework/authtoken/views.py | 26 ++++++++++++++++++++++++++ rest_framework/schemas/inspectors.py | 5 +++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index eb27998597..aaefe3db8f 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -669,6 +669,8 @@ The `ManualSchema` constructor takes two arguments: **`description`**: A string description. Optional. +**`encoding`**: Default `None`. A string encoding, e.g `application/json`. Optional. + --- ## Core API diff --git a/rest_framework/authtoken/views.py b/rest_framework/authtoken/views.py index 6254d2f7f9..6eed3782eb 100644 --- a/rest_framework/authtoken/views.py +++ b/rest_framework/authtoken/views.py @@ -3,6 +3,9 @@ from rest_framework.authtoken.serializers import AuthTokenSerializer from rest_framework.response import Response from rest_framework.views import APIView +from rest_framework.schemas import ManualSchema +import coreapi +import coreschema class ObtainAuthToken(APIView): @@ -11,6 +14,29 @@ class ObtainAuthToken(APIView): parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,) renderer_classes = (renderers.JSONRenderer,) serializer_class = AuthTokenSerializer + schema = ManualSchema( + fields=[ + coreapi.Field( + name="username", + required=True, + location='form', + schema=coreschema.String( + title="Username", + description="Valid username for authentication", + ), + ), + coreapi.Field( + name="password", + required=True, + location='form', + schema=coreschema.String( + title="Password", + description="Valid password for authentication", + ), + ), + ], + encoding="application/json", + ) def post(self, request, *args, **kwargs): serializer = self.serializer_class(data=request.data, diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index 86c6cb71a9..171b88b0b3 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -445,7 +445,7 @@ class ManualSchema(ViewInspector): Allows providing a list of coreapi.Fields, plus an optional description. """ - def __init__(self, fields, description=''): + def __init__(self, fields, description='', encoding=None): """ Parameters: @@ -455,6 +455,7 @@ def __init__(self, fields, description=''): assert all(isinstance(f, coreapi.Field) for f in fields), "`fields` must be a list of coreapi.Field instances" self._fields = fields self._description = description + self._encoding = encoding def get_link(self, path, method, base_url): @@ -464,7 +465,7 @@ def get_link(self, path, method, base_url): return coreapi.Link( url=urlparse.urljoin(base_url, path), action=method.lower(), - encoding=None, + encoding=self._encoding, fields=self._fields, description=self._description ) From c456b3c510870ed8e0117cb69abc2360a7aa0fca Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 5 Feb 2018 10:24:13 -0500 Subject: [PATCH 104/585] Fix request formdata handling (#5800) * Rename 'wsgi' request test to more accurate 'http' * Test duplicate request stream parsing * Fix setting post/files on the underlying request --- rest_framework/request.py | 9 +++--- tests/test_request.py | 63 ++++++++++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/rest_framework/request.py b/rest_framework/request.py index 7e4daf2749..9d4f73d30e 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -278,10 +278,11 @@ def _load_data_and_files(self): else: self._full_data = self._data - # copy data & files refs to the underlying request so that closable - # objects are handled appropriately. - self._request._post = self.POST - self._request._files = self.FILES + # if a form media type, copy data & files refs to the underlying + # http request so that closable objects are handled appropriately. + if is_form_media_type(self.content_type): + self._request._post = self.POST + self._request._files = self.FILES def _load_stream(self): """ diff --git a/tests/test_request.py b/tests/test_request.py index 8c680baa0e..83d295a128 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -13,6 +13,7 @@ from django.contrib.auth.models import User from django.contrib.sessions.middleware import SessionMiddleware from django.core.files.uploadedfile import SimpleUploadedFile +from django.http.request import RawPostDataException from django.test import TestCase, override_settings from django.utils import six @@ -137,6 +138,11 @@ def post(self, request): return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR) +class EchoView(APIView): + def post(self, request): + return Response(status=status.HTTP_200_OK, data=request.data) + + class FileUploadView(APIView): def post(self, request): filenames = [file.temporary_file_path() for file in request.FILES.values()] @@ -149,6 +155,7 @@ def post(self, request): urlpatterns = [ url(https://github.com/r'%5E),%20MockView.as_view()), + url(https://github.com/r'%5Eecho/),%20EchoView.as_view()), url(https://github.com/r'%5Eupload/),%20FileUploadView.as_view()) ] @@ -271,24 +278,64 @@ def test_default_secure_true(self): assert request.scheme == 'https' -class TestWSGIRequestProxy(TestCase): - def test_attribute_access(self): - wsgi_request = factory.get('/') - request = Request(wsgi_request) +class TestHttpRequest(TestCase): + def test_attribute_access_proxy(self): + http_request = factory.get('/') + request = Request(http_request) inner_sentinel = object() - wsgi_request.inner_property = inner_sentinel + http_request.inner_property = inner_sentinel assert request.inner_property is inner_sentinel outer_sentinel = object() request.inner_property = outer_sentinel assert request.inner_property is outer_sentinel - def test_exception(self): + def test_exception_proxy(self): # ensure the exception message is not for the underlying WSGIRequest - wsgi_request = factory.get('/') - request = Request(wsgi_request) + http_request = factory.get('/') + request = Request(http_request) message = "'Request' object has no attribute 'inner_property'" with self.assertRaisesMessage(AttributeError, message): request.inner_property + + @override_settings(ROOT_URLCONF='tests.test_request') + def test_duplicate_request_stream_parsing_exception(self): + """ + Check assumption that duplicate stream parsing will result in a + `RawPostDataException` being raised. + """ + response = APIClient().post('/echo/', data={'a': 'b'}, format='json') + request = response.renderer_context['request'] + + # ensure that request stream was consumed by json parser + assert request.content_type.startswith('application/json') + assert response.data == {'a': 'b'} + + # pass same HttpRequest to view, stream already consumed + with pytest.raises(RawPostDataException): + EchoView.as_view()(request._request) + + @override_settings(ROOT_URLCONF='tests.test_request') + def test_duplicate_request_form_data_access(self): + """ + Form data is copied to the underlying django request for middleware + and file closing reasons. Duplicate processing of a request with form + data is 'safe' in so far as accessing `request.POST` does not trigger + the duplicate stream parse exception. + """ + response = APIClient().post('/echo/', data={'a': 'b'}) + request = response.renderer_context['request'] + + # ensure that request stream was consumed by form parser + assert request.content_type.startswith('multipart/form-data') + assert response.data == {'a': ['b']} + + # pass same HttpRequest to view, form data set on underlying request + response = EchoView.as_view()(request._request) + request = response.renderer_context['request'] + + # ensure that request stream was consumed by form parser + assert request.content_type.startswith('multipart/form-data') + assert response.data == {'a': ['b']} From 1bc826e6fd1c5fc70a3f9af2e89bb0a575e6ce7b Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 7 Feb 2018 14:46:17 -0500 Subject: [PATCH 105/585] Fix authtoken views imports (#5818) --- rest_framework/authtoken/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rest_framework/authtoken/views.py b/rest_framework/authtoken/views.py index 6eed3782eb..ff53c01baf 100644 --- a/rest_framework/authtoken/views.py +++ b/rest_framework/authtoken/views.py @@ -1,11 +1,11 @@ +import coreapi +import coreschema from rest_framework import parsers, renderers from rest_framework.authtoken.models import Token from rest_framework.authtoken.serializers import AuthTokenSerializer from rest_framework.response import Response -from rest_framework.views import APIView from rest_framework.schemas import ManualSchema -import coreapi -import coreschema +from rest_framework.views import APIView class ObtainAuthToken(APIView): From 1438719979c92e9c0895de6f29d557df774eccb3 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Thu, 8 Feb 2018 09:00:44 +0100 Subject: [PATCH 106/585] requirements-testing: update pytest to 3.4.0 (#5815) --- requirements/requirements-testing.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/requirements-testing.txt b/requirements/requirements-testing.txt index 72ce56d26a..e0db14d44a 100644 --- a/requirements/requirements-testing.txt +++ b/requirements/requirements-testing.txt @@ -1,4 +1,4 @@ -# PyTest for running the tests. -pytest==3.2.5 +# Pytest for running the tests. +pytest==3.4.0 pytest-django==3.1.2 pytest-cov==2.5.1 From d1c92c81ff86fb8c6e9ccf2b394852595ff52141 Mon Sep 17 00:00:00 2001 From: Allisson Azevedo Date: Thu, 8 Feb 2018 05:04:51 -0300 Subject: [PATCH 107/585] Add Django Rest Framework Role Filters to Third party packages (#5809) --- docs/api-guide/permissions.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 72cbeab916..f5fc214cd6 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -269,6 +269,10 @@ The [Django Rest Framework Roles][django-rest-framework-roles] package makes it The [Django Rest Framework API Key][django-rest-framework-api-key] package allows you to ensure that every request made to the server requires an API key header. You can generate one from the django admin interface. +## Django Rest Framework Role Filters + +The [Django Rest Framework Role Filters][django-rest-framework-role-filters] package provides simple filtering over multiple types of roles. + [cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html [authentication]: authentication.md [throttling]: throttling.md @@ -282,3 +286,4 @@ The [Django Rest Framework API Key][django-rest-framework-api-key] package allow [dry-rest-permissions]: https://github.com/Helioscene/dry-rest-permissions [django-rest-framework-roles]: https://github.com/computer-lab/django-rest-framework-roles [django-rest-framework-api-key]: https://github.com/manosim/django-rest-framework-api-key +[django-rest-framework-role-filters]: https://github.com/allisson/django-rest-framework-role-filters From 7d0d22ffaabfcfa57f49fcf086667faf7c2509c5 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Mon, 12 Feb 2018 15:14:44 +0100 Subject: [PATCH 108/585] Use single copy of static assets. Update jQuery (#5823) * Move font-awesome to top level. * Use top-level jQuery & Bootstrap * Update to jQuery v3.3.1 Compatible with Bootstrap v3.3.7 c.f. https://github.com/twbs/bootstrap/issues/16834#issuecomment-251996660 * Re-add bootstrap-theme --- .../{docs => }/css/bootstrap-theme.min.css | 0 .../{docs => }/css/font-awesome-4.0.3.css | 0 .../rest_framework/docs/css/bootstrap.min.css | 6 - .../fonts/glyphicons-halflings-regular.eot | Bin 20127 -> 0 bytes .../fonts/glyphicons-halflings-regular.svg | 288 ------------------ .../fonts/glyphicons-halflings-regular.ttf | Bin 45404 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 23424 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff2 | Bin 18028 -> 0 bytes .../rest_framework/docs/js/bootstrap.min.js | 7 - .../docs/js/jquery-1.10.2.min.js | 6 - .../{docs => }/fonts/fontawesome-webfont.eot | Bin .../{docs => }/fonts/fontawesome-webfont.svg | 0 .../{docs => }/fonts/fontawesome-webfont.ttf | Bin .../{docs => }/fonts/fontawesome-webfont.woff | Bin .../rest_framework/js/jquery-1.12.4.min.js | 5 - .../rest_framework/js/jquery-3.3.1.min.js | 2 + .../templates/rest_framework/admin.html | 2 +- .../templates/rest_framework/base.html | 2 +- .../templates/rest_framework/docs/error.html | 2 +- .../templates/rest_framework/docs/index.html | 10 +- 20 files changed, 10 insertions(+), 320 deletions(-) rename rest_framework/static/rest_framework/{docs => }/css/bootstrap-theme.min.css (100%) rename rest_framework/static/rest_framework/{docs => }/css/font-awesome-4.0.3.css (100%) delete mode 100644 rest_framework/static/rest_framework/docs/css/bootstrap.min.css delete mode 100644 rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.eot delete mode 100644 rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.svg delete mode 100644 rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.ttf delete mode 100644 rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.woff delete mode 100644 rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.woff2 delete mode 100644 rest_framework/static/rest_framework/docs/js/bootstrap.min.js delete mode 100644 rest_framework/static/rest_framework/docs/js/jquery-1.10.2.min.js rename rest_framework/static/rest_framework/{docs => }/fonts/fontawesome-webfont.eot (100%) rename rest_framework/static/rest_framework/{docs => }/fonts/fontawesome-webfont.svg (100%) rename rest_framework/static/rest_framework/{docs => }/fonts/fontawesome-webfont.ttf (100%) rename rest_framework/static/rest_framework/{docs => }/fonts/fontawesome-webfont.woff (100%) delete mode 100644 rest_framework/static/rest_framework/js/jquery-1.12.4.min.js create mode 100644 rest_framework/static/rest_framework/js/jquery-3.3.1.min.js diff --git a/rest_framework/static/rest_framework/docs/css/bootstrap-theme.min.css b/rest_framework/static/rest_framework/css/bootstrap-theme.min.css similarity index 100% rename from rest_framework/static/rest_framework/docs/css/bootstrap-theme.min.css rename to rest_framework/static/rest_framework/css/bootstrap-theme.min.css diff --git a/rest_framework/static/rest_framework/docs/css/font-awesome-4.0.3.css b/rest_framework/static/rest_framework/css/font-awesome-4.0.3.css similarity index 100% rename from rest_framework/static/rest_framework/docs/css/font-awesome-4.0.3.css rename to rest_framework/static/rest_framework/css/font-awesome-4.0.3.css diff --git a/rest_framework/static/rest_framework/docs/css/bootstrap.min.css b/rest_framework/static/rest_framework/docs/css/bootstrap.min.css deleted file mode 100644 index 82113bd8a0..0000000000 --- a/rest_framework/static/rest_framework/docs/css/bootstrap.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v3.3.7 (http://getbootstrap.com) - * Copyright 2011-2016 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(https://github.com/fonts/glyphicons-halflings-regular.eot);src:url(https://github.com/fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(https://github.com/fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(https://github.com/fonts/glyphicons-halflings-regular.woff) format('woff'),url(https://github.com/fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(https://github.com/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} -/*# sourceMappingURL=bootstrap.min.css.map */ diff --git a/rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.eot b/rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.eot deleted file mode 100644 index b93a4953fff68df523aa7656497ee339d6026d64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20127 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.ttf b/rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.ttf deleted file mode 100644 index 1413fc609ab6f21774de0cb7e01360095584f65b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45404 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 G`u_lkUM?>H diff --git a/rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.woff2 b/rest_framework/static/rest_framework/docs/fonts/glyphicons-halflings-regular.woff2 deleted file mode 100644 index 64539b54c3751a6d9adb44c8e3a45ba5a73b77f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18028 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 diff --git a/rest_framework/static/rest_framework/docs/js/bootstrap.min.js b/rest_framework/static/rest_framework/docs/js/bootstrap.min.js deleted file mode 100644 index be9574d70b..0000000000 --- a/rest_framework/static/rest_framework/docs/js/bootstrap.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v3.3.7 (http://getbootstrap.com) - * Copyright 2011-2016 Twitter, Inc. - * Licensed under the MIT license - */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
    ',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); diff --git a/rest_framework/static/rest_framework/docs/js/jquery-1.10.2.min.js b/rest_framework/static/rest_framework/docs/js/jquery-1.10.2.min.js deleted file mode 100644 index da4170647d..0000000000 --- a/rest_framework/static/rest_framework/docs/js/jquery-1.10.2.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license -//@ sourceMappingURL=jquery-1.10.2.min.map -*/ -(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="
    ",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
    a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
    t
    ",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
    ",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t -}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); -u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("