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.
* '=' Exact matches.
* '@' Full-text search. (Currently only supported Django's MySQL backend.)
* '$' Regex search.
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.
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

View File

@ -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
<!-- 3.0.1 -->
[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
[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'
__version__ = '3.2.2'
__version__ = '3.2.3'
__author__ = 'Tom Christie'
__license__ = 'BSD 2-Clause'
__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
# 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)

View File

@ -127,21 +127,24 @@ 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
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 = [

View File

@ -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()

View File

@ -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)

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

View File

@ -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