From 314daaedfac3fb49d0b99648bb92bcd786c7805e Mon Sep 17 00:00:00 2001 From: borzdyko Date: Fri, 21 Aug 2015 18:13:13 +0300 Subject: [PATCH 1/9] Using https://docs.djangoproject.com/en/1.8/ref/models/querysets/#regex for search filter by starting field with '$' --- rest_framework/filters.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index e05d31ae3..931263670 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -94,6 +94,8 @@ class SearchFilter(BaseFilterBackend): return "%s__iexact" % field_name[1:] elif field_name.startswith('@'): return "%s__search" % field_name[1:] + if field_name.startswith('$'): + return "%s__iregex" % field_name[1:] else: return "%s__icontains" % field_name From 21d0e5183139e421f7f2b9fd96d765e9a1563b65 Mon Sep 17 00:00:00 2001 From: borzdyko Date: Fri, 21 Aug 2015 18:23:39 +0300 Subject: [PATCH 2/9] Test for regex search filter --- tests/test_filters.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_filters.py b/tests/test_filters.py index 9db685c28..0610b0855 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -407,6 +407,23 @@ class SearchFilterTests(TestCase): ] ) + def test_regexp_search(self): + class SearchListView(generics.ListAPIView): + queryset = SearchFilterModel.objects.all() + serializer_class = SearchFilterSerializer + filter_backends = (filters.SearchFilter,) + search_fields = ('$title', '$text') + + view = SearchListView.as_view() + request = factory.get('/', {'search': 'z{2} ^b'}) + response = view(request) + self.assertEqual( + response.data, + [ + {'id': 2, 'title': 'zz', 'text': 'bcd'} + ] + ) + def test_search_with_nonstandard_search_param(self): with override_settings(REST_FRAMEWORK={'SEARCH_PARAM': 'query'}): reload_module(filters) From f0782b9451be3b04dd4239d1199ea6a7278edd21 Mon Sep 17 00:00:00 2001 From: borzdyko Date: Fri, 21 Aug 2015 18:26:23 +0300 Subject: [PATCH 3/9] Doc for regex search filter --- docs/api-guide/filtering.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index e65c6bf4f..6f4648cfd 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -260,6 +260,7 @@ The search behavior may be restricted by prepending various characters to the `s * '^' Starts-with search. * '=' Exact matches. * '@' Full-text search. (Currently only supported Django's MySQL backend.) +* '$' Regex search. For example: From 01fa2647c9d2d5ed1a7cbff513afd9f3b38efd5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padilla?= Date: Sat, 22 Aug 2015 08:49:05 -0400 Subject: [PATCH 4/9] Bump up maximum supported Django versions --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 489e8a7a6..ef505248b 100644 --- a/tox.ini +++ b/tox.ini @@ -14,8 +14,8 @@ setenv = deps = django15: Django==1.5.6 # Should track minimum supported django16: Django==1.6.3 # Should track minimum supported - django17: Django==1.7.8 # Should track maximum supported - django18: Django==1.8.2 # Should track maximum supported + django17: Django==1.7.10 # Should track maximum supported + django18: Django==1.8.4 # Should track maximum supported djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From 9ac646385f79d352c4105cb96ebb19a3c84b1d77 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 24 Aug 2015 10:13:16 +0100 Subject: [PATCH 5/9] Resolve empty HTML charfield behavior. Closes #3318. --- rest_framework/fields.py | 6 ++++-- tests/test_fields.py | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 10a310280..faf41e7a0 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -385,8 +385,10 @@ class Field(object): # If the field is blank, and null is a valid value then # determine if we should use null instead. return '' if getattr(self, 'allow_blank', False) else None - elif ret == '' and self.default: - return empty + elif ret == '' and not self.required: + # If the field is blank, and emptyness is valid then + # determine if we should use emptyness instead. + return '' if getattr(self, 'allow_blank', False) else empty return ret return dictionary.get(self.field_name, empty) diff --git a/tests/test_fields.py b/tests/test_fields.py index 58c1c9243..8065c8260 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -253,7 +253,7 @@ class TestBooleanHTMLInput: class TestHTMLInput: - def test_empty_html_charfield(self): + def test_empty_html_charfield_with_default(self): class TestSerializer(serializers.Serializer): message = serializers.CharField(default='happy') @@ -261,6 +261,22 @@ class TestHTMLInput: assert serializer.is_valid() assert serializer.validated_data == {'message': 'happy'} + def test_empty_html_charfield_without_default(self): + class TestSerializer(serializers.Serializer): + message = serializers.CharField(allow_blank=True) + + serializer = TestSerializer(data=QueryDict('message=')) + assert serializer.is_valid() + assert serializer.validated_data == {'message': ''} + + def test_empty_html_charfield_without_default_not_required(self): + class TestSerializer(serializers.Serializer): + message = serializers.CharField(allow_blank=True, required=False) + + serializer = TestSerializer(data=QueryDict('message=')) + assert serializer.is_valid() + assert serializer.validated_data == {'message': ''} + def test_empty_html_integerfield(self): class TestSerializer(serializers.Serializer): message = serializers.IntegerField(default=123) From 0198bce34f13e3852f082863bd2add7d0285e48b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 24 Aug 2015 10:59:52 +0100 Subject: [PATCH 6/9] Release 3.2.3 --- docs/topics/release-notes.md | 33 +++++++++++++++++++++++++++++++-- rest_framework/__init__.py | 2 +- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 4fa365d02..68803a5b1 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,18 @@ You can determine your currently installed version using `pip freeze`: ## 3.2.x series +### 3.2.3 + +**Date**: [24th August 2015][3.2.3-milestone]. + +* Added `html_cutoff` and `html_cutoff_text` for limiting select dropdowns. ([#3313][gh3313]) +* Added regex style to `SearchFilter`. ([#3316][gh3316]) +* Resolve issues with setting blank HTML fields. ([#3318][gh3318]) ([#3321][gh3321]) +* Correctly display existing 'select multiple' values in browsable API forms. ([#3290][gh3290]) +* Resolve duplicated validation message for `IPAddressField`. ([#3249[gh3249]) ([#3250][gh3250]) +* Fix to ensure admin renderer continues to work when pagination is disabled. ([#3275][gh3275]) +* Resolve error with `LimitOffsetPagination` when count=0, offset=0. ([#3303][gh3303]) + ### 3.2.2 **Date**: [13th August 2015][3.2.2-milestone]. @@ -285,7 +297,8 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.1.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.1.3+Release%22 [3.2.0-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.2.0+Release%22 [3.2.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.2.1+Release%22 -[3.2.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.2.1+Release%22 +[3.2.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.2.2+Release%22 +[3.2.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.2.3+Release%22 [gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013 @@ -486,4 +499,20 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh2776]: https://github.com/tomchristie/django-rest-framework/issues/2776 [gh3261]: https://github.com/tomchristie/django-rest-framework/issues/3261 [gh3260]: https://github.com/tomchristie/django-rest-framework/issues/3260 -[gh3241]: https://github.com/tomchristie/django-rest-framework/issues/3241 \ No newline at end of file +[gh3241]: https://github.com/tomchristie/django-rest-framework/issues/3241 + + +[gh3249]: https://github.com/tomchristie/django-rest-framework/issues/3249 +[gh3250]: https://github.com/tomchristie/django-rest-framework/issues/3250 +[gh3275]: https://github.com/tomchristie/django-rest-framework/issues/3275 +[gh3288]: https://github.com/tomchristie/django-rest-framework/issues/3288 +[gh3290]: https://github.com/tomchristie/django-rest-framework/issues/3290 +[gh3303]: https://github.com/tomchristie/django-rest-framework/issues/3303 +[gh3313]: https://github.com/tomchristie/django-rest-framework/issues/3313 +[gh3316]: https://github.com/tomchristie/django-rest-framework/issues/3316 +[gh3318]: https://github.com/tomchristie/django-rest-framework/issues/3318 +[gh3321]: https://github.com/tomchristie/django-rest-framework/issues/3321 + + + + diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 4cc499af1..add4fc8ed 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____ __ """ __title__ = 'Django REST framework' -__version__ = '3.2.2' +__version__ = '3.2.3' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2015 Tom Christie' From 65df9fd5c6820733b7ba0ec78152bfffb01301db Mon Sep 17 00:00:00 2001 From: Rikuoja Date: Mon, 24 Aug 2015 17:02:49 +0300 Subject: [PATCH 7/9] Fix #3323 --- rest_framework/filters.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 931263670..90c19aba0 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -102,15 +102,16 @@ class SearchFilter(BaseFilterBackend): def filter_queryset(self, request, queryset, view): search_fields = getattr(view, 'search_fields', None) - orm_lookups = [ - self.construct_search(six.text_type(search_field)) - for search_field in search_fields - ] search_terms = self.get_search_terms(request) if not search_fields or not search_terms: return queryset + orm_lookups = [ + self.construct_search(six.text_type(search_field)) + for search_field in search_fields + ] + base = queryset for search_term in search_terms: queries = [ From 49d799c45428ba88e4487e5cba351f97a5386c99 Mon Sep 17 00:00:00 2001 From: Slava Shklyaev Date: Mon, 24 Aug 2015 21:09:08 +0300 Subject: [PATCH 8/9] Fix typo --- docs/api-guide/pagination.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 15ebc6229..881fbf14e 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -15,7 +15,7 @@ The pagination API can support either: The built-in styles currently all use links included as part of the content of the response. This style is more accessible when using the browsable API. -Pagination is only performed automatically if you're using the generic views or viewsets. If you're using a regular `APIView`, you'll need to call into the pagination API yourself to ensure you return a paginated response. See the source code for the `mixins.ListMixin` and `generics.GenericAPIView` classes for an example. +Pagination is only performed automatically if you're using the generic views or viewsets. If you're using a regular `APIView`, you'll need to call into the pagination API yourself to ensure you return a paginated response. See the source code for the `mixins.ListModelMixin` and `generics.GenericAPIView` classes for an example. ## Setting the pagination style From 086115c7b86bec3b7dcabd01f141cef15adef7f5 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 25 Aug 2015 13:32:35 +0100 Subject: [PATCH 9/9] Clean up after publishing to PyPI --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index 2dfcbc5bd..8e9d0a9ab 100755 --- a/setup.py +++ b/setup.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- import os import re +import shutil import sys from setuptools import setup @@ -55,6 +56,9 @@ if sys.argv[-1] == 'publish': print("You probably want to also tag the version now:") print(" git tag -a %s -m 'version %s'" % (version, version)) print(" git push --tags") + shutil.rmtree('dist') + shutil.rmtree('build') + shutil.rmtree('djangorestframework.egg-info') sys.exit()