mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 09:57:55 +03:00 
			
		
		
		
	Merge branch 'master' into filters
This commit is contained in:
		
						commit
						ea630bf3d1
					
				| 
						 | 
				
			
			@ -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:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 = [
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										4
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								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()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										4
									
								
								tox.ini
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user