[Python-modules-commits] [django-filter] 01/05: Import django-filter_0.13.0.orig.tar.gz

Brian May bam at moszumanska.debian.org
Wed Apr 6 02:48:54 UTC 2016


This is an automated email from the git hooks/post-receive script.

bam pushed a commit to branch master
in repository django-filter.

commit 99b05f1fbdb81b1494e5cfa89576e493450b30b1
Author: Brian May <bam at debian.org>
Date:   Wed Apr 6 12:36:30 2016 +1000

    Import django-filter_0.13.0.orig.tar.gz
---
 CHANGES.rst                                       |  40 ++
 MANIFEST.in                                       |   1 +
 PKG-INFO                                          |  24 +-
 README.rst                                        |  14 +-
 django_filter.egg-info/PKG-INFO                   |  24 +-
 django_filter.egg-info/SOURCES.txt                |  57 +-
 django_filters/__init__.py                        |   2 +-
 django_filters/compat.py                          |  17 +
 django_filters/fields.py                          |  76 +-
 django_filters/filters.py                         | 182 ++++-
 django_filters/filterset.py                       | 245 ++++---
 django_filters/locale/es_AR/LC_MESSAGES/django.mo | Bin 0 -> 744 bytes
 django_filters/locale/es_AR/LC_MESSAGES/django.po |  47 ++
 django_filters/locale/pl/LC_MESSAGES/django.mo    | Bin 0 -> 1027 bytes
 django_filters/locale/pl/LC_MESSAGES/django.po    |  65 ++
 django_filters/locale/zh_CN/LC_MESSAGES/django.po |  64 ++
 django_filters/utils.py                           | 104 +++
 django_filters/widgets.py                         |  66 +-
 docs/_build/.DS_Store                             | Bin 6148 -> 0 bytes
 docs/_build/doctrees/environment.pickle           | Bin 22849 -> 0 bytes
 docs/_build/doctrees/index.doctree                | Bin 3564 -> 0 bytes
 docs/_build/doctrees/install.doctree              | Bin 3497 -> 0 bytes
 docs/_build/doctrees/ref/filters.doctree          | Bin 36171 -> 0 bytes
 docs/_build/doctrees/ref/widgets.doctree          | Bin 6552 -> 0 bytes
 docs/_build/doctrees/tests.doctree                | Bin 7673 -> 0 bytes
 docs/_build/doctrees/usage.doctree                | Bin 41428 -> 0 bytes
 docs/_build/html/.buildinfo                       |   4 -
 docs/_build/html/_sources/index.txt               |  19 -
 docs/_build/html/_sources/install.txt             |   5 -
 docs/_build/html/_sources/ref/filters.txt         | 201 ------
 docs/_build/html/_sources/ref/widgets.txt         |  19 -
 docs/_build/html/_sources/tests.txt               |  47 --
 docs/_build/html/_sources/usage.txt               | 280 --------
 docs/_build/html/_static/ajax-loader.gif          | Bin 673 -> 0 bytes
 docs/_build/html/_static/basic.css                | 537 --------------
 docs/_build/html/_static/comment-bright.png       | Bin 3500 -> 0 bytes
 docs/_build/html/_static/comment-close.png        | Bin 3578 -> 0 bytes
 docs/_build/html/_static/comment.png              | Bin 3445 -> 0 bytes
 docs/_build/html/_static/default.css              | 256 -------
 docs/_build/html/_static/doctools.js              | 238 -------
 docs/_build/html/_static/down-pressed.png         | Bin 368 -> 0 bytes
 docs/_build/html/_static/down.png                 | Bin 363 -> 0 bytes
 docs/_build/html/_static/file.png                 | Bin 392 -> 0 bytes
 docs/_build/html/_static/jquery.js                |   2 -
 docs/_build/html/_static/minus.png                | Bin 199 -> 0 bytes
 docs/_build/html/_static/plus.png                 | Bin 199 -> 0 bytes
 docs/_build/html/_static/pygments.css             |  63 --
 docs/_build/html/_static/searchtools.js           | 622 -----------------
 docs/_build/html/_static/sidebar.js               | 159 -----
 docs/_build/html/_static/underscore.js            |  31 -
 docs/_build/html/_static/up-pressed.png           | Bin 372 -> 0 bytes
 docs/_build/html/_static/up.png                   | Bin 363 -> 0 bytes
 docs/_build/html/_static/websupport.js            | 808 ----------------------
 docs/_build/html/genindex.html                    |  92 ---
 docs/_build/html/index.html                       | 114 ---
 docs/_build/html/install.html                     | 112 ---
 docs/_build/html/objects.inv                      | Bin 211 -> 0 bytes
 docs/_build/html/ref/filters.html                 | 319 ---------
 docs/_build/html/ref/widgets.html                 | 134 ----
 docs/_build/html/search.html                      |  99 ---
 docs/_build/html/searchindex.js                   |   1 -
 docs/_build/html/tests.html                       | 150 ----
 docs/_build/html/usage.html                       | 396 -----------
 docs/conf.py                                      |  10 +-
 docs/index.txt                                    |   2 +
 docs/install.txt                                  |   7 +-
 docs/ref/fields.txt                               |   2 +-
 docs/ref/filters.txt                              | 346 +++++++--
 docs/ref/filterset.txt                            | 164 +++++
 docs/ref/settings.txt                             |  24 +
 docs/ref/widgets.txt                              |  31 +-
 docs/usage.txt                                    | 262 +++----
 requirements/docs.txt                             |   1 -
 requirements/maintainer.txt                       |  31 +-
 requirements/test.txt                             |   2 +-
 requirements/travis-ci.txt                        |   2 +-
 runshell.py                                       |  23 +-
 runtests.py                                       |  30 +-
 setup.py                                          |  18 +-
 tests/models.py                                   |  21 +-
 tests/settings.py                                 |  29 +
 tests/test_fields.py                              | 138 +++-
 tests/test_filtering.py                           | 306 +++++++-
 tests/test_filters.py                             | 303 ++++++--
 tests/test_filterset.py                           | 146 +++-
 tests/test_forms.py                               |   5 +-
 tests/test_utils.py                               | 166 +++++
 tests/test_views.py                               |   5 +-
 tests/test_widgets.py                             |  83 +++
 tests/urls.py                                     |  13 +-
 90 files changed, 2564 insertions(+), 5342 deletions(-)

diff --git a/CHANGES.rst b/CHANGES.rst
index 939edce..ed3eb04 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,43 @@
+Version 0.13.0 (2016-03-11)
+---------------------------
+
+* Add support for filtering by CSV #363
+
+* Add DateTimeFromToRangeFilter #376
+
+* Add Chinese translation #359
+
+* Lots of fixes.
+
+
+Version 0.12.0 (2016-01-07)
+---------------------------
+
+* Raised minimum Django version to 1.8.x
+
+* FEATURE: Add support for custom ORM lookup types #221
+
+* FEATURE: Add JavaScript friendly BooleanWidget #270
+
+* FIXED: (More) Compatability with Django 1.8 and Django 1.9+
+
+* BREAKING CHANGE: custom filter names are now also be used for ordering #230
+
+    If you use ordering on a field you defined as custom filter with custom
+    name, you should now use the filter name as ordering key as well.
+
+    Eg. For a filter like :
+
+        class F(FilterSet):
+            account = CharFilter(name='username')
+            class Meta:
+                model = User
+                fields = ['account', 'status']
+                order_by = True
+
+     Before, ordering was like `?o=username`. Since 0.12.0 it's `o=account`.
+
+
 Version 0.11.0 (2015-08-14)
 ---------------------------
 
diff --git a/MANIFEST.in b/MANIFEST.in
index bdd47ea..13d96d3 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -8,3 +8,4 @@ recursive-include docs *
 recursive-include requirements *
 recursive-include tests *
 recursive-include django_filters/locale *
+prune docs/_build
\ No newline at end of file
diff --git a/PKG-INFO b/PKG-INFO
index bec55d7..d3d1c10 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,10 +1,10 @@
 Metadata-Version: 1.1
 Name: django-filter
-Version: 0.11.0
+Version: 0.13.0
 Summary: Django-filter is a reusable Django application for allowing users to filter querysets dynamically.
-Home-page: http://github.com/alex/django-filter/tree/master
-Author: Alex Gaynor
-Author-email: alex.gaynor at gmail.com
+Home-page: http://github.com/carltongibson/django-filter/tree/master
+Author: Carlton Gibson
+Author-email: carlton.gibson at noumenal.es
 License: BSD
 Description: Django Filter
         =============
@@ -14,14 +14,14 @@ Description: Django Filter
         
         Full documentation on `read the docs`_.
         
-        .. image:: https://secure.travis-ci.org/alex/django-filter.png?branch=master
-           :target: http://travis-ci.org/alex/django-filter
+        .. image:: https://travis-ci.org/carltongibson/django-filter.svg?branch=master
+            :target: https://travis-ci.org/carltongibson/django-filter
         
         Requirements
         ------------
         
-        * Python 2.6+
-        * Django 1.4.5+
+        * Python 2.7, 3.2, 3.3, 3.4, 3.5
+        * Django 1.8, 1.9
         
         Installation
         ------------
@@ -32,7 +32,7 @@ Description: Django Filter
         
         Or clone the repo and add to your PYTHONPATH::
         
-            git clone git at github.com:alex/django-filter.git
+            git clone git at github.com:carltongibson/django-filter.git
         
         Usage
         -----
@@ -54,11 +54,11 @@ Description: Django Filter
         
             def product_list(request):
                 filter = ProductFilter(request.GET, queryset=Product.objects.all())
-                return render_to_response('my_app/template.html', {'filter': filter})
+                return render(request, 'my_app/template.html', {'filter': filter})
         
         Django-filters additionally supports specifying FilterSet fields using a
         dictionary to specify filters with lookup types::
-         
+        
             import django_filters
         
             class ProductFilter(django_filters.FilterSet):
@@ -87,10 +87,10 @@ Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3.2
 Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
 Classifier: Framework :: Django
diff --git a/README.rst b/README.rst
index fbea229..19ec278 100644
--- a/README.rst
+++ b/README.rst
@@ -6,14 +6,14 @@ querysets dynamically.
 
 Full documentation on `read the docs`_.
 
-.. image:: https://secure.travis-ci.org/alex/django-filter.png?branch=master
-   :target: http://travis-ci.org/alex/django-filter
+.. image:: https://travis-ci.org/carltongibson/django-filter.svg?branch=master
+    :target: https://travis-ci.org/carltongibson/django-filter
 
 Requirements
 ------------
 
-* Python 2.6+
-* Django 1.4.5+
+* Python 2.7, 3.2, 3.3, 3.4, 3.5
+* Django 1.8, 1.9
 
 Installation
 ------------
@@ -24,7 +24,7 @@ Install using pip::
 
 Or clone the repo and add to your PYTHONPATH::
 
-    git clone git at github.com:alex/django-filter.git
+    git clone git at github.com:carltongibson/django-filter.git
 
 Usage
 -----
@@ -46,11 +46,11 @@ And then in your view you could do::
 
     def product_list(request):
         filter = ProductFilter(request.GET, queryset=Product.objects.all())
-        return render_to_response('my_app/template.html', {'filter': filter})
+        return render(request, 'my_app/template.html', {'filter': filter})
 
 Django-filters additionally supports specifying FilterSet fields using a
 dictionary to specify filters with lookup types::
- 
+
     import django_filters
 
     class ProductFilter(django_filters.FilterSet):
diff --git a/django_filter.egg-info/PKG-INFO b/django_filter.egg-info/PKG-INFO
index bec55d7..d3d1c10 100644
--- a/django_filter.egg-info/PKG-INFO
+++ b/django_filter.egg-info/PKG-INFO
@@ -1,10 +1,10 @@
 Metadata-Version: 1.1
 Name: django-filter
-Version: 0.11.0
+Version: 0.13.0
 Summary: Django-filter is a reusable Django application for allowing users to filter querysets dynamically.
-Home-page: http://github.com/alex/django-filter/tree/master
-Author: Alex Gaynor
-Author-email: alex.gaynor at gmail.com
+Home-page: http://github.com/carltongibson/django-filter/tree/master
+Author: Carlton Gibson
+Author-email: carlton.gibson at noumenal.es
 License: BSD
 Description: Django Filter
         =============
@@ -14,14 +14,14 @@ Description: Django Filter
         
         Full documentation on `read the docs`_.
         
-        .. image:: https://secure.travis-ci.org/alex/django-filter.png?branch=master
-           :target: http://travis-ci.org/alex/django-filter
+        .. image:: https://travis-ci.org/carltongibson/django-filter.svg?branch=master
+            :target: https://travis-ci.org/carltongibson/django-filter
         
         Requirements
         ------------
         
-        * Python 2.6+
-        * Django 1.4.5+
+        * Python 2.7, 3.2, 3.3, 3.4, 3.5
+        * Django 1.8, 1.9
         
         Installation
         ------------
@@ -32,7 +32,7 @@ Description: Django Filter
         
         Or clone the repo and add to your PYTHONPATH::
         
-            git clone git at github.com:alex/django-filter.git
+            git clone git at github.com:carltongibson/django-filter.git
         
         Usage
         -----
@@ -54,11 +54,11 @@ Description: Django Filter
         
             def product_list(request):
                 filter = ProductFilter(request.GET, queryset=Product.objects.all())
-                return render_to_response('my_app/template.html', {'filter': filter})
+                return render(request, 'my_app/template.html', {'filter': filter})
         
         Django-filters additionally supports specifying FilterSet fields using a
         dictionary to specify filters with lookup types::
-         
+        
             import django_filters
         
             class ProductFilter(django_filters.FilterSet):
@@ -87,10 +87,10 @@ Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3.2
 Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
 Classifier: Framework :: Django
diff --git a/django_filter.egg-info/SOURCES.txt b/django_filter.egg-info/SOURCES.txt
index 0aa0b32..034c49c 100644
--- a/django_filter.egg-info/SOURCES.txt
+++ b/django_filter.egg-info/SOURCES.txt
@@ -13,16 +13,23 @@ django_filter.egg-info/dependency_links.txt
 django_filter.egg-info/not-zip-safe
 django_filter.egg-info/top_level.txt
 django_filters/__init__.py
+django_filters/compat.py
 django_filters/fields.py
 django_filters/filters.py
 django_filters/filterset.py
 django_filters/models.py
+django_filters/utils.py
 django_filters/views.py
 django_filters/widgets.py
 django_filters/locale/de/LC_MESSAGES/django.mo
 django_filters/locale/de/LC_MESSAGES/django.po
+django_filters/locale/es_AR/LC_MESSAGES/django.mo
+django_filters/locale/es_AR/LC_MESSAGES/django.po
 django_filters/locale/fr/LC_MESSAGES/django.mo
 django_filters/locale/fr/LC_MESSAGES/django.po
+django_filters/locale/pl/LC_MESSAGES/django.mo
+django_filters/locale/pl/LC_MESSAGES/django.po
+django_filters/locale/zh_CN/LC_MESSAGES/django.po
 docs/.DS_Store
 docs/Makefile
 docs/conf.py
@@ -31,55 +38,11 @@ docs/install.txt
 docs/make.bat
 docs/tests.txt
 docs/usage.txt
-docs/_build/.DS_Store
-docs/_build/doctrees/environment.pickle
-docs/_build/doctrees/index.doctree
-docs/_build/doctrees/install.doctree
-docs/_build/doctrees/tests.doctree
-docs/_build/doctrees/usage.doctree
-docs/_build/doctrees/ref/filters.doctree
-docs/_build/doctrees/ref/widgets.doctree
-docs/_build/html/.buildinfo
-docs/_build/html/genindex.html
-docs/_build/html/index.html
-docs/_build/html/install.html
-docs/_build/html/objects.inv
-docs/_build/html/search.html
-docs/_build/html/searchindex.js
-docs/_build/html/tests.html
-docs/_build/html/usage.html
-docs/_build/html/_sources/index.txt
-docs/_build/html/_sources/install.txt
-docs/_build/html/_sources/tests.txt
-docs/_build/html/_sources/usage.txt
-docs/_build/html/_sources/ref/filters.txt
-docs/_build/html/_sources/ref/widgets.txt
-docs/_build/html/_static/ajax-loader.gif
-docs/_build/html/_static/basic.css
-docs/_build/html/_static/comment-bright.png
-docs/_build/html/_static/comment-close.png
-docs/_build/html/_static/comment.png
-docs/_build/html/_static/default.css
-docs/_build/html/_static/doctools.js
-docs/_build/html/_static/down-pressed.png
-docs/_build/html/_static/down.png
-docs/_build/html/_static/file.png
-docs/_build/html/_static/jquery.js
-docs/_build/html/_static/minus.png
-docs/_build/html/_static/plus.png
-docs/_build/html/_static/pygments.css
-docs/_build/html/_static/searchtools.js
-docs/_build/html/_static/sidebar.js
-docs/_build/html/_static/underscore.js
-docs/_build/html/_static/up-pressed.png
-docs/_build/html/_static/up.png
-docs/_build/html/_static/websupport.js
-docs/_build/html/ref/filters.html
-docs/_build/html/ref/widgets.html
 docs/ref/fields.txt
 docs/ref/filters.txt
+docs/ref/filterset.txt
+docs/ref/settings.txt
 docs/ref/widgets.txt
-requirements/docs.txt
 requirements/maintainer.txt
 requirements/test.txt
 requirements/travis-ci.txt
@@ -87,6 +50,7 @@ tests/__init__.py
 tests/__init__.pyc
 tests/models.py
 tests/models.pyc
+tests/settings.py
 tests/test_fields.py
 tests/test_fields.pyc
 tests/test_filtering.py
@@ -97,6 +61,7 @@ tests/test_filterset.py
 tests/test_filterset.pyc
 tests/test_forms.py
 tests/test_forms.pyc
+tests/test_utils.py
 tests/test_views.py
 tests/test_views.pyc
 tests/test_widgets.py
diff --git a/django_filters/__init__.py b/django_filters/__init__.py
index 7048b0f..eeb0868 100644
--- a/django_filters/__init__.py
+++ b/django_filters/__init__.py
@@ -3,7 +3,7 @@ from __future__ import absolute_import
 from .filterset import FilterSet
 from .filters import *
 
-__version__ = '0.11.0'
+__version__ = '0.13.0'
 
 
 def parse_version(version):
diff --git a/django_filters/compat.py b/django_filters/compat.py
new file mode 100644
index 0000000..41e76ec
--- /dev/null
+++ b/django_filters/compat.py
@@ -0,0 +1,17 @@
+
+import django
+
+
+def remote_field(field):
+    """
+    https://docs.djangoproject.com/en/1.9/releases/1.9/#field-rel-changes
+    """
+    if django.VERSION >= (1, 9):
+        return field.remote_field
+    return field.rel
+
+
+def remote_model(field):
+    if django.VERSION >= (1, 9):
+        return remote_field(field).model
+    return remote_field(field).to
diff --git a/django_filters/fields.py b/django_filters/fields.py
index a25c0f2..62e28e8 100644
--- a/django_filters/fields.py
+++ b/django_filters/fields.py
@@ -7,13 +7,11 @@ from collections import namedtuple
 from django import forms
 from django.utils.dateparse import parse_datetime
 
-# TODO: Remove this once Django 1.4 is EOL.
-try:
-    from django.utils.encoding import force_str
-except ImportError:
-    force_str = None
+from django.utils.encoding import force_str
+from django.utils.translation import ugettext_lazy as _
 
-from .widgets import RangeWidget, LookupTypeWidget
+from .utils import handle_timezone
+from .widgets import RangeWidget, LookupTypeWidget, CSVWidget
 
 
 class RangeField(forms.MultiValueField):
@@ -44,13 +42,24 @@ class DateRangeField(RangeField):
         if data_list:
             start_date, stop_date = data_list
             if start_date:
-                start_date = datetime.combine(start_date, time.min)
+                start_date = handle_timezone(
+                    datetime.combine(start_date, time.min))
             if stop_date:
-                stop_date = datetime.combine(stop_date, time.max)
+                stop_date = handle_timezone(
+                    datetime.combine(stop_date, time.max))
             return slice(start_date, stop_date)
         return None
 
 
+class DateTimeRangeField(RangeField):
+
+    def __init__(self, *args, **kwargs):
+        fields = (
+            forms.DateTimeField(),
+            forms.DateTimeField())
+        super(DateTimeRangeField, self).__init__(fields, *args, **kwargs)
+
+
 class TimeRangeField(RangeField):
 
     def __init__(self, *args, **kwargs):
@@ -60,7 +69,14 @@ class TimeRangeField(RangeField):
         super(TimeRangeField, self).__init__(fields, *args, **kwargs)
 
 
-Lookup = namedtuple('Lookup', ('value', 'lookup_type'))
+class Lookup(namedtuple('Lookup', ('value', 'lookup_type'))):
+    # python nature is test __len__ on tuple types for boolean check
+    def __len__(self):
+        if not self.value:
+            return 0
+        return 2
+
+
 class LookupTypeField(forms.MultiValueField):
     def __init__(self, field, lookup_choices, *args, **kwargs):
         fields = (
@@ -75,7 +91,7 @@ class LookupTypeField(forms.MultiValueField):
         super(LookupTypeField, self).__init__(fields, *args, **kwargs)
 
     def compress(self, data_list):
-        if len(data_list)==2:
+        if len(data_list) == 2:
             return Lookup(value=data_list[0], lookup_type=data_list[1] or 'exact')
         return Lookup(value=None, lookup_type='exact')
 
@@ -93,13 +109,45 @@ class IsoDateTimeField(forms.DateTimeField):
     input_formats = [ISO_8601]
 
     def strptime(self, value, format):
-        # TODO: Remove this once Django 1.4 is EOL.
-        if force_str is not None:
-            value = force_str(value)
+        value = force_str(value)
 
         if format == self.ISO_8601:
             parsed = parse_datetime(value)
             if parsed is None:  # Continue with other formats if doesn't match
                 raise ValueError
-            return parsed
+            return handle_timezone(parsed)
         return super(IsoDateTimeField, self).strptime(value, format)
+
+
+class BaseCSVField(forms.Field):
+    """
+    Base field for validating CSV types. Value validation is performed by
+    secondary base classes.
+
+    ex::
+        class IntegerCSVField(BaseCSVField, filters.IntegerField):
+            pass
+
+    """
+    widget = CSVWidget
+
+    def clean(self, value):
+        if value is None:
+            return None
+        return [super(BaseCSVField, self).clean(v) for v in value]
+
+
+class BaseRangeField(BaseCSVField):
+    default_error_messages = {
+        'invalid_values': _('Range query expects two values.')
+    }
+
+    def clean(self, value):
+        value = super(BaseRangeField, self).clean(value)
+
+        if value is not None and len(value) != 2:
+            raise forms.ValidationError(
+                self.error_messages['invalid_values'],
+                code='invalid_values')
+
+        return value
diff --git a/django_filters/filters.py b/django_filters/filters.py
index 9295fe5..cb08c47 100644
--- a/django_filters/filters.py
+++ b/django_filters/filters.py
@@ -1,18 +1,22 @@
 from __future__ import absolute_import
 from __future__ import unicode_literals
 
+import warnings
 from datetime import timedelta
 
-
 from django import forms
 from django.db.models import Q
 from django.db.models.sql.constants import QUERY_TERMS
+from django.db.models.constants import LOOKUP_SEP
+from django.conf import settings
 from django.utils import six
 from django.utils.timezone import now
 from django.utils.translation import ugettext_lazy as _
 
 from .fields import (
-    RangeField, LookupTypeField, Lookup, DateRangeField, TimeRangeField, IsoDateTimeField)
+    Lookup, LookupTypeField, BaseCSVField, BaseRangeField, RangeField,
+    DateRangeField, DateTimeRangeField, TimeRangeField, IsoDateTimeField
+)
 
 
 __all__ = [
@@ -20,25 +24,34 @@ __all__ = [
     'TypedChoiceFilter', 'MultipleChoiceFilter', 'DateFilter',
     'DateTimeFilter', 'IsoDateTimeFilter', 'TimeFilter', 'ModelChoiceFilter',
     'ModelMultipleChoiceFilter', 'NumberFilter', 'NumericRangeFilter', 'RangeFilter',
-    'DateRangeFilter', 'DateFromToRangeFilter', 'TimeRangeFilter',
-    'AllValuesFilter', 'MethodFilter'
+    'DateRangeFilter', 'DateFromToRangeFilter', 'DateTimeFromToRangeFilter',
+    'TimeRangeFilter', 'AllValuesFilter', 'MethodFilter'
 ]
 
 
 LOOKUP_TYPES = sorted(QUERY_TERMS)
 
 
+def _lookup_type_warning():
+    warnings.warn('lookup_type is deprecated. Use lookup_expr instead.', DeprecationWarning, stacklevel=3)
+
+
 class Filter(object):
     creation_counter = 0
     field_class = forms.Field
 
     def __init__(self, name=None, label=None, widget=None, action=None,
-        lookup_type='exact', required=False, distinct=False, exclude=False, **kwargs):
+                 lookup_expr='exact', required=False, distinct=False, exclude=False, **kwargs):
         self.name = name
         self.label = label
         if action:
             self.filter = action
-        self.lookup_type = lookup_type
+
+        self.lookup_expr = lookup_expr
+        if 'lookup_type' in kwargs:
+            _lookup_type_warning()
+            self.lookup_expr = kwargs.pop('lookup_type')
+
         self.widget = widget
         self.required = required
         self.extra = kwargs
@@ -54,26 +67,58 @@ class Filter(object):
         """
         return qs.exclude if self.exclude else qs.filter
 
+    def lookup_type():
+        def fget(self):
+            _lookup_type_warning()
+            return self.lookup_expr
+
+        def fset(self, value):
+            _lookup_type_warning()
+            self.lookup_expr = value
+
+        return locals()
+    lookup_type = property(**lookup_type())
+
     @property
     def field(self):
         if not hasattr(self, '_field'):
             help_text = self.extra.pop('help_text', None)
             if help_text is None:
-                help_text = _('This is an exclusion filter') if self.exclude else _('Filter')
-            if (self.lookup_type is None or
-                    isinstance(self.lookup_type, (list, tuple))):
-                if self.lookup_type is None:
-                    lookup = [(x, x) for x in LOOKUP_TYPES]
+                if self.exclude and getattr(settings, "FILTERS_HELP_TEXT_EXCLUDE", True):
+                    help_text = _('This is an exclusion filter')
+                elif not self.exclude and getattr(settings, "FILTERS_HELP_TEXT_FILTER", True):
+                    help_text = _('Filter')
                 else:
-                    lookup = [
-                        (x, x) for x in LOOKUP_TYPES if x in self.lookup_type]
+                    help_text = ''
+
+            if (self.lookup_expr is None or
+                    isinstance(self.lookup_expr, (list, tuple))):
+
+                lookup = []
+
+                for x in LOOKUP_TYPES:
+                    if isinstance(x, (list, tuple)) and len(x) == 2:
+                        choice = (x[0], x[1])
+                    else:
+                        choice = (x, x)
+
+                    if self.lookup_expr is None:
+                        lookup.append(choice)
+                    else:
+                        if isinstance(x, (list, tuple)) and len(x) == 2:
+                            if x[0] in self.lookup_expr:
+                                lookup.append(choice)
+                        else:
+                            if x in self.lookup_expr:
+                                lookup.append(choice)
+
                 self._field = LookupTypeField(self.field_class(
                     required=self.required, widget=self.widget, **self.extra),
                     lookup, required=self.required, label=self.label, help_text=help_text)
             else:
                 self._field = self.field_class(required=self.required,
-                    label=self.label, widget=self.widget,
-                    help_text=help_text, **self.extra)
+                                               label=self.label, widget=self.widget,
+                                               help_text=help_text, **self.extra)
         return self._field
 
     def filter(self, qs, value):
@@ -81,12 +126,12 @@ class Filter(object):
             lookup = six.text_type(value.lookup_type)
             value = value.value
         else:
-            lookup = self.lookup_type
+            lookup = self.lookup_expr
         if value in ([], (), {}, None, ''):
             return qs
-        qs = self.get_method(qs)(**{'%s__%s' % (self.name, lookup): value})
         if self.distinct:
             qs = qs.distinct()
+        qs = self.get_method(qs)(**{'%s__%s' % (self.name, lookup): value})
         return qs
 
 
@@ -97,11 +142,6 @@ class CharFilter(Filter):
 class BooleanFilter(Filter):
     field_class = forms.NullBooleanField
 
-    def filter(self, qs, value):
-        if value is not None:
-            return self.get_method(qs)(**{self.name: value})
-        return qs
-
 
 class ChoiceFilter(Filter):
     field_class = forms.ChoiceField
@@ -111,6 +151,10 @@ class TypedChoiceFilter(Filter):
     field_class = forms.TypedChoiceField
 
 
+class UUIDFilter(Filter):
+    field_class = forms.UUIDField
+
+
 class MultipleChoiceFilter(Filter):
     """
     This filter preforms OR(by default) or AND(using conjoined=True) query
@@ -156,7 +200,7 @@ class MultipleChoiceFilter(Filter):
         return False
 
     def filter(self, qs, value):
-        value = value or () # Make sure we have an iterable
+        value = value or ()  # Make sure we have an iterable
 
         if self.is_noop(qs, value):
             return qs
@@ -185,6 +229,7 @@ class DateFilter(Filter):
 class DateTimeFilter(Filter):
     field_class = forms.DateTimeField
 
+
 class IsoDateTimeFilter(DateTimeFilter):
     """
     Uses IsoDateTimeField to support filtering on ISO 8601 formated datetimes.
@@ -197,6 +242,7 @@ class IsoDateTimeFilter(DateTimeFilter):
     """
     field_class = IsoDateTimeField
 
+
 class TimeFilter(Filter):
     field_class = forms.TimeField
 
@@ -219,7 +265,7 @@ class NumericRangeFilter(Filter):
     def filter(self, qs, value):
         if value:
             if value.start is not None and value.stop is not None:
-                lookup = '%s__%s' % (self.name, self.lookup_type)
+                lookup = '%s__%s' % (self.name, self.lookup_expr)
                 return self.get_method(qs)(**{lookup: (value.start, value.stop)})
             else:
                 if value.start is not None:
@@ -234,19 +280,19 @@ class RangeFilter(Filter):
 
     def filter(self, qs, value):
         if value:
-          if value.start is not None and value.stop is not None:
-            lookup = '%s__range' % self.name
-            return self.get_method(qs)(**{lookup: (value.start, value.stop)})
-          else:
-
-            if value.start is not None:
-              qs = self.get_method(qs)(**{'%s__gte'%self.name:value.start})
-            if value.stop is not None:
-              qs = self.get_method(qs)(**{'%s__lte'%self.name:value.stop})
+            if value.start is not None and value.stop is not None:
+                lookup = '%s__range' % self.name
+                return self.get_method(qs)(**{lookup: (value.start, value.stop)})
+            else:
+                if value.start is not None:
+                    qs = self.get_method(qs)(**{'%s__gte' % self.name: value.start})
+                if value.stop is not None:
+                    qs = self.get_method(qs)(**{'%s__lte' % self.name: value.stop})
         return qs
 
 
-_truncate = lambda dt: dt.replace(hour=0, minute=0, second=0)
+def _truncate(dt):
+    return dt.replace(hour=0, minute=0, second=0)
 
 
 class DateRangeFilter(ChoiceFilter):
@@ -285,13 +331,20 @@ class DateRangeFilter(ChoiceFilter):
             value = int(value)
         except (ValueError, TypeError):
             value = ''
-        return self.options[value][1](qs, self.name)
+        qs = self.options[value][1](qs, self.name)
+        if self.distinct:
+            qs = qs.distinct()
+        return qs
 
 
 class DateFromToRangeFilter(RangeFilter):
     field_class = DateRangeField
 
 
+class DateTimeFromToRangeFilter(RangeFilter):
+    field_class = DateTimeRangeField
+
+
 class TimeRangeFilter(RangeFilter):
     field_class = TimeRangeField
 
@@ -305,6 +358,65 @@ class AllValuesFilter(ChoiceFilter):
         return super(AllValuesFilter, self).field
 
 
+class BaseCSVFilter(Filter):
+    """
+    Base class for CSV type filters, such as IN and RANGE.
+    """
+    base_field_class = BaseCSVField
+
+    def __init__(self, *args, **kwargs):
+        super(BaseCSVFilter, self).__init__(*args, **kwargs)
+
+        class ConcreteCSVField(self.base_field_class, self.field_class):
+            pass
+        ConcreteCSVField.__name__ = self._field_class_name(
+            self.field_class, self.lookup_expr
+        )
+
+        self.field_class = ConcreteCSVField
+
+    @classmethod
+    def _field_class_name(cls, field_class, lookup_expr):
+        """
+        Generate a suitable class name for the concrete field class. This is not
+        completely reliable, as not all field class names are of the format
+        <Type>Field.
+
+        ex::
+
+            BaseCSVFilter._field_class_name(DateTimeField, 'year__in')
+
+            returns 'DateTimeYearInField'
+
+        """
+        # DateTimeField => DateTime
+        type_name = field_class.__name__
+        if type_name.endswith('Field'):
+            type_name = type_name[:-5]
+
+        # year__in => YearIn
+        parts = lookup_expr.split(LOOKUP_SEP)
+        expression_name = ''.join(p.capitalize() for p in parts)
+
+        # DateTimeYearInField
+        return str('%s%sField' % (type_name, expression_name))
+
+
+class BaseInFilter(BaseCSVFilter):
+
+    def __init__(self, *args, **kwargs):
+        kwargs.setdefault('lookup_expr', 'in')
+        super(BaseInFilter, self).__init__(*args, **kwargs)
+
+
+class BaseRangeFilter(BaseCSVFilter):
+    base_field_class = BaseRangeField
+
+    def __init__(self, *args, **kwargs):
+        kwargs.setdefault('lookup_expr', 'range')
+        super(BaseRangeFilter, self).__init__(*args, **kwargs)
+
+
 class MethodFilter(Filter):
     """
     This filter will allow you to run a method that exists on the filterset class
diff --git a/django_filters/filterset.py b/django_filters/filterset.py
index 7904148..5178dd0 100644
--- a/django_filters/filterset.py
+++ b/django_filters/filterset.py
@@ -1,54 +1,30 @@
 from __future__ import absolute_import
 from __future__ import unicode_literals
 
-import types
 import copy
+import re
+from collections import OrderedDict
 
 from django import forms
 from django.forms.forms import NON_FIELD_ERRORS
 from django.core.validators import EMPTY_VALUES
 from django.db import models
-from django.db.models.fields import FieldDoesNotExist
+from django.db.models.constants import LOOKUP_SEP
+from django.db.models.fields.related import ForeignObjectRel
 from django.utils import six
 from django.utils.text import capfirst
 from django.utils.translation import ugettext as _
-from sys import version_info
 
-try:
-    from django.db.models.constants import LOOKUP_SEP
-except ImportError:  # pragma: nocover
-    # Django < 1.5 fallback
-    from django.db.models.sql.constants import LOOKUP_SEP  # noqa
-
-try:
-    from collections import OrderedDict
-except ImportError:  # pragma: nocover
-    # Django < 1.5 fallback
-    from django.utils.datastructures import SortedDict as OrderedDict  # noqa
-
-try:
-    from django.db.models.related import RelatedObject as ForeignObjectRel
-except ImportError:  # pragma: nocover
-    # Django >= 1.8 replaces RelatedObject with ForeignObjectRel
-    from django.db.models.fields.related import ForeignObjectRel
-
-
-from .filters import (Filter, CharFilter, BooleanFilter,
-    ChoiceFilter, DateFilter, DateTimeFilter, TimeFilter, ModelChoiceFilter,
-    ModelMultipleChoiceFilter, NumberFilter)
+from .compat import remote_field, remote_model
+from .filters import (Filter, CharFilter, BooleanFilter, BaseInFilter, BaseRangeFilter,
+                      ChoiceFilter, DateFilter, DateTimeFilter, TimeFilter, ModelChoiceFilter,
+                      ModelMultipleChoiceFilter, NumberFilter, UUIDFilter)
+from .utils import try_dbfield, get_model_field, resolve_field
 
 
 ORDER_BY_FIELD = 'o'
 
 
-# There is a bug with deepcopy in 2.6, patch if we are running python < 2.7
-# http://bugs.python.org/issue1515
-if version_info < (2, 7, 0):
-    def _deepcopy_method(x, memo):
-        return type(x)(x.im_func, copy.deepcopy(x.im_self, memo), x.im_class)
-    copy._deepcopy_dispatch[types.MethodType] = _deepcopy_method
-
... 8832 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-filter.git



More information about the Python-modules-commits mailing list