diff options
49 files changed, 1369 insertions, 710 deletions
diff --git a/.travis.yml b/.travis.yml index 52ed562c..d388f2c9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,15 +7,15 @@ python: - "3.3" env: - - DJANGO=https://www.djangoproject.com/download/1.5c1/tarball/ + - DJANGO="django==1.5 --use-mirrors" - DJANGO="django==1.4.3 --use-mirrors" - DJANGO="django==1.3.5 --use-mirrors" install: - pip install $DJANGO - pip install defusedxml==0.3 - - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install oauth2; fi" - - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth-plus; fi" + - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install oauth2==1.5.211 --use-mirrors; fi" + - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth-plus==2.0 --use-mirrors; fi" - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-filter==0.5.4 --use-mirrors; fi" - "if [[ ${TRAVIS_PYTHON_VERSION::1} == '3' ]]; then pip install https://github.com/alex/django-filter/tarball/master; fi" - export PYTHONPATH=. @@ -79,6 +79,10 @@ To run the tests. ./rest_framework/runtests/runtests.py +To run the tests against all supported configurations, first install [the tox testing tool][tox] globally, using `pip install tox`, then simply run `tox`: + + tox + # License Copyright (c) 2011-2013, Tom Christie @@ -113,6 +117,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [rest-framework-2-announcement]: http://django-rest-framework.org/topics/rest-framework-2-announcement.html [2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion +[tox]: http://testrun.org/tox/latest/ + [docs]: http://django-rest-framework.org/ [urlobject]: https://github.com/zacharyvoase/urlobject [markdown]: http://pypi.python.org/pypi/Markdown/ diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index d7918e10..740f1035 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -10,7 +10,7 @@ Authentication is the mechanism of associating an incoming request with a set of REST framework provides a number of authentication schemes out of the box, and also allows you to implement custom schemes. -Authentication will run the first time either the `request.user` or `request.auth` properties are accessed, and determines how those properties are initialized. +Authentication is always run at the very start of the view, before the permission and throttling checks occur, and before any other code is allowed to proceed. The `request.user` property will typically be set to an instance of the `contrib.auth` package's `User` class. @@ -113,7 +113,12 @@ Unauthenticated responses that are denied permission will result in an `HTTP 401 This authentication scheme uses a simple token-based HTTP Authentication scheme. Token authentication is appropriate for client-server setups, such as native desktop and mobile clients. -To use the `TokenAuthentication` scheme, include `rest_framework.authtoken` in your `INSTALLED_APPS` setting. +To use the `TokenAuthentication` scheme, include `rest_framework.authtoken` in your `INSTALLED_APPS` setting: + + INSTALLED_APPS = ( + ... + 'rest_framework.authtoken' + ) You'll also need to create tokens for your users. @@ -135,10 +140,14 @@ Unauthenticated responses that are denied permission will result in an `HTTP 401 WWW-Authenticate: Token +--- + **Note:** If you use `TokenAuthentication` in production you must ensure that your API is only available over `https` only. --- +#### Generating Tokens + If you want every user to have an automatically generated Token, you can simply catch the User's `post_save` signal. @receiver(post_save, sender=User) @@ -154,8 +163,7 @@ If you've already created some users, you can generate tokens for all existing u for user in User.objects.all(): Token.objects.get_or_create(user=user) -When using `TokenAuthentication`, you may want to provide a mechanism for clients to obtain a token given the username and password. -REST framework provides a built-in view to provide this behavior. To use it, add the `obtain_auth_token` view to your URLconf: +When using `TokenAuthentication`, you may want to provide a mechanism for clients to obtain a token given the username and password. REST framework provides a built-in view to provide this behavior. To use it, add the `obtain_auth_token` view to your URLconf: urlpatterns += patterns('', url(r'^api-token-auth/', 'rest_framework.authtoken.views.obtain_auth_token') @@ -169,6 +177,23 @@ The `obtain_auth_token` view will return a JSON response when valid `username` a Note that the default `obtain_auth_token` view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. 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. +#### Custom user models + +The `rest_framework.authtoken` app includes a south migration that will create the authtoken table. If you're using a [custom user model][custom-user-model] you'll need to make sure that any initial migration that creates the user table runs before the authtoken table is created. + +You can do so by inserting a `needed_by` attribute in your user migration: + + class Migration: + + needed_by = ( + ('authtoken', '0001_initial'), + ) + + def forwards(self): + ... + +For more details, see the [south documentation on dependencies][south-dependencies]. + ## SessionAuthentication This authentication scheme uses Django's default session backend for authentication. Session authentication is appropriate for AJAX clients that are running in the same session context as your website. @@ -205,7 +230,7 @@ In some circumstances instead of returning `None`, you may want to raise an `Aut Typically the approach you should take is: * If authentication is not attempted, return `None`. Any other authentication schemes also in use will still be checked. -* If authentication is attempted but fails, raise a `AuthenticationFailed` exception. An error response will be returned immediately, without checking any other authentication schemes. +* If authentication is attempted but fails, raise a `AuthenticationFailed` exception. An error response will be returned immediately, regardless of any permissions checks, and without checking any other authentication schemes. You *may* also override the `.authenticate_header(self, request)` method. If implemented, it should return a string that will be used as the value of the `WWW-Authenticate` header in a `HTTP 401 Unauthorized` response. @@ -216,7 +241,7 @@ If the `.authenticate_header()` method is not overridden, the authentication sch The following example will authenticate any incoming request as the user given by the username in a custom request header named 'X_USERNAME'. class ExampleAuthentication(authentication.BaseAuthentication): - def has_permission(self, request, view, obj=None): + def authenticate(self, request): username = request.META.get('X_USERNAME') if not username: return None @@ -247,6 +272,8 @@ HTTP digest authentication is a widely implemented scheme that was intended to r [throttling]: throttling.md [csrf-ajax]: https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax [mod_wsgi_official]: http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIPassAuthorization +[custom-user-model]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#specifying-a-custom-user-model +[south-dependencies]: http://south.readthedocs.org/en/latest/dependencies.html [juanriaza]: https://github.com/juanriaza [djangorestframework-digestauth]: https://github.com/juanriaza/django-rest-framework-digestauth -[rfc5849] : http://tools.ietf.org/html/rfc5849
\ No newline at end of file +[rfc5849] : http://tools.ietf.org/html/rfc5849 diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 8c28273b..9a745cf1 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -2,7 +2,7 @@ # Serializer fields -> Each field in a Form class is responsible not only for validating data, but also for "cleaning" it -- normalizing it to a consistent format. +> Each field in a Form class is responsible not only for validating data, but also for "cleaning" it — normalizing it to a consistent format. > > — [Django documentation][cite] @@ -102,7 +102,7 @@ You can customize this behavior by overriding the `.to_native(self, value)` met ## WritableField -A field that supports both read and write operations. By itself `WriteableField` does not perform any translation of input values into a given type. You won't typically use this field directly, but you may want to override it and implement the `.to_native(self, value)` and `.from_native(self, value)` methods. +A field that supports both read and write operations. By itself `WritableField` does not perform any translation of input values into a given type. You won't typically use this field directly, but you may want to override it and implement the `.to_native(self, value)` and `.from_native(self, value)` methods. ## ModelField @@ -181,12 +181,6 @@ Corresponds to `django.forms.fields.RegexField` **Signature:** `RegexField(regex, max_length=None, min_length=None)` -## DateField - -A date representation. - -Corresponds to `django.db.models.fields.DateField` - ## DateTimeField A date and time representation. @@ -203,12 +197,41 @@ If you want to override this behavior, you'll need to declare the `DateTimeField class Meta: model = Comment +**Signature:** `DateTimeField(format=None, input_formats=None)` + +* `format` - A string representing the output format. If not specified, the `DATETIME_FORMAT` setting will be used, which defaults to `'iso-8601'`. +* `input_formats` - A list of strings representing the input formats which may be used to parse the date. If not specified, the `DATETIME_INPUT_FORMATS` setting will be used, which defaults to `['iso-8601']`. + +DateTime format strings may either be [python strftime formats][strftime] which explicitly specifiy the format, or the special string `'iso-8601'`, which indicates that [ISO 8601][iso8601] style datetimes should be used. (eg `'2013-01-29T12:34:56.000000'`) + +## DateField + +A date representation. + +Corresponds to `django.db.models.fields.DateField` + +**Signature:** `DateField(format=None, input_formats=None)` + +* `format` - A string representing the output format. If not specified, the `DATE_FORMAT` setting will be used, which defaults to `'iso-8601'`. +* `input_formats` - A list of strings representing the input formats which may be used to parse the date. If not specified, the `DATE_INPUT_FORMATS` setting will be used, which defaults to `['iso-8601']`. + +Date format strings may either be [python strftime formats][strftime] which explicitly specifiy the format, or the special string `'iso-8601'`, which indicates that [ISO 8601][iso8601] style dates should be used. (eg `'2013-01-29'`) + ## TimeField A time representation. +Optionally takes `format` as parameter to replace the matching pattern. + Corresponds to `django.db.models.fields.TimeField` +**Signature:** `TimeField(format=None, input_formats=None)` + +* `format` - A string representing the output format. If not specified, the `TIME_FORMAT` setting will be used, which defaults to `'iso-8601'`. +* `input_formats` - A list of strings representing the input formats which may be used to parse the date. If not specified, the `TIME_INPUT_FORMATS` setting will be used, which defaults to `['iso-8601']`. + +Time format strings may either be [python strftime formats][strftime] which explicitly specifiy the format, or the special string `'iso-8601'`, which indicates that [ISO 8601][iso8601] style times should be used. (eg `'12:34:56.000000'`) + ## IntegerField An integer representation. @@ -252,3 +275,5 @@ Django's regular [FILE_UPLOAD_HANDLERS] are used for handling uploaded files. [cite]: https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.cleaned_data [FILE_UPLOAD_HANDLERS]: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS +[strftime]: http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior +[iso8601]: http://www.w3.org/TR/NOTE-datetime diff --git a/docs/api-guide/format-suffixes.md b/docs/api-guide/format-suffixes.md index 6d5feba4..dae3dea3 100644 --- a/docs/api-guide/format-suffixes.md +++ b/docs/api-guide/format-suffixes.md @@ -29,18 +29,27 @@ Example: urlpatterns = patterns('blog.views', url(r'^/$', 'api_root'), - url(r'^comment/$', 'comment_root'), - url(r'^comment/(?P<pk>[0-9]+)/$', 'comment_instance') + url(r'^comments/$', 'comment_list'), + url(r'^comments/(?P<pk>[0-9]+)/$', 'comment_detail') ) urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html']) -When using `format_suffix_patterns`, you must make sure to add the `'format'` keyword argument to the corresponding views. For example. +When using `format_suffix_patterns`, you must make sure to add the `'format'` keyword argument to the corresponding views. For example: - @api_view(('GET',)) - def api_root(request, format=None): + @api_view(('GET', 'POST')) + def comment_list(request, format=None): # do stuff... +Or with class based views: + + class CommentList(APIView): + def get(self, request, format=None): + # do stuff... + + def post(self, request, format=None): + # do stuff... + The name of the kwarg used may be modified by using the `FORMAT_SUFFIX_KWARG` setting. Also note that `format_suffix_patterns` does not support descending into `include` URL patterns. @@ -58,4 +67,4 @@ It is actually a misconception. For example, take the following quote from Roy The quote does not mention Accept headers, but it does make it clear that format suffixes should be considered an acceptable pattern. [cite]: http://tech.groups.yahoo.com/group/rest-discuss/message/5857 -[cite2]: http://tech.groups.yahoo.com/group/rest-discuss/message/14844
\ No newline at end of file +[cite2]: http://tech.groups.yahoo.com/group/rest-discuss/message/14844 diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 5de1491b..3c8396aa 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -123,7 +123,7 @@ The template name is determined by (in order of preference): An example of a view that uses `TemplateHTMLRenderer`: - class UserInstance(generics.RetrieveUserAPIView): + class UserDetail(generics.RetrieveUserAPIView): """ A view that returns a templated HTML representations of a given user. """ @@ -301,4 +301,4 @@ Comma-separated values are a plain-text tabular data format, that can be easily [juanriaza]: https://github.com/juanriaza [mjumbewu]: https://github.com/mjumbewu [djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack -[djangorestframework-csv]: https://github.com/mjumbewu/django-rest-framework-csv
\ No newline at end of file +[djangorestframework-csv]: https://github.com/mjumbewu/django-rest-framework-csv diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index e103fbab..11638696 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -34,7 +34,11 @@ The `api_settings` object will check for any user-defined settings, and otherwis # API Reference -## DEFAULT_RENDERER_CLASSES +## API policy settings + +*The following settings control the basic API policies, and are applied to every `APIView` class based view, or `@api_view` function based view.* + +#### DEFAULT_RENDERER_CLASSES A list or tuple of renderer classes, that determines the default set of renderers that may be used when returning a `Response` object. @@ -45,7 +49,7 @@ Default: 'rest_framework.renderers.BrowsableAPIRenderer', ) -## DEFAULT_PARSER_CLASSES +#### DEFAULT_PARSER_CLASSES A list or tuple of parser classes, that determines the default set of parsers used when accessing the `request.DATA` property. @@ -57,7 +61,7 @@ Default: 'rest_framework.parsers.MultiPartParser' ) -## DEFAULT_AUTHENTICATION_CLASSES +#### DEFAULT_AUTHENTICATION_CLASSES A list or tuple of authentication classes, that determines the default set of authenticators used when accessing the `request.user` or `request.auth` properties. @@ -68,7 +72,7 @@ Default: 'rest_framework.authentication.BasicAuthentication' ) -## DEFAULT_PERMISSION_CLASSES +#### DEFAULT_PERMISSION_CLASSES A list or tuple of permission classes, that determines the default set of permissions checked at the start of a view. @@ -78,59 +82,77 @@ Default: 'rest_framework.permissions.AllowAny', ) -## DEFAULT_THROTTLE_CLASSES +#### DEFAULT_THROTTLE_CLASSES A list or tuple of throttle classes, that determines the default set of throttles checked at the start of a view. Default: `()` -## DEFAULT_CONTENT_NEGOTIATION_CLASS +#### DEFAULT_CONTENT_NEGOTIATION_CLASS A content negotiation class, that determines how a renderer is selected for the response, given an incoming request. Default: `'rest_framework.negotiation.DefaultContentNegotiation'` -## DEFAULT_MODEL_SERIALIZER_CLASS +--- + +## Generic view settings + +*The following settings control the behavior of the generic class based views.* + +#### DEFAULT_MODEL_SERIALIZER_CLASS A class that determines the default type of model serializer that should be used by a generic view if `model` is specified, but `serializer_class` is not provided. Default: `'rest_framework.serializers.ModelSerializer'` -## DEFAULT_PAGINATION_SERIALIZER_CLASS +#### DEFAULT_PAGINATION_SERIALIZER_CLASS A class the determines the default serialization style for paginated responses. Default: `rest_framework.pagination.PaginationSerializer` -## FILTER_BACKEND +#### FILTER_BACKEND The filter backend class that should be used for generic filtering. If set to `None` then generic filtering is disabled. -## PAGINATE_BY +#### PAGINATE_BY The default page size to use for pagination. If set to `None`, pagination is disabled by default. Default: `None` -## PAGINATE_BY_PARAM +#### PAGINATE_BY_PARAM The name of a query parameter, which can be used by the client to overide the default page size to use for pagination. If set to `None`, clients may not override the default page size. Default: `None` -## UNAUTHENTICATED_USER +--- + +## Authentication settings + +*The following settings control the behavior of unauthenticated requests.* + +#### UNAUTHENTICATED_USER The class that should be used to initialize `request.user` for unauthenticated requests. Default: `django.contrib.auth.models.AnonymousUser` -## UNAUTHENTICATED_TOKEN +#### UNAUTHENTICATED_TOKEN The class that should be used to initialize `request.auth` for unauthenticated requests. Default: `None` -## FORM_METHOD_OVERRIDE +--- + +## Browser overrides + +*The following settings provide URL or form-based overrides of the default browser behavior.* + +#### FORM_METHOD_OVERRIDE The name of a form field that may be used to override the HTTP method of the form. @@ -138,7 +160,7 @@ If the value of this setting is `None` then form method overloading will be disa Default: `'_method'` -## FORM_CONTENT_OVERRIDE +#### FORM_CONTENT_OVERRIDE The name of a form field that may be used to override the content of the form payload. Must be used together with `FORM_CONTENTTYPE_OVERRIDE`. @@ -146,7 +168,7 @@ If either setting is `None` then form content overloading will be disabled. Default: `'_content'` -## FORM_CONTENTTYPE_OVERRIDE +#### FORM_CONTENTTYPE_OVERRIDE The name of a form field that may be used to override the content type of the form payload. Must be used together with `FORM_CONTENT_OVERRIDE`. @@ -154,7 +176,7 @@ If either setting is `None` then form content overloading will be disabled. Default: `'_content_type'` -## URL_ACCEPT_OVERRIDE +#### URL_ACCEPT_OVERRIDE The name of a URL parameter that may be used to override the HTTP `Accept` header. @@ -162,13 +184,59 @@ If the value of this setting is `None` then URL accept overloading will be disab Default: `'accept'` -## URL_FORMAT_OVERRIDE +#### URL_FORMAT_OVERRIDE The name of a URL parameter that may be used to override the default `Accept` header based content negotiation. Default: `'format'` -## FORMAT_SUFFIX_KWARG +--- + +## Date/Time formatting + +*The following settings are used to control how date and time representations may be parsed and rendered.* + +#### DATETIME_FORMAT + +A format string that should be used by default for rendering the output of `DateTimeField` serializer fields. + +Default: `'iso-8601'` + +#### DATETIME_INPUT_FORMATS + +A list of format strings that should be used by default for parsing inputs to `DateTimeField` serializer fields. + +Default: `['iso-8601']` + +#### DATE_FORMAT + +A format string that should be used by default for rendering the output of `DateField` serializer fields. + +Default: `'iso-8601'` + +#### DATE_INPUT_FORMATS + +A list of format strings that should be used by default for parsing inputs to `DateField` serializer fields. + +Default: `['iso-8601']` + +#### TIME_FORMAT + +A format string that should be used by default for rendering the output of `TimeField` serializer fields. + +Default: `'iso-8601'` + +#### TIME_INPUT_FORMATS + +A list of format strings that should be used by default for parsing inputs to `TimeField` serializer fields. + +Default: `['iso-8601']` + +--- + +## Miscellaneous settings + +#### FORMAT_SUFFIX_KWARG The name of a parameter in the URL conf that may be used to provide a format suffix. diff --git a/docs/css/default.css b/docs/css/default.css index 07c4884d..c160b63d 100644 --- a/docs/css/default.css +++ b/docs/css/default.css @@ -47,7 +47,7 @@ body.index-page #main-content iframe.twitter-share-button { body.index-page #main-content img.travis-build-image { float: right; margin-right: 8px; - margin-top: -9px; + margin-top: -11px; margin-bottom: 0px; } diff --git a/docs/index.md b/docs/index.md index b2c04735..7c472e35 100644 --- a/docs/index.md +++ b/docs/index.md @@ -36,6 +36,9 @@ The following packages are optional: * [PyYAML][yaml] (3.10+) - YAML content-type support. * [defusedxml][defusedxml] (0.3+) - XML content-type support. * [django-filter][django-filter] (0.5.4+) - Filtering support. +* [django-oauth-plus][django-oauth-plus] (2.0+) and [oauth2][oauth2] (1.5.211+) - OAuth 1.0a support. + +**Note**: The `oauth2` python package is badly misnamed, and actually provides oauth1.0a support. ## Installation @@ -133,6 +136,10 @@ Run the tests: ./rest_framework/runtests/runtests.py +To run the tests against all supported configurations, first install [the tox testing tool][tox] globally, using `pip install tox`, then simply run `tox`: + + tox + ## Support For support please see the [REST framework discussion group][group], try the `#restframework` channel on `irc.freenode.net`, or raise a question on [Stack Overflow][stack-overflow], making sure to include the ['django-rest-framework'][django-rest-framework-tag] tag. @@ -176,6 +183,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [yaml]: http://pypi.python.org/pypi/PyYAML [defusedxml]: https://pypi.python.org/pypi/defusedxml [django-filter]: http://pypi.python.org/pypi/django-filter +[oauth2]: https://github.com/simplegeo/python-oauth2 +[django-oauth-plus]: https://bitbucket.org/david/django-oauth-plus/wiki/Home [0.4]: https://github.com/tomchristie/django-rest-framework/tree/0.4.X [image]: img/quickstart.png [sandbox]: http://restframework.herokuapp.com/ @@ -218,6 +227,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [release-notes]: topics/release-notes.md [credits]: topics/credits.md +[tox]: http://testrun.org/tox/latest/ + [group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework [stack-overflow]: http://stackoverflow.com/ [django-rest-framework-tag]: http://stackoverflow.com/questions/tagged/django-rest-framework diff --git a/docs/topics/credits.md b/docs/topics/credits.md index e546548e..190ce490 100644 --- a/docs/topics/credits.md +++ b/docs/topics/credits.md @@ -104,6 +104,10 @@ The following people have helped make REST framework great. * Xavier Ordoquy - [xordoquy] * Adam Wentz - [floppya] * Andreas Pelme - [pelme] +* Ryan Detzel - [ryanrdetzel] +* Omer Katz - [thedrow] +* Wiliam Souza - [waa] +* Jonas Braun - [iekadou] Many thanks to everyone who's contributed to the project. @@ -242,3 +246,7 @@ You can also contact [@_tomchristie][twitter] directly on twitter. [xordoquy]: https://github.com/xordoquy [floppya]: https://github.com/floppya [pelme]: https://github.com/pelme +[ryanrdetzel]: https://github.com/ryanrdetzel +[thedrow]: https://github.com/thedrow +[waa]: https://github.com/wiliamsouza +[iekadou]: https://github.com/iekadou diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 06dc79a6..42b1d8da 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,10 +40,15 @@ You can determine your currently installed version using `pip freeze`: ## 2.2.x series -### Master +### 2.2.2 +**Date**: 6th March 2013 + +* Support for custom input and output formats for `DateField`, `DateTimeField` and `TimeField`. +* Cleanup: Request authentication is no longer lazily evaluated, instead authentication is always run, which results in more consistent, obvious behavior. Eg. Supplying bad auth credentials will now always return an error response, even if no permissions are set on the view. * Bugfix for serializer data being uncacheable with pickle protocol 0. * Bugfixes for model field validation edge-cases. +* Bugfix for authtoken migration while using a custom user model and south. ### 2.2.1 diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md index 3c4e042b..3ee755a2 100644 --- a/docs/tutorial/4-authentication-and-permissions.md +++ b/docs/tutorial/4-authentication-and-permissions.md @@ -72,14 +72,14 @@ We'll also add a couple of views. We'd like to just use read-only views for the serializer_class = UserSerializer - class UserInstance(generics.RetrieveAPIView): + class UserDetail(generics.RetrieveAPIView): model = User serializer_class = UserSerializer Finally we need to add those views into the API, by referencing them from the URL conf. url(r'^users/$', views.UserList.as_view()), - url(r'^users/(?P<pk>[0-9]+)/$', views.UserInstance.as_view()), + url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()), ## Associating Snippets with Users diff --git a/docs/tutorial/5-relationships-and-hyperlinked-apis.md b/docs/tutorial/5-relationships-and-hyperlinked-apis.md index 81be333b..a702a09d 100644 --- a/docs/tutorial/5-relationships-and-hyperlinked-apis.md +++ b/docs/tutorial/5-relationships-and-hyperlinked-apis.md @@ -123,7 +123,7 @@ After adding all those names into our URLconf, our final `'urls.py'` file should views.UserList.as_view(), name='user-list'), url(r'^users/(?P<pk>[0-9]+)/$', - views.UserInstance.as_view(), + views.UserDetail.as_view(), name='user-detail') )) @@ -173,4 +173,4 @@ We've reached the end of our tutorial. If you want to get more involved in the [sandbox]: http://restframework.herokuapp.com/ [github]: https://github.com/tomchristie/django-rest-framework [group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework -[twitter]: https://twitter.com/_tomchristie
\ No newline at end of file +[twitter]: https://twitter.com/_tomchristie diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 29f3d7bc..2180c509 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -1,6 +1,9 @@ -__version__ = '2.2.1' +__version__ = '2.2.2' VERSION = __version__ # synonym # Header encoding (see RFC5987) HTTP_HEADER_ENCODING = 'iso-8859-1' + +# Default datetime input and output formats +ISO_8601 = 'iso-8601' diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index b507c5e1..24a8e336 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -3,12 +3,10 @@ Provides a set of pluggable authentication policies. """ from __future__ import unicode_literals from django.contrib.auth import authenticate -from django.utils.encoding import DjangoUnicodeDecodeError from django.core.exceptions import ImproperlyConfigured from rest_framework import exceptions, HTTP_HEADER_ENCODING from rest_framework.compat import CsrfViewMiddleware -from rest_framework.compat import oauth -from rest_framework.compat import oauth_provider +from rest_framework.compat import oauth, oauth_provider, oauth_provider_store from rest_framework.authtoken.models import Token import base64 @@ -61,11 +59,7 @@ class BasicAuthentication(BaseAuthentication): except (TypeError, UnicodeDecodeError): raise exceptions.AuthenticationFailed('Invalid basic header') - try: - userid, password = auth_parts[0], auth_parts[2] - except DjangoUnicodeDecodeError: - raise exceptions.AuthenticationFailed('Invalid basic header') - + userid, password = auth_parts[0], auth_parts[2] return self.authenticate_credentials(userid, password) def authenticate_credentials(self, userid, password): @@ -159,10 +153,12 @@ class TokenAuthentication(BaseAuthentication): class OAuthAuthentication(BaseAuthentication): - """rest_framework OAuth authentication backend using - django-oath-plus and oauth2""" + """ + OAuth 1.0a authentication backend using `django-oauth-plus` and `oauth2`. + + Note: The `oauth2` package actually provides oauth1.0a support. Urg. + """ www_authenticate_realm = 'api' - require_active = True def __init__(self, **kwargs): super(OAuthAuthentication, self).__init__(**kwargs) @@ -173,50 +169,47 @@ class OAuthAuthentication(BaseAuthentication): if oauth_provider is None: raise ImproperlyConfigured("The 'django-oauth-plus' package could not be imported. It is required for use with the 'OAuthAuthentication' class.") - def authenticate(self, request): """ - Returns two-tuple of (user, auth token) if authentication succeeds, or None otherwise. + Returns two-tuple of (user, token) if authentication succeeds, + or None otherwise. """ - from oauth_provider.store import store - if self.is_valid_request(request): - oauth_request = oauth_provider.utils.get_oauth_request(request) - - if not self.check_nonce(request, oauth_request): - raise exceptions.AuthenticationFailed("Nonce check failed") + if not self.is_valid_request(request): + return None - try: - consumer = store.get_consumer(request, oauth_request, - oauth_request.get_parameter('oauth_consumer_key')) - except oauth_provider.store.InvalidConsumerError, e: - raise exceptions.AuthenticationFailed(e) + oauth_request = oauth_provider.utils.get_oauth_request(request) - if consumer.status != oauth_provider.consts.ACCEPTED: - raise exceptions.AuthenticationFailed('Invalid consumer key status: %s' % consumer.get_status_display()) + if not self.check_nonce(request, oauth_request): + raise exceptions.AuthenticationFailed("Nonce check failed") - try: - token = store.get_access_token(request, oauth_request, - consumer, oauth_request.get_parameter('oauth_token')) + try: + consumer_key = oauth_request.get_parameter('oauth_consumer_key') + consumer = oauth_provider_store.get_consumer(request, oauth_request, consumer_key) + except oauth_provider_store.InvalidConsumerError, err: + raise exceptions.AuthenticationFailed(err) - except oauth_provider.store.InvalidTokenError: - raise exceptions.AuthenticationFailed( - 'Invalid access token: %s' % oauth_request.get_parameter('oauth_token')) + if consumer.status != oauth_provider.consts.ACCEPTED: + msg = 'Invalid consumer key status: %s' % consumer.get_status_display() + raise exceptions.AuthenticationFailed(msg) - try: - self.validate_token(request, consumer, token) - except oauth.Error, e: - raise exceptions.AuthenticationFailed(e.message) + try: + token_param = oauth_request.get_parameter('oauth_token') + token = oauth_provider_store.get_access_token(request, oauth_request, consumer, token_param) + except oauth_provider_store.InvalidTokenError: + msg = 'Invalid access token: %s' % oauth_request.get_parameter('oauth_token') + raise exceptions.AuthenticationFailed(msg) - if not self.check_active(token.user): - raise exceptions.AuthenticationFailed('User not active: %s' % token.user.username) + try: + self.validate_token(request, consumer, token) + except oauth.Error, e: + raise exceptions.AuthenticationFailed(e.message) - if consumer and token: - return (token.user, token) + user = token.user - raise exceptions.AuthenticationFailed( - 'You are not allowed to access this resource.') + if not user.is_active: + raise exceptions.AuthenticationFailed('User inactive or deleted: %s' % user.username) - return None + return (token.user, token) def authenticate_header(self, request): return 'OAuth realm="%s"' % self.www_authenticate_realm @@ -226,9 +219,7 @@ class OAuthAuthentication(BaseAuthentication): Checks to ensure that all the OAuth parameter names are in the provided ``params``. """ - from oauth_provider.consts import OAUTH_PARAMETERS_NAMES - - for param_name in OAUTH_PARAMETERS_NAMES: + for param_name in oauth_provider.consts.OAUTH_PARAMETERS_NAMES: if param_name not in params: return False @@ -237,28 +228,19 @@ class OAuthAuthentication(BaseAuthentication): def is_valid_request(self, request): """ Checks whether the required parameters are either in the HTTP - ``Authorization`` header sent by some clients (the preferred method - according to OAuth spec) or fall back to ``GET/POST``. + `Authorization` header sent by some clients. + (The preferred method according to OAuth spec.) + Or fall back to `GET/POST`. """ - auth_params = request.META.get("HTTP_AUTHORIZATION", []) + auth_params = request.META.get('HTTP_AUTHORIZATION', []) return self.is_in(auth_params) or self.is_in(request.REQUEST) def validate_token(self, request, consumer, token): oauth_server, oauth_request = oauth_provider.utils.initialize_server_request(request) return oauth_server.verify_request(oauth_request, consumer, token) - def check_active(self, user): + def check_nonce(self, request, oauth_request): """ - Ensures the user has an active account. - - Optimized for the ``django.contrib.auth.models.User`` case. + Checks nonce of request. """ - if not self.require_active: - # Ignore & move on. - return True - - return user.is_active - - def check_nonce(self, request, oauth_request): - """Checks nonce of request""" return oauth_provider.store.store.check_nonce(request, oauth_request, oauth_request['oauth_nonce']) diff --git a/rest_framework/authtoken/migrations/0001_initial.py b/rest_framework/authtoken/migrations/0001_initial.py index f4e052e4..d5965e40 100644 --- a/rest_framework/authtoken/migrations/0001_initial.py +++ b/rest_framework/authtoken/migrations/0001_initial.py @@ -4,6 +4,8 @@ from south.db import db from south.v2 import SchemaMigration from django.db import models +from rest_framework.settings import api_settings + try: from django.contrib.auth import get_user_model @@ -45,20 +47,7 @@ class Migration(SchemaMigration): 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) }, "%s.%s" % (User._meta.app_label, User._meta.module_name): { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + 'Meta': {'object_name': User._meta.module_name}, }, 'authtoken.token': { 'Meta': {'object_name': 'Token'}, diff --git a/rest_framework/compat.py b/rest_framework/compat.py index e4bad0cb..6efe6762 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -429,6 +429,7 @@ except ImportError: # OAuth is optional try: + # Note: The `oauth2` package actually provides oauth1.0a support. Urg. import oauth2 as oauth except ImportError: oauth = None @@ -436,5 +437,7 @@ except ImportError: # OAuth is optional try: import oauth_provider + from oauth_provider.store import store as oauth_provider_store except ImportError: - oauth_provider = None
\ No newline at end of file + oauth_provider = None + oauth_provider_store = None diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 86c3a837..fe555ee5 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -13,12 +13,13 @@ from django import forms from django.forms import widgets from django.utils.encoding import is_protected_type from django.utils.translation import ugettext_lazy as _ -from rest_framework.compat import parse_date, parse_datetime -from rest_framework.compat import timezone + +from rest_framework import ISO_8601 +from rest_framework.compat import timezone, parse_date, parse_datetime, parse_time from rest_framework.compat import BytesIO from rest_framework.compat import six from rest_framework.compat import smart_text -from rest_framework.compat import parse_time +from rest_framework.settings import api_settings def is_simple_callable(obj): @@ -50,6 +51,46 @@ def get_component(obj, attr_name): return val +def readable_datetime_formats(formats): + format = ', '.join(formats).replace(ISO_8601, 'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HHMM|-HHMM|Z]') + return humanize_strptime(format) + + +def readable_date_formats(formats): + format = ', '.join(formats).replace(ISO_8601, 'YYYY[-MM[-DD]]') + return humanize_strptime(format) + + +def readable_time_formats(formats): + format = ', '.join(formats).replace(ISO_8601, 'hh:mm[:ss[.uuuuuu]]') + return humanize_strptime(format) + + +def humanize_strptime(format_string): + # Note that we're missing some of the locale specific mappings that + # don't really make sense. + mapping = { + "%Y": "YYYY", + "%y": "YY", + "%m": "MM", + "%b": "[Jan-Dec]", + "%B": "[January-December]", + "%d": "DD", + "%H": "hh", + "%I": "hh", # Requires '%p' to differentiate from '%H'. + "%M": "mm", + "%S": "ss", + "%f": "uuuuuu", + "%a": "[Mon-Sun]", + "%A": "[Monday-Sunday]", + "%p": "[AM|PM]", + "%z": "[+HHMM|-HHMM]" + } + for key, val in mapping.items(): + format_string = format_string.replace(key, val) + return format_string + + class Field(object): read_only = True creation_counter = 0 @@ -447,12 +488,16 @@ class DateField(WritableField): form_field_class = forms.DateField default_error_messages = { - 'invalid': _("'%s' value has an invalid date format. It must be " - "in YYYY-MM-DD format."), - 'invalid_date': _("'%s' value has the correct format (YYYY-MM-DD) " - "but it is an invalid date."), + 'invalid': _("Date has wrong format. Use one of these formats instead: %s"), } empty = None + input_formats = api_settings.DATE_INPUT_FORMATS + format = api_settings.DATE_FORMAT + + def __init__(self, input_formats=None, format=None, *args, **kwargs): + self.input_formats = input_formats if input_formats is not None else self.input_formats + self.format = format if format is not None else self.format + super(DateField, self).__init__(*args, **kwargs) def from_native(self, value): if value in validators.EMPTY_VALUES: @@ -468,17 +513,33 @@ class DateField(WritableField): if isinstance(value, datetime.date): return value - try: - parsed = parse_date(value) - if parsed is not None: - return parsed - except (ValueError, TypeError): - msg = self.error_messages['invalid_date'] % value - raise ValidationError(msg) + for format in self.input_formats: + if format.lower() == ISO_8601: + try: + parsed = parse_date(value) + except (ValueError, TypeError): + pass + else: + if parsed is not None: + return parsed + else: + try: + parsed = datetime.datetime.strptime(value, format) + except (ValueError, TypeError): + pass + else: + return parsed.date() - msg = self.error_messages['invalid'] % value + msg = self.error_messages['invalid'] % readable_date_formats(self.input_formats) raise ValidationError(msg) + def to_native(self, value): + if isinstance(value, datetime.datetime): + value = value.date() + if self.format.lower() == ISO_8601: + return value.isoformat() + return value.strftime(self.format) + class DateTimeField(WritableField): type_name = 'DateTimeField' @@ -486,15 +547,16 @@ class DateTimeField(WritableField): form_field_class = forms.DateTimeField default_error_messages = { - 'invalid': _("'%s' value has an invalid format. It must be in " - "YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."), - 'invalid_date': _("'%s' value has the correct format " - "(YYYY-MM-DD) but it is an invalid date."), - 'invalid_datetime': _("'%s' value has the correct format " - "(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) " - "but it is an invalid date/time."), + 'invalid': _("Datetime has wrong format. Use one of these formats instead: %s"), } empty = None + input_formats = api_settings.DATETIME_INPUT_FORMATS + format = api_settings.DATETIME_FORMAT + + def __init__(self, input_formats=None, format=None, *args, **kwargs): + self.input_formats = input_formats if input_formats is not None else self.input_formats + self.format = format if format is not None else self.format + super(DateTimeField, self).__init__(*args, **kwargs) def from_native(self, value): if value in validators.EMPTY_VALUES: @@ -516,25 +578,31 @@ class DateTimeField(WritableField): value = timezone.make_aware(value, default_timezone) return value - try: - parsed = parse_datetime(value) - if parsed is not None: - return parsed - except (ValueError, TypeError): - msg = self.error_messages['invalid_datetime'] % value - raise ValidationError(msg) - - try: - parsed = parse_date(value) - if parsed is not None: - return datetime.datetime(parsed.year, parsed.month, parsed.day) - except (ValueError, TypeError): - msg = self.error_messages['invalid_date'] % value - raise ValidationError(msg) + for format in self.input_formats: + if format.lower() == ISO_8601: + try: + parsed = parse_datetime(value) + except (ValueError, TypeError): + pass + else: + if parsed is not None: + return parsed + else: + try: + parsed = datetime.datetime.strptime(value, format) + except (ValueError, TypeError): + pass + else: + return parsed - msg = self.error_messages['invalid'] % value + msg = self.error_messages['invalid'] % readable_datetime_formats(self.input_formats) raise ValidationError(msg) + def to_native(self, value): + if self.format.lower() == ISO_8601: + return value.isoformat() + return value.strftime(self.format) + class TimeField(WritableField): type_name = 'TimeField' @@ -542,10 +610,16 @@ class TimeField(WritableField): form_field_class = forms.TimeField default_error_messages = { - 'invalid': _("'%s' value has an invalid format. It must be a valid " - "time in the HH:MM[:ss[.uuuuuu]] format."), + 'invalid': _("Time has wrong format. Use one of these formats instead: %s"), } empty = None + input_formats = api_settings.TIME_INPUT_FORMATS + format = api_settings.TIME_FORMAT + + def __init__(self, input_formats=None, format=None, *args, **kwargs): + self.input_formats = input_formats if input_formats is not None else self.input_formats + self.format = format if format is not None else self.format + super(TimeField, self).__init__(*args, **kwargs) def from_native(self, value): if value in validators.EMPTY_VALUES: @@ -554,13 +628,32 @@ class TimeField(WritableField): if isinstance(value, datetime.time): return value - try: - parsed = parse_time(value) - assert parsed is not None - return parsed - except (ValueError, TypeError): - msg = self.error_messages['invalid'] % value - raise ValidationError(msg) + for format in self.input_formats: + if format.lower() == ISO_8601: + try: + parsed = parse_time(value) + except (ValueError, TypeError): + pass + else: + if parsed is not None: + return parsed + else: + try: + parsed = datetime.datetime.strptime(value, format) + except (ValueError, TypeError): + pass + else: + return parsed.time() + + msg = self.error_messages['invalid'] % readable_time_formats(self.input_formats) + raise ValidationError(msg) + + def to_native(self, value): + if isinstance(value, datetime.datetime): + value = value.time() + if self.format.lower() == ISO_8601: + return value.isoformat() + return value.strftime(self.format) class IntegerField(WritableField): diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index 4e8551f2..97201c4b 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -37,7 +37,7 @@ def _get_validation_exclusions(obj, pk=None, slug_field=None): class CreateModelMixin(object): """ Create a model instance. - Should be mixed in with any `BaseView`. + Should be mixed in with any `GenericAPIView`. """ def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.DATA, files=request.FILES) @@ -94,7 +94,7 @@ class ListModelMixin(object): class RetrieveModelMixin(object): """ Retrieve a model instance. - Should be mixed in with `SingleObjectBaseView`. + Should be mixed in with `SingleObjectAPIView`. """ def retrieve(self, request, *args, **kwargs): self.object = self.get_object() @@ -105,7 +105,7 @@ class RetrieveModelMixin(object): class UpdateModelMixin(object): """ Update a model instance. - Should be mixed in with `SingleObjectBaseView`. + Should be mixed in with `SingleObjectAPIView`. """ def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) @@ -158,7 +158,7 @@ class UpdateModelMixin(object): class DestroyModelMixin(object): """ Destroy a model instance. - Should be mixed in with `SingleObjectBaseView`. + Should be mixed in with `SingleObjectAPIView`. """ def destroy(self, request, *args, **kwargs): obj = self.get_object() diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 266a2402..ba9e9e9c 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -418,12 +418,35 @@ class ModelSerializer(Serializer): """ _options_class = ModelSerializerOptions + field_mapping = { + models.AutoField: IntegerField, + models.FloatField: FloatField, + models.IntegerField: IntegerField, + models.PositiveIntegerField: IntegerField, + models.SmallIntegerField: IntegerField, + models.PositiveSmallIntegerField: IntegerField, + models.DateTimeField: DateTimeField, + models.DateField: DateField, + models.TimeField: TimeField, + models.EmailField: EmailField, + models.CharField: CharField, + models.URLField: URLField, + models.SlugField: SlugField, + models.TextField: CharField, + models.CommaSeparatedIntegerField: CharField, + models.BooleanField: BooleanField, + models.FileField: FileField, + models.ImageField: ImageField, + } + def get_default_fields(self): """ Return all the fields that should be serialized for the model. """ cls = self.opts.model + assert cls is not None, \ + "Serializer class '%s' is missing 'model' Meta option" % self.__class__.__name__ opts = get_concrete_model(cls)._meta pk_field = opts.pk # while pk_field.rel: @@ -513,28 +536,8 @@ class ModelSerializer(Serializer): kwargs['choices'] = model_field.flatchoices return ChoiceField(**kwargs) - field_mapping = { - models.AutoField: IntegerField, - models.FloatField: FloatField, - models.IntegerField: IntegerField, - models.PositiveIntegerField: IntegerField, - models.SmallIntegerField: IntegerField, - models.PositiveSmallIntegerField: IntegerField, - models.DateTimeField: DateTimeField, - models.DateField: DateField, - models.TimeField: TimeField, - models.EmailField: EmailField, - models.CharField: CharField, - models.URLField: URLField, - models.SlugField: SlugField, - models.TextField: CharField, - models.CommaSeparatedIntegerField: CharField, - models.BooleanField: BooleanField, - models.FileField: FileField, - models.ImageField: ImageField, - } try: - return field_mapping[model_field.__class__](**kwargs) + return self.field_mapping[model_field.__class__](**kwargs) except KeyError: return ModelField(model_field=model_field, **kwargs) diff --git a/rest_framework/settings.py b/rest_framework/settings.py index b7aa0bbe..eede0c5a 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -18,8 +18,11 @@ REST framework settings, checking for user settings first, then falling back to the defaults. """ from __future__ import unicode_literals + from django.conf import settings from django.utils import importlib + +from rest_framework import ISO_8601 from rest_framework.compat import six @@ -76,6 +79,22 @@ DEFAULTS = { 'URL_FORMAT_OVERRIDE': 'format', 'FORMAT_SUFFIX_KWARG': 'format', + + # Input and output formats + 'DATE_INPUT_FORMATS': ( + ISO_8601, + ), + 'DATE_FORMAT': ISO_8601, + + 'DATETIME_INPUT_FORMATS': ( + ISO_8601, + ), + 'DATETIME_FORMAT': ISO_8601, + + 'TIME_INPUT_FORMATS': ( + ISO_8601, + ), + 'TIME_FORMAT': ISO_8601, } diff --git a/rest_framework/tests/authentication.py b/rest_framework/tests/authentication.py index 8ef9d3ff..91429841 100644 --- a/rest_framework/tests/authentication.py +++ b/rest_framework/tests/authentication.py @@ -3,30 +3,42 @@ from django.contrib.auth.models import User from django.http import HttpResponse from django.test import Client, TestCase from django.utils import unittest -import time -from rest_framework import HTTP_HEADER_ENCODING, status +from rest_framework import HTTP_HEADER_ENCODING +from rest_framework import exceptions from rest_framework import permissions +from rest_framework import status +from rest_framework.authentication import ( + BaseAuthentication, + TokenAuthentication, + BasicAuthentication, + SessionAuthentication, + OAuthAuthentication +) from rest_framework.authtoken.models import Token -from rest_framework.authentication import TokenAuthentication, BasicAuthentication, SessionAuthentication, OAuthAuthentication from rest_framework.compat import patterns +from rest_framework.tests.utils import RequestFactory from rest_framework.views import APIView -from rest_framework.compat import oauth -from rest_framework.compat import oauth_provider +from rest_framework.compat import oauth, oauth_provider import json import base64 +import time + + +factory = RequestFactory() class MockView(APIView): permission_classes = (permissions.IsAuthenticated,) + def get(self, request): + return HttpResponse({'a': 1, 'b': 2, 'c': 3}) + def post(self, request): return HttpResponse({'a': 1, 'b': 2, 'c': 3}) def put(self, request): return HttpResponse({'a': 1, 'b': 2, 'c': 3}) - def get(self, request): - return HttpResponse({'a': 1, 'b': 2, 'c': 3}) urlpatterns = patterns('', (r'^session/$', MockView.as_view(authentication_classes=[SessionAuthentication])), @@ -54,7 +66,7 @@ class BasicAuthTests(TestCase): base64_credentials = base64.b64encode(credentials.encode(HTTP_HEADER_ENCODING)).decode(HTTP_HEADER_ENCODING) auth = 'Basic %s' % base64_credentials response = self.csrf_client.post('/basic/', {'example': 'example'}, HTTP_AUTHORIZATION=auth) - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, status.HTTP_200_OK) def test_post_json_passing_basic_auth(self): """Ensure POSTing form over basic auth with correct credentials passes and does not require CSRF""" @@ -62,17 +74,17 @@ class BasicAuthTests(TestCase): base64_credentials = base64.b64encode(credentials.encode(HTTP_HEADER_ENCODING)).decode(HTTP_HEADER_ENCODING) auth = 'Basic %s' % base64_credentials response = self.csrf_client.post('/basic/', json.dumps({'example': 'example'}), 'application/json', HTTP_AUTHORIZATION=auth) - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, status.HTTP_200_OK) def test_post_form_failing_basic_auth(self): """Ensure POSTing form over basic auth without correct credentials fails""" response = self.csrf_client.post('/basic/', {'example': 'example'}) - self.assertEqual(response.status_code, 401) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) def test_post_json_failing_basic_auth(self): """Ensure POSTing json over basic auth without correct credentials fails""" response = self.csrf_client.post('/basic/', json.dumps({'example': 'example'}), 'application/json') - self.assertEqual(response.status_code, 401) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(response['WWW-Authenticate'], 'Basic realm="api"') @@ -97,7 +109,7 @@ class SessionAuthTests(TestCase): """ self.csrf_client.login(username=self.username, password=self.password) response = self.csrf_client.post('/session/', {'example': 'example'}) - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_post_form_session_auth_passing(self): """ @@ -105,7 +117,7 @@ class SessionAuthTests(TestCase): """ self.non_csrf_client.login(username=self.username, password=self.password) response = self.non_csrf_client.post('/session/', {'example': 'example'}) - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, status.HTTP_200_OK) def test_put_form_session_auth_passing(self): """ @@ -113,14 +125,14 @@ class SessionAuthTests(TestCase): """ self.non_csrf_client.login(username=self.username, password=self.password) response = self.non_csrf_client.put('/session/', {'example': 'example'}) - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, status.HTTP_200_OK) def test_post_form_session_auth_failing(self): """ Ensure POSTing form over session authentication without logged in user fails. """ response = self.csrf_client.post('/session/', {'example': 'example'}) - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) class TokenAuthTests(TestCase): @@ -141,23 +153,23 @@ class TokenAuthTests(TestCase): """Ensure POSTing json over token auth with correct credentials passes and does not require CSRF""" auth = "Token " + self.key response = self.csrf_client.post('/token/', {'example': 'example'}, HTTP_AUTHORIZATION=auth) - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, status.HTTP_200_OK) def test_post_json_passing_token_auth(self): """Ensure POSTing form over token auth with correct credentials passes and does not require CSRF""" auth = "Token " + self.key response = self.csrf_client.post('/token/', json.dumps({'example': 'example'}), 'application/json', HTTP_AUTHORIZATION=auth) - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, status.HTTP_200_OK) def test_post_form_failing_token_auth(self): """Ensure POSTing form over token auth without correct credentials fails""" response = self.csrf_client.post('/token/', {'example': 'example'}) - self.assertEqual(response.status_code, 401) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) def test_post_json_failing_token_auth(self): """Ensure POSTing json over token auth without correct credentials fails""" response = self.csrf_client.post('/token/', json.dumps({'example': 'example'}), 'application/json') - self.assertEqual(response.status_code, 401) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) def test_token_has_auto_assigned_key_if_none_provided(self): """Ensure creating a token with no key will auto-assign a key""" @@ -170,7 +182,7 @@ class TokenAuthTests(TestCase): client = Client(enforce_csrf_checks=True) response = client.post('/auth-token/', json.dumps({'username': self.username, 'password': self.password}), 'application/json') - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(json.loads(response.content.decode('ascii'))['token'], self.key) def test_token_login_json_bad_creds(self): @@ -192,9 +204,31 @@ class TokenAuthTests(TestCase): client = Client(enforce_csrf_checks=True) response = client.post('/auth-token/', {'username': self.username, 'password': self.password}) - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(json.loads(response.content.decode('ascii'))['token'], self.key) + +class IncorrectCredentialsTests(TestCase): + def test_incorrect_credentials(self): + """ + If a request contains bad authentication credentials, then + authentication should run and error, even if no permissions + are set on the view. + """ + class IncorrectCredentialsAuth(BaseAuthentication): + def authenticate(self, request): + raise exceptions.AuthenticationFailed('Bad credentials') + + request = factory.get('/') + view = MockView.as_view( + authentication_classes=(IncorrectCredentialsAuth,), + permission_classes=() + ) + response = view(request) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.data, {'detail': 'Bad credentials'}) + + class OAuthTests(TestCase): """OAuth 1.0a authentication""" urls = 'rest_framework.tests.authentication' @@ -222,13 +256,11 @@ class OAuthTests(TestCase): self.consumer = Consumer.objects.create(key=self.CONSUMER_KEY, secret=self.CONSUMER_SECRET, name='example', user=self.user, status=self.consts.ACCEPTED) - self.resource = Resource.objects.create(name="resource name", url="api/") self.token = OAuthToken.objects.create(user=self.user, consumer=self.consumer, resource=self.resource, token_type=OAuthToken.ACCESS, key=self.TOKEN_KEY, secret=self.TOKEN_SECRET, is_approved=True ) - def _create_authorization_header(self): params = { 'oauth_version': "1.0", @@ -348,4 +380,3 @@ class OAuthTests(TestCase): response = self.csrf_client.post('/oauth/', HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 200) - diff --git a/rest_framework/tests/decorators.py b/rest_framework/tests/decorators.py index a11af3a5..1016fed3 100644 --- a/rest_framework/tests/decorators.py +++ b/rest_framework/tests/decorators.py @@ -59,11 +59,11 @@ class DecoratorTestCase(TestCase): request = self.factory.get('/') response = view(request) - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, status.HTTP_200_OK) request = self.factory.post('/') response = view(request) - self.assertEqual(response.status_code, 405) + self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) def test_calling_put_method(self): @@ -73,11 +73,11 @@ class DecoratorTestCase(TestCase): request = self.factory.put('/') response = view(request) - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, status.HTTP_200_OK) request = self.factory.post('/') response = view(request) - self.assertEqual(response.status_code, 405) + self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) def test_calling_patch_method(self): @@ -87,11 +87,11 @@ class DecoratorTestCase(TestCase): request = self.factory.patch('/') response = view(request) - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, status.HTTP_200_OK) request = self.factory.post('/') response = view(request) - self.assertEqual(response.status_code, 405) + self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) def test_renderer_classes(self): @@ -139,7 +139,7 @@ class DecoratorTestCase(TestCase): request = self.factory.get('/') response = view(request) - self.assertEquals(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_throttle_classes(self): class OncePerDayUserThrottle(UserRateThrottle): @@ -152,7 +152,7 @@ class DecoratorTestCase(TestCase): request = self.factory.get('/') response = view(request) - self.assertEquals(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_200_OK) response = view(request) - self.assertEquals(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS) + self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS) diff --git a/rest_framework/tests/description.py b/rest_framework/tests/description.py index b564a1d4..5b3315bc 100644 --- a/rest_framework/tests/description.py +++ b/rest_framework/tests/description.py @@ -53,7 +53,7 @@ class TestViewNamesAndDescriptions(TestCase): """Ensure Resource names are based on the classname by default.""" class MockView(APIView): pass - self.assertEquals(MockView().get_name(), 'Mock') + self.assertEqual(MockView().get_name(), 'Mock') def test_resource_name_can_be_set_explicitly(self): """Ensure Resource names can be set using the 'get_name' method.""" @@ -61,7 +61,7 @@ class TestViewNamesAndDescriptions(TestCase): class MockView(APIView): def get_name(self): return example - self.assertEquals(MockView().get_name(), example) + self.assertEqual(MockView().get_name(), example) def test_resource_description_uses_docstring_by_default(self): """Ensure Resource names are based on the docstring by default.""" @@ -81,7 +81,7 @@ class TestViewNamesAndDescriptions(TestCase): # hash style header #""" - self.assertEquals(MockView().get_description(), DESCRIPTION) + self.assertEqual(MockView().get_description(), DESCRIPTION) def test_resource_description_can_be_set_explicitly(self): """Ensure Resource descriptions can be set using the 'get_description' method.""" @@ -91,7 +91,7 @@ class TestViewNamesAndDescriptions(TestCase): """docstring""" def get_description(self): return example - self.assertEquals(MockView().get_description(), example) + self.assertEqual(MockView().get_description(), example) def test_resource_description_supports_unicode(self): @@ -99,7 +99,7 @@ class TestViewNamesAndDescriptions(TestCase): """Проверка""" pass - self.assertEquals(MockView().get_description(), "Проверка") + self.assertEqual(MockView().get_description(), "Проверка") def test_resource_description_does_not_require_docstring(self): @@ -109,13 +109,13 @@ class TestViewNamesAndDescriptions(TestCase): class MockView(APIView): def get_description(self): return example - self.assertEquals(MockView().get_description(), example) + self.assertEqual(MockView().get_description(), example) def test_resource_description_can_be_empty(self): """Ensure that if a resource has no doctring or 'description' class attribute, then it's description is the empty string.""" class MockView(APIView): pass - self.assertEquals(MockView().get_description(), '') + self.assertEqual(MockView().get_description(), '') def test_markdown(self): """Ensure markdown to HTML works as expected""" diff --git a/rest_framework/tests/fields.py b/rest_framework/tests/fields.py index 34f61678..28f18ed8 100644 --- a/rest_framework/tests/fields.py +++ b/rest_framework/tests/fields.py @@ -3,9 +3,11 @@ General serializer field tests. """ from __future__ import unicode_literals import datetime + from django.db import models from django.test import TestCase from django.core import validators + from rest_framework import serializers @@ -43,53 +45,386 @@ class BasicFieldTests(TestCase): auto_now and auto_now_add fields should be read_only by default. """ serializer = TimestampedModelSerializer() - self.assertEquals(serializer.fields['added'].read_only, True) + self.assertEqual(serializer.fields['added'].read_only, True) def test_auto_pk_fields_read_only(self): """ AutoField fields should be read_only by default. """ serializer = TimestampedModelSerializer() - self.assertEquals(serializer.fields['id'].read_only, True) + self.assertEqual(serializer.fields['id'].read_only, True) def test_non_auto_pk_fields_not_read_only(self): """ PK fields other than AutoField fields should not be read_only by default. """ serializer = CharPrimaryKeyModelSerializer() - self.assertEquals(serializer.fields['id'].read_only, False) + self.assertEqual(serializer.fields['id'].read_only, False) + + +class DateFieldTest(TestCase): + """ + Tests for the DateFieldTest from_native() and to_native() behavior + """ + + def test_from_native_string(self): + """ + Make sure from_native() accepts default iso input formats. + """ + f = serializers.DateField() + result_1 = f.from_native('1984-07-31') + + self.assertEqual(datetime.date(1984, 7, 31), result_1) + + def test_from_native_datetime_date(self): + """ + Make sure from_native() accepts a datetime.date instance. + """ + f = serializers.DateField() + result_1 = f.from_native(datetime.date(1984, 7, 31)) + + self.assertEqual(result_1, datetime.date(1984, 7, 31)) + + def test_from_native_custom_format(self): + """ + Make sure from_native() accepts custom input formats. + """ + f = serializers.DateField(input_formats=['%Y -- %d']) + result = f.from_native('1984 -- 31') + + self.assertEqual(datetime.date(1984, 1, 31), result) + + def test_from_native_invalid_default_on_custom_format(self): + """ + Make sure from_native() don't accept default formats if custom format is preset + """ + f = serializers.DateField(input_formats=['%Y -- %d']) + + try: + f.from_native('1984-07-31') + except validators.ValidationError as e: + self.assertEqual(e.messages, ["Date has wrong format. Use one of these formats instead: YYYY -- DD"]) + else: + self.fail("ValidationError was not properly raised") + + def test_from_native_empty(self): + """ + Make sure from_native() returns None on empty param. + """ + f = serializers.DateField() + result = f.from_native('') + + self.assertEqual(result, None) + + def test_from_native_none(self): + """ + Make sure from_native() returns None on None param. + """ + f = serializers.DateField() + result = f.from_native(None) + + self.assertEqual(result, None) + + def test_from_native_invalid_date(self): + """ + Make sure from_native() raises a ValidationError on passing an invalid date. + """ + f = serializers.DateField() + + try: + f.from_native('1984-13-31') + except validators.ValidationError as e: + self.assertEqual(e.messages, ["Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]]"]) + else: + self.fail("ValidationError was not properly raised") + + def test_from_native_invalid_format(self): + """ + Make sure from_native() raises a ValidationError on passing an invalid format. + """ + f = serializers.DateField() + + try: + f.from_native('1984 -- 31') + except validators.ValidationError as e: + self.assertEqual(e.messages, ["Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]]"]) + else: + self.fail("ValidationError was not properly raised") + + def test_to_native(self): + """ + Make sure to_native() returns isoformat as default. + """ + f = serializers.DateField() + + result_1 = f.to_native(datetime.date(1984, 7, 31)) + + self.assertEqual('1984-07-31', result_1) + + def test_to_native_custom_format(self): + """ + Make sure to_native() returns correct custom format. + """ + f = serializers.DateField(format="%Y - %m.%d") + + result_1 = f.to_native(datetime.date(1984, 7, 31)) + + self.assertEqual('1984 - 07.31', result_1) + + +class DateTimeFieldTest(TestCase): + """ + Tests for the DateTimeField from_native() and to_native() behavior + """ + + def test_from_native_string(self): + """ + Make sure from_native() accepts default iso input formats. + """ + f = serializers.DateTimeField() + result_1 = f.from_native('1984-07-31 04:31') + result_2 = f.from_native('1984-07-31 04:31:59') + result_3 = f.from_native('1984-07-31 04:31:59.000200') + + self.assertEqual(datetime.datetime(1984, 7, 31, 4, 31), result_1) + self.assertEqual(datetime.datetime(1984, 7, 31, 4, 31, 59), result_2) + self.assertEqual(datetime.datetime(1984, 7, 31, 4, 31, 59, 200), result_3) + + def test_from_native_datetime_datetime(self): + """ + Make sure from_native() accepts a datetime.datetime instance. + """ + f = serializers.DateTimeField() + result_1 = f.from_native(datetime.datetime(1984, 7, 31, 4, 31)) + result_2 = f.from_native(datetime.datetime(1984, 7, 31, 4, 31, 59)) + result_3 = f.from_native(datetime.datetime(1984, 7, 31, 4, 31, 59, 200)) + + self.assertEqual(result_1, datetime.datetime(1984, 7, 31, 4, 31)) + self.assertEqual(result_2, datetime.datetime(1984, 7, 31, 4, 31, 59)) + self.assertEqual(result_3, datetime.datetime(1984, 7, 31, 4, 31, 59, 200)) + + def test_from_native_custom_format(self): + """ + Make sure from_native() accepts custom input formats. + """ + f = serializers.DateTimeField(input_formats=['%Y -- %H:%M']) + result = f.from_native('1984 -- 04:59') + + self.assertEqual(datetime.datetime(1984, 1, 1, 4, 59), result) + + def test_from_native_invalid_default_on_custom_format(self): + """ + Make sure from_native() don't accept default formats if custom format is preset + """ + f = serializers.DateTimeField(input_formats=['%Y -- %H:%M']) + + try: + f.from_native('1984-07-31 04:31:59') + except validators.ValidationError as e: + self.assertEqual(e.messages, ["Datetime has wrong format. Use one of these formats instead: YYYY -- hh:mm"]) + else: + self.fail("ValidationError was not properly raised") - def test_TimeField_from_native(self): + def test_from_native_empty(self): + """ + Make sure from_native() returns None on empty param. + """ + f = serializers.DateTimeField() + result = f.from_native('') + + self.assertEqual(result, None) + + def test_from_native_none(self): + """ + Make sure from_native() returns None on None param. + """ + f = serializers.DateTimeField() + result = f.from_native(None) + + self.assertEqual(result, None) + + def test_from_native_invalid_datetime(self): + """ + Make sure from_native() raises a ValidationError on passing an invalid datetime. + """ + f = serializers.DateTimeField() + + try: + f.from_native('04:61:59') + except validators.ValidationError as e: + self.assertEqual(e.messages, ["Datetime has wrong format. Use one of these formats instead: " + "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HHMM|-HHMM|Z]"]) + else: + self.fail("ValidationError was not properly raised") + + def test_from_native_invalid_format(self): + """ + Make sure from_native() raises a ValidationError on passing an invalid format. + """ + f = serializers.DateTimeField() + + try: + f.from_native('04 -- 31') + except validators.ValidationError as e: + self.assertEqual(e.messages, ["Datetime has wrong format. Use one of these formats instead: " + "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HHMM|-HHMM|Z]"]) + else: + self.fail("ValidationError was not properly raised") + + def test_to_native(self): + """ + Make sure to_native() returns isoformat as default. + """ + f = serializers.DateTimeField() + + result_1 = f.to_native(datetime.datetime(1984, 7, 31)) + result_2 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31)) + result_3 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31, 59)) + result_4 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31, 59, 200)) + + self.assertEqual('1984-07-31T00:00:00', result_1) + self.assertEqual('1984-07-31T04:31:00', result_2) + self.assertEqual('1984-07-31T04:31:59', result_3) + self.assertEqual('1984-07-31T04:31:59.000200', result_4) + + def test_to_native_custom_format(self): + """ + Make sure to_native() returns correct custom format. + """ + f = serializers.DateTimeField(format="%Y - %H:%M") + + result_1 = f.to_native(datetime.datetime(1984, 7, 31)) + result_2 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31)) + result_3 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31, 59)) + result_4 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31, 59, 200)) + + self.assertEqual('1984 - 00:00', result_1) + self.assertEqual('1984 - 04:31', result_2) + self.assertEqual('1984 - 04:31', result_3) + self.assertEqual('1984 - 04:31', result_4) + + +class TimeFieldTest(TestCase): + """ + Tests for the TimeField from_native() and to_native() behavior + """ + + def test_from_native_string(self): + """ + Make sure from_native() accepts default iso input formats. + """ f = serializers.TimeField() - result = f.from_native('12:34:56.987654') + result_1 = f.from_native('04:31') + result_2 = f.from_native('04:31:59') + result_3 = f.from_native('04:31:59.000200') - self.assertEqual(datetime.time(12, 34, 56, 987654), result) + self.assertEqual(datetime.time(4, 31), result_1) + self.assertEqual(datetime.time(4, 31, 59), result_2) + self.assertEqual(datetime.time(4, 31, 59, 200), result_3) - def test_TimeField_from_native_datetime_time(self): + def test_from_native_datetime_time(self): """ Make sure from_native() accepts a datetime.time instance. """ f = serializers.TimeField() - result = f.from_native(datetime.time(12, 34, 56)) - self.assertEqual(result, datetime.time(12, 34, 56)) + result_1 = f.from_native(datetime.time(4, 31)) + result_2 = f.from_native(datetime.time(4, 31, 59)) + result_3 = f.from_native(datetime.time(4, 31, 59, 200)) - def test_TimeField_from_native_empty(self): + self.assertEqual(result_1, datetime.time(4, 31)) + self.assertEqual(result_2, datetime.time(4, 31, 59)) + self.assertEqual(result_3, datetime.time(4, 31, 59, 200)) + + def test_from_native_custom_format(self): + """ + Make sure from_native() accepts custom input formats. + """ + f = serializers.TimeField(input_formats=['%H -- %M']) + result = f.from_native('04 -- 31') + + self.assertEqual(datetime.time(4, 31), result) + + def test_from_native_invalid_default_on_custom_format(self): + """ + Make sure from_native() don't accept default formats if custom format is preset + """ + f = serializers.TimeField(input_formats=['%H -- %M']) + + try: + f.from_native('04:31:59') + except validators.ValidationError as e: + self.assertEqual(e.messages, ["Time has wrong format. Use one of these formats instead: hh -- mm"]) + else: + self.fail("ValidationError was not properly raised") + + def test_from_native_empty(self): + """ + Make sure from_native() returns None on empty param. + """ f = serializers.TimeField() result = f.from_native('') + self.assertEqual(result, None) - def test_TimeField_from_native_invalid_time(self): + def test_from_native_none(self): + """ + Make sure from_native() returns None on None param. + """ + f = serializers.TimeField() + result = f.from_native(None) + + self.assertEqual(result, None) + + def test_from_native_invalid_time(self): + """ + Make sure from_native() raises a ValidationError on passing an invalid time. + """ + f = serializers.TimeField() + + try: + f.from_native('04:61:59') + except validators.ValidationError as e: + self.assertEqual(e.messages, ["Time has wrong format. Use one of these formats instead: " + "hh:mm[:ss[.uuuuuu]]"]) + else: + self.fail("ValidationError was not properly raised") + + def test_from_native_invalid_format(self): + """ + Make sure from_native() raises a ValidationError on passing an invalid format. + """ f = serializers.TimeField() try: - f.from_native('12:69:12') + f.from_native('04 -- 31') except validators.ValidationError as e: - self.assertEqual(e.messages, ["'12:69:12' value has an invalid " - "format. It must be a valid time " - "in the HH:MM[:ss[.uuuuuu]] format."]) + self.assertEqual(e.messages, ["Time has wrong format. Use one of these formats instead: " + "hh:mm[:ss[.uuuuuu]]"]) else: self.fail("ValidationError was not properly raised") - def test_TimeFieldModelSerializer(self): - serializer = TimeFieldModelSerializer() - self.assertTrue(isinstance(serializer.fields['clock'], serializers.TimeField)) + def test_to_native(self): + """ + Make sure to_native() returns isoformat as default. + """ + f = serializers.TimeField() + result_1 = f.to_native(datetime.time(4, 31)) + result_2 = f.to_native(datetime.time(4, 31, 59)) + result_3 = f.to_native(datetime.time(4, 31, 59, 200)) + + self.assertEqual('04:31:00', result_1) + self.assertEqual('04:31:59', result_2) + self.assertEqual('04:31:59.000200', result_3) + + def test_to_native_custom_format(self): + """ + Make sure to_native() returns correct custom format. + """ + f = serializers.TimeField(format="%H - %S [%f]") + result_1 = f.to_native(datetime.time(4, 31)) + result_2 = f.to_native(datetime.time(4, 31, 59)) + result_3 = f.to_native(datetime.time(4, 31, 59, 200)) + + self.assertEqual('04 - 00 [000000]', result_1) + self.assertEqual('04 - 59 [000000]', result_2) + self.assertEqual('04 - 59 [000200]', result_3) diff --git a/rest_framework/tests/files.py b/rest_framework/tests/files.py index ce00ea6b..487046ac 100644 --- a/rest_framework/tests/files.py +++ b/rest_framework/tests/files.py @@ -33,8 +33,8 @@ class FileSerializerTests(TestCase): serializer = UploadedFileSerializer(data={'created': now}, files={'file': file}) uploaded_file = UploadedFile(file=file, created=now) self.assertTrue(serializer.is_valid()) - self.assertEquals(serializer.object.created, uploaded_file.created) - self.assertEquals(serializer.object.file, uploaded_file.file) + self.assertEqual(serializer.object.created, uploaded_file.created) + self.assertEqual(serializer.object.file, uploaded_file.file) self.assertFalse(serializer.object is uploaded_file) def test_creation_failure(self): diff --git a/rest_framework/tests/filterset.py b/rest_framework/tests/filterset.py index daea6e53..fe92e0bc 100644 --- a/rest_framework/tests/filterset.py +++ b/rest_framework/tests/filterset.py @@ -65,8 +65,8 @@ class IntegrationTestFiltering(TestCase): self.objects = FilterableItem.objects self.data = [ - {'id': obj.id, 'text': obj.text, 'decimal': obj.decimal, 'date': obj.date} - for obj in self.objects.all() + {'id': obj.id, 'text': obj.text, 'decimal': obj.decimal, 'date': obj.date.isoformat()} + for obj in self.objects.all() ] @unittest.skipUnless(django_filters, 'django-filters not installed') @@ -79,24 +79,24 @@ class IntegrationTestFiltering(TestCase): # Basic test with no filter. request = factory.get('/') response = view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, self.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, self.data) # Tests that the decimal filter works. search_decimal = Decimal('2.25') request = factory.get('/?decimal=%s' % search_decimal) response = view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_200_OK) expected_data = [f for f in self.data if f['decimal'] == search_decimal] - self.assertEquals(response.data, expected_data) + self.assertEqual(response.data, expected_data) # Tests that the date filter works. search_date = datetime.date(2012, 9, 22) request = factory.get('/?date=%s' % search_date) # search_date str: '2012-09-22' response = view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - expected_data = [f for f in self.data if f['date'] == search_date] - self.assertEquals(response.data, expected_data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + expected_data = [f for f in self.data if datetime.datetime.strptime(f['date'], '%Y-%m-%d').date() == search_date] + self.assertEqual(response.data, expected_data) @unittest.skipUnless(django_filters, 'django-filters not installed') def test_get_filtered_class_root_view(self): @@ -109,42 +109,43 @@ class IntegrationTestFiltering(TestCase): # Basic test with no filter. request = factory.get('/') response = view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, self.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, self.data) # Tests that the decimal filter set with 'lt' in the filter class works. search_decimal = Decimal('4.25') request = factory.get('/?decimal=%s' % search_decimal) response = view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_200_OK) expected_data = [f for f in self.data if f['decimal'] < search_decimal] - self.assertEquals(response.data, expected_data) + self.assertEqual(response.data, expected_data) # Tests that the date filter set with 'gt' in the filter class works. search_date = datetime.date(2012, 10, 2) request = factory.get('/?date=%s' % search_date) # search_date str: '2012-10-02' response = view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - expected_data = [f for f in self.data if f['date'] > search_date] - self.assertEquals(response.data, expected_data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + expected_data = [f for f in self.data if datetime.datetime.strptime(f['date'], '%Y-%m-%d').date() > search_date] + self.assertEqual(response.data, expected_data) # Tests that the text filter set with 'icontains' in the filter class works. search_text = 'ff' request = factory.get('/?text=%s' % search_text) response = view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_200_OK) expected_data = [f for f in self.data if search_text in f['text'].lower()] - self.assertEquals(response.data, expected_data) + self.assertEqual(response.data, expected_data) # Tests that multiple filters works. search_decimal = Decimal('5.25') search_date = datetime.date(2012, 10, 2) request = factory.get('/?decimal=%s&date=%s' % (search_decimal, search_date)) response = view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - expected_data = [f for f in self.data if f['date'] > search_date and - f['decimal'] < search_decimal] - self.assertEquals(response.data, expected_data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + expected_data = [f for f in self.data if + datetime.datetime.strptime(f['date'], '%Y-%m-%d').date() > search_date and + f['decimal'] < search_decimal] + self.assertEqual(response.data, expected_data) @unittest.skipUnless(django_filters, 'django-filters not installed') def test_incorrectly_configured_filter(self): @@ -166,4 +167,4 @@ class IntegrationTestFiltering(TestCase): search_integer = 10 request = factory.get('/?integer=%s' % search_integer) response = view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/rest_framework/tests/genericrelations.py b/rest_framework/tests/genericrelations.py index 52b47f97..c38bfb9f 100644 --- a/rest_framework/tests/genericrelations.py +++ b/rest_framework/tests/genericrelations.py @@ -67,7 +67,7 @@ class TestGenericRelations(TestCase): 'tags': ['django', 'python'], 'url': 'https://www.djangoproject.com/' } - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_generic_fk(self): """ @@ -97,4 +97,4 @@ class TestGenericRelations(TestCase): 'tagged_item': 'Note: Remember the milk' } ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) diff --git a/rest_framework/tests/generics.py b/rest_framework/tests/generics.py index adbd6253..f8f2ddaa 100644 --- a/rest_framework/tests/generics.py +++ b/rest_framework/tests/generics.py @@ -43,7 +43,7 @@ class SlugBasedInstanceView(InstanceView): class TestRootView(TestCase): def setUp(self): """ - Create 3 BasicModel intances. + Create 3 BasicModel instances. """ items = ['foo', 'bar', 'baz'] for item in items: @@ -61,8 +61,8 @@ class TestRootView(TestCase): """ request = factory.get('/') response = self.view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, self.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, self.data) def test_post_root_view(self): """ @@ -72,10 +72,10 @@ class TestRootView(TestCase): request = factory.post('/', json.dumps(content), content_type='application/json') response = self.view(request).render() - self.assertEquals(response.status_code, status.HTTP_201_CREATED) - self.assertEquals(response.data, {'id': 4, 'text': 'foobar'}) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.data, {'id': 4, 'text': 'foobar'}) created = self.objects.get(id=4) - self.assertEquals(created.text, 'foobar') + self.assertEqual(created.text, 'foobar') def test_put_root_view(self): """ @@ -85,8 +85,8 @@ class TestRootView(TestCase): request = factory.put('/', json.dumps(content), content_type='application/json') response = self.view(request).render() - self.assertEquals(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) - self.assertEquals(response.data, {"detail": "Method 'PUT' not allowed."}) + self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) + self.assertEqual(response.data, {"detail": "Method 'PUT' not allowed."}) def test_delete_root_view(self): """ @@ -94,8 +94,8 @@ class TestRootView(TestCase): """ request = factory.delete('/') response = self.view(request).render() - self.assertEquals(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) - self.assertEquals(response.data, {"detail": "Method 'DELETE' not allowed."}) + self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) + self.assertEqual(response.data, {"detail": "Method 'DELETE' not allowed."}) def test_options_root_view(self): """ @@ -116,8 +116,8 @@ class TestRootView(TestCase): 'name': 'Root', 'description': 'Example description for OPTIONS.' } - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, expected) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, expected) def test_post_cannot_set_id(self): """ @@ -127,10 +127,10 @@ class TestRootView(TestCase): request = factory.post('/', json.dumps(content), content_type='application/json') response = self.view(request).render() - self.assertEquals(response.status_code, status.HTTP_201_CREATED) - self.assertEquals(response.data, {'id': 4, 'text': 'foobar'}) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.data, {'id': 4, 'text': 'foobar'}) created = self.objects.get(id=4) - self.assertEquals(created.text, 'foobar') + self.assertEqual(created.text, 'foobar') class TestInstanceView(TestCase): @@ -155,8 +155,8 @@ class TestInstanceView(TestCase): """ request = factory.get('/1') response = self.view(request, pk=1).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, self.data[0]) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, self.data[0]) def test_post_instance_view(self): """ @@ -166,8 +166,8 @@ class TestInstanceView(TestCase): request = factory.post('/', json.dumps(content), content_type='application/json') response = self.view(request).render() - self.assertEquals(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) - self.assertEquals(response.data, {"detail": "Method 'POST' not allowed."}) + self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) + self.assertEqual(response.data, {"detail": "Method 'POST' not allowed."}) def test_put_instance_view(self): """ @@ -177,10 +177,10 @@ class TestInstanceView(TestCase): request = factory.put('/1', json.dumps(content), content_type='application/json') response = self.view(request, pk='1').render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, {'id': 1, 'text': 'foobar'}) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, {'id': 1, 'text': 'foobar'}) updated = self.objects.get(id=1) - self.assertEquals(updated.text, 'foobar') + self.assertEqual(updated.text, 'foobar') def test_patch_instance_view(self): """ @@ -191,10 +191,10 @@ class TestInstanceView(TestCase): content_type='application/json') response = self.view(request, pk=1).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, {'id': 1, 'text': 'foobar'}) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, {'id': 1, 'text': 'foobar'}) updated = self.objects.get(id=1) - self.assertEquals(updated.text, 'foobar') + self.assertEqual(updated.text, 'foobar') def test_delete_instance_view(self): """ @@ -202,10 +202,10 @@ class TestInstanceView(TestCase): """ request = factory.delete('/1') response = self.view(request, pk=1).render() - self.assertEquals(response.status_code, status.HTTP_204_NO_CONTENT) - self.assertEquals(response.content, six.b('')) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertEqual(response.content, six.b('')) ids = [obj.id for obj in self.objects.all()] - self.assertEquals(ids, [2, 3]) + self.assertEqual(ids, [2, 3]) def test_options_instance_view(self): """ @@ -226,8 +226,8 @@ class TestInstanceView(TestCase): 'name': 'Instance', 'description': 'Example description for OPTIONS.' } - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, expected) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, expected) def test_put_cannot_set_id(self): """ @@ -237,10 +237,10 @@ class TestInstanceView(TestCase): request = factory.put('/1', json.dumps(content), content_type='application/json') response = self.view(request, pk=1).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, {'id': 1, 'text': 'foobar'}) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, {'id': 1, 'text': 'foobar'}) updated = self.objects.get(id=1) - self.assertEquals(updated.text, 'foobar') + self.assertEqual(updated.text, 'foobar') def test_put_to_deleted_instance(self): """ @@ -252,10 +252,10 @@ class TestInstanceView(TestCase): request = factory.put('/1', json.dumps(content), content_type='application/json') response = self.view(request, pk=1).render() - self.assertEquals(response.status_code, status.HTTP_201_CREATED) - self.assertEquals(response.data, {'id': 1, 'text': 'foobar'}) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.data, {'id': 1, 'text': 'foobar'}) updated = self.objects.get(id=1) - self.assertEquals(updated.text, 'foobar') + self.assertEqual(updated.text, 'foobar') def test_put_as_create_on_id_based_url(self): """ @@ -267,9 +267,9 @@ class TestInstanceView(TestCase): request = factory.put('/5', json.dumps(content), content_type='application/json') response = self.view(request, pk=5).render() - self.assertEquals(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) new_obj = self.objects.get(pk=5) - self.assertEquals(new_obj.text, 'foobar') + self.assertEqual(new_obj.text, 'foobar') def test_put_as_create_on_slug_based_url(self): """ @@ -280,10 +280,10 @@ class TestInstanceView(TestCase): request = factory.put('/test_slug', json.dumps(content), content_type='application/json') response = self.slug_based_view(request, slug='test_slug').render() - self.assertEquals(response.status_code, status.HTTP_201_CREATED) - self.assertEquals(response.data, {'slug': 'test_slug', 'text': 'foobar'}) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.data, {'slug': 'test_slug', 'text': 'foobar'}) new_obj = SlugBasedModel.objects.get(slug='test_slug') - self.assertEquals(new_obj.text, 'foobar') + self.assertEqual(new_obj.text, 'foobar') # Regression test for #285 @@ -314,9 +314,9 @@ class TestCreateModelWithAutoNowAddField(TestCase): request = factory.post('/', json.dumps(content), content_type='application/json') response = self.view(request).render() - self.assertEquals(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) created = self.objects.get(id=1) - self.assertEquals(created.content, 'foobar') + self.assertEqual(created.content, 'foobar') # Test for particularly ugly regression with m2m in browseable API @@ -344,9 +344,9 @@ class ExampleView(generics.ListCreateAPIView): class TestM2MBrowseableAPI(TestCase): def test_m2m_in_browseable_api(self): """ - Test for particularly ugly reression with m2m in browseable API + Test for particularly ugly regression with m2m in browseable API """ request = factory.get('/', HTTP_ACCEPT='text/html') view = ExampleView().as_view() response = view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/rest_framework/tests/htmlrenderer.py b/rest_framework/tests/htmlrenderer.py index 702e8024..8f2e2b5a 100644 --- a/rest_framework/tests/htmlrenderer.py +++ b/rest_framework/tests/htmlrenderer.py @@ -4,6 +4,7 @@ from django.http import Http404 from django.test import TestCase from django.template import TemplateDoesNotExist, Template import django.template.loader +from rest_framework import status from rest_framework.compat import patterns, url from rest_framework.decorators import api_view, renderer_classes from rest_framework.renderers import TemplateHTMLRenderer @@ -65,19 +66,19 @@ class TemplateHTMLRendererTests(TestCase): def test_simple_html_view(self): response = self.client.get('/') self.assertContains(response, "example: foobar") - self.assertEquals(response['Content-Type'], 'text/html') + self.assertEqual(response['Content-Type'], 'text/html') def test_not_found_html_view(self): response = self.client.get('/not_found') - self.assertEquals(response.status_code, 404) - self.assertEquals(response.content, six.b("404 Not Found")) - self.assertEquals(response['Content-Type'], 'text/html') + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(response.content, six.b("404 Not Found")) + self.assertEqual(response['Content-Type'], 'text/html') def test_permission_denied_html_view(self): response = self.client.get('/permission_denied') - self.assertEquals(response.status_code, 403) - self.assertEquals(response.content, six.b("403 Forbidden")) - self.assertEquals(response['Content-Type'], 'text/html') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.content, six.b("403 Forbidden")) + self.assertEqual(response['Content-Type'], 'text/html') class TemplateHTMLRendererExceptionTests(TestCase): @@ -106,12 +107,12 @@ class TemplateHTMLRendererExceptionTests(TestCase): def test_not_found_html_view_with_template(self): response = self.client.get('/not_found') - self.assertEquals(response.status_code, 404) - self.assertEquals(response.content, six.b("404: Not found")) - self.assertEquals(response['Content-Type'], 'text/html') + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(response.content, six.b("404: Not found")) + self.assertEqual(response['Content-Type'], 'text/html') def test_permission_denied_html_view_with_template(self): response = self.client.get('/permission_denied') - self.assertEquals(response.status_code, 403) - self.assertEquals(response.content, six.b("403: Permission denied")) - self.assertEquals(response['Content-Type'], 'text/html') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.content, six.b("403: Permission denied")) + self.assertEqual(response['Content-Type'], 'text/html') diff --git a/rest_framework/tests/hyperlinkedserializers.py b/rest_framework/tests/hyperlinkedserializers.py index bc9b8769..9a61f299 100644 --- a/rest_framework/tests/hyperlinkedserializers.py +++ b/rest_framework/tests/hyperlinkedserializers.py @@ -100,7 +100,7 @@ class TestBasicHyperlinkedView(TestCase): def setUp(self): """ - Create 3 BasicModel intances. + Create 3 BasicModel instances. """ items = ['foo', 'bar', 'baz'] for item in items: @@ -119,8 +119,8 @@ class TestBasicHyperlinkedView(TestCase): """ request = factory.get('/basic/') response = self.list_view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, self.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, self.data) def test_get_detail_view(self): """ @@ -128,8 +128,8 @@ class TestBasicHyperlinkedView(TestCase): """ request = factory.get('/basic/1') response = self.detail_view(request, pk=1).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, self.data[0]) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, self.data[0]) class TestManyToManyHyperlinkedView(TestCase): @@ -137,7 +137,7 @@ class TestManyToManyHyperlinkedView(TestCase): def setUp(self): """ - Create 3 BasicModel intances. + Create 3 BasicModel instances. """ items = ['foo', 'bar', 'baz'] anchors = [] @@ -167,8 +167,8 @@ class TestManyToManyHyperlinkedView(TestCase): """ request = factory.get('/manytomany/') response = self.list_view(request) - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, self.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, self.data) def test_get_detail_view(self): """ @@ -176,8 +176,8 @@ class TestManyToManyHyperlinkedView(TestCase): """ request = factory.get('/manytomany/1/') response = self.detail_view(request, pk=1) - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, self.data[0]) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, self.data[0]) class TestCreateWithForeignKeys(TestCase): @@ -235,7 +235,7 @@ class TestOptionalRelationHyperlinkedView(TestCase): def setUp(self): """ - Create 1 OptionalRelationModel intances. + Create 1 OptionalRelationModel instances. """ OptionalRelationModel().save() self.objects = OptionalRelationModel.objects @@ -249,8 +249,8 @@ class TestOptionalRelationHyperlinkedView(TestCase): """ request = factory.get('/optionalrelationmodel-detail/1') response = self.detail_view(request, pk=1) - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, self.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, self.data) def test_put_detail_view(self): """ diff --git a/rest_framework/tests/negotiation.py b/rest_framework/tests/negotiation.py index 5769dd5f..43721b84 100644 --- a/rest_framework/tests/negotiation.py +++ b/rest_framework/tests/negotiation.py @@ -27,14 +27,14 @@ class TestAcceptedMediaType(TestCase): def test_client_without_accept_use_renderer(self): request = Request(factory.get('/')) accepted_renderer, accepted_media_type = self.select_renderer(request) - self.assertEquals(accepted_media_type, 'application/json') + self.assertEqual(accepted_media_type, 'application/json') def test_client_underspecifies_accept_use_renderer(self): request = Request(factory.get('/', HTTP_ACCEPT='*/*')) accepted_renderer, accepted_media_type = self.select_renderer(request) - self.assertEquals(accepted_media_type, 'application/json') + self.assertEqual(accepted_media_type, 'application/json') def test_client_overspecifies_accept_use_client(self): request = Request(factory.get('/', HTTP_ACCEPT='application/json; indent=8')) accepted_renderer, accepted_media_type = self.select_renderer(request) - self.assertEquals(accepted_media_type, 'application/json; indent=8') + self.assertEqual(accepted_media_type, 'application/json; indent=8') diff --git a/rest_framework/tests/pagination.py b/rest_framework/tests/pagination.py index b85ce144..472ffcdd 100644 --- a/rest_framework/tests/pagination.py +++ b/rest_framework/tests/pagination.py @@ -74,27 +74,27 @@ class IntegrationTestPagination(TestCase): """ request = factory.get('/') response = self.view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data['count'], 26) - self.assertEquals(response.data['results'], self.data[:10]) - self.assertNotEquals(response.data['next'], None) - self.assertEquals(response.data['previous'], None) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['count'], 26) + self.assertEqual(response.data['results'], self.data[:10]) + self.assertNotEqual(response.data['next'], None) + self.assertEqual(response.data['previous'], None) request = factory.get(response.data['next']) response = self.view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data['count'], 26) - self.assertEquals(response.data['results'], self.data[10:20]) - self.assertNotEquals(response.data['next'], None) - self.assertNotEquals(response.data['previous'], None) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['count'], 26) + self.assertEqual(response.data['results'], self.data[10:20]) + self.assertNotEqual(response.data['next'], None) + self.assertNotEqual(response.data['previous'], None) request = factory.get(response.data['next']) response = self.view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data['count'], 26) - self.assertEquals(response.data['results'], self.data[20:]) - self.assertEquals(response.data['next'], None) - self.assertNotEquals(response.data['previous'], None) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['count'], 26) + self.assertEqual(response.data['results'], self.data[20:]) + self.assertEqual(response.data['next'], None) + self.assertNotEqual(response.data['previous'], None) class IntegrationTestPaginationAndFiltering(TestCase): @@ -112,8 +112,8 @@ class IntegrationTestPaginationAndFiltering(TestCase): self.objects = FilterableItem.objects self.data = [ - {'id': obj.id, 'text': obj.text, 'decimal': obj.decimal, 'date': obj.date} - for obj in self.objects.all() + {'id': obj.id, 'text': obj.text, 'decimal': obj.decimal, 'date': obj.date.isoformat()} + for obj in self.objects.all() ] self.view = FilterFieldsRootView.as_view() @@ -126,27 +126,27 @@ class IntegrationTestPaginationAndFiltering(TestCase): """ request = factory.get('/?decimal=15.20') response = self.view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data['count'], 15) - self.assertEquals(response.data['results'], self.data[:10]) - self.assertNotEquals(response.data['next'], None) - self.assertEquals(response.data['previous'], None) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['count'], 15) + self.assertEqual(response.data['results'], self.data[:10]) + self.assertNotEqual(response.data['next'], None) + self.assertEqual(response.data['previous'], None) request = factory.get(response.data['next']) response = self.view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data['count'], 15) - self.assertEquals(response.data['results'], self.data[10:15]) - self.assertEquals(response.data['next'], None) - self.assertNotEquals(response.data['previous'], None) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['count'], 15) + self.assertEqual(response.data['results'], self.data[10:15]) + self.assertEqual(response.data['next'], None) + self.assertNotEqual(response.data['previous'], None) request = factory.get(response.data['previous']) response = self.view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data['count'], 15) - self.assertEquals(response.data['results'], self.data[:10]) - self.assertNotEquals(response.data['next'], None) - self.assertEquals(response.data['previous'], None) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['count'], 15) + self.assertEqual(response.data['results'], self.data[:10]) + self.assertNotEqual(response.data['next'], None) + self.assertEqual(response.data['previous'], None) class PassOnContextPaginationSerializer(pagination.PaginationSerializer): @@ -167,16 +167,16 @@ class UnitTestPagination(TestCase): def test_native_pagination(self): serializer = pagination.PaginationSerializer(self.first_page) - self.assertEquals(serializer.data['count'], 26) - self.assertEquals(serializer.data['next'], '?page=2') - self.assertEquals(serializer.data['previous'], None) - self.assertEquals(serializer.data['results'], self.objects[:10]) + self.assertEqual(serializer.data['count'], 26) + self.assertEqual(serializer.data['next'], '?page=2') + self.assertEqual(serializer.data['previous'], None) + self.assertEqual(serializer.data['results'], self.objects[:10]) serializer = pagination.PaginationSerializer(self.last_page) - self.assertEquals(serializer.data['count'], 26) - self.assertEquals(serializer.data['next'], None) - self.assertEquals(serializer.data['previous'], '?page=2') - self.assertEquals(serializer.data['results'], self.objects[20:]) + self.assertEqual(serializer.data['count'], 26) + self.assertEqual(serializer.data['next'], None) + self.assertEqual(serializer.data['previous'], '?page=2') + self.assertEqual(serializer.data['results'], self.objects[20:]) def test_context_available_in_result(self): """ @@ -185,7 +185,7 @@ class UnitTestPagination(TestCase): serializer = PassOnContextPaginationSerializer(self.first_page, context={'foo': 'bar'}) serializer.data results = serializer.fields[serializer.results_field] - self.assertEquals(serializer.context, results.context) + self.assertEqual(serializer.context, results.context) class TestUnpaginated(TestCase): @@ -213,7 +213,7 @@ class TestUnpaginated(TestCase): """ request = factory.get('/') response = self.view(request) - self.assertEquals(response.data, self.data) + self.assertEqual(response.data, self.data) class TestCustomPaginateByParam(TestCase): @@ -241,7 +241,7 @@ class TestCustomPaginateByParam(TestCase): """ request = factory.get('/') response = self.view(request).render() - self.assertEquals(response.data, self.data) + self.assertEqual(response.data, self.data) def test_paginate_by_param(self): """ @@ -249,8 +249,8 @@ class TestCustomPaginateByParam(TestCase): """ request = factory.get('/?page_size=5') response = self.view(request).render() - self.assertEquals(response.data['count'], 13) - self.assertEquals(response.data['results'], self.data[:5]) + self.assertEqual(response.data['count'], 13) + self.assertEqual(response.data['results'], self.data[:5]) ### Tests for context in pagination serializers @@ -285,7 +285,7 @@ class TestContextPassedToCustomField(TestCase): request = factory.get('/') response = self.view(request).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_200_OK) ### Tests for custom pagination serializers @@ -322,4 +322,4 @@ class TestCustomPaginationSerializer(TestCase): 'total_results': 4, 'objects': ['john', 'paul'] } - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) diff --git a/rest_framework/tests/permissions.py b/rest_framework/tests/permissions.py index b8e1d89c..b3993be5 100644 --- a/rest_framework/tests/permissions.py +++ b/rest_framework/tests/permissions.py @@ -60,38 +60,38 @@ class ModelPermissionsIntegrationTests(TestCase): content_type='application/json', HTTP_AUTHORIZATION=self.permitted_credentials) response = root_view(request, pk=1) - self.assertEquals(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) def test_has_put_permissions(self): request = factory.put('/1', json.dumps({'text': 'foobar'}), content_type='application/json', HTTP_AUTHORIZATION=self.permitted_credentials) response = instance_view(request, pk='1') - self.assertEquals(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_200_OK) def test_has_delete_permissions(self): request = factory.delete('/1', HTTP_AUTHORIZATION=self.permitted_credentials) response = instance_view(request, pk=1) - self.assertEquals(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) def test_does_not_have_create_permissions(self): request = factory.post('/', json.dumps({'text': 'foobar'}), content_type='application/json', HTTP_AUTHORIZATION=self.disallowed_credentials) response = root_view(request, pk=1) - self.assertEquals(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_does_not_have_put_permissions(self): request = factory.put('/1', json.dumps({'text': 'foobar'}), content_type='application/json', HTTP_AUTHORIZATION=self.disallowed_credentials) response = instance_view(request, pk='1') - self.assertEquals(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_does_not_have_delete_permissions(self): request = factory.delete('/1', HTTP_AUTHORIZATION=self.disallowed_credentials) response = instance_view(request, pk=1) - self.assertEquals(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_has_put_as_create_permissions(self): # User only has update permissions - should be able to update an entity. @@ -99,14 +99,14 @@ class ModelPermissionsIntegrationTests(TestCase): content_type='application/json', HTTP_AUTHORIZATION=self.updateonly_credentials) response = instance_view(request, pk='1') - self.assertEquals(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_200_OK) # But if PUTing to a new entity, permission should be denied. request = factory.put('/2', json.dumps({'text': 'foobar'}), content_type='application/json', HTTP_AUTHORIZATION=self.updateonly_credentials) response = instance_view(request, pk='2') - self.assertEquals(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) class OwnerModel(models.Model): @@ -145,9 +145,9 @@ class ObjectPermissionsIntegrationTests(TestCase): def test_owner_has_delete_permissions(self): request = factory.delete('/1', HTTP_AUTHORIZATION=self.owner_credentials) response = owner_instance_view(request, pk='1') - self.assertEquals(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) def test_non_owner_does_not_have_delete_permissions(self): request = factory.delete('/1', HTTP_AUTHORIZATION=self.not_owner_credentials) response = owner_instance_view(request, pk='1') - self.assertEquals(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) diff --git a/rest_framework/tests/relations_hyperlink.py b/rest_framework/tests/relations_hyperlink.py index e806ddd7..b5702a48 100644 --- a/rest_framework/tests/relations_hyperlink.py +++ b/rest_framework/tests/relations_hyperlink.py @@ -86,7 +86,7 @@ class HyperlinkedManyToManyTests(TestCase): {'url': 'http://testserver/manytomanysource/2/', 'name': 'source-2', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/']}, {'url': 'http://testserver/manytomanysource/3/', 'name': 'source-3', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_reverse_many_to_many_retrieve(self): queryset = ManyToManyTarget.objects.all() @@ -96,7 +96,7 @@ class HyperlinkedManyToManyTests(TestCase): {'url': 'http://testserver/manytomanytarget/2/', 'name': 'target-2', 'sources': ['http://testserver/manytomanysource/2/', 'http://testserver/manytomanysource/3/']}, {'url': 'http://testserver/manytomanytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/manytomanysource/3/']} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_many_to_many_update(self): data = {'url': 'http://testserver/manytomanysource/1/', 'name': 'source-1', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']} @@ -104,7 +104,7 @@ class HyperlinkedManyToManyTests(TestCase): serializer = ManyToManySourceSerializer(instance, data=data, context={'request': request}) self.assertTrue(serializer.is_valid()) serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) # Ensure source 1 is updated, and everything else is as expected queryset = ManyToManySource.objects.all() @@ -114,7 +114,7 @@ class HyperlinkedManyToManyTests(TestCase): {'url': 'http://testserver/manytomanysource/2/', 'name': 'source-2', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/']}, {'url': 'http://testserver/manytomanysource/3/', 'name': 'source-3', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_reverse_many_to_many_update(self): data = {'url': 'http://testserver/manytomanytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/manytomanysource/1/']} @@ -122,7 +122,7 @@ class HyperlinkedManyToManyTests(TestCase): serializer = ManyToManyTargetSerializer(instance, data=data, context={'request': request}) self.assertTrue(serializer.is_valid()) serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) # Ensure target 1 is updated, and everything else is as expected queryset = ManyToManyTarget.objects.all() @@ -133,14 +133,14 @@ class HyperlinkedManyToManyTests(TestCase): {'url': 'http://testserver/manytomanytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/manytomanysource/3/']} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_many_to_many_create(self): data = {'url': 'http://testserver/manytomanysource/4/', 'name': 'source-4', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/3/']} serializer = ManyToManySourceSerializer(data=data, context={'request': request}) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) self.assertEqual(obj.name, 'source-4') # Ensure source 4 is added, and everything else is as expected @@ -152,14 +152,14 @@ class HyperlinkedManyToManyTests(TestCase): {'url': 'http://testserver/manytomanysource/3/', 'name': 'source-3', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']}, {'url': 'http://testserver/manytomanysource/4/', 'name': 'source-4', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/3/']} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_reverse_many_to_many_create(self): data = {'url': 'http://testserver/manytomanytarget/4/', 'name': 'target-4', 'sources': ['http://testserver/manytomanysource/1/', 'http://testserver/manytomanysource/3/']} serializer = ManyToManyTargetSerializer(data=data, context={'request': request}) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) self.assertEqual(obj.name, 'target-4') # Ensure target 4 is added, and everything else is as expected @@ -171,7 +171,7 @@ class HyperlinkedManyToManyTests(TestCase): {'url': 'http://testserver/manytomanytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/manytomanysource/3/']}, {'url': 'http://testserver/manytomanytarget/4/', 'name': 'target-4', 'sources': ['http://testserver/manytomanysource/1/', 'http://testserver/manytomanysource/3/']} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) class HyperlinkedForeignKeyTests(TestCase): @@ -194,7 +194,7 @@ class HyperlinkedForeignKeyTests(TestCase): {'url': 'http://testserver/foreignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, {'url': 'http://testserver/foreignkeysource/3/', 'name': 'source-3', 'target': 'http://testserver/foreignkeytarget/1/'} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_reverse_foreign_key_retrieve(self): queryset = ForeignKeyTarget.objects.all() @@ -203,14 +203,14 @@ class HyperlinkedForeignKeyTests(TestCase): {'url': 'http://testserver/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/2/', 'http://testserver/foreignkeysource/3/']}, {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': []}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update(self): data = {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/2/'} instance = ForeignKeySource.objects.get(pk=1) serializer = ForeignKeySourceSerializer(instance, data=data, context={'request': request}) self.assertTrue(serializer.is_valid()) - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) serializer.save() # Ensure source 1 is updated, and everything else is as expected @@ -221,14 +221,14 @@ class HyperlinkedForeignKeyTests(TestCase): {'url': 'http://testserver/foreignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, {'url': 'http://testserver/foreignkeysource/3/', 'name': 'source-3', 'target': 'http://testserver/foreignkeytarget/1/'} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update_incorrect_type(self): data = {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': 2} instance = ForeignKeySource.objects.get(pk=1) serializer = ForeignKeySourceSerializer(instance, data=data, context={'request': request}) self.assertFalse(serializer.is_valid()) - self.assertEquals(serializer.errors, {'target': ['Incorrect type. Expected url string, received int.']}) + self.assertEqual(serializer.errors, {'target': ['Incorrect type. Expected url string, received int.']}) def test_reverse_foreign_key_update(self): data = {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/3/']} @@ -243,10 +243,10 @@ class HyperlinkedForeignKeyTests(TestCase): {'url': 'http://testserver/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/2/', 'http://testserver/foreignkeysource/3/']}, {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': []}, ] - self.assertEquals(new_serializer.data, expected) + self.assertEqual(new_serializer.data, expected) serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) # Ensure target 2 is update, and everything else is as expected queryset = ForeignKeyTarget.objects.all() @@ -255,14 +255,14 @@ class HyperlinkedForeignKeyTests(TestCase): {'url': 'http://testserver/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/foreignkeysource/2/']}, {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/3/']}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_create(self): data = {'url': 'http://testserver/foreignkeysource/4/', 'name': 'source-4', 'target': 'http://testserver/foreignkeytarget/2/'} serializer = ForeignKeySourceSerializer(data=data, context={'request': request}) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) self.assertEqual(obj.name, 'source-4') # Ensure source 1 is updated, and everything else is as expected @@ -274,14 +274,14 @@ class HyperlinkedForeignKeyTests(TestCase): {'url': 'http://testserver/foreignkeysource/3/', 'name': 'source-3', 'target': 'http://testserver/foreignkeytarget/1/'}, {'url': 'http://testserver/foreignkeysource/4/', 'name': 'source-4', 'target': 'http://testserver/foreignkeytarget/2/'}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_reverse_foreign_key_create(self): data = {'url': 'http://testserver/foreignkeytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/3/']} serializer = ForeignKeyTargetSerializer(data=data, context={'request': request}) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) self.assertEqual(obj.name, 'target-3') # Ensure target 4 is added, and everything else is as expected @@ -292,14 +292,14 @@ class HyperlinkedForeignKeyTests(TestCase): {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': []}, {'url': 'http://testserver/foreignkeytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/3/']}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update_with_invalid_null(self): data = {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': None} instance = ForeignKeySource.objects.get(pk=1) serializer = ForeignKeySourceSerializer(instance, data=data, context={'request': request}) self.assertFalse(serializer.is_valid()) - self.assertEquals(serializer.errors, {'target': ['This field is required.']}) + self.assertEqual(serializer.errors, {'target': ['This field is required.']}) class HyperlinkedNullableForeignKeyTests(TestCase): @@ -322,14 +322,14 @@ class HyperlinkedNullableForeignKeyTests(TestCase): {'url': 'http://testserver/nullableforeignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_create_with_valid_null(self): data = {'url': 'http://testserver/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None} serializer = NullableForeignKeySourceSerializer(data=data, context={'request': request}) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) self.assertEqual(obj.name, 'source-4') # Ensure source 4 is created, and everything else is as expected @@ -341,7 +341,7 @@ class HyperlinkedNullableForeignKeyTests(TestCase): {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, {'url': 'http://testserver/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_create_with_valid_emptystring(self): """ @@ -353,7 +353,7 @@ class HyperlinkedNullableForeignKeyTests(TestCase): serializer = NullableForeignKeySourceSerializer(data=data, context={'request': request}) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, expected_data) + self.assertEqual(serializer.data, expected_data) self.assertEqual(obj.name, 'source-4') # Ensure source 4 is created, and everything else is as expected @@ -365,14 +365,14 @@ class HyperlinkedNullableForeignKeyTests(TestCase): {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, {'url': 'http://testserver/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update_with_valid_null(self): data = {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None} instance = NullableForeignKeySource.objects.get(pk=1) serializer = NullableForeignKeySourceSerializer(instance, data=data, context={'request': request}) self.assertTrue(serializer.is_valid()) - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) serializer.save() # Ensure source 1 is updated, and everything else is as expected @@ -383,7 +383,7 @@ class HyperlinkedNullableForeignKeyTests(TestCase): {'url': 'http://testserver/nullableforeignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update_with_valid_emptystring(self): """ @@ -395,7 +395,7 @@ class HyperlinkedNullableForeignKeyTests(TestCase): instance = NullableForeignKeySource.objects.get(pk=1) serializer = NullableForeignKeySourceSerializer(instance, data=data, context={'request': request}) self.assertTrue(serializer.is_valid()) - self.assertEquals(serializer.data, expected_data) + self.assertEqual(serializer.data, expected_data) serializer.save() # Ensure source 1 is updated, and everything else is as expected @@ -406,7 +406,7 @@ class HyperlinkedNullableForeignKeyTests(TestCase): {'url': 'http://testserver/nullableforeignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) # reverse foreign keys MUST be read_only # In the general case they do not provide .remove() or .clear() @@ -417,7 +417,7 @@ class HyperlinkedNullableForeignKeyTests(TestCase): # instance = ForeignKeyTarget.objects.get(pk=1) # serializer = ForeignKeyTargetSerializer(instance, data=data) # self.assertTrue(serializer.is_valid()) - # self.assertEquals(serializer.data, data) + # self.assertEqual(serializer.data, data) # serializer.save() # # Ensure target 1 is updated, and everything else is as expected @@ -427,7 +427,7 @@ class HyperlinkedNullableForeignKeyTests(TestCase): # {'id': 1, 'name': 'target-1', 'sources': [1]}, # {'id': 2, 'name': 'target-2', 'sources': []}, # ] - # self.assertEquals(serializer.data, expected) + # self.assertEqual(serializer.data, expected) class HyperlinkedNullableOneToOneTests(TestCase): @@ -448,4 +448,4 @@ class HyperlinkedNullableOneToOneTests(TestCase): {'url': 'http://testserver/onetoonetarget/1/', 'name': 'target-1', 'nullable_source': 'http://testserver/nullableonetoonesource/1/'}, {'url': 'http://testserver/onetoonetarget/2/', 'name': 'target-2', 'nullable_source': None}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) diff --git a/rest_framework/tests/relations_nested.py b/rest_framework/tests/relations_nested.py index 563d1d4d..a125ba65 100644 --- a/rest_framework/tests/relations_nested.py +++ b/rest_framework/tests/relations_nested.py @@ -58,7 +58,7 @@ class ReverseForeignKeyTests(TestCase): {'id': 2, 'name': 'source-2', 'target': {'id': 1, 'name': 'target-1'}}, {'id': 3, 'name': 'source-3', 'target': {'id': 1, 'name': 'target-1'}}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_reverse_foreign_key_retrieve(self): queryset = ForeignKeyTarget.objects.all() @@ -72,7 +72,7 @@ class ReverseForeignKeyTests(TestCase): {'id': 2, 'name': 'target-2', 'sources': [ ]} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) class NestedNullableForeignKeyTests(TestCase): @@ -93,7 +93,7 @@ class NestedNullableForeignKeyTests(TestCase): {'id': 2, 'name': 'source-2', 'target': {'id': 1, 'name': 'target-1'}}, {'id': 3, 'name': 'source-3', 'target': None}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) class NestedNullableOneToOneTests(TestCase): @@ -112,4 +112,4 @@ class NestedNullableOneToOneTests(TestCase): {'id': 1, 'name': 'target-1', 'nullable_source': {'id': 1, 'name': 'source-1', 'target': 1}}, {'id': 2, 'name': 'target-2', 'nullable_source': None}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) diff --git a/rest_framework/tests/relations_pk.py b/rest_framework/tests/relations_pk.py index bcbc2b3e..d6ae3176 100644 --- a/rest_framework/tests/relations_pk.py +++ b/rest_framework/tests/relations_pk.py @@ -62,7 +62,7 @@ class PKManyToManyTests(TestCase): {'id': 2, 'name': 'source-2', 'targets': [1, 2]}, {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_reverse_many_to_many_retrieve(self): queryset = ManyToManyTarget.objects.all() @@ -72,7 +72,7 @@ class PKManyToManyTests(TestCase): {'id': 2, 'name': 'target-2', 'sources': [2, 3]}, {'id': 3, 'name': 'target-3', 'sources': [3]} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_many_to_many_update(self): data = {'id': 1, 'name': 'source-1', 'targets': [1, 2, 3]} @@ -80,7 +80,7 @@ class PKManyToManyTests(TestCase): serializer = ManyToManySourceSerializer(instance, data=data) self.assertTrue(serializer.is_valid()) serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) # Ensure source 1 is updated, and everything else is as expected queryset = ManyToManySource.objects.all() @@ -90,7 +90,7 @@ class PKManyToManyTests(TestCase): {'id': 2, 'name': 'source-2', 'targets': [1, 2]}, {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_reverse_many_to_many_update(self): data = {'id': 1, 'name': 'target-1', 'sources': [1]} @@ -98,7 +98,7 @@ class PKManyToManyTests(TestCase): serializer = ManyToManyTargetSerializer(instance, data=data) self.assertTrue(serializer.is_valid()) serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) # Ensure target 1 is updated, and everything else is as expected queryset = ManyToManyTarget.objects.all() @@ -108,14 +108,14 @@ class PKManyToManyTests(TestCase): {'id': 2, 'name': 'target-2', 'sources': [2, 3]}, {'id': 3, 'name': 'target-3', 'sources': [3]} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_many_to_many_create(self): data = {'id': 4, 'name': 'source-4', 'targets': [1, 3]} serializer = ManyToManySourceSerializer(data=data) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) self.assertEqual(obj.name, 'source-4') # Ensure source 4 is added, and everything else is as expected @@ -127,14 +127,14 @@ class PKManyToManyTests(TestCase): {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]}, {'id': 4, 'name': 'source-4', 'targets': [1, 3]}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_reverse_many_to_many_create(self): data = {'id': 4, 'name': 'target-4', 'sources': [1, 3]} serializer = ManyToManyTargetSerializer(data=data) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) self.assertEqual(obj.name, 'target-4') # Ensure target 4 is added, and everything else is as expected @@ -146,7 +146,7 @@ class PKManyToManyTests(TestCase): {'id': 3, 'name': 'target-3', 'sources': [3]}, {'id': 4, 'name': 'target-4', 'sources': [1, 3]} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) class PKForeignKeyTests(TestCase): @@ -167,7 +167,7 @@ class PKForeignKeyTests(TestCase): {'id': 2, 'name': 'source-2', 'target': 1}, {'id': 3, 'name': 'source-3', 'target': 1} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_reverse_foreign_key_retrieve(self): queryset = ForeignKeyTarget.objects.all() @@ -176,14 +176,14 @@ class PKForeignKeyTests(TestCase): {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, {'id': 2, 'name': 'target-2', 'sources': []}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update(self): data = {'id': 1, 'name': 'source-1', 'target': 2} instance = ForeignKeySource.objects.get(pk=1) serializer = ForeignKeySourceSerializer(instance, data=data) self.assertTrue(serializer.is_valid()) - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) serializer.save() # Ensure source 1 is updated, and everything else is as expected @@ -194,14 +194,14 @@ class PKForeignKeyTests(TestCase): {'id': 2, 'name': 'source-2', 'target': 1}, {'id': 3, 'name': 'source-3', 'target': 1} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update_incorrect_type(self): data = {'id': 1, 'name': 'source-1', 'target': 'foo'} instance = ForeignKeySource.objects.get(pk=1) serializer = ForeignKeySourceSerializer(instance, data=data) self.assertFalse(serializer.is_valid()) - self.assertEquals(serializer.errors, {'target': ['Incorrect type. Expected pk value, received %s.' % six.text_type.__name__]}) + self.assertEqual(serializer.errors, {'target': ['Incorrect type. Expected pk value, received %s.' % six.text_type.__name__]}) def test_reverse_foreign_key_update(self): data = {'id': 2, 'name': 'target-2', 'sources': [1, 3]} @@ -216,10 +216,10 @@ class PKForeignKeyTests(TestCase): {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, {'id': 2, 'name': 'target-2', 'sources': []}, ] - self.assertEquals(new_serializer.data, expected) + self.assertEqual(new_serializer.data, expected) serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) # Ensure target 2 is update, and everything else is as expected queryset = ForeignKeyTarget.objects.all() @@ -228,14 +228,14 @@ class PKForeignKeyTests(TestCase): {'id': 1, 'name': 'target-1', 'sources': [2]}, {'id': 2, 'name': 'target-2', 'sources': [1, 3]}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_create(self): data = {'id': 4, 'name': 'source-4', 'target': 2} serializer = ForeignKeySourceSerializer(data=data) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) self.assertEqual(obj.name, 'source-4') # Ensure source 4 is added, and everything else is as expected @@ -247,14 +247,14 @@ class PKForeignKeyTests(TestCase): {'id': 3, 'name': 'source-3', 'target': 1}, {'id': 4, 'name': 'source-4', 'target': 2}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_reverse_foreign_key_create(self): data = {'id': 3, 'name': 'target-3', 'sources': [1, 3]} serializer = ForeignKeyTargetSerializer(data=data) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) self.assertEqual(obj.name, 'target-3') # Ensure target 3 is added, and everything else is as expected @@ -265,14 +265,14 @@ class PKForeignKeyTests(TestCase): {'id': 2, 'name': 'target-2', 'sources': []}, {'id': 3, 'name': 'target-3', 'sources': [1, 3]}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update_with_invalid_null(self): data = {'id': 1, 'name': 'source-1', 'target': None} instance = ForeignKeySource.objects.get(pk=1) serializer = ForeignKeySourceSerializer(instance, data=data) self.assertFalse(serializer.is_valid()) - self.assertEquals(serializer.errors, {'target': ['This field is required.']}) + self.assertEqual(serializer.errors, {'target': ['This field is required.']}) class PKNullableForeignKeyTests(TestCase): @@ -293,14 +293,14 @@ class PKNullableForeignKeyTests(TestCase): {'id': 2, 'name': 'source-2', 'target': 1}, {'id': 3, 'name': 'source-3', 'target': None}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_create_with_valid_null(self): data = {'id': 4, 'name': 'source-4', 'target': None} serializer = NullableForeignKeySourceSerializer(data=data) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) self.assertEqual(obj.name, 'source-4') # Ensure source 4 is created, and everything else is as expected @@ -312,7 +312,7 @@ class PKNullableForeignKeyTests(TestCase): {'id': 3, 'name': 'source-3', 'target': None}, {'id': 4, 'name': 'source-4', 'target': None} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_create_with_valid_emptystring(self): """ @@ -324,7 +324,7 @@ class PKNullableForeignKeyTests(TestCase): serializer = NullableForeignKeySourceSerializer(data=data) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, expected_data) + self.assertEqual(serializer.data, expected_data) self.assertEqual(obj.name, 'source-4') # Ensure source 4 is created, and everything else is as expected @@ -336,14 +336,14 @@ class PKNullableForeignKeyTests(TestCase): {'id': 3, 'name': 'source-3', 'target': None}, {'id': 4, 'name': 'source-4', 'target': None} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update_with_valid_null(self): data = {'id': 1, 'name': 'source-1', 'target': None} instance = NullableForeignKeySource.objects.get(pk=1) serializer = NullableForeignKeySourceSerializer(instance, data=data) self.assertTrue(serializer.is_valid()) - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) serializer.save() # Ensure source 1 is updated, and everything else is as expected @@ -354,7 +354,7 @@ class PKNullableForeignKeyTests(TestCase): {'id': 2, 'name': 'source-2', 'target': 1}, {'id': 3, 'name': 'source-3', 'target': None} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update_with_valid_emptystring(self): """ @@ -366,7 +366,7 @@ class PKNullableForeignKeyTests(TestCase): instance = NullableForeignKeySource.objects.get(pk=1) serializer = NullableForeignKeySourceSerializer(instance, data=data) self.assertTrue(serializer.is_valid()) - self.assertEquals(serializer.data, expected_data) + self.assertEqual(serializer.data, expected_data) serializer.save() # Ensure source 1 is updated, and everything else is as expected @@ -377,7 +377,7 @@ class PKNullableForeignKeyTests(TestCase): {'id': 2, 'name': 'source-2', 'target': 1}, {'id': 3, 'name': 'source-3', 'target': None} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) # reverse foreign keys MUST be read_only # In the general case they do not provide .remove() or .clear() @@ -388,7 +388,7 @@ class PKNullableForeignKeyTests(TestCase): # instance = ForeignKeyTarget.objects.get(pk=1) # serializer = ForeignKeyTargetSerializer(instance, data=data) # self.assertTrue(serializer.is_valid()) - # self.assertEquals(serializer.data, data) + # self.assertEqual(serializer.data, data) # serializer.save() # # Ensure target 1 is updated, and everything else is as expected @@ -398,7 +398,7 @@ class PKNullableForeignKeyTests(TestCase): # {'id': 1, 'name': 'target-1', 'sources': [1]}, # {'id': 2, 'name': 'target-2', 'sources': []}, # ] - # self.assertEquals(serializer.data, expected) + # self.assertEqual(serializer.data, expected) class PKNullableOneToOneTests(TestCase): @@ -417,4 +417,4 @@ class PKNullableOneToOneTests(TestCase): {'id': 1, 'name': 'target-1', 'nullable_source': 1}, {'id': 2, 'name': 'target-2', 'nullable_source': None}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) diff --git a/rest_framework/tests/relations_slug.py b/rest_framework/tests/relations_slug.py index 0c7421f3..435c821c 100644 --- a/rest_framework/tests/relations_slug.py +++ b/rest_framework/tests/relations_slug.py @@ -24,7 +24,7 @@ class NullableForeignKeySourceSerializer(serializers.ModelSerializer): model = NullableForeignKeySource -# TODO: M2M Tests, FKTests (Non-nulable), One2One +# TODO: M2M Tests, FKTests (Non-nullable), One2One class SlugForeignKeyTests(TestCase): def setUp(self): target = ForeignKeyTarget(name='target-1') @@ -43,7 +43,7 @@ class SlugForeignKeyTests(TestCase): {'id': 2, 'name': 'source-2', 'target': 'target-1'}, {'id': 3, 'name': 'source-3', 'target': 'target-1'} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_reverse_foreign_key_retrieve(self): queryset = ForeignKeyTarget.objects.all() @@ -52,14 +52,14 @@ class SlugForeignKeyTests(TestCase): {'id': 1, 'name': 'target-1', 'sources': ['source-1', 'source-2', 'source-3']}, {'id': 2, 'name': 'target-2', 'sources': []}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update(self): data = {'id': 1, 'name': 'source-1', 'target': 'target-2'} instance = ForeignKeySource.objects.get(pk=1) serializer = ForeignKeySourceSerializer(instance, data=data) self.assertTrue(serializer.is_valid()) - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) serializer.save() # Ensure source 1 is updated, and everything else is as expected @@ -70,14 +70,14 @@ class SlugForeignKeyTests(TestCase): {'id': 2, 'name': 'source-2', 'target': 'target-1'}, {'id': 3, 'name': 'source-3', 'target': 'target-1'} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update_incorrect_type(self): data = {'id': 1, 'name': 'source-1', 'target': 123} instance = ForeignKeySource.objects.get(pk=1) serializer = ForeignKeySourceSerializer(instance, data=data) self.assertFalse(serializer.is_valid()) - self.assertEquals(serializer.errors, {'target': ['Object with name=123 does not exist.']}) + self.assertEqual(serializer.errors, {'target': ['Object with name=123 does not exist.']}) def test_reverse_foreign_key_update(self): data = {'id': 2, 'name': 'target-2', 'sources': ['source-1', 'source-3']} @@ -92,10 +92,10 @@ class SlugForeignKeyTests(TestCase): {'id': 1, 'name': 'target-1', 'sources': ['source-1', 'source-2', 'source-3']}, {'id': 2, 'name': 'target-2', 'sources': []}, ] - self.assertEquals(new_serializer.data, expected) + self.assertEqual(new_serializer.data, expected) serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) # Ensure target 2 is update, and everything else is as expected queryset = ForeignKeyTarget.objects.all() @@ -104,7 +104,7 @@ class SlugForeignKeyTests(TestCase): {'id': 1, 'name': 'target-1', 'sources': ['source-2']}, {'id': 2, 'name': 'target-2', 'sources': ['source-1', 'source-3']}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_create(self): data = {'id': 4, 'name': 'source-4', 'target': 'target-2'} @@ -112,7 +112,7 @@ class SlugForeignKeyTests(TestCase): serializer.is_valid() self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) self.assertEqual(obj.name, 'source-4') # Ensure source 4 is added, and everything else is as expected @@ -124,14 +124,14 @@ class SlugForeignKeyTests(TestCase): {'id': 3, 'name': 'source-3', 'target': 'target-1'}, {'id': 4, 'name': 'source-4', 'target': 'target-2'}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_reverse_foreign_key_create(self): data = {'id': 3, 'name': 'target-3', 'sources': ['source-1', 'source-3']} serializer = ForeignKeyTargetSerializer(data=data) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) self.assertEqual(obj.name, 'target-3') # Ensure target 3 is added, and everything else is as expected @@ -142,14 +142,14 @@ class SlugForeignKeyTests(TestCase): {'id': 2, 'name': 'target-2', 'sources': []}, {'id': 3, 'name': 'target-3', 'sources': ['source-1', 'source-3']}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update_with_invalid_null(self): data = {'id': 1, 'name': 'source-1', 'target': None} instance = ForeignKeySource.objects.get(pk=1) serializer = ForeignKeySourceSerializer(instance, data=data) self.assertFalse(serializer.is_valid()) - self.assertEquals(serializer.errors, {'target': ['This field is required.']}) + self.assertEqual(serializer.errors, {'target': ['This field is required.']}) class SlugNullableForeignKeyTests(TestCase): @@ -170,14 +170,14 @@ class SlugNullableForeignKeyTests(TestCase): {'id': 2, 'name': 'source-2', 'target': 'target-1'}, {'id': 3, 'name': 'source-3', 'target': None}, ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_create_with_valid_null(self): data = {'id': 4, 'name': 'source-4', 'target': None} serializer = NullableForeignKeySourceSerializer(data=data) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) self.assertEqual(obj.name, 'source-4') # Ensure source 4 is created, and everything else is as expected @@ -189,7 +189,7 @@ class SlugNullableForeignKeyTests(TestCase): {'id': 3, 'name': 'source-3', 'target': None}, {'id': 4, 'name': 'source-4', 'target': None} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_create_with_valid_emptystring(self): """ @@ -201,7 +201,7 @@ class SlugNullableForeignKeyTests(TestCase): serializer = NullableForeignKeySourceSerializer(data=data) self.assertTrue(serializer.is_valid()) obj = serializer.save() - self.assertEquals(serializer.data, expected_data) + self.assertEqual(serializer.data, expected_data) self.assertEqual(obj.name, 'source-4') # Ensure source 4 is created, and everything else is as expected @@ -213,14 +213,14 @@ class SlugNullableForeignKeyTests(TestCase): {'id': 3, 'name': 'source-3', 'target': None}, {'id': 4, 'name': 'source-4', 'target': None} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update_with_valid_null(self): data = {'id': 1, 'name': 'source-1', 'target': None} instance = NullableForeignKeySource.objects.get(pk=1) serializer = NullableForeignKeySourceSerializer(instance, data=data) self.assertTrue(serializer.is_valid()) - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) serializer.save() # Ensure source 1 is updated, and everything else is as expected @@ -231,7 +231,7 @@ class SlugNullableForeignKeyTests(TestCase): {'id': 2, 'name': 'source-2', 'target': 'target-1'}, {'id': 3, 'name': 'source-3', 'target': None} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_foreign_key_update_with_valid_emptystring(self): """ @@ -243,7 +243,7 @@ class SlugNullableForeignKeyTests(TestCase): instance = NullableForeignKeySource.objects.get(pk=1) serializer = NullableForeignKeySourceSerializer(instance, data=data) self.assertTrue(serializer.is_valid()) - self.assertEquals(serializer.data, expected_data) + self.assertEqual(serializer.data, expected_data) serializer.save() # Ensure source 1 is updated, and everything else is as expected @@ -254,4 +254,4 @@ class SlugNullableForeignKeyTests(TestCase): {'id': 2, 'name': 'source-2', 'target': 'target-1'}, {'id': 3, 'name': 'source-3', 'target': None} ] - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) diff --git a/rest_framework/tests/renderers.py b/rest_framework/tests/renderers.py index 0f3fe3f1..40bac9cb 100644 --- a/rest_framework/tests/renderers.py +++ b/rest_framework/tests/renderers.py @@ -34,7 +34,7 @@ class BasicRendererTests(TestCase): def test_expected_results(self): for value, renderer_cls, expected in expected_results: output = renderer_cls().render(value) - self.assertEquals(output, expected) + self.assertEqual(output, expected) class RendererA(BaseRenderer): @@ -134,39 +134,39 @@ class RendererEndToEndTests(TestCase): def test_default_renderer_serializes_content(self): """If the Accept header is not set the default renderer should serialize the response.""" resp = self.client.get('/') - self.assertEquals(resp['Content-Type'], RendererA.media_type) - self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererA.media_type) + self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) def test_head_method_serializes_no_content(self): """No response must be included in HEAD requests.""" resp = self.client.head('/') - self.assertEquals(resp.status_code, DUMMYSTATUS) - self.assertEquals(resp['Content-Type'], RendererA.media_type) - self.assertEquals(resp.content, six.b('')) + self.assertEqual(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererA.media_type) + self.assertEqual(resp.content, six.b('')) def test_default_renderer_serializes_content_on_accept_any(self): """If the Accept header is set to */* the default renderer should serialize the response.""" resp = self.client.get('/', HTTP_ACCEPT='*/*') - self.assertEquals(resp['Content-Type'], RendererA.media_type) - self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererA.media_type) + self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) def test_specified_renderer_serializes_content_default_case(self): """If the Accept header is set the specified renderer should serialize the response. (In this case we check that works for the default renderer)""" resp = self.client.get('/', HTTP_ACCEPT=RendererA.media_type) - self.assertEquals(resp['Content-Type'], RendererA.media_type) - self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererA.media_type) + self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) def test_specified_renderer_serializes_content_non_default_case(self): """If the Accept header is set the specified renderer should serialize the response. (In this case we check that works for a non-default renderer)""" resp = self.client.get('/', HTTP_ACCEPT=RendererB.media_type) - self.assertEquals(resp['Content-Type'], RendererB.media_type) - self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererB.media_type) + self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) def test_specified_renderer_serializes_content_on_accept_query(self): """The '_accept' query string should behave in the same way as the Accept header.""" @@ -175,14 +175,14 @@ class RendererEndToEndTests(TestCase): RendererB.media_type ) resp = self.client.get('/' + param) - self.assertEquals(resp['Content-Type'], RendererB.media_type) - self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererB.media_type) + self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) def test_unsatisfiable_accept_header_on_request_returns_406_status(self): """If the Accept header is unsatisfiable we should return a 406 Not Acceptable response.""" resp = self.client.get('/', HTTP_ACCEPT='foo/bar') - self.assertEquals(resp.status_code, status.HTTP_406_NOT_ACCEPTABLE) + self.assertEqual(resp.status_code, status.HTTP_406_NOT_ACCEPTABLE) def test_specified_renderer_serializes_content_on_format_query(self): """If a 'format' query is specified, the renderer with the matching @@ -192,17 +192,17 @@ class RendererEndToEndTests(TestCase): RendererB.format ) resp = self.client.get('/' + param) - self.assertEquals(resp['Content-Type'], RendererB.media_type) - self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererB.media_type) + self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) def test_specified_renderer_serializes_content_on_format_kwargs(self): """If a 'format' keyword arg is specified, the renderer with the matching format attribute should serialize the response.""" resp = self.client.get('/something.formatb') - self.assertEquals(resp['Content-Type'], RendererB.media_type) - self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererB.media_type) + self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) def test_specified_renderer_is_used_on_format_query_with_matching_accept(self): """If both a 'format' query and a matching Accept header specified, @@ -213,9 +213,9 @@ class RendererEndToEndTests(TestCase): ) resp = self.client.get('/' + param, HTTP_ACCEPT=RendererB.media_type) - self.assertEquals(resp['Content-Type'], RendererB.media_type) - self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererB.media_type) + self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) _flat_repr = '{"foo": ["bar", "baz"]}' @@ -243,7 +243,7 @@ class JSONRendererTests(TestCase): renderer = JSONRenderer() content = renderer.render(obj, 'application/json') # Fix failing test case which depends on version of JSON library. - self.assertEquals(content, _flat_repr) + self.assertEqual(content, _flat_repr) def test_with_content_type_args(self): """ @@ -252,7 +252,7 @@ class JSONRendererTests(TestCase): obj = {'foo': ['bar', 'baz']} renderer = JSONRenderer() content = renderer.render(obj, 'application/json; indent=2') - self.assertEquals(strip_trailing_whitespace(content), _indented_repr) + self.assertEqual(strip_trailing_whitespace(content), _indented_repr) class JSONPRendererTests(TestCase): @@ -268,9 +268,9 @@ class JSONPRendererTests(TestCase): """ resp = self.client.get('/jsonp/jsonrenderer', HTTP_ACCEPT='application/javascript') - self.assertEquals(resp.status_code, 200) - self.assertEquals(resp['Content-Type'], 'application/javascript') - self.assertEquals(resp.content, + self.assertEqual(resp.status_code, status.HTTP_200_OK) + self.assertEqual(resp['Content-Type'], 'application/javascript') + self.assertEqual(resp.content, ('callback(%s);' % _flat_repr).encode('ascii')) def test_without_callback_without_json_renderer(self): @@ -279,9 +279,9 @@ class JSONPRendererTests(TestCase): """ resp = self.client.get('/jsonp/nojsonrenderer', HTTP_ACCEPT='application/javascript') - self.assertEquals(resp.status_code, 200) - self.assertEquals(resp['Content-Type'], 'application/javascript') - self.assertEquals(resp.content, + self.assertEqual(resp.status_code, status.HTTP_200_OK) + self.assertEqual(resp['Content-Type'], 'application/javascript') + self.assertEqual(resp.content, ('callback(%s);' % _flat_repr).encode('ascii')) def test_with_callback(self): @@ -291,9 +291,9 @@ class JSONPRendererTests(TestCase): callback_func = 'myjsonpcallback' resp = self.client.get('/jsonp/nojsonrenderer?callback=' + callback_func, HTTP_ACCEPT='application/javascript') - self.assertEquals(resp.status_code, 200) - self.assertEquals(resp['Content-Type'], 'application/javascript') - self.assertEquals(resp.content, + self.assertEqual(resp.status_code, status.HTTP_200_OK) + self.assertEqual(resp['Content-Type'], 'application/javascript') + self.assertEqual(resp.content, ('%s(%s);' % (callback_func, _flat_repr)).encode('ascii')) @@ -312,7 +312,7 @@ if yaml: obj = {'foo': ['bar', 'baz']} renderer = YAMLRenderer() content = renderer.render(obj, 'application/yaml') - self.assertEquals(content, _yaml_repr) + self.assertEqual(content, _yaml_repr) def test_render_and_parse(self): """ @@ -326,7 +326,7 @@ if yaml: content = renderer.render(obj, 'application/yaml') data = parser.parse(StringIO(content)) - self.assertEquals(obj, data) + self.assertEqual(obj, data) class XMLRendererTestCase(TestCase): diff --git a/rest_framework/tests/response.py b/rest_framework/tests/response.py index 3e1da905..aecf83f4 100644 --- a/rest_framework/tests/response.py +++ b/rest_framework/tests/response.py @@ -85,39 +85,39 @@ class RendererIntegrationTests(TestCase): def test_default_renderer_serializes_content(self): """If the Accept header is not set the default renderer should serialize the response.""" resp = self.client.get('/') - self.assertEquals(resp['Content-Type'], RendererA.media_type) - self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererA.media_type) + self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) def test_head_method_serializes_no_content(self): """No response must be included in HEAD requests.""" resp = self.client.head('/') - self.assertEquals(resp.status_code, DUMMYSTATUS) - self.assertEquals(resp['Content-Type'], RendererA.media_type) - self.assertEquals(resp.content, six.b('')) + self.assertEqual(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererA.media_type) + self.assertEqual(resp.content, six.b('')) def test_default_renderer_serializes_content_on_accept_any(self): """If the Accept header is set to */* the default renderer should serialize the response.""" resp = self.client.get('/', HTTP_ACCEPT='*/*') - self.assertEquals(resp['Content-Type'], RendererA.media_type) - self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererA.media_type) + self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) def test_specified_renderer_serializes_content_default_case(self): """If the Accept header is set the specified renderer should serialize the response. (In this case we check that works for the default renderer)""" resp = self.client.get('/', HTTP_ACCEPT=RendererA.media_type) - self.assertEquals(resp['Content-Type'], RendererA.media_type) - self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererA.media_type) + self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) def test_specified_renderer_serializes_content_non_default_case(self): """If the Accept header is set the specified renderer should serialize the response. (In this case we check that works for a non-default renderer)""" resp = self.client.get('/', HTTP_ACCEPT=RendererB.media_type) - self.assertEquals(resp['Content-Type'], RendererB.media_type) - self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererB.media_type) + self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) def test_specified_renderer_serializes_content_on_accept_query(self): """The '_accept' query string should behave in the same way as the Accept header.""" @@ -126,34 +126,34 @@ class RendererIntegrationTests(TestCase): RendererB.media_type ) resp = self.client.get('/' + param) - self.assertEquals(resp['Content-Type'], RendererB.media_type) - self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererB.media_type) + self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) def test_specified_renderer_serializes_content_on_format_query(self): """If a 'format' query is specified, the renderer with the matching format attribute should serialize the response.""" resp = self.client.get('/?format=%s' % RendererB.format) - self.assertEquals(resp['Content-Type'], RendererB.media_type) - self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererB.media_type) + self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) def test_specified_renderer_serializes_content_on_format_kwargs(self): """If a 'format' keyword arg is specified, the renderer with the matching format attribute should serialize the response.""" resp = self.client.get('/something.formatb') - self.assertEquals(resp['Content-Type'], RendererB.media_type) - self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererB.media_type) + self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) def test_specified_renderer_is_used_on_format_query_with_matching_accept(self): """If both a 'format' query and a matching Accept header specified, the renderer with the matching format attribute should serialize the response.""" resp = self.client.get('/?format=%s' % RendererB.format, HTTP_ACCEPT=RendererB.media_type) - self.assertEquals(resp['Content-Type'], RendererB.media_type) - self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) - self.assertEquals(resp.status_code, DUMMYSTATUS) + self.assertEqual(resp['Content-Type'], RendererB.media_type) + self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) + self.assertEqual(resp.status_code, DUMMYSTATUS) class Issue122Tests(TestCase): diff --git a/rest_framework/tests/reverse.py b/rest_framework/tests/reverse.py index 4ad4d684..cb8d8132 100644 --- a/rest_framework/tests/reverse.py +++ b/rest_framework/tests/reverse.py @@ -17,7 +17,7 @@ urlpatterns = patterns('', class ReverseTests(TestCase): """ - Tests for fully qualifed URLs when using `reverse`. + Tests for fully qualified URLs when using `reverse`. """ urls = 'rest_framework.tests.reverse' diff --git a/rest_framework/tests/serializer.py b/rest_framework/tests/serializer.py index 671494b5..51065017 100644 --- a/rest_framework/tests/serializer.py +++ b/rest_framework/tests/serializer.py @@ -91,6 +91,11 @@ class PositiveIntegerAsChoiceSerializer(serializers.ModelSerializer): fields = ['some_integer'] +class BrokenModelSerializer(serializers.ModelSerializer): + class Meta: + fields = ['some_field'] + + class BasicTests(TestCase): def setUp(self): self.comment = Comment( @@ -107,7 +112,7 @@ class BasicTests(TestCase): self.expected = { 'email': 'tom@example.com', 'content': 'Happy new year!', - 'created': datetime.datetime(2012, 1, 1), + 'created': '2012-01-01T00:00:00', 'sub_comment': 'And Merry Christmas!' } self.person_data = {'name': 'dwight', 'age': 35} @@ -122,39 +127,39 @@ class BasicTests(TestCase): 'created': None, 'sub_comment': '' } - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_retrieve(self): serializer = CommentSerializer(self.comment) - self.assertEquals(serializer.data, self.expected) + self.assertEqual(serializer.data, self.expected) def test_create(self): serializer = CommentSerializer(data=self.data) expected = self.comment - self.assertEquals(serializer.is_valid(), True) - self.assertEquals(serializer.object, expected) + self.assertEqual(serializer.is_valid(), True) + self.assertEqual(serializer.object, expected) self.assertFalse(serializer.object is expected) - self.assertEquals(serializer.data['sub_comment'], 'And Merry Christmas!') + self.assertEqual(serializer.data['sub_comment'], 'And Merry Christmas!') def test_update(self): serializer = CommentSerializer(self.comment, data=self.data) expected = self.comment - self.assertEquals(serializer.is_valid(), True) - self.assertEquals(serializer.object, expected) + self.assertEqual(serializer.is_valid(), True) + self.assertEqual(serializer.object, expected) self.assertTrue(serializer.object is expected) - self.assertEquals(serializer.data['sub_comment'], 'And Merry Christmas!') + self.assertEqual(serializer.data['sub_comment'], 'And Merry Christmas!') def test_partial_update(self): msg = 'Merry New Year!' partial_data = {'content': msg} serializer = CommentSerializer(self.comment, data=partial_data) - self.assertEquals(serializer.is_valid(), False) + self.assertEqual(serializer.is_valid(), False) serializer = CommentSerializer(self.comment, data=partial_data, partial=True) expected = self.comment self.assertEqual(serializer.is_valid(), True) - self.assertEquals(serializer.object, expected) + self.assertEqual(serializer.object, expected) self.assertTrue(serializer.object is expected) - self.assertEquals(serializer.data['content'], msg) + self.assertEqual(serializer.data['content'], msg) def test_model_fields_as_expected(self): """ @@ -162,7 +167,7 @@ class BasicTests(TestCase): in the Meta data """ serializer = PersonSerializer(self.person) - self.assertEquals(set(serializer.data.keys()), + self.assertEqual(set(serializer.data.keys()), set(['name', 'age', 'info'])) def test_field_with_dictionary(self): @@ -171,18 +176,18 @@ class BasicTests(TestCase): """ serializer = PersonSerializer(self.person) expected = self.person_data - self.assertEquals(serializer.data['info'], expected) + self.assertEqual(serializer.data['info'], expected) def test_read_only_fields(self): """ Attempting to update fields set as read_only should have no effect. """ serializer = PersonSerializer(self.person, data={'name': 'dwight', 'age': 99}) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) instance = serializer.save() - self.assertEquals(serializer.errors, {}) + self.assertEqual(serializer.errors, {}) # Assert age is unchanged (35) - self.assertEquals(instance.age, self.person_data['age']) + self.assertEqual(instance.age, self.person_data['age']) class DictStyleSerializer(serializers.Serializer): @@ -201,7 +206,7 @@ class DictStyleSerializerTests(TestCase): data = {'email': 'foo@example.com'} serializer = DictStyleSerializer(data=data) self.assertTrue(serializer.is_valid()) - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) def test_dict_style_serialize(self): """ @@ -209,7 +214,7 @@ class DictStyleSerializerTests(TestCase): """ data = {'email': 'foo@example.com'} serializer = DictStyleSerializer(data) - self.assertEquals(serializer.data, data) + self.assertEqual(serializer.data, data) class ValidationTests(TestCase): @@ -228,13 +233,13 @@ class ValidationTests(TestCase): def test_create(self): serializer = CommentSerializer(data=self.data) - self.assertEquals(serializer.is_valid(), False) - self.assertEquals(serializer.errors, {'content': ['Ensure this value has at most 1000 characters (it has 1001).']}) + self.assertEqual(serializer.is_valid(), False) + self.assertEqual(serializer.errors, {'content': ['Ensure this value has at most 1000 characters (it has 1001).']}) def test_update(self): serializer = CommentSerializer(self.comment, data=self.data) - self.assertEquals(serializer.is_valid(), False) - self.assertEquals(serializer.errors, {'content': ['Ensure this value has at most 1000 characters (it has 1001).']}) + self.assertEqual(serializer.is_valid(), False) + self.assertEqual(serializer.errors, {'content': ['Ensure this value has at most 1000 characters (it has 1001).']}) def test_update_missing_field(self): data = { @@ -242,8 +247,8 @@ class ValidationTests(TestCase): 'created': datetime.datetime(2012, 1, 1) } serializer = CommentSerializer(self.comment, data=data) - self.assertEquals(serializer.is_valid(), False) - self.assertEquals(serializer.errors, {'email': ['This field is required.']}) + self.assertEqual(serializer.is_valid(), False) + self.assertEqual(serializer.errors, {'email': ['This field is required.']}) def test_missing_bool_with_default(self): """Make sure that a boolean value with a 'False' value is not @@ -253,8 +258,8 @@ class ValidationTests(TestCase): #No 'done' value. } serializer = ActionItemSerializer(self.actionitem, data=data) - self.assertEquals(serializer.is_valid(), True) - self.assertEquals(serializer.errors, {}) + self.assertEqual(serializer.is_valid(), True) + self.assertEqual(serializer.errors, {}) def test_bad_type_data_is_false(self): """ @@ -262,18 +267,18 @@ class ValidationTests(TestCase): """ data = ['i am', 'a', 'list'] serializer = CommentSerializer(self.comment, data=data, many=True) - self.assertEquals(serializer.is_valid(), False) - self.assertEquals(serializer.errors, {'non_field_errors': ['Invalid data']}) + self.assertEqual(serializer.is_valid(), False) + self.assertEqual(serializer.errors, {'non_field_errors': ['Invalid data']}) data = 'and i am a string' serializer = CommentSerializer(self.comment, data=data) - self.assertEquals(serializer.is_valid(), False) - self.assertEquals(serializer.errors, {'non_field_errors': ['Invalid data']}) + self.assertEqual(serializer.is_valid(), False) + self.assertEqual(serializer.errors, {'non_field_errors': ['Invalid data']}) data = 42 serializer = CommentSerializer(self.comment, data=data) - self.assertEquals(serializer.is_valid(), False) - self.assertEquals(serializer.errors, {'non_field_errors': ['Invalid data']}) + self.assertEqual(serializer.is_valid(), False) + self.assertEqual(serializer.errors, {'non_field_errors': ['Invalid data']}) def test_cross_field_validation(self): @@ -297,23 +302,23 @@ class ValidationTests(TestCase): serializer = CommentSerializerWithCrossFieldValidator(data=data) self.assertFalse(serializer.is_valid()) - self.assertEquals(serializer.errors, {'non_field_errors': ['Email address not in content']}) + self.assertEqual(serializer.errors, {'non_field_errors': ['Email address not in content']}) def test_null_is_true_fields(self): """ Omitting a value for null-field should validate. """ serializer = PersonSerializer(data={'name': 'marko'}) - self.assertEquals(serializer.is_valid(), True) - self.assertEquals(serializer.errors, {}) + self.assertEqual(serializer.is_valid(), True) + self.assertEqual(serializer.errors, {}) def test_modelserializer_max_length_exceeded(self): data = { 'title': 'x' * 201, } serializer = ActionItemSerializer(data=data) - self.assertEquals(serializer.is_valid(), False) - self.assertEquals(serializer.errors, {'title': ['Ensure this value has at most 200 characters (it has 201).']}) + self.assertEqual(serializer.is_valid(), False) + self.assertEqual(serializer.errors, {'title': ['Ensure this value has at most 200 characters (it has 201).']}) def test_modelserializer_max_length_exceeded_with_custom_restore(self): """ @@ -326,8 +331,8 @@ class ValidationTests(TestCase): 'title': 'x' * 201, } serializer = ActionItemSerializerCustomRestore(data=data) - self.assertEquals(serializer.is_valid(), False) - self.assertEquals(serializer.errors, {'title': ['Ensure this value has at most 200 characters (it has 201).']}) + self.assertEqual(serializer.is_valid(), False) + self.assertEqual(serializer.errors, {'title': ['Ensure this value has at most 200 characters (it has 201).']}) def test_default_modelfield_max_length_exceeded(self): data = { @@ -335,8 +340,8 @@ class ValidationTests(TestCase): 'info': 'x' * 13, } serializer = ActionItemSerializer(data=data) - self.assertEquals(serializer.is_valid(), False) - self.assertEquals(serializer.errors, {'info': ['Ensure this value has at most 12 characters (it has 13).']}) + self.assertEqual(serializer.is_valid(), False) + self.assertEqual(serializer.errors, {'info': ['Ensure this value has at most 12 characters (it has 13).']}) def test_datetime_validation_failure(self): """ @@ -349,10 +354,22 @@ class ValidationTests(TestCase): data['created'] = 0 serializer = CommentSerializer(data=data) - self.assertEquals(serializer.is_valid(), False) + self.assertEqual(serializer.is_valid(), False) self.assertIn('created', serializer.errors) + def test_missing_model_field_exception_msg(self): + """ + Assert that a meaningful exception message is outputted when the model + field is missing (e.g. when mistyping ``model``). + """ + try: + serializer = BrokenModelSerializer() + except AssertionError as e: + self.assertEqual(e.args[0], "Serializer class 'BrokenModelSerializer' is missing 'model' Meta option") + except: + self.fail('Wrong exception type thrown.') + class CustomValidationTests(TestCase): class CommentSerializerWithFieldValidator(CommentSerializer): @@ -382,7 +399,7 @@ class CustomValidationTests(TestCase): serializer = self.CommentSerializerWithFieldValidator(data=data) self.assertFalse(serializer.is_valid()) - self.assertEquals(serializer.errors, {'content': ['Test not in value']}) + self.assertEqual(serializer.errors, {'content': ['Test not in value']}) def test_missing_data(self): """ @@ -394,7 +411,7 @@ class CustomValidationTests(TestCase): } serializer = self.CommentSerializerWithFieldValidator(data=incomplete_data) self.assertFalse(serializer.is_valid()) - self.assertEquals(serializer.errors, {'content': ['This field is required.']}) + self.assertEqual(serializer.errors, {'content': ['This field is required.']}) def test_wrong_data(self): """ @@ -407,14 +424,14 @@ class CustomValidationTests(TestCase): } serializer = self.CommentSerializerWithFieldValidator(data=wrong_data) self.assertFalse(serializer.is_valid()) - self.assertEquals(serializer.errors, {'email': ['Enter a valid e-mail address.']}) + self.assertEqual(serializer.errors, {'email': ['Enter a valid e-mail address.']}) class PositiveIntegerAsChoiceTests(TestCase): def test_positive_integer_in_json_is_correctly_parsed(self): data = {'some_integer': 1} serializer = PositiveIntegerAsChoiceSerializer(data=data) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) class ModelValidationTests(TestCase): @@ -465,15 +482,15 @@ class RegexValidationTest(TestCase): def test_create_failed(self): serializer = BookSerializer(data={'isbn': '1234567890'}) self.assertFalse(serializer.is_valid()) - self.assertEquals(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']}) + self.assertEqual(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']}) serializer = BookSerializer(data={'isbn': '12345678901234'}) self.assertFalse(serializer.is_valid()) - self.assertEquals(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']}) + self.assertEqual(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']}) serializer = BookSerializer(data={'isbn': 'abcdefghijklm'}) self.assertFalse(serializer.is_valid()) - self.assertEquals(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']}) + self.assertEqual(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']}) def test_create_success(self): serializer = BookSerializer(data={'isbn': '1234567890123'}) @@ -518,7 +535,7 @@ class ManyToManyTests(TestCase): """ serializer = self.serializer_class(instance=self.instance) expected = self.data - self.assertEquals(serializer.data, expected) + self.assertEqual(serializer.data, expected) def test_create(self): """ @@ -526,11 +543,11 @@ class ManyToManyTests(TestCase): """ data = {'rel': [self.anchor.id]} serializer = self.serializer_class(data=data) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) instance = serializer.save() - self.assertEquals(len(ManyToManyModel.objects.all()), 2) - self.assertEquals(instance.pk, 2) - self.assertEquals(list(instance.rel.all()), [self.anchor]) + self.assertEqual(len(ManyToManyModel.objects.all()), 2) + self.assertEqual(instance.pk, 2) + self.assertEqual(list(instance.rel.all()), [self.anchor]) def test_update(self): """ @@ -540,11 +557,11 @@ class ManyToManyTests(TestCase): new_anchor.save() data = {'rel': [self.anchor.id, new_anchor.id]} serializer = self.serializer_class(self.instance, data=data) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) instance = serializer.save() - self.assertEquals(len(ManyToManyModel.objects.all()), 1) - self.assertEquals(instance.pk, 1) - self.assertEquals(list(instance.rel.all()), [self.anchor, new_anchor]) + self.assertEqual(len(ManyToManyModel.objects.all()), 1) + self.assertEqual(instance.pk, 1) + self.assertEqual(list(instance.rel.all()), [self.anchor, new_anchor]) def test_create_empty_relationship(self): """ @@ -553,11 +570,11 @@ class ManyToManyTests(TestCase): """ data = {'rel': []} serializer = self.serializer_class(data=data) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) instance = serializer.save() - self.assertEquals(len(ManyToManyModel.objects.all()), 2) - self.assertEquals(instance.pk, 2) - self.assertEquals(list(instance.rel.all()), []) + self.assertEqual(len(ManyToManyModel.objects.all()), 2) + self.assertEqual(instance.pk, 2) + self.assertEqual(list(instance.rel.all()), []) def test_update_empty_relationship(self): """ @@ -568,11 +585,11 @@ class ManyToManyTests(TestCase): new_anchor.save() data = {'rel': []} serializer = self.serializer_class(self.instance, data=data) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) instance = serializer.save() - self.assertEquals(len(ManyToManyModel.objects.all()), 1) - self.assertEquals(instance.pk, 1) - self.assertEquals(list(instance.rel.all()), []) + self.assertEqual(len(ManyToManyModel.objects.all()), 1) + self.assertEqual(instance.pk, 1) + self.assertEqual(list(instance.rel.all()), []) def test_create_empty_relationship_flat_data(self): """ @@ -583,11 +600,11 @@ class ManyToManyTests(TestCase): data = MultiValueDict() data.setlist('rel', ['']) serializer = self.serializer_class(data=data) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) instance = serializer.save() - self.assertEquals(len(ManyToManyModel.objects.all()), 2) - self.assertEquals(instance.pk, 2) - self.assertEquals(list(instance.rel.all()), []) + self.assertEqual(len(ManyToManyModel.objects.all()), 2) + self.assertEqual(instance.pk, 2) + self.assertEqual(list(instance.rel.all()), []) class ReadOnlyManyToManyTests(TestCase): @@ -621,12 +638,12 @@ class ReadOnlyManyToManyTests(TestCase): new_anchor.save() data = {'rel': [self.anchor.id, new_anchor.id]} serializer = self.serializer_class(self.instance, data=data) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) instance = serializer.save() - self.assertEquals(len(ReadOnlyManyToManyModel.objects.all()), 1) - self.assertEquals(instance.pk, 1) + self.assertEqual(len(ReadOnlyManyToManyModel.objects.all()), 1) + self.assertEqual(instance.pk, 1) # rel is still as original (1 entry) - self.assertEquals(list(instance.rel.all()), [self.anchor]) + self.assertEqual(list(instance.rel.all()), [self.anchor]) def test_update_without_relationship(self): """ @@ -637,12 +654,12 @@ class ReadOnlyManyToManyTests(TestCase): new_anchor.save() data = {} serializer = self.serializer_class(self.instance, data=data) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) instance = serializer.save() - self.assertEquals(len(ReadOnlyManyToManyModel.objects.all()), 1) - self.assertEquals(instance.pk, 1) + self.assertEqual(len(ReadOnlyManyToManyModel.objects.all()), 1) + self.assertEqual(instance.pk, 1) # rel is still as original (1 entry) - self.assertEquals(list(instance.rel.all()), [self.anchor]) + self.assertEqual(list(instance.rel.all()), [self.anchor]) class DefaultValueTests(TestCase): @@ -657,35 +674,35 @@ class DefaultValueTests(TestCase): def test_create_using_default(self): data = {} serializer = self.serializer_class(data=data) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) instance = serializer.save() - self.assertEquals(len(self.objects.all()), 1) - self.assertEquals(instance.pk, 1) - self.assertEquals(instance.text, 'foobar') + self.assertEqual(len(self.objects.all()), 1) + self.assertEqual(instance.pk, 1) + self.assertEqual(instance.text, 'foobar') def test_create_overriding_default(self): data = {'text': 'overridden'} serializer = self.serializer_class(data=data) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) instance = serializer.save() - self.assertEquals(len(self.objects.all()), 1) - self.assertEquals(instance.pk, 1) - self.assertEquals(instance.text, 'overridden') + self.assertEqual(len(self.objects.all()), 1) + self.assertEqual(instance.pk, 1) + self.assertEqual(instance.text, 'overridden') def test_partial_update_default(self): """ Regression test for issue #532 """ data = {'text': 'overridden'} serializer = self.serializer_class(data=data, partial=True) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) instance = serializer.save() data = {'extra': 'extra_value'} serializer = self.serializer_class(instance=instance, data=data, partial=True) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) instance = serializer.save() - self.assertEquals(instance.extra, 'extra_value') - self.assertEquals(instance.text, 'overridden') + self.assertEqual(instance.extra, 'extra_value') + self.assertEqual(instance.text, 'overridden') class CallableDefaultValueTests(TestCase): @@ -700,20 +717,20 @@ class CallableDefaultValueTests(TestCase): def test_create_using_default(self): data = {} serializer = self.serializer_class(data=data) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) instance = serializer.save() - self.assertEquals(len(self.objects.all()), 1) - self.assertEquals(instance.pk, 1) - self.assertEquals(instance.text, 'foobar') + self.assertEqual(len(self.objects.all()), 1) + self.assertEqual(instance.pk, 1) + self.assertEqual(instance.text, 'foobar') def test_create_overriding_default(self): data = {'text': 'overridden'} serializer = self.serializer_class(data=data) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) instance = serializer.save() - self.assertEquals(len(self.objects.all()), 1) - self.assertEquals(instance.pk, 1) - self.assertEquals(instance.text, 'overridden') + self.assertEqual(len(self.objects.all()), 1) + self.assertEqual(instance.pk, 1) + self.assertEqual(instance.text, 'overridden') class ManyRelatedTests(TestCase): @@ -836,7 +853,7 @@ class RelatedTraversalTest(TestCase): obj = ClassWithQuerysetMethod() serializer = QuerysetMethodSerializer(obj) - self.assertEquals(serializer.data, {'blogposts': ['BlogPost object']}) + self.assertEqual(serializer.data, {'blogposts': ['BlogPost object']}) class SerializerMethodFieldTests(TestCase): @@ -899,15 +916,15 @@ class BlankFieldTests(TestCase): def test_create_blank_field(self): serializer = self.serializer_class(data=self.data) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) def test_create_model_blank_field(self): serializer = self.model_serializer_class(data=self.data) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) def test_create_model_null_field(self): serializer = self.model_serializer_class(data={'title': None}) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) def test_create_not_blank_field(self): """ @@ -915,7 +932,7 @@ class BlankFieldTests(TestCase): is considered invalid in a non-model serializer """ serializer = self.not_blank_serializer_class(data=self.data) - self.assertEquals(serializer.is_valid(), False) + self.assertEqual(serializer.is_valid(), False) def test_create_model_not_blank_field(self): """ @@ -923,11 +940,11 @@ class BlankFieldTests(TestCase): is considered invalid in a model serializer """ serializer = self.not_blank_model_serializer_class(data=self.data) - self.assertEquals(serializer.is_valid(), False) + self.assertEqual(serializer.is_valid(), False) def test_create_model_empty_field(self): serializer = self.model_serializer_class(data={}) - self.assertEquals(serializer.is_valid(), True) + self.assertEqual(serializer.is_valid(), True) #test for issue #460 diff --git a/rest_framework/tests/status.py b/rest_framework/tests/status.py index c0d11b5f..e1644a6b 100644 --- a/rest_framework/tests/status.py +++ b/rest_framework/tests/status.py @@ -9,5 +9,5 @@ class TestStatus(TestCase): def test_status(self): """Ensure the status module is present and correct.""" - self.assertEquals(200, status.HTTP_200_OK) - self.assertEquals(404, status.HTTP_404_NOT_FOUND) + self.assertEqual(200, status.HTTP_200_OK) + self.assertEqual(404, status.HTTP_404_NOT_FOUND) diff --git a/rest_framework/tests/throttling.py b/rest_framework/tests/throttling.py index 4616f325..11cbd8eb 100644 --- a/rest_framework/tests/throttling.py +++ b/rest_framework/tests/throttling.py @@ -103,7 +103,7 @@ class ThrottlingTests(TestCase): self.set_throttle_timer(view, timer) response = view.as_view()(request) if expect is not None: - self.assertEquals(response['X-Throttle-Wait-Seconds'], expect) + self.assertEqual(response['X-Throttle-Wait-Seconds'], expect) else: self.assertFalse('X-Throttle-Wait-Seconds' in response) diff --git a/rest_framework/tests/urlpatterns.py b/rest_framework/tests/urlpatterns.py index 82cd6cdb..29ed4a96 100644 --- a/rest_framework/tests/urlpatterns.py +++ b/rest_framework/tests/urlpatterns.py @@ -32,8 +32,8 @@ class FormatSuffixTests(TestCase): callback, callback_args, callback_kwargs = resolver.resolve(request.path_info) except Exception: self.fail("Failed to resolve URL: %s" % request.path_info) - self.assertEquals(callback_args, test_path.args) - self.assertEquals(callback_kwargs, test_path.kwargs) + self.assertEqual(callback_args, test_path.args) + self.assertEqual(callback_kwargs, test_path.kwargs) def test_format_suffix(self): urlpatterns = patterns( diff --git a/rest_framework/tests/validation.py b/rest_framework/tests/validation.py index 4d46cbdc..cbdd6515 100644 --- a/rest_framework/tests/validation.py +++ b/rest_framework/tests/validation.py @@ -37,7 +37,7 @@ class TestPreSaveValidationExclusions(TestCase): content_type='application/json') view = UpdateValidationModel().as_view() response = view(request, pk=obj.pk).render() - self.assertEquals(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_200_OK) # Regression for #653 @@ -62,4 +62,4 @@ class TestPreSaveValidationExclusions(TestCase): # We've set `required=False` on the serializer, but the model # does not have `blank=True`, so this serializer should not validate. serializer = ShouldValidateModelSerializer(data={'renamed': ''}) - self.assertEquals(serializer.is_valid(), False) + self.assertEqual(serializer.is_valid(), False) diff --git a/rest_framework/tests/views.py b/rest_framework/tests/views.py index 7063c3fb..994cf6dc 100644 --- a/rest_framework/tests/views.py +++ b/rest_framework/tests/views.py @@ -52,8 +52,8 @@ class ClassBasedViewIntegrationTests(TestCase): expected = { 'detail': 'JSON parse error - No JSON object could be decoded' } - self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEquals(sanitise_json_error(response.data), expected) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(sanitise_json_error(response.data), expected) def test_400_parse_error_tunneled_content(self): content = 'f00bar' @@ -67,8 +67,8 @@ class ClassBasedViewIntegrationTests(TestCase): expected = { 'detail': 'JSON parse error - No JSON object could be decoded' } - self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEquals(sanitise_json_error(response.data), expected) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(sanitise_json_error(response.data), expected) class FunctionBasedViewIntegrationTests(TestCase): @@ -81,8 +81,8 @@ class FunctionBasedViewIntegrationTests(TestCase): expected = { 'detail': 'JSON parse error - No JSON object could be decoded' } - self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEquals(sanitise_json_error(response.data), expected) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(sanitise_json_error(response.data), expected) def test_400_parse_error_tunneled_content(self): content = 'f00bar' @@ -96,5 +96,5 @@ class FunctionBasedViewIntegrationTests(TestCase): expected = { 'detail': 'JSON parse error - No JSON object could be decoded' } - self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEquals(sanitise_json_error(response.data), expected) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(sanitise_json_error(response.data), expected) diff --git a/rest_framework/views.py b/rest_framework/views.py index 69377bc0..81cbdcbb 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -8,7 +8,7 @@ from django.utils.html import escape from django.utils.safestring import mark_safe from django.views.decorators.csrf import csrf_exempt from rest_framework import status, exceptions -from rest_framework.compat import View, apply_markdown, smart_text +from rest_framework.compat import View, apply_markdown from rest_framework.response import Response from rest_framework.request import Request from rest_framework.settings import api_settings @@ -257,6 +257,16 @@ class APIView(View): return (renderers[0], renderers[0].media_type) raise + def perform_authentication(self, request): + """ + Perform authentication on the incoming request. + + Note that if you override this and simply 'pass', then authentication + will instead be performed lazily, the first time either + `request.user` or `request.auth` is accessed. + """ + request.user + def check_permissions(self, request): """ Check if the request should be permitted. @@ -305,6 +315,7 @@ class APIView(View): self.format_kwarg = self.get_format_suffix(**kwargs) # Ensure that the incoming request is permitted + self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request) @@ -1,53 +1,65 @@ [tox] downloadcache = {toxworkdir}/cache/ -envlist = py3.3-django1.5,py3.2-django1.5,py2.7-django1.5,py2.7-django1.4,py2.7-django1.3,py2.6-django1.5,py2.6-django1.4,py2.6-django1.3 +envlist = py3.3-django1.5,py3.2-django1.5,py2.7-django1.5,py2.6-django1.5,py2.7-django1.4,py2.6-django1.4,py2.7-django1.3,py2.6-django1.3 [testenv] commands = {envpython} rest_framework/runtests/runtests.py [testenv:py3.3-django1.5] basepython = python3.3 -deps = https://www.djangoproject.com/download/1.5c1/tarball/ +deps = django==1.5 https://github.com/alex/django-filter/archive/master.tar.gz defusedxml==0.3 [testenv:py3.2-django1.5] basepython = python3.2 -deps = https://www.djangoproject.com/download/1.5c1/tarball/ +deps = django==1.5 https://github.com/alex/django-filter/archive/master.tar.gz defusedxml==0.3 [testenv:py2.7-django1.5] basepython = python2.7 -deps = https://www.djangoproject.com/download/1.5c1/tarball/ +deps = django==1.5 django-filter==0.5.4 + django-oauth-plus==2.0 + oauth2==1.5.211 [testenv:py2.6-django1.5] basepython = python2.6 -deps = https://www.djangoproject.com/download/1.5c1/tarball/ +deps = django==1.5 django-filter==0.5.4 defusedxml==0.3 + django-oauth-plus==2.0 + oauth2==1.5.211 [testenv:py2.7-django1.4] basepython = python2.7 deps = django==1.4.3 django-filter==0.5.4 defusedxml==0.3 + django-oauth-plus==2.0 + oauth2==1.5.211 [testenv:py2.6-django1.4] basepython = python2.6 deps = django==1.4.3 django-filter==0.5.4 defusedxml==0.3 + django-oauth-plus==2.0 + oauth2==1.5.211 [testenv:py2.7-django1.3] basepython = python2.7 deps = django==1.3.5 django-filter==0.5.4 defusedxml==0.3 + django-oauth-plus==2.0 + oauth2==1.5.211 [testenv:py2.6-django1.3] basepython = python2.6 deps = django==1.3.5 django-filter==0.5.4 defusedxml==0.3 + django-oauth-plus==2.0 + oauth2==1.5.211 |
