diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index fe9610ce3..48b6e7202 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -22,7 +22,7 @@ jobs:
- '3.11'
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 83fe0b714..c37da7449 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v3.4.0
+ rev: v4.5.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
@@ -9,12 +9,19 @@ repos:
- id: check-symlinks
- id: check-toml
- repo: https://github.com/pycqa/isort
- rev: 5.12.0
+ rev: 5.13.2
hooks:
- id: isort
- repo: https://github.com/PyCQA/flake8
- rev: 3.9.0
+ rev: 7.0.0
hooks:
- id: flake8
additional_dependencies:
- flake8-tidy-imports
+- repo: https://github.com/adamchainz/blacken-docs
+ rev: 1.16.0
+ hooks:
+ - id: blacken-docs
+ exclude: ^(?!docs).*$
+ additional_dependencies:
+ - black==23.1.0
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d567d45a8..fb01f8bf7 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,6 +1,6 @@
# Contributing to REST framework
-At this point in it's lifespan we consider Django REST framework to be essentially feature-complete. We may accept pull requests that track the continued development of Django versions, but would prefer not to accept new features or code formatting changes.
+At this point in its lifespan we consider Django REST framework to be essentially feature-complete. We may accept pull requests that track the continued development of Django versions, but would prefer not to accept new features or code formatting changes.
Apart from minor documentation changes, the [GitHub discussions page](https://github.com/encode/django-rest-framework/discussions) should generally be your starting point. Please only raise an issue or pull request if you've been recommended to do so after discussion.
diff --git a/README.md b/README.md
index c2975a418..078ac0711 100644
--- a/README.md
+++ b/README.md
@@ -27,8 +27,9 @@ The initial aim is to provide a single full-time position on REST framework.
[![][posthog-img]][posthog-url]
[![][cryptapi-img]][cryptapi-url]
[![][fezto-img]][fezto-url]
+[![][svix-img]][svix-url]
-Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Spacinov][spacinov-url], [Retool][retool-url], [bit.io][bitio-url], [PostHog][posthog-url], [CryptAPI][cryptapi-url], and [FEZTO][fezto-url].
+Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Spacinov][spacinov-url], [Retool][retool-url], [bit.io][bitio-url], [PostHog][posthog-url], [CryptAPI][cryptapi-url], [FEZTO][fezto-url], and [Svix][svix-url].
---
@@ -38,7 +39,7 @@ Django REST framework is a powerful and flexible toolkit for building Web APIs.
Some reasons you might want to use REST framework:
-* The [Web browsable API][sandbox] is a huge usability win for your developers.
+* The Web browsable API is a huge usability win for your developers.
* [Authentication policies][authentication] including optional packages for [OAuth1a][oauth1-section] and [OAuth2][oauth2-section].
* [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources.
* Customizable all the way down - just use [regular function-based views][functionview-section] if you don't need the [more][generic-views] [powerful][viewsets] [features][routers].
@@ -200,6 +201,7 @@ Please see the [security policy][security-policy].
[posthog-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/posthog-readme.png
[cryptapi-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/cryptapi-readme.png
[fezto-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/fezto-readme.png
+[svix-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/svix-premium.png
[sentry-url]: https://getsentry.com/welcome/
[stream-url]: https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage
@@ -209,6 +211,7 @@ Please see the [security policy][security-policy].
[posthog-url]: https://posthog.com?utm_source=drf&utm_medium=sponsorship&utm_campaign=open-source-sponsorship
[cryptapi-url]: https://cryptapi.io
[fezto-url]: https://www.fezto.xyz/?utm_source=DjangoRESTFramework
+[svix-url]: https://www.svix.com/?utm_source=django-REST&utm_medium=sponsorship
[oauth1-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth
[oauth2-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit
diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md
index dc55a1e8f..d6e6293fd 100644
--- a/docs/api-guide/authentication.md
+++ b/docs/api-guide/authentication.md
@@ -454,7 +454,7 @@ More information can be found in the [Documentation](https://django-rest-durin.r
[basicauth]: https://tools.ietf.org/html/rfc2617
[permission]: permissions.md
[throttling]: throttling.md
-[csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax
+[csrf-ajax]: https://docs.djangoproject.com/en/stable/howto/csrf/#using-csrf-protection-with-ajax
[mod_wsgi_official]: https://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIPassAuthorization.html
[django-oauth-toolkit-getting-started]: https://django-oauth-toolkit.readthedocs.io/en/latest/rest-framework/getting_started.html
[django-rest-framework-oauth]: https://jpadilla.github.io/django-rest-framework-oauth/
diff --git a/docs/api-guide/caching.md b/docs/api-guide/caching.md
index ab4f82cd2..503acb09e 100644
--- a/docs/api-guide/caching.md
+++ b/docs/api-guide/caching.md
@@ -28,33 +28,33 @@ from rest_framework import viewsets
class UserViewSet(viewsets.ViewSet):
# With cookie: cache requested url for each user for 2 hours
- @method_decorator(cache_page(60*60*2))
+ @method_decorator(cache_page(60 * 60 * 2))
@method_decorator(vary_on_cookie)
def list(self, request, format=None):
content = {
- 'user_feed': request.user.get_user_feed()
+ "user_feed": request.user.get_user_feed(),
}
return Response(content)
class ProfileView(APIView):
# With auth: cache requested url for each user for 2 hours
- @method_decorator(cache_page(60*60*2))
- @method_decorator(vary_on_headers("Authorization",))
+ @method_decorator(cache_page(60 * 60 * 2))
+ @method_decorator(vary_on_headers("Authorization"))
def get(self, request, format=None):
content = {
- 'user_feed': request.user.get_user_feed()
+ "user_feed": request.user.get_user_feed(),
}
return Response(content)
class PostView(APIView):
# Cache page for the requested url
- @method_decorator(cache_page(60*60*2))
+ @method_decorator(cache_page(60 * 60 * 2))
def get(self, request, format=None):
content = {
- 'title': 'Post title',
- 'body': 'Post content'
+ "title": "Post title",
+ "body": "Post content",
}
return Response(content)
```
diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md
index 0766fe3ef..ea944e1ff 100644
--- a/docs/api-guide/fields.md
+++ b/docs/api-guide/fields.md
@@ -303,7 +303,7 @@ Corresponds to `django.db.models.fields.DecimalField`.
* `min_value` Validate that the number provided is no less than this value.
* `localize` Set to `True` to enable localization of input and output based on the current locale. This will also force `coerce_to_string` to `True`. Defaults to `False`. Note that data formatting is enabled if you have set `USE_L10N=True` in your settings file.
* `rounding` Sets the rounding mode used when quantizing to the configured precision. Valid values are [`decimal` module rounding modes][python-decimal-rounding-modes]. Defaults to `None`.
-* `normalize_output` Will normalize the decimal value when serialized. This will strip all trailing zeroes and change the value's precision to the minimum required precision to be able to represent the value without loosing data. Defaults to `False`.
+* `normalize_output` Will normalize the decimal value when serialized. This will strip all trailing zeroes and change the value's precision to the minimum required precision to be able to represent the value without losing data. Defaults to `False`.
#### Example usage
diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md
index 3f0b15530..7af98dbf5 100644
--- a/docs/api-guide/schemas.md
+++ b/docs/api-guide/schemas.md
@@ -94,11 +94,13 @@ urlpatterns = [
# Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
# * `title` and `description` parameters are passed to `SchemaGenerator`.
# * Provide view name for use with `reverse()`.
- path('openapi', get_schema_view(
- title="Your Project",
- description="API for all things …",
- version="1.0.0"
- ), name='openapi-schema'),
+ path(
+ "openapi",
+ get_schema_view(
+ title="Your Project", description="API for all things …", version="1.0.0"
+ ),
+ name="openapi-schema",
+ ),
# ...
]
```
@@ -259,11 +261,13 @@ class CustomSchema(AutoSchema):
"""
AutoSchema subclass using schema_extra_info on the view.
"""
+
...
+
class CustomView(APIView):
schema = CustomSchema()
- schema_extra_info = ... some extra info ...
+ schema_extra_info = ... # some extra info
```
Here, the `AutoSchema` subclass goes looking for `schema_extra_info` on the
@@ -278,10 +282,13 @@ class BaseSchema(AutoSchema):
"""
AutoSchema subclass that knows how to use extra_info.
"""
+
...
+
class CustomSchema(BaseSchema):
- extra_info = ... some extra info ...
+ extra_info = ... # some extra info
+
class CustomView(APIView):
schema = CustomSchema()
@@ -302,10 +309,9 @@ class CustomSchema(BaseSchema):
self.extra_info = kwargs.pop("extra_info")
super().__init__(**kwargs)
+
class CustomView(APIView):
- schema = CustomSchema(
- extra_info=... some extra info ...
- )
+ schema = CustomSchema(extra_info=...) # some extra info
```
This saves you having to create a custom subclass per-view for a commonly used option.
diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md
index d42000260..47e2ce993 100644
--- a/docs/api-guide/settings.md
+++ b/docs/api-guide/settings.md
@@ -163,6 +163,12 @@ The string that should used for any versioning parameters, such as in the media
Default: `'version'`
+#### DEFAULT_VERSIONING_CLASS
+
+The default versioning scheme to use.
+
+Default: `None`
+
---
## Authentication settings
diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md
index 2a93e4cdc..e181d4c61 100644
--- a/docs/api-guide/validators.md
+++ b/docs/api-guide/validators.md
@@ -173,11 +173,9 @@ If you want the date field to be entirely hidden from the user, then use `Hidden
# Advanced field defaults
Validators that are applied across multiple fields in the serializer can sometimes require a field input that should not be provided by the API client, but that *is* available as input to the validator.
+For this purposes use `HiddenField`. This field will be present in `validated_data` but *will not* be used in the serializer output representation.
-Two patterns that you may want to use for this sort of validation include:
-
-* Using `HiddenField`. This field will be present in `validated_data` but *will not* be used in the serializer output representation.
-* Using a standard field with `read_only=True`, but that also includes a `default=…` argument. This field *will* be used in the serializer output representation, but cannot be set directly by the user.
+**Note:** Using a `read_only=True` field is excluded from writable fields so it won't use a `default=…` argument. Look [3.8 announcement](https://www.django-rest-framework.org/community/3.8-announcement/#altered-the-behaviour-of-read_only-plus-default-on-field).
REST framework includes a couple of defaults that may be useful in this context.
@@ -189,7 +187,7 @@ A default class that can be used to represent the current user. In order to use
default=serializers.CurrentUserDefault()
)
-#### CreateOnlyDefault
+#### CreateOnlyDefault
A default class that can be used to *only set a default argument during create operations*. During updates the field is omitted.
diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md
index 39db18bca..43007e95d 100644
--- a/docs/api-guide/viewsets.md
+++ b/docs/api-guide/viewsets.md
@@ -201,15 +201,16 @@ To view all extra actions, call the `.get_extra_actions()` method.
Extra actions can map additional HTTP methods to separate `ViewSet` methods. For example, the above password set/unset methods could be consolidated into a single route. Note that additional mappings do not accept arguments.
```python
- @action(detail=True, methods=['put'], name='Change Password')
- def password(self, request, pk=None):
- """Update the user's password."""
- ...
+@action(detail=True, methods=["put"], name="Change Password")
+def password(self, request, pk=None):
+ """Update the user's password."""
+ ...
- @password.mapping.delete
- def delete_password(self, request, pk=None):
- """Delete the user's password."""
- ...
+
+@password.mapping.delete
+def delete_password(self, request, pk=None):
+ """Delete the user's password."""
+ ...
```
## Reversing action URLs
@@ -220,14 +221,14 @@ Note that the `basename` is provided by the router during `ViewSet` registration
Using the example from the previous section:
-```python
->>> view.reverse_action('set-password', args=['1'])
+```pycon
+>>> view.reverse_action("set-password", args=["1"])
'http://localhost:8000/api/users/1/set_password'
```
Alternatively, you can use the `url_name` attribute set by the `@action` decorator.
-```python
+```pycon
>>> view.reverse_action(view.set_password.url_name, args=['1'])
'http://localhost:8000/api/users/1/set_password'
```
@@ -310,7 +311,7 @@ You may need to provide custom `ViewSet` classes that do not have the full set o
To create a base viewset class that provides `create`, `list` and `retrieve` operations, inherit from `GenericViewSet`, and mixin the required actions:
- from rest_framework import mixins
+ from rest_framework import mixins, viewsets
class CreateListRetrieveViewSet(mixins.CreateModelMixin,
mixins.ListModelMixin,
diff --git a/docs/community/3.10-announcement.md b/docs/community/3.10-announcement.md
index 23b6330a7..a2135fd20 100644
--- a/docs/community/3.10-announcement.md
+++ b/docs/community/3.10-announcement.md
@@ -41,8 +41,8 @@ update your REST framework settings to include `DEFAULT_SCHEMA_CLASS` explicitly
```python
REST_FRAMEWORK = {
- ...
- 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'
+ ...: ...,
+ "DEFAULT_SCHEMA_CLASS": "rest_framework.schemas.coreapi.AutoSchema",
}
```
@@ -74,10 +74,11 @@ urlpatterns = [
# Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
# * `title` and `description` parameters are passed to `SchemaGenerator`.
# * Provide view name for use with `reverse()`.
- path('openapi', get_schema_view(
- title="Your Project",
- description="API for all things …"
- ), name='openapi-schema'),
+ path(
+ "openapi",
+ get_schema_view(title="Your Project", description="API for all things …"),
+ name="openapi-schema",
+ ),
# ...
]
```
diff --git a/docs/community/3.11-announcement.md b/docs/community/3.11-announcement.md
index 83dd636d1..2fc37a764 100644
--- a/docs/community/3.11-announcement.md
+++ b/docs/community/3.11-announcement.md
@@ -43,10 +43,11 @@ be extracted from the class docstring:
```python
class DocStringExampleListView(APIView):
-"""
-get: A description of my GET operation.
-post: A description of my POST operation.
-"""
+ """
+ get: A description of my GET operation.
+ post: A description of my POST operation.
+ """
+
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get(self, request, *args, **kwargs):
@@ -63,7 +64,7 @@ In some circumstances a Validator class or a Default class may need to access th
* Uniqueness validators need to be able to determine the name of the field to which they are applied, in order to run an appropriate database query.
* The `CurrentUserDefault` needs to be able to determine the context with which the serializer was instantiated, in order to return the current user instance.
-Previous our approach to this was that implementations could include a `set_context` method, which would be called prior to validation. However this approach had issues with potential race conditions. We have now move this approach into a pending deprecation state. It will continue to function, but will be escalated to a deprecated state in 3.12, and removed entirely in 3.13.
+Our previous approach to this was that implementations could include a `set_context` method, which would be called prior to validation. However this approach had issues with potential race conditions. We have now move this approach into a pending deprecation state. It will continue to function, but will be escalated to a deprecated state in 3.12, and removed entirely in 3.13.
Instead, validators or defaults which require the serializer context, should include a `requires_context = True` attribute on the class.
diff --git a/docs/community/3.12-announcement.md b/docs/community/3.12-announcement.md
index 3bfeb6576..b192f7290 100644
--- a/docs/community/3.12-announcement.md
+++ b/docs/community/3.12-announcement.md
@@ -41,7 +41,7 @@ The tags used for a particular view may also be overridden...
```python
class MyOrders(APIView):
- schema = AutoSchema(tags=['users', 'orders'])
+ schema = AutoSchema(tags=["users", "orders"])
...
```
@@ -68,7 +68,7 @@ may be overridden if needed](https://www.django-rest-framework.org/api-guide/sch
```python
class MyOrders(APIView):
- schema = AutoSchema(component_name="OrderDetails")
+ schema = AutoSchema(component_name="OrderDetails")
```
## More Public API
@@ -118,10 +118,11 @@ class SitesSearchView(generics.ListAPIView):
by a search against the site name or location. (Location searches are
matched against the region and country names.)
"""
+
queryset = Sites.objects.all()
serializer_class = SitesSerializer
filter_backends = [filters.SearchFilter]
- search_fields = ['site_name', 'location__region', 'location__country']
+ search_fields = ["site_name", "location__region", "location__country"]
```
### Searches against annotate fields
@@ -135,10 +136,11 @@ class PublisherSearchView(generics.ListAPIView):
Search for publishers, optionally filtering the search against the average
rating of all their books.
"""
- queryset = Publisher.objects.annotate(avg_rating=Avg('book__rating'))
+
+ queryset = Publisher.objects.annotate(avg_rating=Avg("book__rating"))
serializer_class = PublisherSerializer
filter_backends = [filters.SearchFilter]
- search_fields = ['avg_rating']
+ search_fields = ["avg_rating"]
```
---
diff --git a/docs/community/3.5-announcement.md b/docs/community/3.5-announcement.md
index 91bfce428..43a628dd4 100644
--- a/docs/community/3.5-announcement.md
+++ b/docs/community/3.5-announcement.md
@@ -64,14 +64,10 @@ from rest_framework.schemas import get_schema_view
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer
schema_view = get_schema_view(
- title='Example API',
- renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer]
+ title="Example API", renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer]
)
-urlpatterns = [
- path('swagger/', schema_view),
- ...
-]
+urlpatterns = [path("swagger/", schema_view), ...]
```
There have been a large number of fixes to the schema generation. These should
diff --git a/docs/community/3.9-announcement.md b/docs/community/3.9-announcement.md
index d673fdd18..6bc5e3cc3 100644
--- a/docs/community/3.9-announcement.md
+++ b/docs/community/3.9-announcement.md
@@ -65,15 +65,12 @@ from rest_framework.renderers import JSONOpenAPIRenderer
from django.urls import path
schema_view = get_schema_view(
- title='Server Monitoring API',
- url='https://www.example.org/api/',
- renderer_classes=[JSONOpenAPIRenderer]
+ title="Server Monitoring API",
+ url="https://www.example.org/api/",
+ renderer_classes=[JSONOpenAPIRenderer],
)
-urlpatterns = [
- path('schema.json', schema_view),
- ...
-]
+urlpatterns = [path("schema.json", schema_view), ...]
```
And here's how you can use the `generateschema` management command:
diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md
index fba7f63d6..71f29e5c0 100644
--- a/docs/community/release-notes.md
+++ b/docs/community/release-notes.md
@@ -47,7 +47,7 @@ Date: 22nd September 2022
* Stop calling `set_context` on Validators. [[#8589](https://github.com/encode/django-rest-framework/pull/8589)]
* Return `NotImplemented` from `ErrorDetails.__ne__`. [[#8538](https://github.com/encode/django-rest-framework/pull/8538)]
* Don't evaluate `DateTimeField.default_timezone` when a custom timezone is set. [[#8531](https://github.com/encode/django-rest-framework/pull/8531)]
-* Make relative URLs clickable in Browseable API. [[#8464](https://github.com/encode/django-rest-framework/pull/8464)]
+* Make relative URLs clickable in Browsable API. [[#8464](https://github.com/encode/django-rest-framework/pull/8464)]
* Support `ManyRelatedField` falling back to the default value when the attribute specified by dot notation doesn't exist. Matches `ManyRelatedField.get_attribute` to `Field.get_attribute`. [[#7574](https://github.com/encode/django-rest-framework/pull/7574)]
* Make `schemas.openapi.get_reference` public. [[#7515](https://github.com/encode/django-rest-framework/pull/7515)]
* Make `ReturnDict` support `dict` union operators on Python 3.9 and later. [[#8302](https://github.com/encode/django-rest-framework/pull/8302)]
@@ -65,7 +65,7 @@ Date: 15th December 2021
Date: 13th December 2021
-* Django 4.0 compatability. [#8178]
+* Django 4.0 compatibility. [#8178]
* Add `max_length` and `min_length` options to `ListSerializer`. [#8165]
* Add `get_request_serializer` and `get_response_serializer` hooks to `AutoSchema`. [#7424]
* Fix OpenAPI representation of null-able read only fields. [#8116]
@@ -306,7 +306,11 @@ Be sure to upgrade to Python 3 before upgrading to Django REST Framework 3.10.
class NullableCharField(serializers.CharField):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.validators = [v for v in self.validators if not isinstance(v, ProhibitNullCharactersValidator)]
+ self.validators = [
+ v
+ for v in self.validators
+ if not isinstance(v, ProhibitNullCharactersValidator)
+ ]
```
* Add `OpenAPIRenderer` and `generate_schema` management command. [#6229][gh6229]
* Add OpenAPIRenderer by default, and add schema docs. [#6233][gh6233]
@@ -950,7 +954,7 @@ See the [release announcement][3.6-release].
* Prevent raising exception when limit is 0. ([#4098][gh4098])
* TokenAuthentication: Allow custom keyword in the header. ([#4097][gh4097])
* Handle incorrectly padded HTTP basic auth header. ([#4090][gh4090])
-* LimitOffset pagination crashes Browseable API when limit=0. ([#4079][gh4079])
+* LimitOffset pagination crashes Browsable API when limit=0. ([#4079][gh4079])
* Fixed DecimalField arbitrary precision support. ([#4075][gh4075])
* Added support for custom CSRF cookie names. ([#4049][gh4049])
* Fix regression introduced by #4035. ([#4041][gh4041])
diff --git a/docs/community/tutorials-and-resources.md b/docs/community/tutorials-and-resources.md
index 23faf7912..f283e0e4c 100644
--- a/docs/community/tutorials-and-resources.md
+++ b/docs/community/tutorials-and-resources.md
@@ -19,6 +19,10 @@ There are a wide range of resources available for learning and using Django REST
+## Courses
+
+* [Developing RESTful APIs with Django REST Framework][developing-restful-apis-with-django-rest-framework]
+
## Tutorials
* [Beginner's Guide to the Django REST Framework][beginners-guide-to-the-django-rest-framework]
@@ -130,3 +134,4 @@ Want your Django REST Framework talk/tutorial/article to be added to our website
[pycon-us-2017]: https://www.youtube.com/watch?v=Rk6MHZdust4
[django-rest-react-valentinog]: https://www.valentinog.com/blog/tutorial-api-django-rest-react/
[doordash-implementing-rest-apis]: https://doordash.engineering/2013/10/07/implementing-rest-apis-with-embedded-privacy/
+[developing-restful-apis-with-django-rest-framework]: https://testdriven.io/courses/django-rest-framework/
diff --git a/docs/img/premium/svix-premium.png b/docs/img/premium/svix-premium.png
new file mode 100644
index 000000000..68ff06387
Binary files /dev/null and b/docs/img/premium/svix-premium.png differ
diff --git a/docs/index.md b/docs/index.md
index ad241c0a3..a7f1444a3 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -48,7 +48,7 @@ Django REST framework is a powerful and flexible toolkit for building Web APIs.
Some reasons you might want to use REST framework:
-* The [Web browsable API][sandbox] is a huge usability win for your developers.
+* The Web browsable API is a huge usability win for your developers.
* [Authentication policies][authentication] including packages for [OAuth1a][oauth1-section] and [OAuth2][oauth2-section].
* [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources.
* Customizable all the way down - just use [regular function-based views][functionview-section] if you don't need the [more][generic-views] [powerful][viewsets] [features][routers].
@@ -74,10 +74,11 @@ continued development by **[signing up for a paid plan][funding]**.
-*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage), [Spacinov](https://www.spacinov.com/), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [PostHog](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [CryptAPI](https://cryptapi.io), and [FEZTO](https://www.fezto.xyz/?utm_source=DjangoRESTFramework).*
+*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage), [Spacinov](https://www.spacinov.com/), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [PostHog](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [CryptAPI](https://cryptapi.io), [FEZTO](https://www.fezto.xyz/?utm_source=DjangoRESTFramework), and [Svix](https://www.svix.com/?utm_source=django-REST&utm_medium=sponsorship).*
---
@@ -86,7 +87,7 @@ continued development by **[signing up for a paid plan][funding]**.
REST framework requires the following:
* Python (3.6, 3.7, 3.8, 3.9, 3.10, 3.11)
-* Django (3.0, 3.1, 3.2, 4.0, 4.1)
+* Django (3.0, 3.1, 3.2, 4.0, 4.1, 4.2)
We **highly recommend** and only officially support the latest patch release of
each Python and Django series.
diff --git a/docs/topics/ajax-csrf-cors.md b/docs/topics/ajax-csrf-cors.md
index 094ecc4a4..678fa00e7 100644
--- a/docs/topics/ajax-csrf-cors.md
+++ b/docs/topics/ajax-csrf-cors.md
@@ -35,7 +35,7 @@ The best way to deal with CORS in REST framework is to add the required response
[cite]: https://blog.codinghorror.com/preventing-csrf-and-xsrf-attacks/
[csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
-[csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax
+[csrf-ajax]: https://docs.djangoproject.com/en/stable/howto/csrf/#using-csrf-protection-with-ajax
[cors]: https://www.w3.org/TR/cors/
[adamchainz]: https://github.com/adamchainz
[django-cors-headers]: https://github.com/adamchainz/django-cors-headers
diff --git a/docs/topics/browsable-api.md b/docs/topics/browsable-api.md
index 39473cdd8..9a95edfc6 100644
--- a/docs/topics/browsable-api.md
+++ b/docs/topics/browsable-api.md
@@ -15,6 +15,18 @@ If you include fully-qualified URLs in your resource output, they will be 'urliz
By default, the API will return the format specified by the headers, which in the case of the browser is HTML. The format can be specified using `?format=` in the request, so you can look at the raw JSON response in a browser by adding `?format=json` to the URL. There are helpful extensions for viewing JSON in [Firefox][ffjsonview] and [Chrome][chromejsonview].
+## Authentication
+
+To quickly add authentication to the browesable api, add a routes named `"login"` and `"logout"` under the namespace `"rest_framework"`. DRF provides default routes for this which you can add to your urlconf:
+
+```python
+urlpatterns = [
+ # ...
+ url(r"^api-auth/", include("rest_framework.urls", namespace="rest_framework"))
+]
+```
+
+
## Customizing
The browsable API is built with [Twitter's Bootstrap][bootstrap] (v 3.4.1), making it easy to customize the look-and-feel.
diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md
index b85d6310a..edb989290 100644
--- a/docs/topics/documenting-your-api.md
+++ b/docs/topics/documenting-your-api.md
@@ -96,10 +96,14 @@ urlpatterns = [
# ...
# Route TemplateView to serve Swagger UI template.
# * Provide `extra_context` with view name of `SchemaView`.
- path('swagger-ui/', TemplateView.as_view(
- template_name='swagger-ui.html',
- extra_context={'schema_url':'openapi-schema'}
- ), name='swagger-ui'),
+ path(
+ "swagger-ui/",
+ TemplateView.as_view(
+ template_name="swagger-ui.html",
+ extra_context={"schema_url": "openapi-schema"},
+ ),
+ name="swagger-ui",
+ ),
]
```
@@ -145,10 +149,13 @@ urlpatterns = [
# ...
# Route TemplateView to serve the ReDoc template.
# * Provide `extra_context` with view name of `SchemaView`.
- path('redoc/', TemplateView.as_view(
- template_name='redoc.html',
- extra_context={'schema_url':'openapi-schema'}
- ), name='redoc'),
+ path(
+ "redoc/",
+ TemplateView.as_view(
+ template_name="redoc.html", extra_context={"schema_url": "openapi-schema"}
+ ),
+ name="redoc",
+ ),
]
```
diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md
index f9b6c5e9a..6fa2111e7 100644
--- a/docs/tutorial/6-viewsets-and-routers.md
+++ b/docs/tutorial/6-viewsets-and-routers.md
@@ -10,7 +10,7 @@ A `ViewSet` class is only bound to a set of method handlers at the last moment,
Let's take our current set of views, and refactor them into view sets.
-First of all let's refactor our `UserList` and `UserDetail` classes into a single `UserViewSet` class. We can remove the two view classes, and replace them with a single ViewSet class:
+First of all let's refactor our `UserList` and `UserDetail` classes into a single `UserViewSet` class. In the `snippets/views.py` file, we can remove the two view classes and replace them with a single ViewSet class:
from rest_framework import viewsets
diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md
index 09f249f6e..9a9da4043 100644
--- a/docs/tutorial/quickstart.md
+++ b/docs/tutorial/quickstart.md
@@ -105,7 +105,7 @@ Right, we'd better write some views then. Open `tutorial/quickstart/views.py` a
"""
API endpoint that allows groups to be viewed or edited.
"""
- queryset = Group.objects.all()
+ queryset = Group.objects.all().order_by('name')
serializer_class = GroupSerializer
permission_classes = [permissions.IsAuthenticated]
diff --git a/requirements/requirements-documentation.txt b/requirements/requirements-documentation.txt
index bce03abc5..25f5121f2 100644
--- a/requirements/requirements-documentation.txt
+++ b/requirements/requirements-documentation.txt
@@ -1,5 +1,5 @@
# MkDocs to build our documentation.
-mkdocs>=1.1.2,<1.2
+mkdocs==1.2.4
jinja2>=2.10,<3.1.0 # contextfilter has been renamed
# pylinkvalidator to check for broken links in documentation.
diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt
index 0379f8c38..e54100f52 100644
--- a/requirements/requirements-optionals.txt
+++ b/requirements/requirements-optionals.txt
@@ -4,7 +4,7 @@ coreschema==0.0.4
django-filter
django-guardian>=2.4.0,<2.5
inflection==0.5.1
-markdown==3.3
+markdown>=3.3.7
psycopg2-binary>=2.9.5,<2.10
-pygments==2.12
+pygments>=2.12.0,<2.14.0
pyyaml>=5.3.1,<5.4
diff --git a/requirements/requirements-testing.txt b/requirements/requirements-testing.txt
index 0f6d7ff87..2b39316a0 100644
--- a/requirements/requirements-testing.txt
+++ b/requirements/requirements-testing.txt
@@ -1,5 +1,5 @@
# Pytest for running the tests.
-pytest>=6.2.0,<8.0
+pytest>=7.0.1,<8.0
pytest-cov>=4.0.0,<5.0
pytest-django>=4.5.2,<5.0
importlib-metadata<5.0
diff --git a/rest_framework/compat.py b/rest_framework/compat.py
index 7e80704e1..472b8ad24 100644
--- a/rest_framework/compat.py
+++ b/rest_framework/compat.py
@@ -169,6 +169,21 @@ else:
}
+if django.VERSION >= (5, 1):
+ # Django 5.1+: use the stock ip_address_validators function
+ # Note: Before Django 5.1, ip_address_validators returns a tuple containing
+ # 1) the list of validators and 2) the error message. Starting from
+ # Django 5.1 ip_address_validators only returns the list of validators
+ from django.core.validators import ip_address_validators
+else:
+ # Django <= 5.1: create a compatibility shim for ip_address_validators
+ from django.core.validators import \
+ ip_address_validators as _ip_address_validators
+
+ def ip_address_validators(protocol, unpack_ipv4):
+ return _ip_address_validators(protocol, unpack_ipv4)[0]
+
+
# `separators` argument to `json.dumps()` differs between 2.x and 3.x
# See: https://bugs.python.org/issue22767
SHORT_SEPARATORS = (',', ':')
diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py
index 3b572c09e..864ff7395 100644
--- a/rest_framework/decorators.py
+++ b/rest_framework/decorators.py
@@ -36,7 +36,7 @@ def api_view(http_method_names=None):
# WrappedAPIView.__doc__ = func.doc <--- Not possible to do this
# api_view applied without (method_names)
- assert not(isinstance(http_method_names, types.FunctionType)), \
+ assert not isinstance(http_method_names, types.FunctionType), \
'@api_view missing list of allowed HTTP methods'
# api_view applied with eg. string instead of list of strings
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index 0b56fa7fb..fda656507 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -16,7 +16,7 @@ from django.core.exceptions import ValidationError as DjangoValidationError
from django.core.validators import (
EmailValidator, MaxLengthValidator, MaxValueValidator, MinLengthValidator,
MinValueValidator, ProhibitNullCharactersValidator, RegexValidator,
- URLValidator, ip_address_validators
+ URLValidator
)
from django.forms import FilePathField as DjangoFilePathField
from django.forms import ImageField as DjangoImageField
@@ -36,6 +36,7 @@ except ImportError:
pytz = None
from rest_framework import ISO_8601
+from rest_framework.compat import ip_address_validators
from rest_framework.exceptions import ErrorDetail, ValidationError
from rest_framework.settings import api_settings
from rest_framework.utils import html, humanize_datetime, json, representation
@@ -866,7 +867,7 @@ class IPAddressField(CharField):
self.protocol = protocol.lower()
self.unpack_ipv4 = (self.protocol == 'both')
super().__init__(**kwargs)
- validators, error_message = ip_address_validators(protocol, self.unpack_ipv4)
+ validators = ip_address_validators(protocol, self.unpack_ipv4)
self.validators.extend(validators)
def to_internal_value(self, data):
diff --git a/rest_framework/filters.py b/rest_framework/filters.py
index 065e72f94..86effe24e 100644
--- a/rest_framework/filters.py
+++ b/rest_framework/filters.py
@@ -21,14 +21,14 @@ from rest_framework.settings import api_settings
def search_smart_split(search_terms):
- """generator that first splits string by spaces, leaving quoted phrases togheter,
+ """generator that first splits string by spaces, leaving quoted phrases together,
then it splits non-quoted phrases by commas.
"""
for term in smart_split(search_terms):
# trim commas to avoid bad matching for quoted phrases
term = term.strip(',')
if term.startswith(('"', "'")) and term[0] == term[-1]:
- # quoted phrases are kept togheter without any other split
+ # quoted phrases are kept together without any other split
yield unescape_string_literal(term)
else:
# non-quoted tokens are split by comma, keeping only non-empty ones
diff --git a/rest_framework/schemas/generators.py b/rest_framework/schemas/generators.py
index d3c6446aa..f59e25c21 100644
--- a/rest_framework/schemas/generators.py
+++ b/rest_framework/schemas/generators.py
@@ -102,12 +102,12 @@ class EndpointEnumerator:
Given a URL conf regex, return a URI template string.
"""
# ???: Would it be feasible to adjust this such that we generate the
- # path, plus the kwargs, plus the type from the convertor, such that we
+ # path, plus the kwargs, plus the type from the converter, such that we
# could feed that straight into the parameter schema object?
path = simplify_regex(path_regex)
- # Strip Django 2.0 convertors as they are incompatible with uritemplate format
+ # Strip Django 2.0 converters as they are incompatible with uritemplate format
return re.sub(_PATH_PARAMETER_COMPONENT_RE, r'{\g}', path)
def should_include_endpoint(self, path, callback):
diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py
index ad19f6df8..c154494e2 100644
--- a/rest_framework/schemas/openapi.py
+++ b/rest_framework/schemas/openapi.py
@@ -84,7 +84,7 @@ class SchemaGenerator(BaseSchemaGenerator):
continue
if components_schemas[k] == components[k]:
continue
- warnings.warn('Schema component "{}" has been overriden with a different value.'.format(k))
+ warnings.warn('Schema component "{}" has been overridden with a different value.'.format(k))
components_schemas.update(components)
diff --git a/rest_framework/settings.py b/rest_framework/settings.py
index 96b664574..b0d7bacec 100644
--- a/rest_framework/settings.py
+++ b/rest_framework/settings.py
@@ -116,7 +116,7 @@ DEFAULTS = {
'COERCE_DECIMAL_TO_STRING': True,
'UPLOADED_FILES_USE_URL': True,
- # Browseable API
+ # Browsable API
'HTML_SELECT_CUTOFF': 1000,
'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...",
diff --git a/rest_framework/static/rest_framework/js/jquery-3.5.1.min.js b/rest_framework/static/rest_framework/js/jquery-3.5.1.min.js
deleted file mode 100644
index b0614034a..000000000
--- a/rest_framework/static/rest_framework/js/jquery-3.5.1.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */
-!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"