[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