From 53a1205dd03703e6f93bbf7bcb2686c617638242 Mon Sep 17 00:00:00 2001 From: Vadim Date: Fri, 24 Jun 2016 15:04:18 +0300 Subject: [PATCH 0001/1069] Fix partial update for the ListSerializer. --- rest_framework/serializers.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 3fcc85c3b7..9f06f5a4f4 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -533,6 +533,10 @@ def __init__(self, *args, **kwargs): super(ListSerializer, self).__init__(*args, **kwargs) self.child.bind(field_name='', parent=self) + def bind(self, field_name, parent): + super(ListSerializer, self).bind(field_name, parent) + self.partial = self.parent.partial + def get_initial(self): if hasattr(self, 'initial_data'): return self.to_representation(self.initial_data) @@ -584,6 +588,9 @@ def to_internal_value(self, data): }) if not self.allow_empty and len(data) == 0: + if self.partial: + raise SkipField() + message = self.error_messages['empty'] raise ValidationError({ api_settings.NON_FIELD_ERRORS_KEY: [message] From c0f4dfd8f33819dab001b5b23da148cc7830b1ba Mon Sep 17 00:00:00 2001 From: Vadim Date: Wed, 29 Jun 2016 09:48:31 +0300 Subject: [PATCH 0002/1069] Add tests for the ListSerializer for the TestSerializerPartialUsage. Additional fix partial update for the ListSerializer. --- rest_framework/serializers.py | 2 +- tests/test_serializer_lists.py | 214 +++++++++++++++++++++++++++++++++ 2 files changed, 215 insertions(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 9f06f5a4f4..c4057dc46f 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -588,7 +588,7 @@ def to_internal_value(self, data): }) if not self.allow_empty and len(data) == 0: - if self.partial: + if self.parent and self.partial: raise SkipField() message = self.error_messages['empty'] diff --git a/tests/test_serializer_lists.py b/tests/test_serializer_lists.py index 607ddba04a..740197c280 100644 --- a/tests/test_serializer_lists.py +++ b/tests/test_serializer_lists.py @@ -318,3 +318,217 @@ class MultipleChoiceSerializer(serializers.Serializer): assert serializer.is_valid() assert serializer.validated_data == {} assert serializer.errors == {} + + def test_allow_empty_true(self): + class ListSerializer(serializers.Serializer): + update_field = serializers.IntegerField() + store_field = serializers.IntegerField() + + instance = [ + {'update_field': 11, 'store_field': 12}, + {'update_field': 21, 'store_field': 22}, + ] + + serializer = ListSerializer(instance, data=[], partial=True, many=True) + assert serializer.is_valid() + assert serializer.validated_data == [] + assert serializer.errors == [] + + def test_update_allow_empty_true(self): + class ListSerializer(serializers.Serializer): + update_field = serializers.IntegerField() + store_field = serializers.IntegerField() + + instance = [ + {'update_field': 11, 'store_field': 12}, + {'update_field': 21, 'store_field': 22}, + ] + input_data = [{'update_field': 31}, {'update_field': 41}] + updated_data_list = [ + {'update_field': 31, 'store_field': 12}, + {'update_field': 41, 'store_field': 22}, + ] + + serializer = ListSerializer( + instance, data=input_data, partial=True, many=True) + assert serializer.is_valid() + + for index, data in enumerate(serializer.validated_data): + for key, value in data.items(): + assert value == updated_data_list[index][key] + + assert serializer.errors == [] + + def test_allow_empty_false(self): + class ListSerializer(serializers.Serializer): + update_field = serializers.IntegerField() + store_field = serializers.IntegerField() + + instance = [ + {'update_field': 11, 'store_field': 12}, + {'update_field': 21, 'store_field': 22}, + ] + + serializer = ListSerializer( + instance, data=[], allow_empty=False, partial=True, many=True) + assert not serializer.is_valid() + assert serializer.validated_data == [] + assert len(serializer.errors) == 1 + assert serializer.errors['non_field_errors'] == [u'This list may not be empty.'] + + def test_update_allow_empty_false(self): + class ListSerializer(serializers.Serializer): + update_field = serializers.IntegerField() + store_field = serializers.IntegerField() + + instance = [ + {'update_field': 11, 'store_field': 12}, + {'update_field': 21, 'store_field': 22}, + ] + input_data = [{'update_field': 31}, {'update_field': 41}] + updated_data_list = [ + {'update_field': 31, 'store_field': 12}, + {'update_field': 41, 'store_field': 22}, + ] + + serializer = ListSerializer( + instance, data=input_data, allow_empty=False, partial=True, many=True) + assert serializer.is_valid() + + for index, data in enumerate(serializer.validated_data): + for key, value in data.items(): + assert value == updated_data_list[index][key] + + assert serializer.errors == [] + + def test_as_field_allow_empty_true(self): + class ListSerializer(serializers.Serializer): + update_field = serializers.IntegerField() + store_field = serializers.IntegerField() + + class Serializer(serializers.Serializer): + extra_field = serializers.IntegerField() + list_field = ListSerializer(many=True) + + instance = { + 'extra_field': 1, + 'list_field': [ + {'update_field': 11, 'store_field': 12}, + {'update_field': 21, 'store_field': 22}, + ] + } + + serializer = Serializer(instance, data={}, partial=True) + assert serializer.is_valid() + assert serializer.validated_data == {} + assert serializer.errors == {} + + def test_udate_as_field_allow_empty_true(self): + class ListSerializer(serializers.Serializer): + update_field = serializers.IntegerField() + store_field = serializers.IntegerField() + + class Serializer(serializers.Serializer): + extra_field = serializers.IntegerField() + list_field = ListSerializer(many=True) + + instance = { + 'extra_field': 1, + 'list_field': [ + {'update_field': 11, 'store_field': 12}, + {'update_field': 21, 'store_field': 22}, + ] + } + input_data_1 = {'extra_field': 2} + input_data_2 = { + 'list_field': [ + {'update_field': 31}, + {'update_field': 41}, + ] + } + + # data_1 + serializer = Serializer(instance, data=input_data_1, partial=True) + assert serializer.is_valid() + assert len(serializer.validated_data) == 1 + assert serializer.validated_data['extra_field'] == 2 + assert serializer.errors == {} + + # data_2 + serializer = Serializer(instance, data=input_data_2, partial=True) + assert serializer.is_valid() + + updated_data_list = [ + {'update_field': 31, 'store_field': 12}, + {'update_field': 41, 'store_field': 22}, + ] + for index, data in enumerate(serializer.validated_data['list_field']): + for key, value in data.items(): + assert value == updated_data_list[index][key] + + assert serializer.errors == {} + + def test_as_field_allow_empty_false(self): + class ListSerializer(serializers.Serializer): + update_field = serializers.IntegerField() + store_field = serializers.IntegerField() + + class Serializer(serializers.Serializer): + extra_field = serializers.IntegerField() + list_field = ListSerializer(many=True, allow_empty=False) + + instance = { + 'extra_field': 1, + 'list_field': [ + {'update_field': 11, 'store_field': 12}, + {'update_field': 21, 'store_field': 22}, + ] + } + + serializer = Serializer(instance, data={}, partial=True) + assert serializer.is_valid() + assert serializer.validated_data == {} + assert serializer.errors == {} + + def test_update_as_field_allow_empty_false(self): + class ListSerializer(serializers.Serializer): + update_field = serializers.IntegerField() + store_field = serializers.IntegerField() + + class Serializer(serializers.Serializer): + extra_field = serializers.IntegerField() + list_field = ListSerializer(many=True, allow_empty=False) + + instance = { + 'extra_field': 1, + 'list_field': [ + {'update_field': 11, 'store_field': 12}, + {'update_field': 21, 'store_field': 22}, + ] + } + input_data_1 = {'extra_field': 2} + input_data_2 = { + 'list_field': [ + {'update_field': 31}, + {'update_field': 41}, + ] + } + updated_data_list = [ + {'update_field': 31, 'store_field': 12}, + {'update_field': 41, 'store_field': 22}, + ] + + # data_1 + serializer = Serializer(instance, data=input_data_1, partial=True) + assert serializer.is_valid() + assert serializer.errors == {} + + # data_2 + serializer = Serializer(instance, data=input_data_2, partial=True) + assert serializer.is_valid() + + for index, data in enumerate(serializer.validated_data['list_field']): + for key, value in data.items(): + assert value == updated_data_list[index][key] + + assert serializer.errors == {} From c752e9618fedc6eb3e103cf8740742ff3e731899 Mon Sep 17 00:00:00 2001 From: Vadim Date: Wed, 29 Jun 2016 10:21:28 +0300 Subject: [PATCH 0003/1069] Fix test for py32-django18. --- tests/test_serializer_lists.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_serializer_lists.py b/tests/test_serializer_lists.py index 740197c280..a7955d83c7 100644 --- a/tests/test_serializer_lists.py +++ b/tests/test_serializer_lists.py @@ -374,7 +374,7 @@ class ListSerializer(serializers.Serializer): assert not serializer.is_valid() assert serializer.validated_data == [] assert len(serializer.errors) == 1 - assert serializer.errors['non_field_errors'] == [u'This list may not be empty.'] + assert serializer.errors['non_field_errors'][0] == 'This list may not be empty.' def test_update_allow_empty_false(self): class ListSerializer(serializers.Serializer): From ec4761225e5a1683b046752e7090e91abfa52f54 Mon Sep 17 00:00:00 2001 From: dfavato Date: Tue, 23 Aug 2016 14:52:20 -0300 Subject: [PATCH 0004/1069] Set self.count before self.limit in LimitOffsetPagination By doing this it is possible to override get_limit in order to return all records if the request has a predefined param. For example, if one wants that all records are retrieved if url has &limit=-1, get_limit could return self.count in this case. Otherwise, if self.count is set after self.limit then, to achive the same result, one has to override get_limit and paginate_queryset, or run get_limit twice. --- rest_framework/pagination.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index c54894efc5..755debea3d 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -303,12 +303,12 @@ class LimitOffsetPagination(BasePagination): template = 'rest_framework/pagination/numbers.html' def paginate_queryset(self, queryset, request, view=None): + self.count = _get_count(queryset) self.limit = self.get_limit(request) if self.limit is None: return None self.offset = self.get_offset(request) - self.count = _get_count(queryset) self.request = request if self.count > self.limit and self.template is not None: self.display_page_controls = True From 388cf7622c24fee33f998cbe44198bada1872ebb Mon Sep 17 00:00:00 2001 From: Michael Barr Date: Wed, 9 Nov 2016 07:59:11 -0500 Subject: [PATCH 0005/1069] Adds Django/Python Trove Classifiers (#4662) --- setup.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/setup.py b/setup.py index ca62366edc..5d14ddf6a2 100755 --- a/setup.py +++ b/setup.py @@ -92,11 +92,19 @@ def get_package_data(package): 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Framework :: Django', + 'Framework :: Django :: 1.8', + 'Framework :: Django :: 1.9', + 'Framework :: Django :: 1.10', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Topic :: Internet :: WWW/HTTP', ] ) From 8bab7f8d582c6c8c25e913ec4092899c78875a2c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 10 Nov 2016 16:36:56 +0000 Subject: [PATCH 0006/1069] Only apply the nested writes test to writable fields. (#4669) --- rest_framework/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 1bdcd12c38..02c24b70eb 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -769,7 +769,7 @@ def raise_errors_on_nested_writes(method_name, serializer, validated_data): isinstance(field, BaseSerializer) and (field.source in validated_data) and isinstance(validated_data[field.source], (list, dict)) - for key, field in serializer.fields.items() + for field in serializer._writable_fields ), ( 'The `.{method_name}()` method does not support writable nested ' 'fields by default.\nWrite an explicit `.{method_name}()` method for ' From 24791cb353d1924086b30abe2188280547d9a6c4 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 11 Nov 2016 09:44:35 +0000 Subject: [PATCH 0007/1069] Invalidate any existing prefetch cache on PUT requests. (#4668) --- docs/api-guide/testing.md | 11 ++++++++++- rest_framework/mixins.py | 5 ++--- tests/test_prefetch_related.py | 20 +++++++++++++++++++- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md index 1f8c892339..de79a1e2fc 100644 --- a/docs/api-guide/testing.md +++ b/docs/api-guide/testing.md @@ -187,7 +187,12 @@ As usual CSRF validation will only apply to any session authenticated views. Th # RequestsClient REST framework also includes a client for interacting with your application -using the popular Python library, `requests`. +using the popular Python library, `requests`. This may be useful if: + +* You are expecting to interface with the API primarily from another Python service, +and want to test the service at the same level as the client will see. +* You want to write tests in such a way that they can also be run against a staging or +live environment. (See "Live tests" below.) This exposes exactly the same interface as if you were using a requests session directly. @@ -198,6 +203,10 @@ directly. Note that the requests client requires you to pass fully qualified URLs. +## `RequestsClient` and working with the database + +The `RequestsClient` class is useful if + ## Headers & Authentication Custom headers and authentication credentials can be provided in the same way diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index 47a4923a19..f3695e6656 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -71,9 +71,8 @@ def update(self, request, *args, **kwargs): if getattr(instance, '_prefetched_objects_cache', None): # If 'prefetch_related' has been applied to a queryset, we need to - # refresh the instance from the database. - instance = self.get_object() - serializer = self.get_serializer(instance) + # forcibly invalidate the prefetch cache on the instance. + instance._prefetched_objects_cache = {} return Response(serializer.data) diff --git a/tests/test_prefetch_related.py b/tests/test_prefetch_related.py index fc697adc1a..a9fa238eab 100644 --- a/tests/test_prefetch_related.py +++ b/tests/test_prefetch_related.py @@ -14,7 +14,7 @@ class Meta: class UserUpdate(generics.UpdateAPIView): - queryset = User.objects.all().prefetch_related('groups') + queryset = User.objects.exclude(username='exclude').prefetch_related('groups') serializer_class = UserSerializer @@ -39,3 +39,21 @@ def test_prefetch_related_updates(self): 'email': 'tom@example.com' } assert response.data == expected + + def test_prefetch_related_excluding_instance_from_original_queryset(self): + """ + Regression test for https://github.com/tomchristie/django-rest-framework/issues/4661 + """ + view = UserUpdate.as_view() + pk = self.user.pk + groups_pk = self.groups[0].pk + request = factory.put('/', {'username': 'exclude', 'groups': [groups_pk]}, format='json') + response = view(request, pk=pk) + assert User.objects.get(pk=pk).groups.count() == 1 + expected = { + 'id': pk, + 'username': 'exclude', + 'groups': [1], + 'email': 'tom@example.com' + } + assert response.data == expected From 0c02bbbfa728267909ea73aeac57b2e99aba5857 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Mon, 14 Nov 2016 16:58:16 +0000 Subject: [PATCH 0008/1069] Correct a small typo in exceptions documentation --- docs/api-guide/exceptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index df8cad42df..03f16222d9 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -47,7 +47,7 @@ Any example validation error might look like this: You can implement custom exception handling by creating a handler function that converts exceptions raised in your API views into response objects. This allows you to control the style of error responses used by your API. -The function must take a pair of arguments, this first is the exception to be handled, and the second is a dictionary containing any extra context such as the view currently being handled. The exception handler function should either return a `Response` object, or return `None` if the exception cannot be handled. If the handler returns `None` then the exception will be re-raised and Django will return a standard HTTP 500 'server error' response. +The function must take a pair of arguments, the first is the exception to be handled, and the second is a dictionary containing any extra context such as the view currently being handled. The exception handler function should either return a `Response` object, or return `None` if the exception cannot be handled. If the handler returns `None` then the exception will be re-raised and Django will return a standard HTTP 500 'server error' response. For example, you might want to ensure that all error responses include the HTTP status code in the body of the response, like so: From 4c2d6b9bb751c41a4e240811dc66664e00203481 Mon Sep 17 00:00:00 2001 From: Thodoris Sotiropoulos Date: Thu, 17 Nov 2016 18:51:22 +0200 Subject: [PATCH 0009/1069] Do not ignore source when building fields dynamically This commit fixes the issue when you set the keyword argument `source` and your have not set the serializer fields explicitly. Then the construction of field failed because there is not actually any model field with that name. However, you are still able to imply the name of model field by providing the `source` keyword argument. --- rest_framework/serializers.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 02c24b70eb..fda57bfb0d 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -995,13 +995,15 @@ def get_fields(self): fields[field_name] = declared_fields[field_name] continue + extra_field_kwargs = extra_kwargs.get(field_name, {}) + source = extra_field_kwargs.get('source') or field_name + # Determine the serializer field class and keyword arguments. field_class, field_kwargs = self.build_field( - field_name, info, model, depth + source, info, model, depth ) # Include any kwargs defined in `Meta.extra_kwargs` - extra_field_kwargs = extra_kwargs.get(field_name, {}) field_kwargs = self.include_extra_kwargs( field_kwargs, extra_field_kwargs ) From 1b9013ebae6f4b28de6b88c65a97b5ab0e817200 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Tue, 22 Nov 2016 19:46:12 +0600 Subject: [PATCH 0010/1069] update django-filter to version 1.0.0 --- requirements/requirements-optionals.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt index 86c4f77092..a59f153d15 100644 --- a/requirements/requirements-optionals.txt +++ b/requirements/requirements-optionals.txt @@ -1,5 +1,5 @@ # Optional packages which may be used with REST framework. markdown==2.6.4 django-guardian==1.4.6 -django-filter==0.15.3 +django-filter==1.0.0 coreapi==2.0.8 From 8030f5b74f49e3144e8fb45f367227bac1192aec Mon Sep 17 00:00:00 2001 From: Vinay Anantharaman Date: Tue, 22 Nov 2016 14:50:47 -0800 Subject: [PATCH 0011/1069] Edit to the import in Setting filter backends `django_filters` doesn't export `rest_framework` by default so it's required to import it. --- docs/api-guide/filtering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 1b49d3a737..3f212ced3a 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -98,7 +98,7 @@ The default filter backends may be set globally, using the `DEFAULT_FILTER_BACKE You can also set the filter backends on a per-view, or per-viewset basis, using the `GenericAPIView` class-based views. - import django_filters + import django_filters.rest_framework from django.contrib.auth.models import User from myapp.serializers import UserSerializer from rest_framework import generics From 4b59ec27fa725a83646792b560fae1b646baadcd Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Wed, 23 Nov 2016 19:17:00 +0600 Subject: [PATCH 0012/1069] convert tests asserts to pytest style (#4696) --- tests/test_views.py | 16 ++++++++-------- tests/test_viewsets.py | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/test_views.py b/tests/test_views.py index 05c499481b..adafa1cd7e 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -71,8 +71,8 @@ def test_400_parse_error(self): expected = { 'detail': JSON_ERROR } - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(sanitise_json_error(response.data), expected) + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert sanitise_json_error(response.data) == expected class FunctionBasedViewIntegrationTests(TestCase): @@ -85,8 +85,8 @@ def test_400_parse_error(self): expected = { 'detail': JSON_ERROR } - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(sanitise_json_error(response.data), expected) + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert sanitise_json_error(response.data) == expected class TestCustomExceptionHandler(TestCase): @@ -107,8 +107,8 @@ def test_class_based_view_exception_handler(self): request = factory.get('/', content_type='application/json') response = view(request) expected = 'Error!' - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.data, expected) + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.data == expected def test_function_based_view_exception_handler(self): view = error_view @@ -116,5 +116,5 @@ def test_function_based_view_exception_handler(self): request = factory.get('/', content_type='application/json') response = view(request) expected = 'Error!' - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.data, expected) + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.data == expected diff --git a/tests/test_viewsets.py b/tests/test_viewsets.py index 0d9b6b310b..a3c4e6be59 100644 --- a/tests/test_viewsets.py +++ b/tests/test_viewsets.py @@ -21,15 +21,15 @@ def test_initialize_view_set_with_actions(self): }) response = my_view(request) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, {'ACTION': 'LIST'}) + assert response.status_code == status.HTTP_200_OK + assert response.data == {'ACTION': 'LIST'} def test_initialize_view_set_with_empty_actions(self): try: BasicViewSet.as_view() except TypeError as e: - self.assertEqual(str(e), "The `actions` argument must be provided " - "when calling `.as_view()` on a ViewSet. " - "For example `.as_view({'get': 'list'})`") + assert str(e) == ("The `actions` argument must be provided " + "when calling `.as_view()` on a ViewSet. " + "For example `.as_view({'get': 'list'})`") else: self.fail("actions must not be empty.") From 5ec223bca0f2df5be06b583f58012e115d2f52a2 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Wed, 23 Nov 2016 20:05:34 +0600 Subject: [PATCH 0013/1069] converted validators and write_only_fields test to pytest style (#4697) --- tests/test_validators.py | 13 +++++-------- tests/test_write_only_fields.py | 6 +++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/test_validators.py b/tests/test_validators.py index ab2c87c119..37e662b8e0 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -94,23 +94,20 @@ def test_updated_instance_excluded(self): def test_doesnt_pollute_model(self): instance = AnotherUniquenessModel.objects.create(code='100') serializer = AnotherUniquenessSerializer(instance) - self.assertEqual( - AnotherUniquenessModel._meta.get_field('code').validators, []) + assert AnotherUniquenessModel._meta.get_field('code').validators == [] # Accessing data shouldn't effect validators on the model serializer.data - self.assertEqual( - AnotherUniquenessModel._meta.get_field('code').validators, []) + assert AnotherUniquenessModel._meta.get_field('code').validators == [] def test_related_model_is_unique(self): data = {'username': 'Existing', 'email': 'new-email@example.com'} rs = RelatedModelSerializer(data=data) - self.assertFalse(rs.is_valid()) - self.assertEqual(rs.errors, - {'username': ['This field must be unique.']}) + assert not rs.is_valid() + assert rs.errors == {'username': ['This field must be unique.']} data = {'username': 'new-username', 'email': 'new-email@example.com'} rs = RelatedModelSerializer(data=data) - self.assertTrue(rs.is_valid()) + assert rs.is_valid() def test_value_error_treated_as_not_unique(self): serializer = UniquenessIntegerSerializer(data={'integer': 'abc'}) diff --git a/tests/test_write_only_fields.py b/tests/test_write_only_fields.py index 3a289afab2..272a05ff32 100644 --- a/tests/test_write_only_fields.py +++ b/tests/test_write_only_fields.py @@ -20,8 +20,8 @@ def write_only_fields_are_present_on_input(self): 'password': '123' } serializer = self.Serializer(data=data) - self.assertTrue(serializer.is_valid()) - self.assertEqual(serializer.validated_data, data) + assert serializer.is_valid() + assert serializer.validated_data == data def write_only_fields_are_not_present_on_output(self): instance = { @@ -29,4 +29,4 @@ def write_only_fields_are_not_present_on_output(self): 'password': '123' } serializer = self.Serializer(instance) - self.assertEqual(serializer.data, {'email': 'foo@example.com'}) + assert serializer.data == {'email': 'foo@example.com'} From a13b8d5560845612318fb030eb610bd34d986955 Mon Sep 17 00:00:00 2001 From: Nik Nyby Date: Wed, 23 Nov 2016 10:13:03 -0500 Subject: [PATCH 0014/1069] docs: grammar fix --- docs/api-guide/validators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index 1af9f02a56..e5b9494969 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -159,7 +159,7 @@ If you want the date field to be entirely hidden from the user, then use `Hidden --- -**Note**: The `UniqueForValidation` classes always imposes an implicit constraint that the fields they are applied to are always treated as required. Fields with `default` values are an exception to this as they always supply a value even when omitted from user input. +**Note**: The `UniqueForValidation` classes impose an implicit constraint that the fields they are applied to are always treated as required. Fields with `default` values are an exception to this as they always supply a value even when omitted from user input. --- From eaec60ae1d4e34cfef115df6c626cf96f6750e56 Mon Sep 17 00:00:00 2001 From: Nik Nyby Date: Wed, 23 Nov 2016 11:10:39 -0500 Subject: [PATCH 0015/1069] docs: grammar fix - it's -> its (#4698) --- docs/api-guide/validators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index e5b9494969..d6350ae5ed 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -153,7 +153,7 @@ The field will not be writable to the user, but the default value will still be #### Using with a hidden date field. -If you want the date field to be entirely hidden from the user, then use `HiddenField`. This field type does not accept user input, but instead always returns it's default value to the `validated_data` in the serializer. +If you want the date field to be entirely hidden from the user, then use `HiddenField`. This field type does not accept user input, but instead always returns its default value to the `validated_data` in the serializer. published = serializers.HiddenField(default=timezone.now) From abc62afddb9e49b199ee5f4f491093014515da0e Mon Sep 17 00:00:00 2001 From: Nik Nyby Date: Thu, 24 Nov 2016 04:39:18 -0500 Subject: [PATCH 0016/1069] docs typo fix (#4701) Remove unnecessary "a" --- docs/api-guide/validators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index d6350ae5ed..e041e9072b 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -20,7 +20,7 @@ With `ModelForm` the validation is performed partially on the form, and partiall * It is easy to switch between using shortcut `ModelSerializer` classes and using explicit `Serializer` classes. Any validation behavior being used for `ModelSerializer` is simple to replicate. * Printing the `repr` of a serializer instance will show you exactly what validation rules it applies. There's no extra hidden validation behavior being called on the model instance. -When you're using `ModelSerializer` all of this is handled automatically for you. If you want to drop down to using a `Serializer` classes instead, then you need to define the validation rules explicitly. +When you're using `ModelSerializer` all of this is handled automatically for you. If you want to drop down to using `Serializer` classes instead, then you need to define the validation rules explicitly. #### Example From cd3fd36d0e5fa7e2d4907a7f74cd45641a8ddd03 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Sun, 27 Nov 2016 23:55:09 +0600 Subject: [PATCH 0017/1069] converted generic relations assert to pytest style --- tests/test_relations_generic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_relations_generic.py b/tests/test_relations_generic.py index babc2269c1..a3798b0a39 100644 --- a/tests/test_relations_generic.py +++ b/tests/test_relations_generic.py @@ -75,7 +75,7 @@ class Meta: 'tags': ['django', 'python'], 'url': 'https://www.djangoproject.com/' } - self.assertEqual(serializer.data, expected) + assert serializer.data == expected def test_generic_fk(self): """ @@ -105,4 +105,4 @@ class Meta: 'tagged_item': 'Note: Remember the milk' } ] - self.assertEqual(serializer.data, expected) + assert serializer.data == expected From 42d6098c74e3c8536458f4d423bedb652da16db7 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Mon, 28 Nov 2016 15:43:48 +0600 Subject: [PATCH 0018/1069] converted primary key relations test asserts to pytest (#4709) --- tests/test_relations_pk.py | 118 ++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/tests/test_relations_pk.py b/tests/test_relations_pk.py index 7a3d459272..ea02f25779 100644 --- a/tests/test_relations_pk.py +++ b/tests/test_relations_pk.py @@ -84,7 +84,7 @@ def test_many_to_many_retrieve(self): {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]} ] with self.assertNumQueries(4): - self.assertEqual(serializer.data, expected) + assert serializer.data == expected def test_many_to_many_retrieve_prefetch_related(self): queryset = ManyToManySource.objects.all().prefetch_related('targets') @@ -101,15 +101,15 @@ def test_reverse_many_to_many_retrieve(self): {'id': 3, 'name': 'target-3', 'sources': [3]} ] with self.assertNumQueries(4): - self.assertEqual(serializer.data, expected) + assert serializer.data == expected def test_many_to_many_update(self): data = {'id': 1, 'name': 'source-1', 'targets': [1, 2, 3]} instance = ManyToManySource.objects.get(pk=1) serializer = ManyToManySourceSerializer(instance, data=data) - self.assertTrue(serializer.is_valid()) + assert serializer.is_valid() serializer.save() - self.assertEqual(serializer.data, data) + assert serializer.data == data # Ensure source 1 is updated, and everything else is as expected queryset = ManyToManySource.objects.all() @@ -119,15 +119,15 @@ def test_many_to_many_update(self): {'id': 2, 'name': 'source-2', 'targets': [1, 2]}, {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]} ] - self.assertEqual(serializer.data, expected) + assert serializer.data == expected def test_reverse_many_to_many_update(self): data = {'id': 1, 'name': 'target-1', 'sources': [1]} instance = ManyToManyTarget.objects.get(pk=1) serializer = ManyToManyTargetSerializer(instance, data=data) - self.assertTrue(serializer.is_valid()) + assert serializer.is_valid() serializer.save() - self.assertEqual(serializer.data, data) + assert serializer.data == data # Ensure target 1 is updated, and everything else is as expected queryset = ManyToManyTarget.objects.all() @@ -137,15 +137,15 @@ def test_reverse_many_to_many_update(self): {'id': 2, 'name': 'target-2', 'sources': [2, 3]}, {'id': 3, 'name': 'target-3', 'sources': [3]} ] - self.assertEqual(serializer.data, expected) + assert 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()) + assert serializer.is_valid() obj = serializer.save() - self.assertEqual(serializer.data, data) - self.assertEqual(obj.name, 'source-4') + assert serializer.data == data + assert obj.name == 'source-4' # Ensure source 4 is added, and everything else is as expected queryset = ManyToManySource.objects.all() @@ -156,7 +156,7 @@ def test_many_to_many_create(self): {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]}, {'id': 4, 'name': 'source-4', 'targets': [1, 3]}, ] - self.assertEqual(serializer.data, expected) + assert serializer.data == expected def test_many_to_many_unsaved(self): source = ManyToManySource(name='source-unsaved') @@ -166,15 +166,15 @@ def test_many_to_many_unsaved(self): expected = {'id': None, 'name': 'source-unsaved', 'targets': []} # no query if source hasn't been created yet with self.assertNumQueries(0): - self.assertEqual(serializer.data, expected) + assert 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()) + assert serializer.is_valid() obj = serializer.save() - self.assertEqual(serializer.data, data) - self.assertEqual(obj.name, 'target-4') + assert serializer.data == data + assert obj.name == 'target-4' # Ensure target 4 is added, and everything else is as expected queryset = ManyToManyTarget.objects.all() @@ -185,7 +185,7 @@ def test_reverse_many_to_many_create(self): {'id': 3, 'name': 'target-3', 'sources': [3]}, {'id': 4, 'name': 'target-4', 'sources': [1, 3]} ] - self.assertEqual(serializer.data, expected) + assert serializer.data == expected class PKForeignKeyTests(TestCase): @@ -207,7 +207,7 @@ def test_foreign_key_retrieve(self): {'id': 3, 'name': 'source-3', 'target': 1} ] with self.assertNumQueries(1): - self.assertEqual(serializer.data, expected) + assert serializer.data == expected def test_reverse_foreign_key_retrieve(self): queryset = ForeignKeyTarget.objects.all() @@ -217,7 +217,7 @@ def test_reverse_foreign_key_retrieve(self): {'id': 2, 'name': 'target-2', 'sources': []}, ] with self.assertNumQueries(3): - self.assertEqual(serializer.data, expected) + assert serializer.data == expected def test_reverse_foreign_key_retrieve_prefetch_related(self): queryset = ForeignKeyTarget.objects.all().prefetch_related('sources') @@ -229,9 +229,9 @@ 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()) + assert serializer.is_valid() serializer.save() - self.assertEqual(serializer.data, data) + assert serializer.data == data # Ensure source 1 is updated, and everything else is as expected queryset = ForeignKeySource.objects.all() @@ -241,20 +241,20 @@ def test_foreign_key_update(self): {'id': 2, 'name': 'source-2', 'target': 1}, {'id': 3, 'name': 'source-3', 'target': 1} ] - self.assertEqual(serializer.data, expected) + assert 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.assertEqual(serializer.errors, {'target': ['Incorrect type. Expected pk value, received %s.' % six.text_type.__name__]}) + assert not serializer.is_valid() + assert 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]} instance = ForeignKeyTarget.objects.get(pk=2) serializer = ForeignKeyTargetSerializer(instance, data=data) - self.assertTrue(serializer.is_valid()) + assert serializer.is_valid() # We shouldn't have saved anything to the db yet since save # hasn't been called. queryset = ForeignKeyTarget.objects.all() @@ -263,10 +263,10 @@ def test_reverse_foreign_key_update(self): {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, {'id': 2, 'name': 'target-2', 'sources': []}, ] - self.assertEqual(new_serializer.data, expected) + assert new_serializer.data == expected serializer.save() - self.assertEqual(serializer.data, data) + assert serializer.data == data # Ensure target 2 is update, and everything else is as expected queryset = ForeignKeyTarget.objects.all() @@ -275,15 +275,15 @@ def test_reverse_foreign_key_update(self): {'id': 1, 'name': 'target-1', 'sources': [2]}, {'id': 2, 'name': 'target-2', 'sources': [1, 3]}, ] - self.assertEqual(serializer.data, expected) + assert 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()) + assert serializer.is_valid() obj = serializer.save() - self.assertEqual(serializer.data, data) - self.assertEqual(obj.name, 'source-4') + assert serializer.data == data + assert obj.name == 'source-4' # Ensure source 4 is added, and everything else is as expected queryset = ForeignKeySource.objects.all() @@ -294,15 +294,15 @@ def test_foreign_key_create(self): {'id': 3, 'name': 'source-3', 'target': 1}, {'id': 4, 'name': 'source-4', 'target': 2}, ] - self.assertEqual(serializer.data, expected) + assert 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()) + assert serializer.is_valid() obj = serializer.save() - self.assertEqual(serializer.data, data) - self.assertEqual(obj.name, 'target-3') + assert serializer.data == data + assert obj.name == 'target-3' # Ensure target 3 is added, and everything else is as expected queryset = ForeignKeyTarget.objects.all() @@ -312,14 +312,14 @@ def test_reverse_foreign_key_create(self): {'id': 2, 'name': 'target-2', 'sources': []}, {'id': 3, 'name': 'target-3', 'sources': [1, 3]}, ] - self.assertEqual(serializer.data, expected) + assert 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.assertEqual(serializer.errors, {'target': ['This field may not be null.']}) + assert not serializer.is_valid() + assert serializer.errors == {'target': ['This field may not be null.']} def test_foreign_key_with_unsaved(self): source = ForeignKeySource(name='source-unsaved') @@ -329,7 +329,7 @@ def test_foreign_key_with_unsaved(self): # no query if source hasn't been created yet with self.assertNumQueries(0): - self.assertEqual(serializer.data, expected) + assert serializer.data == expected def test_foreign_key_with_empty(self): """ @@ -338,7 +338,7 @@ def test_foreign_key_with_empty(self): https://github.com/tomchristie/django-rest-framework/issues/1072 """ serializer = NullableForeignKeySourceSerializer() - self.assertEqual(serializer.data['target'], None) + assert serializer.data['target'] is None def test_foreign_key_not_required(self): """ @@ -350,7 +350,7 @@ class Meta(ForeignKeySourceSerializer.Meta): extra_kwargs = {'target': {'required': False}} serializer = ModelSerializer(data={'name': 'test'}) serializer.is_valid(raise_exception=True) - self.assertNotIn('target', serializer.validated_data) + assert 'target' not in serializer.validated_data class PKNullableForeignKeyTests(TestCase): @@ -371,15 +371,15 @@ def test_foreign_key_retrieve_with_null(self): {'id': 2, 'name': 'source-2', 'target': 1}, {'id': 3, 'name': 'source-3', 'target': None}, ] - self.assertEqual(serializer.data, expected) + assert 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()) + assert serializer.is_valid() obj = serializer.save() - self.assertEqual(serializer.data, data) - self.assertEqual(obj.name, 'source-4') + assert serializer.data == data + assert obj.name == 'source-4' # Ensure source 4 is created, and everything else is as expected queryset = NullableForeignKeySource.objects.all() @@ -390,7 +390,7 @@ def test_foreign_key_create_with_valid_null(self): {'id': 3, 'name': 'source-3', 'target': None}, {'id': 4, 'name': 'source-4', 'target': None} ] - self.assertEqual(serializer.data, expected) + assert serializer.data == expected def test_foreign_key_create_with_valid_emptystring(self): """ @@ -400,10 +400,10 @@ def test_foreign_key_create_with_valid_emptystring(self): data = {'id': 4, 'name': 'source-4', 'target': ''} expected_data = {'id': 4, 'name': 'source-4', 'target': None} serializer = NullableForeignKeySourceSerializer(data=data) - self.assertTrue(serializer.is_valid()) + assert serializer.is_valid() obj = serializer.save() - self.assertEqual(serializer.data, expected_data) - self.assertEqual(obj.name, 'source-4') + assert serializer.data == expected_data + assert obj.name == 'source-4' # Ensure source 4 is created, and everything else is as expected queryset = NullableForeignKeySource.objects.all() @@ -414,15 +414,15 @@ def test_foreign_key_create_with_valid_emptystring(self): {'id': 3, 'name': 'source-3', 'target': None}, {'id': 4, 'name': 'source-4', 'target': None} ] - self.assertEqual(serializer.data, expected) + assert 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()) + assert serializer.is_valid() serializer.save() - self.assertEqual(serializer.data, data) + assert serializer.data == data # Ensure source 1 is updated, and everything else is as expected queryset = NullableForeignKeySource.objects.all() @@ -432,7 +432,7 @@ def test_foreign_key_update_with_valid_null(self): {'id': 2, 'name': 'source-2', 'target': 1}, {'id': 3, 'name': 'source-3', 'target': None} ] - self.assertEqual(serializer.data, expected) + assert serializer.data == expected def test_foreign_key_update_with_valid_emptystring(self): """ @@ -443,9 +443,9 @@ def test_foreign_key_update_with_valid_emptystring(self): expected_data = {'id': 1, 'name': 'source-1', 'target': None} instance = NullableForeignKeySource.objects.get(pk=1) serializer = NullableForeignKeySourceSerializer(instance, data=data) - self.assertTrue(serializer.is_valid()) + assert serializer.is_valid() serializer.save() - self.assertEqual(serializer.data, expected_data) + assert serializer.data == expected_data # Ensure source 1 is updated, and everything else is as expected queryset = NullableForeignKeySource.objects.all() @@ -455,18 +455,18 @@ def test_foreign_key_update_with_valid_emptystring(self): {'id': 2, 'name': 'source-2', 'target': 1}, {'id': 3, 'name': 'source-3', 'target': None} ] - self.assertEqual(serializer.data, expected) + assert serializer.data == expected def test_null_uuid_foreign_key_serializes_as_none(self): source = NullableUUIDForeignKeySource(name='Source') serializer = NullableUUIDForeignKeySourceSerializer(source) data = serializer.data - self.assertEqual(data["target"], None) + assert data["target"] is None def test_nullable_uuid_foreign_key_is_valid_when_none(self): data = {"name": "Source", "target": None} serializer = NullableUUIDForeignKeySourceSerializer(data=data) - self.assertTrue(serializer.is_valid(), serializer.errors) + assert serializer.is_valid(), serializer.errors class PKNullableOneToOneTests(TestCase): @@ -485,4 +485,4 @@ def test_reverse_foreign_key_retrieve_with_null(self): {'id': 1, 'name': 'target-1', 'nullable_source': None}, {'id': 2, 'name': 'target-2', 'nullable_source': 1}, ] - self.assertEqual(serializer.data, expected) + assert serializer.data == expected From 9f4c9691f47c534338cdaa4d8a22efc201c8fb62 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Mon, 28 Nov 2016 20:31:27 +0600 Subject: [PATCH 0019/1069] converted filters tests asserts to pytest style (#4711) --- tests/test_filters.py | 300 +++++++++++++++++------------------------- 1 file changed, 124 insertions(+), 176 deletions(-) diff --git a/tests/test_filters.py b/tests/test_filters.py index 12fb858958..0cc326239a 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -155,8 +155,8 @@ def test_backend_deprecation(self): request = factory.get('/') response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, self.data) + assert response.status_code == status.HTTP_200_OK + assert response.data == self.data self.assertTrue(issubclass(w[-1].category, PendingDeprecationWarning)) self.assertIn("'rest_framework.filters.DjangoFilterBackend' is pending deprecation.", str(w[-1].message)) @@ -175,9 +175,9 @@ class DFFilterFieldsRootView(FilterFieldsRootView): request = factory.get('/') response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, self.data) - self.assertEqual(len(w), 0) + assert response.status_code == status.HTTP_200_OK + assert response.data == self.data + assert len(w) == 0 @unittest.skipUnless(django_filters, 'django-filter not installed') def test_get_filtered_fields_root_view(self): @@ -189,24 +189,24 @@ def test_get_filtered_fields_root_view(self): # Basic test with no filter. request = factory.get('/') response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, self.data) + assert response.status_code == status.HTTP_200_OK + assert response.data == self.data # Tests that the decimal filter works. search_decimal = Decimal('2.25') request = factory.get('/', {'decimal': '%s' % search_decimal}) response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert response.status_code == status.HTTP_200_OK expected_data = [f for f in self.data if Decimal(f['decimal']) == search_decimal] - self.assertEqual(response.data, expected_data) + assert response.data == expected_data # Tests that the date filter works. search_date = datetime.date(2012, 9, 22) request = factory.get('/', {'date': '%s' % search_date}) # search_date str: '2012-09-22' response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert response.status_code == status.HTTP_200_OK expected_data = [f for f in self.data if parse_date(f['date']) == search_date] - self.assertEqual(response.data, expected_data) + assert response.data == expected_data @unittest.skipUnless(django_filters, 'django-filter not installed') def test_filter_with_queryset(self): @@ -219,9 +219,9 @@ def test_filter_with_queryset(self): search_decimal = Decimal('2.25') request = factory.get('/', {'decimal': '%s' % search_decimal}) response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert response.status_code == status.HTTP_200_OK expected_data = [f for f in self.data if Decimal(f['decimal']) == search_decimal] - self.assertEqual(response.data, expected_data) + assert response.data == expected_data @unittest.skipUnless(django_filters, 'django-filter not installed') def test_filter_with_get_queryset_only(self): @@ -245,32 +245,32 @@ def test_get_filtered_class_root_view(self): # Basic test with no filter. request = factory.get('/') response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, self.data) + assert response.status_code == status.HTTP_200_OK + assert response.data == self.data # Tests that the decimal filter set with 'lt' in the filter class works. search_decimal = Decimal('4.25') request = factory.get('/', {'decimal': '%s' % search_decimal}) response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert response.status_code == status.HTTP_200_OK expected_data = [f for f in self.data if Decimal(f['decimal']) < search_decimal] - self.assertEqual(response.data, expected_data) + assert response.data == expected_data # Tests that the date filter set with 'gt' in the filter class works. search_date = datetime.date(2012, 10, 2) request = factory.get('/', {'date': '%s' % search_date}) # search_date str: '2012-10-02' response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert response.status_code == status.HTTP_200_OK expected_data = [f for f in self.data if parse_date(f['date']) > search_date] - self.assertEqual(response.data, expected_data) + assert response.data == expected_data # Tests that the text filter set with 'icontains' in the filter class works. search_text = 'ff' request = factory.get('/', {'text': '%s' % search_text}) response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert response.status_code == status.HTTP_200_OK expected_data = [f for f in self.data if search_text in f['text'].lower()] - self.assertEqual(response.data, expected_data) + assert response.data == expected_data # Tests that multiple filters works. search_decimal = Decimal('5.25') @@ -280,10 +280,10 @@ def test_get_filtered_class_root_view(self): 'date': '%s' % (search_date,) }) response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert response.status_code == status.HTTP_200_OK expected_data = [f for f in self.data if parse_date(f['date']) > search_date and Decimal(f['decimal']) < search_decimal] - self.assertEqual(response.data, expected_data) + assert response.data == expected_data @unittest.skipUnless(django_filters, 'django-filter not installed') def test_incorrectly_configured_filter(self): @@ -304,8 +304,8 @@ def test_base_model_filter(self): request = factory.get('/?text=aaa') response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.data), 1) + assert response.status_code == status.HTTP_200_OK + assert len(response.data) == 1 @unittest.skipUnless(django_filters, 'django-filter not installed') def test_base_model_filter_with_proxy(self): @@ -316,8 +316,8 @@ def test_base_model_filter_with_proxy(self): request = factory.get('/?text=aaa') response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.data), 1) + assert response.status_code == status.HTTP_200_OK + assert len(response.data) == 1 @unittest.skipUnless(django_filters, 'django-filter not installed') def test_unknown_filter(self): @@ -329,7 +329,7 @@ def test_unknown_filter(self): search_integer = 10 request = factory.get('/', {'integer': '%s' % search_integer}) response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert response.status_code == status.HTTP_200_OK @override_settings(ROOT_URLCONF='tests.test_filters') @@ -351,8 +351,8 @@ def test_get_filtered_detail_view(self): # Basic test with no filter. response = self.client.get(self._get_url(item)) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, data) + assert response.status_code == status.HTTP_200_OK + assert response.data == data # Tests that the decimal filter set that should fail. search_decimal = Decimal('4.25') @@ -360,7 +360,7 @@ def test_get_filtered_detail_view(self): response = self.client.get( '{url}'.format(url=self._get_url(high_item)), {'decimal': '{param}'.format(param=search_decimal)}) - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + assert response.status_code == status.HTTP_404_NOT_FOUND # Tests that the decimal filter set that should succeed. search_decimal = Decimal('4.25') @@ -369,8 +369,8 @@ def test_get_filtered_detail_view(self): response = self.client.get( '{url}'.format(url=self._get_url(low_item)), {'decimal': '{param}'.format(param=search_decimal)}) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, low_item_data) + assert response.status_code == status.HTTP_200_OK + assert response.data == low_item_data # Tests that multiple filters works. search_decimal = Decimal('5.25') @@ -382,8 +382,8 @@ def test_get_filtered_detail_view(self): 'decimal': '{decimal}'.format(decimal=search_decimal), 'date': '{date}'.format(date=search_date) }) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, valid_item_data) + assert response.status_code == status.HTTP_200_OK + assert response.data == valid_item_data class SearchFilterModel(models.Model): @@ -424,13 +424,10 @@ class SearchListView(generics.ListAPIView): view = SearchListView.as_view() request = factory.get('/', {'search': 'b'}) response = view(request) - self.assertEqual( - response.data, - [ - {'id': 1, 'title': 'z', 'text': 'abc'}, - {'id': 2, 'title': 'zz', 'text': 'bcd'} - ] - ) + assert response.data == [ + {'id': 1, 'title': 'z', 'text': 'abc'}, + {'id': 2, 'title': 'zz', 'text': 'bcd'} + ] def test_exact_search(self): class SearchListView(generics.ListAPIView): @@ -442,12 +439,9 @@ class SearchListView(generics.ListAPIView): view = SearchListView.as_view() request = factory.get('/', {'search': 'zzz'}) response = view(request) - self.assertEqual( - response.data, - [ - {'id': 3, 'title': 'zzz', 'text': 'cde'} - ] - ) + assert response.data == [ + {'id': 3, 'title': 'zzz', 'text': 'cde'} + ] def test_startswith_search(self): class SearchListView(generics.ListAPIView): @@ -459,12 +453,9 @@ class SearchListView(generics.ListAPIView): view = SearchListView.as_view() request = factory.get('/', {'search': 'b'}) response = view(request) - self.assertEqual( - response.data, - [ - {'id': 2, 'title': 'zz', 'text': 'bcd'} - ] - ) + assert response.data == [ + {'id': 2, 'title': 'zz', 'text': 'bcd'} + ] def test_regexp_search(self): class SearchListView(generics.ListAPIView): @@ -476,12 +467,9 @@ class SearchListView(generics.ListAPIView): view = SearchListView.as_view() request = factory.get('/', {'search': 'z{2} ^b'}) response = view(request) - self.assertEqual( - response.data, - [ - {'id': 2, 'title': 'zz', 'text': 'bcd'} - ] - ) + assert response.data == [ + {'id': 2, 'title': 'zz', 'text': 'bcd'} + ] def test_search_with_nonstandard_search_param(self): with override_settings(REST_FRAMEWORK={'SEARCH_PARAM': 'query'}): @@ -496,13 +484,10 @@ class SearchListView(generics.ListAPIView): view = SearchListView.as_view() request = factory.get('/', {'query': 'b'}) response = view(request) - self.assertEqual( - response.data, - [ - {'id': 1, 'title': 'z', 'text': 'abc'}, - {'id': 2, 'title': 'zz', 'text': 'bcd'} - ] - ) + assert response.data == [ + {'id': 1, 'title': 'z', 'text': 'abc'}, + {'id': 2, 'title': 'zz', 'text': 'bcd'} + ] reload_module(filters) @@ -528,15 +513,13 @@ def test_must_call_distinct(self): filter_ = filters.SearchFilter() prefixes = [''] + list(filter_.lookup_prefixes) for prefix in prefixes: - self.assertFalse( - filter_.must_call_distinct( - SearchFilterModelFk._meta, ["%stitle" % prefix] - ) + assert not filter_.must_call_distinct( + SearchFilterModelFk._meta, + ["%stitle" % prefix] ) - self.assertFalse( - filter_.must_call_distinct( - SearchFilterModelFk._meta, ["%stitle" % prefix, "%sattribute__label" % prefix] - ) + assert not filter_.must_call_distinct( + SearchFilterModelFk._meta, + ["%stitle" % prefix, "%sattribute__label" % prefix] ) def test_must_call_distinct_restores_meta_for_each_field(self): @@ -545,10 +528,9 @@ def test_must_call_distinct_restores_meta_for_each_field(self): filter_ = filters.SearchFilter() prefixes = [''] + list(filter_.lookup_prefixes) for prefix in prefixes: - self.assertFalse( - filter_.must_call_distinct( - SearchFilterModelFk._meta, ["%sattribute__label" % prefix, "%stitle" % prefix] - ) + assert not filter_.must_call_distinct( + SearchFilterModelFk._meta, + ["%sattribute__label" % prefix, "%stitle" % prefix] ) @@ -596,21 +578,20 @@ class SearchListView(generics.ListAPIView): view = SearchListView.as_view() request = factory.get('/', {'search': 'zz'}) response = view(request) - self.assertEqual(len(response.data), 1) + assert len(response.data) == 1 def test_must_call_distinct(self): filter_ = filters.SearchFilter() prefixes = [''] + list(filter_.lookup_prefixes) for prefix in prefixes: - self.assertFalse( - filter_.must_call_distinct( - SearchFilterModelM2M._meta, ["%stitle" % prefix] - ) + assert not filter_.must_call_distinct( + SearchFilterModelM2M._meta, + ["%stitle" % prefix] ) - self.assertTrue( - filter_.must_call_distinct( - SearchFilterModelM2M._meta, ["%stitle" % prefix, "%sattributes__label" % prefix] - ) + + assert filter_.must_call_distinct( + SearchFilterModelM2M._meta, + ["%stitle" % prefix, "%sattributes__label" % prefix] ) @@ -672,14 +653,11 @@ class DjangoFilterOrderingView(generics.ListAPIView): request = factory.get('/') response = view(request) - self.assertEqual( - response.data, - [ - {'id': 3, 'date': '2014-10-08', 'text': 'cde'}, - {'id': 2, 'date': '2013-10-08', 'text': 'bcd'}, - {'id': 1, 'date': '2012-10-08', 'text': 'abc'} - ] - ) + assert response.data == [ + {'id': 3, 'date': '2014-10-08', 'text': 'cde'}, + {'id': 2, 'date': '2013-10-08', 'text': 'bcd'}, + {'id': 1, 'date': '2012-10-08', 'text': 'abc'} + ] class OrderingFilterTests(TestCase): @@ -713,14 +691,11 @@ class OrderingListView(generics.ListAPIView): view = OrderingListView.as_view() request = factory.get('/', {'ordering': 'text'}) response = view(request) - self.assertEqual( - response.data, - [ - {'id': 1, 'title': 'zyx', 'text': 'abc'}, - {'id': 2, 'title': 'yxw', 'text': 'bcd'}, - {'id': 3, 'title': 'xwv', 'text': 'cde'}, - ] - ) + assert response.data == [ + {'id': 1, 'title': 'zyx', 'text': 'abc'}, + {'id': 2, 'title': 'yxw', 'text': 'bcd'}, + {'id': 3, 'title': 'xwv', 'text': 'cde'}, + ] def test_reverse_ordering(self): class OrderingListView(generics.ListAPIView): @@ -733,14 +708,11 @@ class OrderingListView(generics.ListAPIView): view = OrderingListView.as_view() request = factory.get('/', {'ordering': '-text'}) response = view(request) - self.assertEqual( - response.data, - [ - {'id': 3, 'title': 'xwv', 'text': 'cde'}, - {'id': 2, 'title': 'yxw', 'text': 'bcd'}, - {'id': 1, 'title': 'zyx', 'text': 'abc'}, - ] - ) + assert response.data == [ + {'id': 3, 'title': 'xwv', 'text': 'cde'}, + {'id': 2, 'title': 'yxw', 'text': 'bcd'}, + {'id': 1, 'title': 'zyx', 'text': 'abc'}, + ] def test_incorrectfield_ordering(self): class OrderingListView(generics.ListAPIView): @@ -753,14 +725,11 @@ class OrderingListView(generics.ListAPIView): view = OrderingListView.as_view() request = factory.get('/', {'ordering': 'foobar'}) response = view(request) - self.assertEqual( - response.data, - [ - {'id': 3, 'title': 'xwv', 'text': 'cde'}, - {'id': 2, 'title': 'yxw', 'text': 'bcd'}, - {'id': 1, 'title': 'zyx', 'text': 'abc'}, - ] - ) + assert response.data == [ + {'id': 3, 'title': 'xwv', 'text': 'cde'}, + {'id': 2, 'title': 'yxw', 'text': 'bcd'}, + {'id': 1, 'title': 'zyx', 'text': 'abc'}, + ] def test_default_ordering(self): class OrderingListView(generics.ListAPIView): @@ -773,14 +742,11 @@ class OrderingListView(generics.ListAPIView): view = OrderingListView.as_view() request = factory.get('') response = view(request) - self.assertEqual( - response.data, - [ - {'id': 3, 'title': 'xwv', 'text': 'cde'}, - {'id': 2, 'title': 'yxw', 'text': 'bcd'}, - {'id': 1, 'title': 'zyx', 'text': 'abc'}, - ] - ) + assert response.data == [ + {'id': 3, 'title': 'xwv', 'text': 'cde'}, + {'id': 2, 'title': 'yxw', 'text': 'bcd'}, + {'id': 1, 'title': 'zyx', 'text': 'abc'}, + ] def test_default_ordering_using_string(self): class OrderingListView(generics.ListAPIView): @@ -793,14 +759,11 @@ class OrderingListView(generics.ListAPIView): view = OrderingListView.as_view() request = factory.get('') response = view(request) - self.assertEqual( - response.data, - [ - {'id': 3, 'title': 'xwv', 'text': 'cde'}, - {'id': 2, 'title': 'yxw', 'text': 'bcd'}, - {'id': 1, 'title': 'zyx', 'text': 'abc'}, - ] - ) + assert response.data == [ + {'id': 3, 'title': 'xwv', 'text': 'cde'}, + {'id': 2, 'title': 'yxw', 'text': 'bcd'}, + {'id': 1, 'title': 'zyx', 'text': 'abc'}, + ] def test_ordering_by_aggregate_field(self): # create some related models to aggregate order by @@ -824,14 +787,11 @@ class OrderingListView(generics.ListAPIView): view = OrderingListView.as_view() request = factory.get('/', {'ordering': 'relateds__count'}) response = view(request) - self.assertEqual( - response.data, - [ - {'id': 1, 'title': 'zyx', 'text': 'abc'}, - {'id': 3, 'title': 'xwv', 'text': 'cde'}, - {'id': 2, 'title': 'yxw', 'text': 'bcd'}, - ] - ) + assert response.data == [ + {'id': 1, 'title': 'zyx', 'text': 'abc'}, + {'id': 3, 'title': 'xwv', 'text': 'cde'}, + {'id': 2, 'title': 'yxw', 'text': 'bcd'}, + ] def test_ordering_with_nonstandard_ordering_param(self): with override_settings(REST_FRAMEWORK={'ORDERING_PARAM': 'order'}): @@ -847,14 +807,11 @@ class OrderingListView(generics.ListAPIView): view = OrderingListView.as_view() request = factory.get('/', {'order': 'text'}) response = view(request) - self.assertEqual( - response.data, - [ - {'id': 1, 'title': 'zyx', 'text': 'abc'}, - {'id': 2, 'title': 'yxw', 'text': 'bcd'}, - {'id': 3, 'title': 'xwv', 'text': 'cde'}, - ] - ) + assert response.data == [ + {'id': 1, 'title': 'zyx', 'text': 'abc'}, + {'id': 2, 'title': 'yxw', 'text': 'bcd'}, + {'id': 3, 'title': 'xwv', 'text': 'cde'}, + ] reload_module(filters) @@ -884,14 +841,11 @@ def get_serializer_class(self): view = OrderingListView.as_view() request = factory.get('/', {'ordering': 'text'}) response = view(request) - self.assertEqual( - response.data, - [ - {'id': 1, 'title': 'zyx', 'text': 'abc'}, - {'id': 2, 'title': 'yxw', 'text': 'bcd'}, - {'id': 3, 'title': 'xwv', 'text': 'cde'}, - ] - ) + assert response.data == [ + {'id': 1, 'title': 'zyx', 'text': 'abc'}, + {'id': 2, 'title': 'yxw', 'text': 'bcd'}, + {'id': 3, 'title': 'xwv', 'text': 'cde'}, + ] def test_ordering_with_improper_configuration(self): class OrderingListView(generics.ListAPIView): @@ -967,14 +921,11 @@ class OrderingListView(generics.ListAPIView): username_field = 'username' # Note: Inverse username ordering correctly applied. - self.assertEqual( - response.data, - [ - {'id': 3, username_field: 'userC'}, - {'id': 2, username_field: 'userB'}, - {'id': 1, username_field: 'userA'}, - ] - ) + assert response.data == [ + {'id': 3, username_field: 'userC'}, + {'id': 2, username_field: 'userB'}, + {'id': 1, username_field: 'userA'}, + ] def test_cannot_order_by_non_serializer_fields(self): for serializer_cls in [ @@ -997,11 +948,8 @@ class OrderingListView(generics.ListAPIView): username_field = 'username' # Note: The passwords are not in order. Default ordering is used. - self.assertEqual( - response.data, - [ - {'id': 1, username_field: 'userA'}, # PassB - {'id': 2, username_field: 'userB'}, # PassC - {'id': 3, username_field: 'userC'}, # PassA - ] - ) + assert response.data == [ + {'id': 1, username_field: 'userA'}, # PassB + {'id': 2, username_field: 'userB'}, # PassC + {'id': 3, username_field: 'userC'}, # PassA + ] From 649876674933c7c05225e8f33a278ce4210df598 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 29 Nov 2016 04:49:18 -0500 Subject: [PATCH 0020/1069] Fix django deprecation warnings (#4712) --- tests/test_model_serializer.py | 4 ++-- tests/test_prefetch_related.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 06848302f0..b839f56ca4 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -94,7 +94,7 @@ class Issue3674ParentModel(models.Model): class Issue3674ChildModel(models.Model): - parent = models.ForeignKey(Issue3674ParentModel, related_name='children') + parent = models.ForeignKey(Issue3674ParentModel, related_name='children', on_delete=models.CASCADE) value = models.CharField(primary_key=True, max_length=64) @@ -1013,7 +1013,7 @@ class TestParentModel(models.Model): title = models.CharField(max_length=64) class TestChildModel(models.Model): - parent = models.ForeignKey(TestParentModel, related_name='children') + parent = models.ForeignKey(TestParentModel, related_name='children', on_delete=models.CASCADE) value = models.CharField(primary_key=True, max_length=64) class TestChildModelSerializer(serializers.ModelSerializer): diff --git a/tests/test_prefetch_related.py b/tests/test_prefetch_related.py index a9fa238eab..750173b381 100644 --- a/tests/test_prefetch_related.py +++ b/tests/test_prefetch_related.py @@ -2,6 +2,7 @@ from django.test import TestCase from rest_framework import generics, serializers +from rest_framework.compat import set_many from rest_framework.test import APIRequestFactory factory = APIRequestFactory() @@ -22,7 +23,7 @@ class TestPrefetchRelatedUpdates(TestCase): def setUp(self): self.user = User.objects.create(username='tom', email='tom@example.com') self.groups = [Group.objects.create(name='a'), Group.objects.create(name='b')] - self.user.groups = self.groups + set_many(self.user, 'groups', self.groups) self.user.save() def test_prefetch_related_updates(self): From 1e0988686cda1f9c150e239b945c1d9a0ea9b19c Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Tue, 29 Nov 2016 13:27:00 +0100 Subject: [PATCH 0021/1069] Update the Python doc links to use https and point to Python 3 (#4713) --- docs/api-guide/fields.md | 2 +- docs/api-guide/settings.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 17168b7210..3dcc9d0a5e 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -669,7 +669,7 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide [html-and-forms]: ../topics/html-and-forms.md [FILE_UPLOAD_HANDLERS]: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS [ecma262]: http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 -[strftime]: http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior +[strftime]: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior [django-widgets]: https://docs.djangoproject.com/en/dev/ref/forms/widgets/ [iso8601]: http://www.w3.org/TR/NOTE-datetime [drf-compound-fields]: https://drf-compound-fields.readthedocs.io diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 58ceeeeb41..a1ea12d6e6 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -456,7 +456,7 @@ An integer of 0 or more, that may be used to specify the number of application p Default: `None` -[cite]: http://www.python.org/dev/peps/pep-0020/ +[cite]: https://www.python.org/dev/peps/pep-0020/ [rfc4627]: http://www.ietf.org/rfc/rfc4627.txt [heroku-minified-json]: https://github.com/interagent/http-api-design#keep-json-minified-in-all-responses -[strftime]: http://docs.python.org/2/library/time.html#time.strftime +[strftime]: https://docs.python.org/3/library/time.html#time.strftime From aed8387e05a7a665439044d90b7fac5d1fd61225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Larra=C3=ADn?= Date: Tue, 29 Nov 2016 12:35:43 -0300 Subject: [PATCH 0022/1069] docs: Fix description of `DecimalField`'s `max_digits` (#4714) As of PR #4377, `max_digits=None` is allowed for `DecimalField`. --- docs/api-guide/fields.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 3dcc9d0a5e..6b6ae612d6 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -261,7 +261,7 @@ Corresponds to `django.db.models.fields.DecimalField`. **Signature**: `DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None)` -- `max_digits` The maximum number of digits allowed in the number. Note that this number must be greater than or equal to decimal_places. +- `max_digits` The maximum number of digits allowed in the number. It must be either `None` or an integer greater than or equal to `decimal_places`. - `decimal_places` The number of decimal places to store with the number. - `coerce_to_string` Set to `True` if string values should be returned for the representation, or `False` if `Decimal` objects should be returned. Defaults to the same value as the `COERCE_DECIMAL_TO_STRING` settings key, which will be `True` unless overridden. If `Decimal` objects are returned by the serializer, then the final output format will be determined by the renderer. Note that setting `localize` will force the value to `True`. - `max_value` Validate that the number provided is no greater than this value. From 27641e07b5b56ea6fcd4676419e8fd99c58e9c66 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Wed, 30 Nov 2016 01:13:21 +0600 Subject: [PATCH 0023/1069] converted test asserts of generics-test to pytest --- tests/test_generics.py | 102 ++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/tests/test_generics.py b/tests/test_generics.py index 247237584b..2c3679c461 100644 --- a/tests/test_generics.py +++ b/tests/test_generics.py @@ -98,8 +98,8 @@ def test_get_root_view(self): request = factory.get('/') with self.assertNumQueries(1): response = self.view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, self.data) + assert response.status_code == status.HTTP_200_OK + assert response.data == self.data def test_post_root_view(self): """ @@ -109,10 +109,10 @@ def test_post_root_view(self): request = factory.post('/', data, format='json') with self.assertNumQueries(1): response = self.view(request).render() - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(response.data, {'id': 4, 'text': 'foobar'}) + assert response.status_code == status.HTTP_201_CREATED + assert response.data == {'id': 4, 'text': 'foobar'} created = self.objects.get(id=4) - self.assertEqual(created.text, 'foobar') + assert created.text == 'foobar' def test_put_root_view(self): """ @@ -122,8 +122,8 @@ def test_put_root_view(self): request = factory.put('/', data, format='json') with self.assertNumQueries(0): response = self.view(request).render() - self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) - self.assertEqual(response.data, {"detail": 'Method "PUT" not allowed.'}) + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED + assert response.data == {"detail": 'Method "PUT" not allowed.'} def test_delete_root_view(self): """ @@ -132,8 +132,8 @@ def test_delete_root_view(self): request = factory.delete('/') with self.assertNumQueries(0): response = self.view(request).render() - self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) - self.assertEqual(response.data, {"detail": 'Method "DELETE" not allowed.'}) + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED + assert response.data == {"detail": 'Method "DELETE" not allowed.'} def test_post_cannot_set_id(self): """ @@ -143,10 +143,10 @@ def test_post_cannot_set_id(self): request = factory.post('/', data, format='json') with self.assertNumQueries(1): response = self.view(request).render() - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(response.data, {'id': 4, 'text': 'foobar'}) + assert response.status_code == status.HTTP_201_CREATED + assert response.data == {'id': 4, 'text': 'foobar'} created = self.objects.get(id=4) - self.assertEqual(created.text, 'foobar') + assert created.text == 'foobar' def test_post_error_root_view(self): """ @@ -156,7 +156,7 @@ def test_post_error_root_view(self): request = factory.post('/', data, HTTP_ACCEPT='text/html') response = self.view(request).render() expected_error = 'Ensure this field has no more than 100 characters.' - self.assertIn(expected_error, response.rendered_content.decode('utf-8')) + assert expected_error in response.rendered_content.decode('utf-8') EXPECTED_QUERIES_FOR_PUT = 2 @@ -185,8 +185,8 @@ def test_get_instance_view(self): request = factory.get('/1') with self.assertNumQueries(1): response = self.view(request, pk=1).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, self.data[0]) + assert response.status_code == status.HTTP_200_OK + assert response.data == self.data[0] def test_post_instance_view(self): """ @@ -196,8 +196,8 @@ def test_post_instance_view(self): request = factory.post('/', data, format='json') with self.assertNumQueries(0): response = self.view(request).render() - self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) - self.assertEqual(response.data, {"detail": 'Method "POST" not allowed.'}) + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED + assert response.data == {"detail": 'Method "POST" not allowed.'} def test_put_instance_view(self): """ @@ -207,10 +207,10 @@ def test_put_instance_view(self): request = factory.put('/1', data, format='json') with self.assertNumQueries(EXPECTED_QUERIES_FOR_PUT): response = self.view(request, pk='1').render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(dict(response.data), {'id': 1, 'text': 'foobar'}) + assert response.status_code == status.HTTP_200_OK + assert dict(response.data) == {'id': 1, 'text': 'foobar'} updated = self.objects.get(id=1) - self.assertEqual(updated.text, 'foobar') + assert updated.text == 'foobar' def test_patch_instance_view(self): """ @@ -221,10 +221,10 @@ def test_patch_instance_view(self): with self.assertNumQueries(EXPECTED_QUERIES_FOR_PUT): response = self.view(request, pk=1).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, {'id': 1, 'text': 'foobar'}) + assert response.status_code == status.HTTP_200_OK + assert response.data == {'id': 1, 'text': 'foobar'} updated = self.objects.get(id=1) - self.assertEqual(updated.text, 'foobar') + assert updated.text == 'foobar' def test_delete_instance_view(self): """ @@ -233,10 +233,10 @@ def test_delete_instance_view(self): request = factory.delete('/1') with self.assertNumQueries(2): response = self.view(request, pk=1).render() - self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - self.assertEqual(response.content, six.b('')) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert response.content == six.b('') ids = [obj.id for obj in self.objects.all()] - self.assertEqual(ids, [2, 3]) + assert ids == [2, 3] def test_get_instance_view_incorrect_arg(self): """ @@ -246,7 +246,7 @@ def test_get_instance_view_incorrect_arg(self): request = factory.get('/a') with self.assertNumQueries(0): response = self.view(request, pk='a').render() - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + assert response.status_code == status.HTTP_404_NOT_FOUND def test_put_cannot_set_id(self): """ @@ -256,10 +256,10 @@ def test_put_cannot_set_id(self): request = factory.put('/1', data, format='json') with self.assertNumQueries(EXPECTED_QUERIES_FOR_PUT): response = self.view(request, pk=1).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, {'id': 1, 'text': 'foobar'}) + assert response.status_code == status.HTTP_200_OK + assert response.data == {'id': 1, 'text': 'foobar'} updated = self.objects.get(id=1) - self.assertEqual(updated.text, 'foobar') + assert updated.text == 'foobar' def test_put_to_deleted_instance(self): """ @@ -271,7 +271,7 @@ def test_put_to_deleted_instance(self): request = factory.put('/1', data, format='json') with self.assertNumQueries(1): response = self.view(request, pk=1).render() - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + assert response.status_code == status.HTTP_404_NOT_FOUND def test_put_to_filtered_out_instance(self): """ @@ -282,7 +282,7 @@ def test_put_to_filtered_out_instance(self): filtered_out_pk = BasicModel.objects.filter(text='filtered out')[0].pk request = factory.put('/{0}'.format(filtered_out_pk), data, format='json') response = self.view(request, pk=filtered_out_pk).render() - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + assert response.status_code == status.HTTP_404_NOT_FOUND def test_patch_cannot_create_an_object(self): """ @@ -292,8 +292,8 @@ def test_patch_cannot_create_an_object(self): request = factory.patch('/999', data, format='json') with self.assertNumQueries(1): response = self.view(request, pk=999).render() - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertFalse(self.objects.filter(id=999).exists()) + assert response.status_code == status.HTTP_404_NOT_FOUND + assert not self.objects.filter(id=999).exists() def test_put_error_instance_view(self): """ @@ -303,7 +303,7 @@ def test_put_error_instance_view(self): request = factory.put('/', data, HTTP_ACCEPT='text/html') response = self.view(request, pk=1).render() expected_error = 'Ensure this field has no more than 100 characters.' - self.assertIn(expected_error, response.rendered_content.decode('utf-8')) + assert expected_error in response.rendered_content.decode('utf-8') class TestFKInstanceView(TestCase): @@ -363,8 +363,8 @@ def test_overridden_get_object_view(self): request = factory.get('/1') with self.assertNumQueries(1): response = self.view(request, pk=1).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, self.data[0]) + assert response.status_code == status.HTTP_200_OK + assert response.data == self.data[0] # Regression test for #285 @@ -394,9 +394,9 @@ def test_create_model_with_auto_now_add_field(self): data = {'email': 'foobar@example.com', 'content': 'foobar'} request = factory.post('/', data, format='json') response = self.view(request).render() - self.assertEqual(response.status_code, status.HTTP_201_CREATED) + assert response.status_code == status.HTTP_201_CREATED created = self.objects.get(id=1) - self.assertEqual(created.content, 'foobar') + assert created.content == 'foobar' # Test for particularly ugly regression with m2m in browsable API @@ -432,7 +432,7 @@ def test_m2m_in_browsable_api(self): request = factory.get('/', HTTP_ACCEPT='text/html') view = ExampleView().as_view() response = view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert response.status_code == status.HTTP_200_OK class InclusiveFilterBackend(object): @@ -489,9 +489,9 @@ def test_get_root_view_filters_by_name_with_filter_backend(self): root_view = RootView.as_view(filter_backends=(InclusiveFilterBackend,)) request = factory.get('/') response = root_view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.data), 1) - self.assertEqual(response.data, [{'id': 1, 'text': 'foo'}]) + assert response.status_code == status.HTTP_200_OK + assert len(response.data) == 1 + assert response.data == [{'id': 1, 'text': 'foo'}] def test_get_root_view_filters_out_all_models_with_exclusive_filter_backend(self): """ @@ -500,8 +500,8 @@ def test_get_root_view_filters_out_all_models_with_exclusive_filter_backend(self root_view = RootView.as_view(filter_backends=(ExclusiveFilterBackend,)) request = factory.get('/') response = root_view(request).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, []) + assert response.status_code == status.HTTP_200_OK + assert response.data == [] def test_get_instance_view_filters_out_name_with_filter_backend(self): """ @@ -510,8 +510,8 @@ def test_get_instance_view_filters_out_name_with_filter_backend(self): instance_view = InstanceView.as_view(filter_backends=(ExclusiveFilterBackend,)) request = factory.get('/1') response = instance_view(request, pk=1).render() - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual(response.data, {'detail': 'Not found.'}) + assert response.status_code == status.HTTP_404_NOT_FOUND + assert response.data == {'detail': 'Not found.'} def test_get_instance_view_will_return_single_object_when_filter_does_not_exclude_it(self): """ @@ -520,8 +520,8 @@ def test_get_instance_view_will_return_single_object_when_filter_does_not_exclud instance_view = InstanceView.as_view(filter_backends=(InclusiveFilterBackend,)) request = factory.get('/1') response = instance_view(request, pk=1).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, {'id': 1, 'text': 'foo'}) + assert response.status_code == status.HTTP_200_OK + assert response.data == {'id': 1, 'text': 'foo'} def test_dynamic_serializer_form_in_browsable_api(self): """ @@ -530,8 +530,8 @@ def test_dynamic_serializer_form_in_browsable_api(self): view = DynamicSerializerView.as_view() request = factory.get('/') response = view(request).render() - self.assertContains(response, 'field_b') - self.assertNotContains(response, 'field_a') + assert 'field_b' in response + assert 'field_a' not in response class TestGuardedQueryset(TestCase): From 4a0829d6ec58b8b2fd12c36a34cdcc319def2695 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Wed, 30 Nov 2016 02:08:37 +0600 Subject: [PATCH 0024/1069] attempt to fix test --- tests/test_generics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_generics.py b/tests/test_generics.py index 2c3679c461..67191d329e 100644 --- a/tests/test_generics.py +++ b/tests/test_generics.py @@ -530,7 +530,7 @@ def test_dynamic_serializer_form_in_browsable_api(self): view = DynamicSerializerView.as_view() request = factory.get('/') response = view(request).render() - assert 'field_b' in response + assert response is 'field_b' assert 'field_a' not in response From a5c8a8c2265c6462208c2defc0b943bc51ace6df Mon Sep 17 00:00:00 2001 From: Jeff Fein-Worton Date: Tue, 29 Nov 2016 18:00:10 -0800 Subject: [PATCH 0025/1069] typo --- docs/api-guide/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 7cdb595313..f2d2fcaaef 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -164,7 +164,7 @@ As with `DjangoModelPermissions`, this permission must only be applied to views Note that `DjangoObjectPermissions` **does not** require the `django-guardian` package, and should support other object-level backends equally well. -As with `DjangoModelPermissions` you can use custom model permissions by overriding `DjangoModelPermissions` and setting the `.perms_map` property. Refer to the source code for details. +As with `DjangoModelPermissions` you can use custom model permissions by overriding `DjangoObjectPermissions` and setting the `.perms_map` property. Refer to the source code for details. --- From a5bb9825f3b238d89fdb5d8754d057116fff56a9 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Wed, 30 Nov 2016 09:56:22 +0600 Subject: [PATCH 0026/1069] attempt to fix test again --- tests/test_generics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_generics.py b/tests/test_generics.py index 67191d329e..2c3679c461 100644 --- a/tests/test_generics.py +++ b/tests/test_generics.py @@ -530,7 +530,7 @@ def test_dynamic_serializer_form_in_browsable_api(self): view = DynamicSerializerView.as_view() request = factory.get('/') response = view(request).render() - assert response is 'field_b' + assert 'field_b' in response assert 'field_a' not in response From f5a900a404a85163ba36ec34034d0bacd626b010 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Wed, 30 Nov 2016 10:01:37 +0600 Subject: [PATCH 0027/1069] some reverts to fix test --- tests/test_generics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_generics.py b/tests/test_generics.py index 2c3679c461..aa39511543 100644 --- a/tests/test_generics.py +++ b/tests/test_generics.py @@ -530,8 +530,8 @@ def test_dynamic_serializer_form_in_browsable_api(self): view = DynamicSerializerView.as_view() request = factory.get('/') response = view(request).render() - assert 'field_b' in response - assert 'field_a' not in response + self.assertContains(response, 'field_b') + self.assertNotContains(response, 'field_a') class TestGuardedQueryset(TestCase): From 10b5f36fec92871e1d96c85a008324683a32f6b5 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Wed, 30 Nov 2016 12:35:34 +0600 Subject: [PATCH 0028/1069] added fixes --- tests/test_generics.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_generics.py b/tests/test_generics.py index aa39511543..c24cda0061 100644 --- a/tests/test_generics.py +++ b/tests/test_generics.py @@ -530,8 +530,9 @@ def test_dynamic_serializer_form_in_browsable_api(self): view = DynamicSerializerView.as_view() request = factory.get('/') response = view(request).render() - self.assertContains(response, 'field_b') - self.assertNotContains(response, 'field_a') + content = response.content.decode('utf8') + assert 'field_b' in content + assert 'field_a' not in content class TestGuardedQueryset(TestCase): From e03d88ced7f2b22f96f50c2a885264147b242b4a Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Wed, 30 Nov 2016 15:48:33 +0600 Subject: [PATCH 0029/1069] more pytest style assert (#4719) --- tests/test_validation.py | 38 +++++++++++++++++++--------------- tests/test_validation_error.py | 16 +++++++------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/tests/test_validation.py b/tests/test_validation.py index bc950dd22a..8ff4aaf38c 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -60,11 +60,11 @@ def test_nested_validation_error_detail(self): } }) - self.assertEqual(serializers.as_serializer_error(e), { + assert serializers.as_serializer_error(e) == { 'nested': { 'field': ['error'], } - }) + } class TestPreSaveValidationExclusionsSerializer(TestCase): @@ -75,20 +75,20 @@ def test_renamed_fields_are_model_validated(self): # 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.assertEqual(serializer.is_valid(), False) - self.assertIn('renamed', serializer.errors) - self.assertNotIn('should_validate_field', serializer.errors) + assert serializer.is_valid() is False + assert 'renamed' in serializer.errors + assert 'should_validate_field' not in serializer.errors class TestCustomValidationMethods(TestCase): def test_custom_validation_method_is_executed(self): serializer = ShouldValidateModelSerializer(data={'renamed': 'fo'}) - self.assertFalse(serializer.is_valid()) - self.assertIn('renamed', serializer.errors) + assert not serializer.is_valid() + assert 'renamed' in serializer.errors def test_custom_validation_method_passing(self): serializer = ShouldValidateModelSerializer(data={'renamed': 'foo'}) - self.assertTrue(serializer.is_valid()) + assert serializer.is_valid() class ValidationSerializer(serializers.Serializer): @@ -108,12 +108,12 @@ class TestAvoidValidation(TestCase): """ def test_serializer_errors_has_only_invalid_data_error(self): serializer = ValidationSerializer(data='invalid data') - self.assertFalse(serializer.is_valid()) - self.assertDictEqual(serializer.errors, { + assert not serializer.is_valid() + assert serializer.errors == { 'non_field_errors': [ 'Invalid data. Expected a dictionary, but got %s.' % type('').__name__ ] - }) + } # regression tests for issue: 1493 @@ -137,27 +137,31 @@ class TestMaxValueValidatorValidation(TestCase): def test_max_value_validation_serializer_success(self): serializer = ValidationMaxValueValidatorModelSerializer(data={'number_value': 99}) - self.assertTrue(serializer.is_valid()) + assert serializer.is_valid() def test_max_value_validation_serializer_fails(self): serializer = ValidationMaxValueValidatorModelSerializer(data={'number_value': 101}) - self.assertFalse(serializer.is_valid()) - self.assertDictEqual({'number_value': ['Ensure this value is less than or equal to 100.']}, serializer.errors) + assert not serializer.is_valid() + assert serializer.errors == { + 'number_value': [ + 'Ensure this value is less than or equal to 100.' + ] + } def test_max_value_validation_success(self): obj = ValidationMaxValueValidatorModel.objects.create(number_value=100) request = factory.patch('/{0}'.format(obj.pk), {'number_value': 98}, format='json') view = UpdateMaxValueValidationModel().as_view() response = view(request, pk=obj.pk).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert response.status_code == status.HTTP_200_OK def test_max_value_validation_fail(self): obj = ValidationMaxValueValidatorModel.objects.create(number_value=100) request = factory.patch('/{0}'.format(obj.pk), {'number_value': 101}, format='json') view = UpdateMaxValueValidationModel().as_view() response = view(request, pk=obj.pk).render() - self.assertEqual(response.content, b'{"number_value":["Ensure this value is less than or equal to 100."]}') - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + assert response.content == b'{"number_value":["Ensure this value is less than or equal to 100."]}' + assert response.status_code == status.HTTP_400_BAD_REQUEST # regression tests for issue: 1533 diff --git a/tests/test_validation_error.py b/tests/test_validation_error.py index 8e371a3494..562fe37e6b 100644 --- a/tests/test_validation_error.py +++ b/tests/test_validation_error.py @@ -54,16 +54,16 @@ def test_class_based_view_exception_handler(self): request = factory.get('/', content_type='application/json') response = view(request) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.data, self.expected_response_data) + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.data == self.expected_response_data def test_function_based_view_exception_handler(self): view = error_view request = factory.get('/', content_type='application/json') response = view(request) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.data, self.expected_response_data) + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.data == self.expected_response_data class TestValidationErrorWithCodes(TestCase): @@ -89,13 +89,13 @@ def test_class_based_view_exception_handler(self): request = factory.get('/', content_type='application/json') response = view(request) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.data, self.expected_response_data) + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.data == self.expected_response_data def test_function_based_view_exception_handler(self): view = error_view request = factory.get('/', content_type='application/json') response = view(request) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.data, self.expected_response_data) + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.data == self.expected_response_data From 504f4b44c6dc96e1c1dd2cece3e9d3ac1052011d Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Wed, 30 Nov 2016 16:17:30 +0600 Subject: [PATCH 0030/1069] converted asserts of atomic requests test to pytest --- tests/test_atomic_requests.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/test_atomic_requests.py b/tests/test_atomic_requests.py index 09d7f2fb1f..9085bfc896 100644 --- a/tests/test_atomic_requests.py +++ b/tests/test_atomic_requests.py @@ -67,8 +67,8 @@ def test_no_exception_commit_transaction(self): with self.assertNumQueries(1): response = self.view(request) - self.assertFalse(transaction.get_rollback()) - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert not transaction.get_rollback() + assert response.status_code == status.HTTP_200_OK assert BasicModel.objects.count() == 1 @@ -98,7 +98,7 @@ def test_generic_exception_delegate_transaction_management(self): # 3 - release savepoint with transaction.atomic(): self.assertRaises(Exception, self.view, request) - self.assertFalse(transaction.get_rollback()) + assert not transaction.get_rollback() assert BasicModel.objects.count() == 1 @@ -128,9 +128,8 @@ def test_api_exception_rollback_transaction(self): # 4 - release savepoint (django>=1.8 only) with transaction.atomic(): response = self.view(request) - self.assertTrue(transaction.get_rollback()) - self.assertEqual(response.status_code, - status.HTTP_500_INTERNAL_SERVER_ERROR) + assert transaction.get_rollback() + assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR assert BasicModel.objects.count() == 0 @@ -151,5 +150,4 @@ def test_api_exception_rollback_transaction_non_atomic_view(self): # without checking connection.in_atomic_block view raises 500 # due attempt to rollback without transaction - self.assertEqual(response.status_code, - status.HTTP_404_NOT_FOUND) + assert response.status_code == status.HTTP_404_NOT_FOUND From a9b6c974852c7b5c6fb95c586fdab9fbe29e73f7 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Wed, 30 Nov 2016 16:24:48 +0600 Subject: [PATCH 0031/1069] converted asserts of decorators test to pytest --- tests/test_decorators.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/tests/test_decorators.py b/tests/test_decorators.py index 46e4a6ad79..b187e5fd6a 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -56,11 +56,11 @@ def view(request): request = self.factory.get('/') response = view(request) - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert response.status_code == status.HTTP_200_OK request = self.factory.post('/') response = view(request) - self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED def test_calling_put_method(self): @@ -70,11 +70,11 @@ def view(request): request = self.factory.put('/') response = view(request) - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert response.status_code == status.HTTP_200_OK request = self.factory.post('/') response = view(request) - self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED def test_calling_patch_method(self): @@ -84,11 +84,11 @@ def view(request): request = self.factory.patch('/') response = view(request) - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert response.status_code == status.HTTP_200_OK request = self.factory.post('/') response = view(request) - self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) + assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED def test_renderer_classes(self): @@ -99,16 +99,15 @@ def view(request): request = self.factory.get('/') response = view(request) - self.assertTrue(isinstance(response.accepted_renderer, JSONRenderer)) + assert isinstance(response.accepted_renderer, JSONRenderer) def test_parser_classes(self): @api_view(['GET']) @parser_classes([JSONParser]) def view(request): - self.assertEqual(len(request.parsers), 1) - self.assertTrue(isinstance(request.parsers[0], - JSONParser)) + assert len(request.parsers) == 1 + assert isinstance(request.parsers[0], JSONParser) return Response({}) request = self.factory.get('/') @@ -119,9 +118,8 @@ def test_authentication_classes(self): @api_view(['GET']) @authentication_classes([BasicAuthentication]) def view(request): - self.assertEqual(len(request.authenticators), 1) - self.assertTrue(isinstance(request.authenticators[0], - BasicAuthentication)) + assert len(request.authenticators) == 1 + assert isinstance(request.authenticators[0], BasicAuthentication) return Response({}) request = self.factory.get('/') @@ -136,7 +134,7 @@ def view(request): request = self.factory.get('/') response = view(request) - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + assert response.status_code == status.HTTP_403_FORBIDDEN def test_throttle_classes(self): class OncePerDayUserThrottle(UserRateThrottle): @@ -149,7 +147,7 @@ def view(request): request = self.factory.get('/') response = view(request) - self.assertEqual(response.status_code, status.HTTP_200_OK) + assert response.status_code == status.HTTP_200_OK response = view(request) - self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS) + assert response.status_code == status.HTTP_429_TOO_MANY_REQUESTS From 9a3f8d9a9c63fc14a4e13738407f2b262de6649a Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Wed, 30 Nov 2016 16:42:43 +0600 Subject: [PATCH 0032/1069] converted asserts of descriptions test to pytest --- tests/test_description.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_description.py b/tests/test_description.py index fcb88287b4..08d8bddece 100644 --- a/tests/test_description.py +++ b/tests/test_description.py @@ -60,7 +60,7 @@ def test_view_name_uses_class_name(self): """ class MockView(APIView): pass - self.assertEqual(MockView().get_view_name(), 'Mock') + assert MockView().get_view_name() == 'Mock' def test_view_description_uses_docstring(self): """Ensure view descriptions are based on the docstring.""" @@ -80,7 +80,7 @@ class MockView(APIView): # hash style header #""" - self.assertEqual(MockView().get_view_description(), DESCRIPTION) + assert MockView().get_view_description() == DESCRIPTION def test_view_description_can_be_empty(self): """ @@ -89,7 +89,7 @@ def test_view_description_can_be_empty(self): """ class MockView(APIView): pass - self.assertEqual(MockView().get_view_description(), '') + assert MockView().get_view_description() == '' def test_view_description_can_be_promise(self): """ @@ -111,7 +111,7 @@ def __str__(self): class MockView(APIView): __doc__ = MockLazyStr("a gettext string") - self.assertEqual(MockView().get_view_description(), 'a gettext string') + assert MockView().get_view_description() == 'a gettext string' def test_markdown(self): """ @@ -120,7 +120,7 @@ def test_markdown(self): if apply_markdown: gte_21_match = apply_markdown(DESCRIPTION) == MARKED_DOWN_gte_21 lt_21_match = apply_markdown(DESCRIPTION) == MARKED_DOWN_lt_21 - self.assertTrue(gte_21_match or lt_21_match) + assert gte_21_match or lt_21_match def test_dedent_tabs(): From 7e8b01dbd2e780b1204d8c362dcb40d31d1b27d5 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Wed, 30 Nov 2016 16:45:48 +0600 Subject: [PATCH 0033/1069] converted asserts of encoders test to pytest --- tests/test_encoders.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_encoders.py b/tests/test_encoders.py index d6f6819328..6871414766 100644 --- a/tests/test_encoders.py +++ b/tests/test_encoders.py @@ -20,21 +20,21 @@ def test_encode_decimal(self): Tests encoding a decimal """ d = Decimal(3.14) - self.assertEqual(d, float(d)) + assert d == float(d) def test_encode_datetime(self): """ Tests encoding a datetime object """ current_time = datetime.now() - self.assertEqual(self.encoder.default(current_time), current_time.isoformat()) + assert self.encoder.default(current_time) == current_time.isoformat() def test_encode_time(self): """ Tests encoding a timezone """ current_time = datetime.now().time() - self.assertEqual(self.encoder.default(current_time), current_time.isoformat()[:12]) + assert self.encoder.default(current_time) == current_time.isoformat()[:12] def test_encode_time_tz(self): """ @@ -64,18 +64,18 @@ def test_encode_date(self): Tests encoding a date object """ current_date = date.today() - self.assertEqual(self.encoder.default(current_date), current_date.isoformat()) + assert self.encoder.default(current_date) == current_date.isoformat() def test_encode_timedelta(self): """ Tests encoding a timedelta object """ delta = timedelta(hours=1) - self.assertEqual(self.encoder.default(delta), str(delta.total_seconds())) + assert self.encoder.default(delta) == str(delta.total_seconds()) def test_encode_uuid(self): """ Tests encoding a UUID object """ unique_id = uuid4() - self.assertEqual(self.encoder.default(unique_id), str(unique_id)) + assert self.encoder.default(unique_id) == str(unique_id) From 1a741bb2a2759d9b20afc6160b5c7d58ec5ff382 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Wed, 30 Nov 2016 17:12:01 +0600 Subject: [PATCH 0034/1069] converted asserts of exceptions test to pytest (#4723) --- tests/test_exceptions.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index f1d172211d..8b5628ef2c 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -16,28 +16,22 @@ def test_get_error_details(self): example = "string" lazy_example = _(example) - self.assertEqual( - _get_error_details(lazy_example), - example - ) + assert _get_error_details(lazy_example) == example + assert isinstance( _get_error_details(lazy_example), ErrorDetail ) - self.assertEqual( - _get_error_details({'nested': lazy_example})['nested'], - example - ) + assert _get_error_details({'nested': lazy_example})['nested'] == example + assert isinstance( _get_error_details({'nested': lazy_example})['nested'], ErrorDetail ) - self.assertEqual( - _get_error_details([[lazy_example]])[0][0], - example - ) + assert _get_error_details([[lazy_example]])[0][0] == example + assert isinstance( _get_error_details([[lazy_example]])[0][0], ErrorDetail From 4f6c326a9955e70e64eb83499015bba10175468a Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Wed, 30 Nov 2016 18:52:32 +0600 Subject: [PATCH 0035/1069] converted remaining unittes asserts of fields test to pytest (#4724) --- tests/test_fields.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_fields.py b/tests/test_fields.py index 92030e3ca9..069ba879d4 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1014,16 +1014,16 @@ class TestLocalizedDecimalField(TestCase): @override_settings(USE_L10N=True, LANGUAGE_CODE='pl') def test_to_internal_value(self): field = serializers.DecimalField(max_digits=2, decimal_places=1, localize=True) - self.assertEqual(field.to_internal_value('1,1'), Decimal('1.1')) + assert field.to_internal_value('1,1') == Decimal('1.1') @override_settings(USE_L10N=True, LANGUAGE_CODE='pl') def test_to_representation(self): field = serializers.DecimalField(max_digits=2, decimal_places=1, localize=True) - self.assertEqual(field.to_representation(Decimal('1.1')), '1,1') + assert field.to_representation(Decimal('1.1')) == '1,1' def test_localize_forces_coerce_to_string(self): field = serializers.DecimalField(max_digits=2, decimal_places=1, coerce_to_string=False, localize=True) - self.assertTrue(isinstance(field.to_representation(Decimal('1.1')), six.string_types)) + assert isinstance(field.to_representation(Decimal('1.1')), six.string_types) class TestQuantizedValueForDecimal(TestCase): @@ -1031,19 +1031,19 @@ def test_int_quantized_value_for_decimal(self): field = serializers.DecimalField(max_digits=4, decimal_places=2) value = field.to_internal_value(12).as_tuple() expected_digit_tuple = (0, (1, 2, 0, 0), -2) - self.assertEqual(value, expected_digit_tuple) + assert value == expected_digit_tuple def test_string_quantized_value_for_decimal(self): field = serializers.DecimalField(max_digits=4, decimal_places=2) value = field.to_internal_value('12').as_tuple() expected_digit_tuple = (0, (1, 2, 0, 0), -2) - self.assertEqual(value, expected_digit_tuple) + assert value == expected_digit_tuple def test_part_precision_string_quantized_value_for_decimal(self): field = serializers.DecimalField(max_digits=4, decimal_places=2) value = field.to_internal_value('12.0').as_tuple() expected_digit_tuple = (0, (1, 2, 0, 0), -2) - self.assertEqual(value, expected_digit_tuple) + assert value == expected_digit_tuple class TestNoDecimalPlaces(FieldValues): From 22578525eff075e329e8840c42c358d59105a0d3 Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Wed, 30 Nov 2016 13:58:34 +0100 Subject: [PATCH 0036/1069] Documentation update (#4717) --- docs/api-guide/authentication.md | 2 +- docs/api-guide/fields.md | 6 +++--- docs/api-guide/filtering.md | 4 ++-- docs/api-guide/generic-views.md | 2 +- docs/api-guide/pagination.md | 2 +- docs/api-guide/parsers.md | 2 +- docs/api-guide/permissions.md | 4 ++-- docs/api-guide/relations.md | 6 +++--- docs/api-guide/renderers.md | 4 ++-- docs/api-guide/responses.md | 2 +- docs/api-guide/reverse.md | 4 ++-- docs/api-guide/schemas.md | 4 ++-- docs/api-guide/serializers.md | 2 +- docs/api-guide/testing.md | 4 ++-- docs/api-guide/throttling.md | 4 ++-- docs/api-guide/validators.md | 2 +- docs/topics/2.2-announcement.md | 8 ++++---- docs/topics/2.4-announcement.md | 2 +- docs/topics/3.0-announcement.md | 4 ++-- docs/topics/ajax-csrf-cors.md | 2 +- docs/topics/release-notes.md | 2 +- 21 files changed, 36 insertions(+), 36 deletions(-) diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index bf3a31eb7b..4a01188f38 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -363,7 +363,7 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a [oauth]: http://oauth.net/2/ [permission]: permissions.md [throttling]: throttling.md -[csrf-ajax]: https://docs.djangoproject.com/en/dev/ref/csrf/#ajax +[csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax [mod_wsgi_official]: http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIPassAuthorization [django-oauth-toolkit-getting-started]: https://django-oauth-toolkit.readthedocs.io/en/latest/rest-framework/getting_started.html [django-rest-framework-oauth]: http://jpadilla.github.io/django-rest-framework-oauth/ diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 6b6ae612d6..b527b016b2 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -665,12 +665,12 @@ The [django-rest-framework-gis][django-rest-framework-gis] package provides geog The [django-rest-framework-hstore][django-rest-framework-hstore] package provides an `HStoreField` to support [django-hstore][django-hstore] `DictionaryField` model field. -[cite]: https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.cleaned_data +[cite]: https://docs.djangoproject.com/en/stable/ref/forms/api/#django.forms.Form.cleaned_data [html-and-forms]: ../topics/html-and-forms.md -[FILE_UPLOAD_HANDLERS]: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS +[FILE_UPLOAD_HANDLERS]: https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS [ecma262]: http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 [strftime]: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior -[django-widgets]: https://docs.djangoproject.com/en/dev/ref/forms/widgets/ +[django-widgets]: https://docs.djangoproject.com/en/stable/ref/forms/widgets/ [iso8601]: http://www.w3.org/TR/NOTE-datetime [drf-compound-fields]: https://drf-compound-fields.readthedocs.io [drf-extra-fields]: https://github.com/Hipo/drf-extra-fields diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 3f212ced3a..8a23a2ea33 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -455,14 +455,14 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter] [drf-url-filter][drf-url-filter] is a simple Django app to apply filters on drf `ModelViewSet`'s `Queryset` in a clean, simple and configurable way. It also supports validations on incoming query params and their values. A beautiful python package `Voluptuous` is being used for validations on the incoming query parameters. The best part about voluptuous is you can define your own validations as per your query params requirements. -[cite]: https://docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-specific-objects-with-filters +[cite]: https://docs.djangoproject.com/en/stable/topics/db/queries/#retrieving-specific-objects-with-filters [django-filter]: https://github.com/alex/django-filter [django-filter-docs]: https://django-filter.readthedocs.io/en/latest/index.html [guardian]: https://django-guardian.readthedocs.io/ [view-permissions]: https://django-guardian.readthedocs.io/en/latest/userguide/assign.html [view-permissions-blogpost]: http://blog.nyaruka.com/adding-a-view-permission-to-django-models [nullbooleanselect]: https://github.com/django/django/blob/master/django/forms/widgets.py -[search-django-admin]: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields +[search-django-admin]: https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields [django-rest-framework-filters]: https://github.com/philipn/django-rest-framework-filters [django-rest-framework-word-search-filter]: https://github.com/trollknurr/django-rest-framework-word-search-filter [django-url-filter]: https://github.com/miki725/django-url-filter diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index c368d0b468..606a3787ac 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -382,7 +382,7 @@ The [django-rest-framework-bulk package][django-rest-framework-bulk] implements [Django Rest Multiple Models][django-rest-multiple-models] provides a generic view (and mixin) for sending multiple serialized models and/or querysets via a single API request. -[cite]: https://docs.djangoproject.com/en/dev/ref/class-based-views/#base-vs-generic-views +[cite]: https://docs.djangoproject.com/en/stable/ref/class-based-views/#base-vs-generic-views [GenericAPIView]: #genericapiview [ListModelMixin]: #listmodelmixin [CreateModelMixin]: #createmodelmixin diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index f82614eca7..bc7a5602db 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -325,7 +325,7 @@ The [`DRF-extensions` package][drf-extensions] includes a [`PaginateByMaxMixin` The [`drf-proxy-pagination` package][drf-proxy-pagination] includes a `ProxyPagination` class which allows to choose pagination class with a query parameter. -[cite]: https://docs.djangoproject.com/en/dev/topics/pagination/ +[cite]: https://docs.djangoproject.com/en/stable/topics/pagination/ [github-link-pagination]: https://developer.github.com/guides/traversing-with-pagination/ [link-header]: ../img/link-header-pagination.png [drf-extensions]: http://chibisov.github.io/drf-extensions/docs/ diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index ef2859fe16..7bf932d069 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -224,7 +224,7 @@ Modify your REST framework settings. [jquery-ajax]: http://api.jquery.com/jQuery.ajax/ [cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion -[upload-handlers]: https://docs.djangoproject.com/en/dev/topics/http/file-uploads/#upload-handlers +[upload-handlers]: https://docs.djangoproject.com/en/stable/topics/http/file-uploads/#upload-handlers [rest-framework-yaml]: http://jpadilla.github.io/django-rest-framework-yaml/ [rest-framework-xml]: http://jpadilla.github.io/django-rest-framework-xml/ [yaml]: http://www.yaml.org/ diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index f2d2fcaaef..be29813276 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -269,8 +269,8 @@ The [Django Rest Framework Roles][django-rest-framework-roles] package makes it [authentication]: authentication.md [throttling]: throttling.md [filtering]: filtering.md -[contribauth]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#custom-permissions -[objectpermissions]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#handling-object-permissions +[contribauth]: https://docs.djangoproject.com/en/stable/topics/auth/customizing/#custom-permissions +[objectpermissions]: https://docs.djangoproject.com/en/stable/topics/auth/customizing/#handling-object-permissions [guardian]: https://github.com/lukaszb/django-guardian [get_objects_for_user]: http://pythonhosted.org/django-guardian/api/guardian.shortcuts.html#get-objects-for-user [2.2-announcement]: ../topics/2.2-announcement.md diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index aabe49412b..662fd48095 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -505,7 +505,7 @@ For example, given the following model for a tag, which has a generic relationsh """ Tags arbitrary model instances using a generic relation. - See: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/ + See: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/ """ tag_name = models.SlugField() content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) @@ -593,9 +593,9 @@ The [drf-nested-routers package][drf-nested-routers] provides routers and relati The [rest-framework-generic-relations][drf-nested-relations] library provides read/write serialization for generic foreign keys. [cite]: http://lwn.net/Articles/193245/ -[reverse-relationships]: https://docs.djangoproject.com/en/dev/topics/db/queries/#following-relationships-backward +[reverse-relationships]: https://docs.djangoproject.com/en/stable/topics/db/queries/#following-relationships-backward [routers]: http://www.django-rest-framework.org/api-guide/routers#defaultrouter -[generic-relations]: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#id1 +[generic-relations]: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#id1 [2.2-announcement]: ../topics/2.2-announcement.md [drf-nested-routers]: https://github.com/alanjds/drf-nested-routers [drf-nested-relations]: https://github.com/Ian-Foote/rest-framework-generic-relations diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index a95778350e..648eafdde3 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -476,7 +476,7 @@ Comma-separated values are a plain-text tabular data format, that can be easily [Rest Framework Latex] provides a renderer that outputs PDFs using Laulatex. It is maintained by [Pebble (S/F Software)][mypebble]. -[cite]: https://docs.djangoproject.com/en/dev/ref/template-response/#the-rendering-process +[cite]: https://docs.djangoproject.com/en/stable/stable/template-response/#the-rendering-process [conneg]: content-negotiation.md [html-and-forms]: ../topics/html-and-forms.md [browser-accept-headers]: http://www.gethifi.com/blog/browser-rest-http-accept-headers @@ -485,7 +485,7 @@ Comma-separated values are a plain-text tabular data format, that can be easily [quote]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven [application/vnd.github+json]: http://developer.github.com/v3/media/ [application/vnd.collection+json]: http://www.amundsen.com/media-types/collection/ -[django-error-views]: https://docs.djangoproject.com/en/dev/topics/http/views/#customizing-error-views +[django-error-views]: https://docs.djangoproject.com/en/stable/topics/http/views/#customizing-error-views [rest-framework-jsonp]: http://jpadilla.github.io/django-rest-framework-jsonp/ [cors]: http://www.w3.org/TR/cors/ [cors-docs]: http://www.django-rest-framework.org/topics/ajax-csrf-cors/ diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index 97f3127106..8ee14eefae 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -91,5 +91,5 @@ As with any other `TemplateResponse`, this method is called to render the serial You won't typically need to call `.render()` yourself, as it's handled by Django's standard response cycle. -[cite]: https://docs.djangoproject.com/en/dev/ref/template-response/ +[cite]: https://docs.djangoproject.com/en/stable/stable/template-response/ [statuscodes]: status-codes.md diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index 35d88e2db1..ee0b2054f2 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -51,5 +51,5 @@ As with the `reverse` function, you should **include the request as a keyword ar api_root = reverse_lazy('api-root', request=request) [cite]: http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5 -[reverse]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse -[reverse-lazy]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-lazy +[reverse]: https://docs.djangoproject.com/en/stable/topics/http/urls/#reverse +[reverse-lazy]: https://docs.djangoproject.com/en/stable/topics/http/urls/#reverse-lazy diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 7da6190345..c89c100fea 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -541,5 +541,5 @@ A short description of the meaning and intended usage of the input field. [open-api]: https://openapis.org/ [json-hyperschema]: http://json-schema.org/latest/json-schema-hypermedia.html [api-blueprint]: https://apiblueprint.org/ -[static-files]: https://docs.djangoproject.com/en/dev/howto/static-files/ -[named-arguments]: https://docs.djangoproject.com/en/dev/topics/http/urls/#named-groups +[static-files]: https://docs.djangoproject.com/en/stable/howto/static-files/ +[named-arguments]: https://docs.djangoproject.com/en/stable/topics/http/urls/#named-groups diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 290e32f4fa..d36812f3f9 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -1118,7 +1118,7 @@ The [html-json-forms][html-json-forms] package provides an algorithm and seriali [cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion [relations]: relations.md -[model-managers]: https://docs.djangoproject.com/en/dev/topics/db/managers/ +[model-managers]: https://docs.djangoproject.com/en/stable/topics/db/managers/ [encapsulation-blogpost]: http://www.dabapps.com/blog/django-models-and-encapsulation/ [django-rest-marshmallow]: http://tomchristie.github.io/django-rest-marshmallow/ [marshmallow]: https://marshmallow.readthedocs.io/en/latest/ diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md index de79a1e2fc..410f6d78a5 100644 --- a/docs/api-guide/testing.md +++ b/docs/api-guide/testing.md @@ -373,6 +373,6 @@ For example, to add support for using `format='html'` in test requests, you migh } [cite]: http://jacobian.org/writing/django-apps-with-buildout/#s-create-a-test-wrapper -[client]: https://docs.djangoproject.com/en/dev/topics/testing/tools/#the-test-client -[requestfactory]: https://docs.djangoproject.com/en/dev/topics/testing/advanced/#django.test.client.RequestFactory +[client]: https://docs.djangoproject.com/en/stable/topics/testing/tools/#the-test-client +[requestfactory]: https://docs.djangoproject.com/en/stable/topics/testing/advanced/#django.test.client.RequestFactory [configuration]: #configuration diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index da4d5f7252..2a6337e81c 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -193,5 +193,5 @@ The following is an example of a rate throttle, that will randomly throttle 1 in [cite]: https://dev.twitter.com/docs/error-codes-responses [permissions]: permissions.md [identifing-clients]: http://oxpedia.org/wiki/index.php?title=AppSuite:Grizzly#Multiple_Proxies_in_front_of_the_cluster -[cache-setting]: https://docs.djangoproject.com/en/dev/ref/settings/#caches -[cache-docs]: https://docs.djangoproject.com/en/dev/topics/cache/#setting-up-the-cache +[cache-setting]: https://docs.djangoproject.com/en/stable/stable/settings/#caches +[cache-docs]: https://docs.djangoproject.com/en/stable/topics/cache/#setting-up-the-cache diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index e041e9072b..0e58c6fff7 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -300,4 +300,4 @@ In some advanced cases you might want a validator to be passed the serializer fi # In `__call__` we can then use that information to modify the validation behavior. self.is_update = serializer_field.parent.instance is not None -[cite]: https://docs.djangoproject.com/en/dev/ref/validators/ +[cite]: https://docs.djangoproject.com/en/stable/ref/validators/ diff --git a/docs/topics/2.2-announcement.md b/docs/topics/2.2-announcement.md index e6220f4277..ca4ed2efac 100644 --- a/docs/topics/2.2-announcement.md +++ b/docs/topics/2.2-announcement.md @@ -147,10 +147,10 @@ When using a serializer with a `HyperlinkedRelatedField` or `HyperlinkedIdentity From version 2.2 onwards, serializers with hyperlinked relationships *always* require a `'request'` key to be supplied in the context dictionary. The implicit behavior will continue to function, but its use will raise a `PendingDeprecationWarning`. [xordoquy]: https://github.com/xordoquy -[django-python-3]: https://docs.djangoproject.com/en/dev/faq/install/#can-i-use-django-with-python-3 -[porting-python-3]: https://docs.djangoproject.com/en/dev/topics/python3/ -[python-compat]: https://docs.djangoproject.com/en/dev/releases/1.5/#python-compatibility -[django-deprecation-policy]: https://docs.djangoproject.com/en/dev/internals/release-process/#internal-release-deprecation-policy +[django-python-3]: https://docs.djangoproject.com/en/stable/faq/install/#can-i-use-django-with-python-3 +[porting-python-3]: https://docs.djangoproject.com/en/stable/topics/python3/ +[python-compat]: https://docs.djangoproject.com/en/stable/releases/1.5/#python-compatibility +[django-deprecation-policy]: https://docs.djangoproject.com/en/stable/internals/release-process/#internal-release-deprecation-policy [credits]: http://www.django-rest-framework.org/topics/credits [mailing-list]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework [django-rest-framework-docs]: https://github.com/marcgibbons/django-rest-framework-docs diff --git a/docs/topics/2.4-announcement.md b/docs/topics/2.4-announcement.md index 3009daa49b..96f68c8656 100644 --- a/docs/topics/2.4-announcement.md +++ b/docs/topics/2.4-announcement.md @@ -162,7 +162,7 @@ The next planned release will be 3.0, featuring an improved and simplified seria Once again, many thanks to all the generous [backers and sponsors][kickstarter-sponsors] who've helped make this possible! -[lts-releases]: https://docs.djangoproject.com/en/dev/internals/release-process/#long-term-support-lts-releases +[lts-releases]: https://docs.djangoproject.com/en/stable/internals/release-process/#long-term-support-lts-releases [2-4-release-notes]: release-notes#240 [view-name-and-description-settings]: ../api-guide/settings#view-names-and-descriptions [client-ip-identification]: ../api-guide/throttling#how-clients-are-identified diff --git a/docs/topics/3.0-announcement.md b/docs/topics/3.0-announcement.md index e6cbf72385..25ab4fd5be 100644 --- a/docs/topics/3.0-announcement.md +++ b/docs/topics/3.0-announcement.md @@ -870,7 +870,7 @@ The `COMPACT_JSON` setting has been added, and can be used to revert this behavi #### File fields as URLs -The `FileField` and `ImageField` classes are now represented as URLs by default. You should ensure you set Django's [standard `MEDIA_URL` setting](https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-MEDIA_URL) appropriately, and ensure your application [serves the uploaded files](https://docs.djangoproject.com/en/dev/howto/static-files/#serving-uploaded-files-in-development). +The `FileField` and `ImageField` classes are now represented as URLs by default. You should ensure you set Django's [standard `MEDIA_URL` setting](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-MEDIA_URL) appropriately, and ensure your application [serves the uploaded files](https://docs.djangoproject.com/en/stable/howto/static-files/#serving-uploaded-files-in-development). You can revert this behavior, and display filenames in the representation by using the `UPLOADED_FILES_USE_URL` settings key: @@ -962,4 +962,4 @@ You can follow development on the GitHub site, where we use [milestones to indic [kickstarter]: http://kickstarter.com/projects/tomchristie/django-rest-framework-3 [sponsors]: http://www.django-rest-framework.org/topics/kickstarter-announcement/#sponsors [mixins.py]: https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/mixins.py -[django-localization]: https://docs.djangoproject.com/en/dev/topics/i18n/translation/#localization-how-to-create-language-files +[django-localization]: https://docs.djangoproject.com/en/stable/topics/i18n/translation/#localization-how-to-create-language-files diff --git a/docs/topics/ajax-csrf-cors.md b/docs/topics/ajax-csrf-cors.md index ad88810da3..4960e08810 100644 --- a/docs/topics/ajax-csrf-cors.md +++ b/docs/topics/ajax-csrf-cors.md @@ -35,7 +35,7 @@ The best way to deal with CORS in REST framework is to add the required response [cite]: http://www.codinghorror.com/blog/2008/10/preventing-csrf-and-xsrf-attacks.html [csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) -[csrf-ajax]: https://docs.djangoproject.com/en/dev/ref/csrf/#ajax +[csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax [cors]: http://www.w3.org/TR/cors/ [ottoyiu]: https://github.com/ottoyiu/ [django-cors-headers]: https://github.com/ottoyiu/django-cors-headers/ diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index e5683360b6..5628bcaeca 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -598,7 +598,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [cite]: http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/ar01s04.html [deprecation-policy]: #deprecation-policy -[django-deprecation-policy]: https://docs.djangoproject.com/en/dev/internals/release-process/#internal-release-deprecation-policy +[django-deprecation-policy]: https://docs.djangoproject.com/en/stable/internals/release-process/#internal-release-deprecation-policy [defusedxml-announce]: http://blog.python.org/2013/02/announcing-defusedxml-fixes-for-xml.html [743]: https://github.com/tomchristie/django-rest-framework/pull/743 [staticfiles14]: https://docs.djangoproject.com/en/1.4/howto/static-files/#with-a-template-tag From 16f5d42cbc3e966ab2d4d8dc00163942f2769e43 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Thu, 1 Dec 2016 10:11:25 +0100 Subject: [PATCH 0037/1069] Add additional link to HTML & Forms topic page (#4726) Just makes the topic page easier to find. Closes #1673 --- docs/api-guide/renderers.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 648eafdde3..236504850d 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -123,6 +123,8 @@ You can use `TemplateHTMLRenderer` either to return regular HTML pages using RES If you're building websites that use `TemplateHTMLRenderer` along with other renderer classes, you should consider listing `TemplateHTMLRenderer` as the first class in the `renderer_classes` list, so that it will be prioritised first even for browsers that send poorly formed `ACCEPT:` headers. +See the [_HTML & Forms_ Topic Page][html-and-forms] for further examples of `TemplateHTMLRenderer` usage. + **.media_type**: `text/html` **.format**: `'.html'` From 932d04a4beb79afa7a52bfec7f66bb6ccfe53814 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Thu, 1 Dec 2016 22:17:36 +0600 Subject: [PATCH 0038/1069] Browsable API tests asserts to pytest (#4725) --- tests/browsable_api/test_browsable_api.py | 18 ++++++++++++------ .../browsable_api/test_browsable_nested_api.py | 8 ++++---- tests/browsable_api/test_form_rendering.py | 4 ++-- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/tests/browsable_api/test_browsable_api.py b/tests/browsable_api/test_browsable_api.py index 3d49c353b5..684d7ae143 100644 --- a/tests/browsable_api/test_browsable_api.py +++ b/tests/browsable_api/test_browsable_api.py @@ -26,16 +26,19 @@ def tearDown(self): def test_name_shown_when_logged_in(self): self.client.login(username=self.username, password=self.password) response = self.client.get('/') - self.assertContains(response, 'john') + content = response.content.decode('utf8') + assert 'john' in content def test_logout_shown_when_logged_in(self): self.client.login(username=self.username, password=self.password) response = self.client.get('/') - self.assertContains(response, '>Log out<') + content = response.content.decode('utf8') + assert '>Log out<' in content def test_login_shown_when_logged_out(self): response = self.client.get('/') - self.assertContains(response, '>Log in<') + content = response.content.decode('utf8') + assert '>Log in<' in content @override_settings(ROOT_URLCONF='tests.browsable_api.no_auth_urls') @@ -58,13 +61,16 @@ def tearDown(self): def test_name_shown_when_logged_in(self): self.client.login(username=self.username, password=self.password) response = self.client.get('/') - self.assertContains(response, 'john') + content = response.content.decode('utf8') + assert 'john' in content def test_dropdown_not_shown_when_logged_in(self): self.client.login(username=self.username, password=self.password) response = self.client.get('/') - self.assertNotContains(response, '