Merge branch 'master' into filters

This commit is contained in:
Tom Christie 2015-08-27 14:02:15 +01:00
commit ea630bf3d1
10 changed files with 85 additions and 13 deletions

View File

@ -260,6 +260,7 @@ The search behavior may be restricted by prepending various characters to the `s
* '^' Starts-with search. * '^' Starts-with search.
* '=' Exact matches. * '=' Exact matches.
* '@' Full-text search. (Currently only supported Django's MySQL backend.) * '@' Full-text search. (Currently only supported Django's MySQL backend.)
* '$' Regex search.
For example: For example:

View File

@ -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. 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 ## Setting the pagination style

View File

@ -40,6 +40,18 @@ You can determine your currently installed version using `pip freeze`:
## 3.2.x series ## 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 ### 3.2.2
**Date**: [13th August 2015][3.2.2-milestone]. **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.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.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.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
<!-- 3.0.1 --> <!-- 3.0.1 -->
[gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013 [gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013
@ -487,3 +500,19 @@ For older release notes, [please see the version 2.x documentation][old-release-
[gh3261]: https://github.com/tomchristie/django-rest-framework/issues/3261 [gh3261]: https://github.com/tomchristie/django-rest-framework/issues/3261
[gh3260]: https://github.com/tomchristie/django-rest-framework/issues/3260 [gh3260]: https://github.com/tomchristie/django-rest-framework/issues/3260
[gh3241]: https://github.com/tomchristie/django-rest-framework/issues/3241 [gh3241]: https://github.com/tomchristie/django-rest-framework/issues/3241
<!-- 3.2.3 -->
[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

View File

@ -8,7 +8,7 @@ ______ _____ _____ _____ __
""" """
__title__ = 'Django REST framework' __title__ = 'Django REST framework'
__version__ = '3.2.2' __version__ = '3.2.3'
__author__ = 'Tom Christie' __author__ = 'Tom Christie'
__license__ = 'BSD 2-Clause' __license__ = 'BSD 2-Clause'
__copyright__ = 'Copyright 2011-2015 Tom Christie' __copyright__ = 'Copyright 2011-2015 Tom Christie'

View File

@ -385,8 +385,10 @@ class Field(object):
# If the field is blank, and null is a valid value then # If the field is blank, and null is a valid value then
# determine if we should use null instead. # determine if we should use null instead.
return '' if getattr(self, 'allow_blank', False) else None return '' if getattr(self, 'allow_blank', False) else None
elif ret == '' and self.default: elif ret == '' and not self.required:
return empty # 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 ret
return dictionary.get(self.field_name, empty) return dictionary.get(self.field_name, empty)

View File

@ -127,21 +127,24 @@ class SearchFilter(BaseFilterBackend):
return "%s__iexact" % field_name[1:] return "%s__iexact" % field_name[1:]
elif field_name.startswith('@'): elif field_name.startswith('@'):
return "%s__search" % field_name[1:] return "%s__search" % field_name[1:]
if field_name.startswith('$'):
return "%s__iregex" % field_name[1:]
else: else:
return "%s__icontains" % field_name return "%s__icontains" % field_name
def filter_queryset(self, request, queryset, view): def filter_queryset(self, request, queryset, view):
search_fields = getattr(view, 'search_fields', None) 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) search_terms = self.get_search_terms(request)
if not search_fields or not search_terms: if not search_fields or not search_terms:
return queryset return queryset
orm_lookups = [
self.construct_search(six.text_type(search_field))
for search_field in search_fields
]
base = queryset base = queryset
for search_term in search_terms: for search_term in search_terms:
queries = [ queries = [

View File

@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
import re import re
import shutil
import sys import sys
from setuptools import setup from setuptools import setup
@ -55,6 +56,9 @@ if sys.argv[-1] == 'publish':
print("You probably want to also tag the version now:") print("You probably want to also tag the version now:")
print(" git tag -a %s -m 'version %s'" % (version, version)) print(" git tag -a %s -m 'version %s'" % (version, version))
print(" git push --tags") print(" git push --tags")
shutil.rmtree('dist')
shutil.rmtree('build')
shutil.rmtree('djangorestframework.egg-info')
sys.exit() sys.exit()

View File

@ -253,7 +253,7 @@ class TestBooleanHTMLInput:
class TestHTMLInput: class TestHTMLInput:
def test_empty_html_charfield(self): def test_empty_html_charfield_with_default(self):
class TestSerializer(serializers.Serializer): class TestSerializer(serializers.Serializer):
message = serializers.CharField(default='happy') message = serializers.CharField(default='happy')
@ -261,6 +261,22 @@ class TestHTMLInput:
assert serializer.is_valid() assert serializer.is_valid()
assert serializer.validated_data == {'message': 'happy'} 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): def test_empty_html_integerfield(self):
class TestSerializer(serializers.Serializer): class TestSerializer(serializers.Serializer):
message = serializers.IntegerField(default=123) message = serializers.IntegerField(default=123)

View File

@ -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): def test_search_with_nonstandard_search_param(self):
with override_settings(REST_FRAMEWORK={'SEARCH_PARAM': 'query'}): with override_settings(REST_FRAMEWORK={'SEARCH_PARAM': 'query'}):
reload_module(filters) reload_module(filters)

View File

@ -14,8 +14,8 @@ setenv =
deps = deps =
django15: Django==1.5.6 # Should track minimum supported django15: Django==1.5.6 # Should track minimum supported
django16: Django==1.6.3 # Should track minimum supported django16: Django==1.6.3 # Should track minimum supported
django17: Django==1.7.8 # Should track maximum supported django17: Django==1.7.10 # Should track maximum supported
django18: Django==1.8.2 # Should track maximum supported django18: Django==1.8.4 # Should track maximum supported
djangomaster: https://github.com/django/django/archive/master.tar.gz djangomaster: https://github.com/django/django/archive/master.tar.gz
-rrequirements/requirements-testing.txt -rrequirements/requirements-testing.txt
-rrequirements/requirements-optionals.txt -rrequirements/requirements-optionals.txt