Hello community,
here is the log from the commit of package python-django-filter for openSUSE:Factory checked in at 2019-03-01 16:47:09
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-django-filter (Old)
and /work/SRC/openSUSE:Factory/.python-django-filter.new.28833 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-django-filter"
Fri Mar 1 16:47:09 2019 rev:2 rq:679822 version:2.1.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-django-filter/python-django-filter.changes 2018-11-27 10:42:15.672205595 +0100
+++ /work/SRC/openSUSE:Factory/.python-django-filter.new.28833/python-django-filter.changes 2019-03-01 16:47:09.801805044 +0100
@@ -1,0 +2,8 @@
+Wed Feb 27 13:42:24 UTC 2019 - Tomáš Chvátal
+
+- Update to 2.1.0:
+ * Fixed a regression in FilterView introduced in 2.0. An empty QuerySet was incorrectly used whenever the FilterSet was unbound (i.e. when there were no GET parameters). The correct, pre-2.0 behaviour is now restored.
+ * A workaround was to set strict=False on the FilterSet. This is no longer necessary, so you may restore strict behaviour as desired.
+ * Added IsoDateTimeFromToRangeFilter. Allows From-To filtering using ISO-8601 formatted dates.
+
+-------------------------------------------------------------------
Old:
----
django-filter-2.0.0.tar.gz
New:
----
django-filter-2.1.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-django-filter.spec ++++++
--- /var/tmp/diff_new_pack.vQiEC3/_old 2019-03-01 16:47:10.321804846 +0100
+++ /var/tmp/diff_new_pack.vQiEC3/_new 2019-03-01 16:47:10.321804846 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-django-filter
#
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-django-filter
-Version: 2.0.0
+Version: 2.1.0
Release: 0
Summary: Reusable Django app to allow users to filter queryset dynamically
License: BSD-3-Clause
@@ -31,7 +31,8 @@
BuildRequires: %{python_module djangorestframework}
BuildRequires: %{python_module mock}
BuildRequires: %{python_module setuptools}
-BuildRequires: python-rpm-macros fdupes
+BuildRequires: fdupes
+BuildRequires: python-rpm-macros
Requires: python-Django
Recommends: python-django-crispy-forms
BuildArch: noarch
++++++ django-filter-2.0.0.tar.gz -> django-filter-2.1.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/CHANGES.rst new/django-filter-2.1.0/CHANGES.rst
--- old/django-filter-2.0.0/CHANGES.rst 2018-07-13 12:07:38.000000000 +0200
+++ new/django-filter-2.1.0/CHANGES.rst 2019-01-20 20:39:10.000000000 +0100
@@ -1,3 +1,17 @@
+Version 2.1 (2019-1-20)
+-----------------------
+
+* Fixed a regression in ``FilterView`` introduced in 2.0. An empty ``QuerySet`` was
+ incorrectly used whenever the FilterSet was unbound (i.e. when there were
+ no GET parameters). The correct, pre-2.0 behaviour is now restored.
+
+ A workaround was to set ``strict=False`` on the ``FilterSet``. This is no
+ longer necessary, so you may restore `strict` behaviour as desired.
+
+* Added ``IsoDateTimeFromToRangeFilter``. Allows From-To filtering using
+ ISO-8601 formatted dates.
+
+
Version 2.0 (2018-7-13)
-----------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/PKG-INFO new/django-filter-2.1.0/PKG-INFO
--- old/django-filter-2.0.0/PKG-INFO 2018-07-13 12:14:01.000000000 +0200
+++ new/django-filter-2.1.0/PKG-INFO 2019-01-20 20:59:24.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: django-filter
-Version: 2.0.0
+Version: 2.1.0
Summary: Django-filter is a reusable Django application for allowing users to filter querysets dynamically.
Home-page: https://github.com/carltongibson/django-filter/tree/master
Author: Alex Gaynor
@@ -16,6 +16,9 @@
Full documentation on `read the docs`_.
+ .. image:: https://dev.azure.com/noumenal/Django%20Filter/_apis/build/status/Django%20F...
+ :target: https://dev.azure.com/noumenal/Django%20Filter/_build/latest?definitionId=3
+
.. image:: https://travis-ci.org/carltongibson/django-filter.svg?branch=master
:target: https://travis-ci.org/carltongibson/django-filter
@@ -25,12 +28,11 @@
.. image:: https://badge.fury.io/py/django-filter.svg
:target: http://badge.fury.io/py/django-filter
-
Requirements
------------
* **Python**: 3.4, 3.5, 3.6, 3.7
- * **Django**: 1.11, 2.0
+ * **Django**: 1.11, 2.0, 2.1, 2.2
* **DRF**: 3.8+
From Version 2.0 Django Filter is Python 3 only.
@@ -124,6 +126,7 @@
Classifier: Framework :: Django
Classifier: Framework :: Django :: 1.11
Classifier: Framework :: Django :: 2.0
+Classifier: Framework :: Django :: 2.1
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
@@ -131,3 +134,4 @@
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Framework :: Django
+Requires-Python: >=3.4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/README.rst new/django-filter-2.1.0/README.rst
--- old/django-filter-2.0.0/README.rst 2018-07-13 11:46:28.000000000 +0200
+++ new/django-filter-2.1.0/README.rst 2019-01-20 16:04:33.000000000 +0100
@@ -6,6 +6,9 @@
Full documentation on `read the docs`_.
+.. image:: https://dev.azure.com/noumenal/Django%20Filter/_apis/build/status/Django%20F...
+ :target: https://dev.azure.com/noumenal/Django%20Filter/_build/latest?definitionId=3
+
.. image:: https://travis-ci.org/carltongibson/django-filter.svg?branch=master
:target: https://travis-ci.org/carltongibson/django-filter
@@ -15,12 +18,11 @@
.. image:: https://badge.fury.io/py/django-filter.svg
:target: http://badge.fury.io/py/django-filter
-
Requirements
------------
* **Python**: 3.4, 3.5, 3.6, 3.7
-* **Django**: 1.11, 2.0
+* **Django**: 1.11, 2.0, 2.1, 2.2
* **DRF**: 3.8+
From Version 2.0 Django Filter is Python 3 only.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/django_filter.egg-info/PKG-INFO new/django-filter-2.1.0/django_filter.egg-info/PKG-INFO
--- old/django-filter-2.0.0/django_filter.egg-info/PKG-INFO 2018-07-13 12:14:01.000000000 +0200
+++ new/django-filter-2.1.0/django_filter.egg-info/PKG-INFO 2019-01-20 20:59:24.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: django-filter
-Version: 2.0.0
+Version: 2.1.0
Summary: Django-filter is a reusable Django application for allowing users to filter querysets dynamically.
Home-page: https://github.com/carltongibson/django-filter/tree/master
Author: Alex Gaynor
@@ -16,6 +16,9 @@
Full documentation on `read the docs`_.
+ .. image:: https://dev.azure.com/noumenal/Django%20Filter/_apis/build/status/Django%20F...
+ :target: https://dev.azure.com/noumenal/Django%20Filter/_build/latest?definitionId=3
+
.. image:: https://travis-ci.org/carltongibson/django-filter.svg?branch=master
:target: https://travis-ci.org/carltongibson/django-filter
@@ -25,12 +28,11 @@
.. image:: https://badge.fury.io/py/django-filter.svg
:target: http://badge.fury.io/py/django-filter
-
Requirements
------------
* **Python**: 3.4, 3.5, 3.6, 3.7
- * **Django**: 1.11, 2.0
+ * **Django**: 1.11, 2.0, 2.1, 2.2
* **DRF**: 3.8+
From Version 2.0 Django Filter is Python 3 only.
@@ -124,6 +126,7 @@
Classifier: Framework :: Django
Classifier: Framework :: Django :: 1.11
Classifier: Framework :: Django :: 2.0
+Classifier: Framework :: Django :: 2.1
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
@@ -131,3 +134,4 @@
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Framework :: Django
+Requires-Python: >=3.4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/django_filters/__init__.py new/django-filter-2.1.0/django_filters/__init__.py
--- old/django-filter-2.0.0/django_filters/__init__.py 2018-07-13 11:51:33.000000000 +0200
+++ new/django-filter-2.1.0/django_filters/__init__.py 2019-01-20 20:39:10.000000000 +0100
@@ -10,7 +10,7 @@
from . import rest_framework
del pkgutil
-__version__ = '2.0.0'
+__version__ = '2.1.0'
def parse_version(version):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/django_filters/fields.py new/django-filter-2.1.0/django_filters/fields.py
--- old/django-filter-2.0.0/django_filters/fields.py 2018-07-13 11:44:46.000000000 +0200
+++ new/django-filter-2.1.0/django_filters/fields.py 2019-01-20 09:57:58.000000000 +0100
@@ -70,6 +70,16 @@
super().__init__(fields, *args, **kwargs)
+class IsoDateTimeRangeField(RangeField):
+ widget = DateRangeWidget
+
+ def __init__(self, *args, **kwargs):
+ fields = (
+ IsoDateTimeField(),
+ IsoDateTimeField())
+ super().__init__(fields, *args, **kwargs)
+
+
class TimeRangeField(RangeField):
widget = DateRangeWidget
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/django_filters/filters.py new/django-filter-2.1.0/django_filters/filters.py
--- old/django-filter-2.0.0/django_filters/filters.py 2018-07-13 11:44:46.000000000 +0200
+++ new/django-filter-2.1.0/django_filters/filters.py 2019-01-20 09:57:58.000000000 +0100
@@ -18,6 +18,7 @@
DateRangeField,
DateTimeRangeField,
IsoDateTimeField,
+ IsoDateTimeRangeField,
LookupChoiceField,
ModelChoiceField,
ModelMultipleChoiceField,
@@ -44,6 +45,7 @@
'DurationFilter',
'Filter',
'IsoDateTimeFilter',
+ 'IsoDateTimeFromToRangeFilter',
'LookupChoiceFilter',
'ModelChoiceFilter',
'ModelMultipleChoiceFilter',
@@ -271,7 +273,7 @@
class IsoDateTimeFilter(DateTimeFilter):
"""
- Uses IsoDateTimeField to support filtering on ISO 8601 formated datetimes.
+ Uses IsoDateTimeField to support filtering on ISO 8601 formatted datetimes.
For context see:
@@ -467,6 +469,10 @@
field_class = DateTimeRangeField
+class IsoDateTimeFromToRangeFilter(RangeFilter):
+ field_class = IsoDateTimeRangeField
+
+
class TimeRangeFilter(RangeFilter):
field_class = TimeRangeField
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/django_filters/utils.py new/django-filter-2.1.0/django_filters/utils.py
--- old/django-filter-2.0.0/django_filters/utils.py 2018-07-13 11:44:46.000000000 +0200
+++ new/django-filter-2.1.0/django_filters/utils.py 2019-01-20 09:57:58.000000000 +0100
@@ -317,7 +317,8 @@
from rest_framework.exceptions import ValidationError, ErrorDetail
exc = OrderedDict(
- (key, [ErrorDetail(e.message, code=e.code) for e in error_list])
+ (key, [ErrorDetail(e.message % (e.params or ()), code=e.code)
+ for e in error_list])
for key, error_list in error_dict.as_data().items()
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/django_filters/views.py new/django-filter-2.1.0/django_filters/views.py
--- old/django-filter-2.0.0/django_filters/views.py 2018-07-13 11:44:46.000000000 +0200
+++ new/django-filter-2.1.0/django_filters/views.py 2019-01-20 11:23:25.000000000 +0100
@@ -77,7 +77,7 @@
filterset_class = self.get_filterset_class()
self.filterset = self.get_filterset(filterset_class)
- if self.filterset.is_valid() or not self.get_strict():
+ if not self.filterset.is_bound or self.filterset.is_valid() or not self.get_strict():
self.object_list = self.filterset.qs
else:
self.object_list = self.filterset.queryset.none()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/django_filters/widgets.py new/django-filter-2.1.0/django_filters/widgets.py
--- old/django-filter-2.0.0/django_filters/widgets.py 2018-07-13 11:45:02.000000000 +0200
+++ new/django-filter-2.1.0/django_filters/widgets.py 2019-01-20 09:57:58.000000000 +0100
@@ -1,4 +1,4 @@
-from collections import Iterable
+from collections.abc import Iterable
from itertools import chain
from re import search, sub
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/docs/conf.py new/django-filter-2.1.0/docs/conf.py
--- old/django-filter-2.0.0/docs/conf.py 2018-07-13 11:50:37.000000000 +0200
+++ new/django-filter-2.1.0/docs/conf.py 2019-01-20 20:39:10.000000000 +0100
@@ -41,16 +41,16 @@
# General information about the project.
project = u'django-filter'
-copyright = u'2013, Alex Gaynor and others.'
+copyright = u'2019, Alex Gaynor, Carlton Gibson and others.'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
-version = '2.0.0'
+version = '2.1'
# The full version, including alpha/beta/rc tags.
-release = '2.0.0'
+release = '2.1.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/docs/guide/install.txt new/django-filter-2.1.0/docs/guide/install.txt
--- old/django-filter-2.0.0/docs/guide/install.txt 2018-07-13 11:45:02.000000000 +0200
+++ new/django-filter-2.1.0/docs/guide/install.txt 2019-01-20 20:39:10.000000000 +0100
@@ -30,5 +30,5 @@
* **Python**: 3.4, 3.5, 3.6, 3.7
-* **Django**: 1.11, 2.0
-* **DRF**: 3.7
+* **Django**: 1.11, 2.0, 2.1, 2.2
+* **DRF**: 3.8+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/docs/guide/migration.txt new/django-filter-2.1.0/docs/guide/migration.txt
--- old/django-filter-2.0.0/docs/guide/migration.txt 2018-07-13 11:44:50.000000000 +0200
+++ new/django-filter-2.1.0/docs/guide/migration.txt 2018-10-25 10:05:51.000000000 +0200
@@ -33,7 +33,7 @@
This release contains several changes that break forwards compatibility. This
includes removed features, renamed attributes and arguments, and some reworked
features. Due to the nature of these changes, it is not feasible to release
-a fully forards-compatible migration release. Please review the below list of
+a fully forwards-compatible migration release. Please review the below list of
changes and update your code accordingly.
@@ -74,7 +74,7 @@
The ``Meta.together`` has been deprecated in favor of userland implementations
that override the ``clean`` method of the ``Meta.form`` class. An example will
-be provided in a "recipes" secion in future docs.
+be provided in a "recipes" section in future docs.
FilterSet "strictness" handling moved to view (`#788`__)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/docs/guide/rest_framework.txt new/django-filter-2.1.0/docs/guide/rest_framework.txt
--- old/django-filter-2.0.0/docs/guide/rest_framework.txt 2018-07-13 11:44:50.000000000 +0200
+++ new/django-filter-2.1.0/docs/guide/rest_framework.txt 2019-01-20 09:57:58.000000000 +0100
@@ -30,7 +30,7 @@
queryset = Product.objects.all()
serializer_class = ProductSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('category', 'in_stock')
+ filterset_fields = ('category', 'in_stock')
If you want to use the django-filter backend by default, add it to the ``DEFAULT_FILTER_BACKENDS`` setting.
@@ -51,10 +51,10 @@
}
-Adding a FilterSet with ``filter_class``
-----------------------------------------
+Adding a FilterSet with ``filterset_class``
+-------------------------------------------
-To enable filtering with a ``FilterSet``, add it to the ``filter_class`` parameter on your view class.
+To enable filtering with a ``FilterSet``, add it to the ``filterset_class`` parameter on your view class.
.. code-block:: python
@@ -64,8 +64,8 @@
class ProductFilter(filters.FilterSet):
- min_price = filters.NumberFilter(name="price", lookup_expr='gte')
- max_price = filters.NumberFilter(name="price", lookup_expr='lte')
+ min_price = filters.NumberFilter(field_name="price", lookup_expr='gte')
+ max_price = filters.NumberFilter(field_name="price", lookup_expr='lte')
class Meta:
model = Product
@@ -76,13 +76,13 @@
queryset = Product.objects.all()
serializer_class = ProductSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_class = ProductFilter
+ filterset_class = ProductFilter
-Using the ``filter_fields`` shortcut
-------------------------------------
+Using the ``filterset_fields`` shortcut
+---------------------------------------
-You may bypass creating a ``FilterSet`` by instead adding ``filter_fields`` to your view class. This is equivalent to creating a FilterSet with just :ref:`Meta.fields <fields>`.
+You may bypass creating a ``FilterSet`` by instead adding ``filterset_fields`` to your view class. This is equivalent to creating a FilterSet with just :ref:`Meta.fields <fields>`.
.. code-block:: python
@@ -95,7 +95,7 @@
class ProductList(generics.ListAPIView):
queryset = Product.objects.all()
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('category', 'in_stock')
+ filterset_fields = ('category', 'in_stock')
# Equivalent FilterSet:
@@ -105,6 +105,10 @@
fields = ('category', 'in_stock')
+Note that using ``filterset_fields`` and ``filterset_class`` together is not
+supported.
+
+
Overriding FilterSet creation
-----------------------------
@@ -129,7 +133,7 @@
return kwargs
- class BooksFilter(filters.FitlerSet):
+ class BooksFilter(filters.FilterSet):
def __init__(self, *args, author=None, **kwargs):
super().__init__(*args, **kwargs)
# do something w/ author
@@ -137,9 +141,9 @@
class BookViewSet(viewsets.ModelViewSet):
filter_backends = [MyFilterBackend]
- filter_class = BookFilter
+ filterset_class = BookFilter
- def get_filteset_kwargs(self):
+ def get_filterset_kwargs(self):
return {
'author': self.get_author(),
}
@@ -224,5 +228,4 @@
- ``BooleanFilter``'s use the API-friendly ``BooleanWidget``, which accepts lowercase ``true``/``false``.
- Filter generation uses ``IsoDateTimeFilter`` for datetime model fields.
-- Raised ``ValidationError``'s are reraised as their DRF equivalent. This behavior is useful when setting FilterSet
- strictness to ``STRICTNESS.RAISE_VALIDATION_ERROR``.
+- Raised ``ValidationError``'s are reraised as their DRF equivalent.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/docs/guide/tips.txt new/django-filter-2.1.0/docs/guide/tips.txt
--- old/django-filter-2.0.0/docs/guide/tips.txt 2017-05-19 08:53:12.000000000 +0200
+++ new/django-filter-2.1.0/docs/guide/tips.txt 2018-10-25 10:05:51.000000000 +0200
@@ -10,11 +10,11 @@
how filters work.
-Filter ``name`` and ``lookup_expr`` not configured
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Filter ``field_name`` and ``lookup_expr`` not configured
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-While ``name`` and ``lookup_expr`` are optional, it is recommended that you specify
-them. By default, if ``name`` is not specified, the filter's name on the
+While ``field_name`` and ``lookup_expr`` are optional, it is recommended that you specify
+them. By default, if ``field_name`` is not specified, the filter's name on the
filterset class will be used. Additionally, ``lookup_expr`` defaults to
``exact``. The following is an example of a misconfigured price filter:
@@ -36,7 +36,7 @@
.. code-block:: python
class ProductFilter(django_filters.FilterSet):
- price__gt = django_filters.NumberFilter(name='price', lookup_expr='gt')
+ price__gt = django_filters.NumberFilter(field_name='price', lookup_expr='gt')
Missing ``lookup_expr`` for text search filters
@@ -68,7 +68,7 @@
.. code-block:: python
class ProductFilter(django_filters.FilterSet):
- uncategorized = django_filters.NumberFilter(name='category', lookup_expr='isnull')
+ uncategorized = django_filters.NumberFilter(field_name='category', lookup_expr='isnull')
So what's the issue? While the underlying column type for ``category`` is an
integer, ``isnull`` lookups expect a boolean value. A ``NumberFilter`` however
@@ -84,8 +84,8 @@
pass
class ProductFilter(django_filters.FilterSet):
- categories = NumberInFilter(name='category', lookup_expr='in')
- uncategorized = django_filters.BooleanFilter(name='category', lookup_expr='isnull')
+ categories = NumberInFilter(field_name='category', lookup_expr='in')
+ uncategorized = django_filters.BooleanFilter(field_name='category', lookup_expr='isnull')
More info on constructing ``in`` and ``range`` csv :ref:`filters <base-in-filter>`.
@@ -112,7 +112,7 @@
.. code-block:: python
class ProductFilter(django_filters.FilterSet):
- uncategorized = django_filters.BooleanFilter(name='category', lookup_expr='isnull')
+ uncategorized = django_filters.BooleanFilter(field_name='category', lookup_expr='isnull')
.. note::
@@ -124,7 +124,7 @@
.. code-block:: python
class ProductFilter(django_filters.FilterSet):
- has_category = django_filters.BooleanFilter(name='category', lookup_expr='isnull', exclude=True)
+ has_category = django_filters.BooleanFilter(field_name='category', lookup_expr='isnull', exclude=True)
Solution 2: Using ``ChoiceFilter``'s null choice
""""""""""""""""""""""""""""""""""""""""""""""""
@@ -137,7 +137,7 @@
class ProductFilter(django_filters.FilterSet):
category = django_filters.ModelChoiceFilter(
- name='category', lookup_expr='isnull',
+ field_name='category', lookup_expr='isnull',
null_label='Uncategorized',
queryset=Category.objects.all(),
)
@@ -203,7 +203,7 @@
class MyFilterSet(filters.FilterSet):
- myfield__isempty = EmptyStringFilter(name='myfield')
+ myfield__isempty = EmptyStringFilter(field_name='myfield')
class Meta:
model = MyModel
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/docs/guide/usage.txt new/django-filter-2.1.0/docs/guide/usage.txt
--- old/django-filter-2.0.0/docs/guide/usage.txt 2017-05-19 08:53:12.000000000 +0200
+++ new/django-filter-2.1.0/docs/guide/usage.txt 2018-10-25 10:05:51.000000000 +0200
@@ -54,12 +54,12 @@
class ProductFilter(django_filters.FilterSet):
price = django_filters.NumberFilter()
- price__gt = django_filters.NumberFilter(name='price', lookup_expr='gt')
- price__lt = django_filters.NumberFilter(name='price', lookup_expr='lt')
+ price__gt = django_filters.NumberFilter(field_name='price', lookup_expr='gt')
+ price__lt = django_filters.NumberFilter(field_name='price', lookup_expr='lt')
- release_year = django_filters.NumberFilter(name='release_date', lookup_expr='year')
- release_year__gt = django_filters.NumberFilter(name='release_date', lookup_expr='year__gt')
- release_year__lt = django_filters.NumberFilter(name='release_date', lookup_expr='year__lt')
+ release_year = django_filters.NumberFilter(field_name='release_date', lookup_expr='year')
+ release_year__gt = django_filters.NumberFilter(field_name='release_date', lookup_expr='year__gt')
+ release_year__lt = django_filters.NumberFilter(field_name='release_date', lookup_expr='year__lt')
manufacturer__name = django_filters.CharFilter(lookup_expr='icontains')
@@ -68,7 +68,7 @@
There are two main arguments for filters:
-- ``name``: The name of the model field to filter on. You can traverse
+- ``field_name``: The name of the model field to filter on. You can traverse
"relationship paths" using Django's ``__`` syntax to filter fields on a
related model. ex, ``manufacturer__name``.
- ``lookup_expr``: The `field lookup`_ to use when filtering. Django's ``__``
@@ -77,11 +77,10 @@
.. _`field lookup`: https://docs.djangoproject.com/en/dev/ref/models/querysets/#field-lookups
-Together, the field ``name`` and ``lookup_expr`` represent a complete Django
+Together, the field ``field_name`` and ``lookup_expr`` represent a complete Django
lookup expression. A detailed explanation of lookup expressions is provided in
Django's `lookup reference`_. django-filter supports expressions containing
-both transforms and a final lookup for version 1.9 of Django and above.
-For Django version 1.8, transformed expressions are not supported.
+both transforms and a final lookup.
.. _`lookup reference`: https://docs.djangoproject.com/en/dev/ref/models/lookups/#module-django.db.m...
@@ -312,7 +311,7 @@
url(r'^list/$', FilterView.as_view(model=Product)),
]
-If you provide a ``model`` optionally you can set ``filter_fields`` to specify a list or a tuple of
+If you provide a ``model`` optionally you can set ``filterset_fields`` to specify a list or a tuple of
the fields that you want to include for the automatic construction of the filterset class.
You must provide a template at ``<app>/<model>_filter.html`` which gets the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/docs/ref/filters.txt new/django-filter-2.1.0/docs/ref/filters.txt
--- old/django-filter-2.0.0/docs/ref/filters.txt 2018-07-13 11:44:46.000000000 +0200
+++ new/django-filter-2.1.0/docs/ref/filters.txt 2019-01-20 09:57:58.000000000 +0100
@@ -9,42 +9,45 @@
Core Arguments
--------------
-The following are the core arguments that apply to all filters.
+The following are the core arguments that apply to all filters. Note that they
+are joined to construct the complete `lookup expression`_ that is the left hand
+side of the ORM ``.filter()`` call.
-``name``
-~~~~~~~~
+.. _`lookup expression`: https://docs.djangoproject.com/en/dev/ref/models/lookups/#module-django.db.m...
-The name of the field this filter is supposed to filter on, if this is not
-provided it automatically becomes the filter's name on the ``FilterSet``.
-You can traverse "relationship paths" using Django's ``__`` syntax to filter
-fields on a related model. eg, ``manufacturer__name``.
+``field_name``
+~~~~~~~~~~~~~~
-``label``
-~~~~~~~~~
+The name of the model field that is filtered against. If this argument is not
+provided, it defaults the filter's attribute name on the ``FilterSet`` class.
+Field names can traverse relationships by joining the related parts with the ORM
+lookup separator (``__``). e.g., a product's ``manufacturer__name``.
-The label as it will apear in the HTML, analogous to a form field's label
-argument. If a label is not provided, a verbose label will be generated based
-on the field ``name`` and the parts of the ``lookup_expr``.
-(See: :ref:`verbose-lookups-setting`).
+``lookup_expr``
+~~~~~~~~~~~~~~~
-``widget``
-~~~~~~~~~~
+The `field lookup`_ that should be performed in the filter call. Defaults to
+``exact``. The ``lookup_expr`` can contain transforms if the expression parts
+are joined by the ORM lookup separator (``__``). e.g., filter a datetime by its
+year part ``year__gt``.
-The django.form Widget class which will represent the ``Filter``. In addition
-to the widgets that are included with Django that you can use there are
-additional ones that django-filter provides which may be useful:
+.. _`Field lookup`: https://docs.djangoproject.com/en/dev/ref/models/querysets/#field-lookups
- * :ref:`LinkWidget <link-widget>` -- this displays the options in a manner
- similar to the way the Django Admin does, as a series of links. The link
- for the selected option will have ``class="selected"``.
- * :ref:`BooleanWidget <boolean-widget>` -- this widget converts its input
- into Python's True/False values. It will convert all case variations of
- ``True`` and ``False`` into the internal Python values.
- * :ref:`CSVWidget <csv-widget>` -- this widget expects a comma separated
- value and converts it into a list of string values. It is expected that
- the field class handle a list of values as well as type conversion.
- * :ref:`RangeWidget <range-widget>` -- this widget is used with ``RangeFilter``
- to generate two form input elements using a single field.
+.. _keyword-only-arguments:
+
+Keyword-only Arguments:
+-----------------------
+
+The following are optional arguments that can be used to modify the behavior of
+all filters.
+
+``label``
+~~~~~~~~~
+
+The label as it will appear in the HTML, analogous to a form field's label
+argument. If a label is not provided, a verbose label will be generated based
+on the field ``field_name`` and the parts of the ``lookup_expr``
+(see: :ref:`verbose-lookups-setting`).
.. _filter-method:
@@ -53,18 +56,17 @@
An optional argument that tells the filter how to handle the queryset. It can
accept either a callable or the name of a method on the ``FilterSet``. The
-method receives a ``QuerySet``, the name of the model field to filter on, and
-the value to filter with. It should return a ``Queryset`` that is filtered
-appropriately.
+callable receives a ``QuerySet``, the name of the model field to filter on, and
+the value to filter with. It should return a filtered ``Queryset``.
-The passed in value is validated and cleaned by the filter's ``field_class``,
-so raw value transformation and empty value checking should be unnecessary.
+Note that the value is validated by the ``Filter.field``, so raw value
+transformation and empty value checking should be unnecessary.
.. code-block:: python
class F(FilterSet):
"""Filter for Books by if books are published or not"""
- published = BooleanFilter(name='published_on', method='filter_published')
+ published = BooleanFilter(field_name='published_on', method='filter_published')
def filter_published(self, queryset, name, value):
# construct the full lookup expression.
@@ -86,70 +88,52 @@
class F(FilterSet):
"""Filter for Books by if books are published or not"""
- published = BooleanFilter(name='published_on', method=filter_not_empty)
+ published = BooleanFilter(field_name='published_on', method=filter_not_empty)
class Meta:
model = Book
fields = ['published']
-``lookup_expr``
-~~~~~~~~~~~~~~~
-
-The lookup expression that should be performed using `Django's ORM`_.
-
-.. _`Django's ORM`: https://docs.djangoproject.com/en/dev/ref/models/querysets/#field-lookups
-
-A ``list`` or ``tuple`` of lookup types is also accepted, allowing the user to
-select the lookup from a dropdown. The list of lookup types are filtered against
-``filters.LOOKUP_TYPES``. If `lookup_expr=None` is passed, then a list of all lookup
-types will be generated::
-
- class ProductFilter(django_filters.FilterSet):
- name = django_filters.CharFilter(lookup_expr=['exact', 'iexact'])
-
-You can enable custom lookups by adding them to ``LOOKUP_TYPES``::
-
- from django_filters import filters
-
- filters.LOOKUP_TYPES = ['gt', 'gte', 'lt', 'lte', 'custom_lookup_type']
-
-Additionally, you can provide human-friendly help text by overriding ``LOOKUP_TYPES``::
-
- # filters.py
- from django_filters import filters
-
- filters.LOOKUP_TYPES = [
- ('', '---------'),
- ('exact', 'Is equal to'),
- ('not_exact', 'Is not equal to'),
- ('lt', 'Lesser than'),
- ('gt', 'Greater than'),
- ('gte', 'Greater than or equal to'),
- ('lte', 'Lesser than or equal to'),
- ('startswith', 'Starts with'),
- ('endswith', 'Ends with'),
- ('contains', 'Contains'),
- ('not_contains', 'Does not contain'),
- ]
-
``distinct``
~~~~~~~~~~~~
-A boolean value that specifies whether the Filter will use distinct on the
-queryset. This option can be used to eliminate duplicate results when using filters that span related models. Defaults to ``False``.
+A boolean that specifies whether the Filter will use distinct on the queryset.
+This option can be used to eliminate duplicate results when using filters that
+span relationships. Defaults to ``False``.
``exclude``
~~~~~~~~~~~
-A boolean value that specifies whether the Filter should use ``filter`` or ``exclude`` on the queryset.
-Defaults to ``False``.
+A boolean that specifies whether the Filter should use ``filter`` or ``exclude``
+on the queryset. Defaults to ``False``.
``**kwargs``
~~~~~~~~~~~~
-Any additional keyword arguments are stored as the ``extra`` parameter on the filter. They are provided to the accompanying form Field and can be used to provide arguments like ``choices``.
+Any additional keyword arguments are stored as the ``extra`` parameter on the
+filter. They are provided to the accompanying form ``Field`` and can be used to
+provide arguments like ``choices``. Some field-related arguments:
+
+``widget``
+""""""""""
+
+The django.form Widget class which will represent the ``Filter``. In addition
+to the widgets that are included with Django that you can use there are
+additional ones that django-filter provides which may be useful:
+
+ * :ref:`LinkWidget <link-widget>` -- this displays the options in a manner
+ similar to the way the Django Admin does, as a series of links. The link
+ for the selected option will have ``class="selected"``.
+ * :ref:`BooleanWidget <boolean-widget>` -- this widget converts its input
+ into Python's True/False values. It will convert all case variations of
+ ``True`` and ``False`` into the internal Python values.
+ * :ref:`CSVWidget <csv-widget>` -- this widget expects a comma separated
+ value and converts it into a list of string values. It is expected that
+ the field class handle a list of values as well as type conversion.
+ * :ref:`RangeWidget <range-widget>` -- this widget is used with ``RangeFilter``
+ to generate two form input elements using a single field.
ModelChoiceFilter and ModelMultipleChoiceFilter arguments
@@ -389,7 +373,7 @@
class FooFilter(BaseFilterSet):
foo = django_filters.filters.ModelMultipleChoiceFilter(
- name='attr__uuid',
+ field_name='attr__uuid',
to_field_name='uuid',
queryset=Foo.objects.all(),
)
@@ -581,6 +565,39 @@
f = F({'published_before': '2016-01-01 10:00'})
assert len(f.qs) == 2
+``IsoDateTimeFromToRangeFilter``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Similar to a ``RangeFilter`` except it uses ISO 8601 formatted values instead of numerical values. It can be used with ``IsoDateTimeField``.
+
+Example::
+
+ class Article(models.Model):
+ published = dajngo_filters.IsoDateTimeField()
+
+ class F(FilterSet):
+ published = IsoDateTimeFromToRangeFilter()
+
+ class Meta:
+ model = Article
+ fields = ['published']
+
+ Article.objects.create(published='2016-01-01T8:00:00+01:00')
+ Article.objects.create(published='2016-01-01T9:30:00+01:00')
+ Article.objects.create(published='2016-01-02T8:00:00+01:00')
+
+ # Range: Articles published 2016-01-01 between 8:00 and 10:00
+ f = F({'published_after': '2016-01-01T8:00:00+01:00', 'published_before': '2016-01-01T10:00:00+01:00'})
+ assert len(f.qs) == 2
+
+ # Min-Only: Articles published after 2016-01-01 8:00
+ f = F({'published_after': '2016-01-01T8:00:00+01:00'})
+ assert len(f.qs) == 3
+
+ # Max-Only: Articles published before 2016-01-01 10:00
+ f = F({'published_before': '2016-01-01T10:00:00+0100'})
+ assert len(f.qs) == 2
+
``TimeRangeFilter``
~~~~~~~~~~~~~~~~~~~
@@ -632,7 +649,7 @@
A combined filter that allows users to select the lookup expression from a dropdown.
* ``lookup_choices`` is an optional argument that accepts multiple input
- formats, and is ultimately normlized as the choices used in the lookup
+ formats, and is ultimately normalized as the choices used in the lookup
dropdown. See ``.get_lookup_choices()`` for more information.
* ``field_class`` is an optional argument that allows you to set the inner
@@ -665,7 +682,7 @@
pass
class F(FilterSet):
- id__in = NumberInFilter(name='id', lookup_expr='in')
+ id__in = NumberInFilter(field_name='id', lookup_expr='in')
class Meta:
model = User
@@ -688,11 +705,11 @@
Example::
- class NumberRangeFilter(BaseInFilter, NumberFilter):
+ class NumberRangeFilter(BaseRangeFilter, NumberFilter):
pass
class F(FilterSet):
- id__range = NumberRangeFilter(name='id', lookup_expr='range')
+ id__range = NumberRangeFilter(field_name='id', lookup_expr='range')
class Meta:
model = User
@@ -730,8 +747,8 @@
.. code-block:: python
class UserFilter(FilterSet):
- account = CharFilter(name='username')
- status = NumberFilter(name='status')
+ account = CharFilter(field_name='username')
+ status = NumberFilter(field_name='status')
o = OrderingFilter(
# tuple-mapping retains order
@@ -768,8 +785,8 @@
.. code-block:: python
class UserFilter(FilterSet):
- account = CharFilter(name='username')
- status = NumberFilter(name='status')
+ account = CharFilter(field_name='username')
+ status = NumberFilter(field_name='status')
o = OrderingFilter(
choices=(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/docs/ref/widgets.txt new/django-filter-2.1.0/docs/ref/widgets.txt
--- old/django-filter-2.0.0/docs/ref/widgets.txt 2017-10-19 11:26:45.000000000 +0200
+++ new/django-filter-2.1.0/docs/ref/widgets.txt 2018-10-25 10:05:51.000000000 +0200
@@ -54,7 +54,7 @@
This widget is used with ``RangeFilter`` and its subclasses. It generates two
form input elements which generally act as start/end values in a range.
-Under the hood, it is django's ``forms.TextInput`` widget and excepts
+Under the hood, it is Django's ``forms.TextInput`` widget and excepts
the same arguments and values. To use it, pass it to ``widget`` argument of
a ``RangeField``:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/requirements/maintainer.txt new/django-filter-2.1.0/requirements/maintainer.txt
--- old/django-filter-2.0.0/requirements/maintainer.txt 2016-09-20 22:19:17.000000000 +0200
+++ new/django-filter-2.1.0/requirements/maintainer.txt 2018-12-17 21:18:30.000000000 +0100
@@ -15,13 +15,12 @@
Pygments==2.1.3
pytz==2016.6.1
PyYAML==3.11
-requests==2.9.1
-requests-toolbelt==0.6.0
+requests==2.20.0
six==1.9.0
snowballstemmer==1.2.1
Sphinx==1.3.6
sphinx-autobuild==0.6.0
sphinx-rtd-theme==0.1.9
tornado==4.2.1
-twine==1.6.5
+twine
watchdog==0.8.3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/requirements/test-ci.txt new/django-filter-2.1.0/requirements/test-ci.txt
--- old/django-filter-2.0.0/requirements/test-ci.txt 2017-05-19 08:53:12.000000000 +0200
+++ new/django-filter-2.1.0/requirements/test-ci.txt 2019-01-20 09:57:58.000000000 +0100
@@ -5,3 +5,4 @@
coverage
mock
pytz
+unittest-xml-reporting
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/setup.py new/django-filter-2.1.0/setup.py
--- old/django-filter-2.0.0/setup.py 2018-07-13 11:50:17.000000000 +0200
+++ new/django-filter-2.1.0/setup.py 2019-01-20 20:57:39.000000000 +0100
@@ -6,7 +6,7 @@
readme = f.read()
f.close()
-version = '2.0.0'
+version = '2.1.0'
if sys.argv[-1] == 'publish':
if os.system("pip freeze | grep wheel"):
@@ -45,6 +45,8 @@
'Framework :: Django',
'Framework :: Django :: 1.11',
'Framework :: Django :: 2.0',
+ 'Framework :: Django :: 2.1',
+# 'Framework :: Django :: 2.2',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
@@ -54,6 +56,7 @@
'Framework :: Django',
],
zip_safe=False,
+ python_requires='>=3.4',
install_requires=[
'Django>=1.11',
],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/tests/templates/tests/book_filter.html new/django-filter-2.1.0/tests/templates/tests/book_filter.html
--- old/django-filter-2.0.0/tests/templates/tests/book_filter.html 2014-09-24 19:46:23.000000000 +0200
+++ new/django-filter-2.1.0/tests/templates/tests/book_filter.html 2019-01-20 11:21:40.000000000 +0100
@@ -1,5 +1,5 @@
{{ filter.form }}
-{% for obj in filter.qs %}
+{% for obj in object_list %}
{{ obj }}
{% endfor %}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/tests/test_fields.py new/django-filter-2.1.0/tests/test_fields.py
--- old/django-filter-2.0.0/tests/test_fields.py 2018-07-13 11:44:46.000000000 +0200
+++ new/django-filter-2.1.0/tests/test_fields.py 2019-01-20 09:57:58.000000000 +0100
@@ -12,6 +12,7 @@
DateRangeField,
DateTimeRangeField,
IsoDateTimeField,
+ IsoDateTimeRangeField,
Lookup,
LookupChoiceField,
RangeField,
@@ -90,6 +91,22 @@
datetime(2015, 1, 10, 8, 45, 0)))
+class IsoDateTimeRangeFieldTests(TestCase):
+
+ def test_field(self):
+ f = IsoDateTimeRangeField()
+ self.assertEqual(len(f.fields), 2)
+
+ @override_settings(USE_TZ=False)
+ def test_clean(self):
+ w = RangeWidget()
+ f = IsoDateTimeRangeField(widget=w)
+ self.assertEqual(
+ f.clean(['2015-01-01T10:30:01.123000+01:00', '2015-01-10T08:45:02.345000+01:00']),
+ slice(datetime(2015, 1, 1, 9, 30, 1, 123000),
+ datetime(2015, 1, 10, 7, 45, 2, 345000)))
+
+
class TimeRangeFieldTests(TestCase):
def test_field(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/tests/test_filtering.py new/django-filter-2.1.0/tests/test_filtering.py
--- old/django-filter-2.0.0/tests/test_filtering.py 2018-07-13 11:45:02.000000000 +0200
+++ new/django-filter-2.1.0/tests/test_filtering.py 2019-01-20 09:57:58.000000000 +0100
@@ -19,6 +19,7 @@
DateRangeFilter,
DateTimeFromToRangeFilter,
DurationFilter,
+ IsoDateTimeFromToRangeFilter,
LookupChoiceFilter,
ModelChoiceFilter,
ModelMultipleChoiceFilter,
@@ -977,6 +978,33 @@
self.assertEqual(len(results.qs), 2)
+class IsoDateTimeFromToRangeFilterTests(TestCase):
+
+ def test_filtering(self):
+ tz = timezone.get_current_timezone()
+ Article.objects.create(
+ published=datetime.datetime(2016, 1, 1, 10, 0, tzinfo=tz))
+ Article.objects.create(
+ published=datetime.datetime(2016, 1, 2, 12, 45, tzinfo=tz))
+ Article.objects.create(
+ published=datetime.datetime(2016, 1, 3, 18, 15, tzinfo=tz))
+ Article.objects.create(
+ published=datetime.datetime(2016, 1, 3, 19, 30, tzinfo=tz))
+
+ class F(FilterSet):
+ published = IsoDateTimeFromToRangeFilter()
+
+ class Meta:
+ model = Article
+ fields = ['published']
+
+ dt = (datetime.datetime.now(tz=tz))
+ results = F(data={
+ 'published_after': '2016-01-02T10:00:00.000000' + dt.strftime("%z"),
+ 'published_before': '2016-01-03T19:00:00.000000' + dt.strftime("%z")})
+ self.assertEqual(len(results.qs), 2)
+
+
class TimeRangeFilterTests(TestCase):
def test_filtering(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/tests/test_filters.py new/django-filter-2.1.0/tests/test_filters.py
--- old/django-filter-2.0.0/tests/test_filters.py 2018-07-13 11:44:46.000000000 +0200
+++ new/django-filter-2.1.0/tests/test_filters.py 2019-01-20 09:57:58.000000000 +0100
@@ -14,6 +14,7 @@
BaseCSVField,
DateRangeField,
DateTimeRangeField,
+ IsoDateTimeRangeField,
Lookup,
RangeField,
TimeRangeField
@@ -33,6 +34,7 @@
DateTimeFromToRangeFilter,
DurationFilter,
Filter,
+ IsoDateTimeFromToRangeFilter,
LookupChoiceFilter,
ModelChoiceFilter,
ModelMultipleChoiceFilter,
@@ -1153,6 +1155,52 @@
f.filter(qs, value)
qs.filter.assert_called_once_with(
None__range=(datetime(2015, 4, 7, 8, 30), datetime(2015, 9, 6, 11, 45)))
+
+
+class IsoDateTimeFromToRangeFilterTests(TestCase):
+
+ def test_default_field(self):
+ f = IsoDateTimeFromToRangeFilter()
+ field = f.field
+ self.assertIsInstance(field, IsoDateTimeRangeField)
+
+ def test_filtering_range(self):
+ qs = mock.Mock(spec=['filter'])
+ value = mock.Mock(
+ start=datetime(2015, 4, 7, 8, 30), stop=datetime(2015, 9, 6, 11, 45))
+ f = IsoDateTimeFromToRangeFilter()
+ f.filter(qs, value)
+ qs.filter.assert_called_once_with(
+ None__range=(datetime(2015, 4, 7, 8, 30), datetime(2015, 9, 6, 11, 45)))
+
+ def test_filtering_start(self):
+ qs = mock.Mock(spec=['filter'])
+ value = mock.Mock(start=datetime(2015, 4, 7, 8, 30), stop=None)
+ f = IsoDateTimeFromToRangeFilter()
+ f.filter(qs, value)
+ qs.filter.assert_called_once_with(None__gte=datetime(2015, 4, 7, 8, 30))
+
+ def test_filtering_stop(self):
+ qs = mock.Mock(spec=['filter'])
+ value = mock.Mock(start=None, stop=datetime(2015, 9, 6, 11, 45))
+ f = IsoDateTimeFromToRangeFilter()
+ f.filter(qs, value)
+ qs.filter.assert_called_once_with(None__lte=datetime(2015, 9, 6, 11, 45))
+
+ def test_filtering_skipped_with_none_value(self):
+ qs = mock.Mock(spec=['filter'])
+ f = IsoDateTimeFromToRangeFilter()
+ result = f.filter(qs, None)
+ self.assertEqual(qs, result)
+
+ def test_filtering_ignores_lookup_expr(self):
+ qs = mock.Mock()
+ value = mock.Mock(
+ start=datetime(2015, 4, 7, 8, 30), stop=datetime(2015, 9, 6, 11, 45))
+ f = IsoDateTimeFromToRangeFilter(lookup_expr='gte')
+ f.filter(qs, value)
+ qs.filter.assert_called_once_with(
+ None__range=(datetime(2015, 4, 7, 8, 30), datetime(2015, 9, 6, 11, 45)))
class TimeRangeFilterTests(TestCase):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/tests/test_utils.py new/django-filter-2.1.0/tests/test_utils.py
--- old/django-filter-2.0.0/tests/test_utils.py 2018-07-13 11:44:46.000000000 +0200
+++ new/django-filter-2.1.0/tests/test_utils.py 2019-01-20 09:57:58.000000000 +0100
@@ -10,6 +10,7 @@
from django_filters import FilterSet
from django_filters.exceptions import FieldLookupError
+from django_filters.filters import MultipleChoiceFilter
from django_filters.utils import (
MigrationNotice,
RenameAttributesBase,
@@ -511,17 +512,30 @@
model = Article
fields = ['id', 'author', 'name']
+ choice = MultipleChoiceFilter(choices=[('1', 'one'), ('2', 'two')])
+
def test_error_detail(self):
- f = self.F(data={'id': 'foo', 'author': 'bar', 'name': 'baz'})
+ f = self.F(data={
+ 'id': 'foo',
+ 'author': 'bar',
+ 'name': 'baz',
+ 'choice': ['3'],
+ })
exc = translate_validation(f.errors)
self.assertDictEqual(exc.detail, {
'id': ['Enter a number.'],
'author': ['Select a valid choice. That choice is not one of the available choices.'],
+ 'choice': ['Select a valid choice. 3 is not one of the available choices.'],
})
def test_full_error_details(self):
- f = self.F(data={'id': 'foo', 'author': 'bar', 'name': 'baz'})
+ f = self.F(data={
+ 'id': 'foo',
+ 'author': 'bar',
+ 'name': 'baz',
+ 'choice': ['3'],
+ })
exc = translate_validation(f.errors)
self.assertEqual(exc.get_full_details(), {
@@ -530,4 +544,8 @@
'message': 'Select a valid choice. That choice is not one of the available choices.',
'code': 'invalid_choice',
}],
+ 'choice': [{
+ 'message': 'Select a valid choice. 3 is not one of the available choices.',
+ 'code': 'invalid_choice',
+ }],
})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-filter-2.0.0/tests/test_views.py new/django-filter-2.1.0/tests/test_views.py
--- old/django-filter-2.0.0/tests/test_views.py 2018-07-13 11:44:46.000000000 +0200
+++ new/django-filter-2.1.0/tests/test_views.py 2019-01-20 12:50:09.000000000 +0100
@@ -123,6 +123,19 @@
self.assertEqual(message, expected)
self.assertEqual(len(recorded), 0)
+ def test_view_with_unbound_filter_form_returns_initial_queryset(self):
+ factory = RequestFactory()
+ request = factory.get(self.base_url)
+
+ queryset = Book.objects.filter(title='Snowcrash')
+ view = FilterView.as_view(model=Book, queryset=queryset)
+
+ response = view(request)
+ titles = [o.title for o in response.context_data['object_list']]
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(titles, ['Snowcrash'])
+
class GenericFunctionalViewTests(GenericViewTestCase):
base_url = '/books-legacy/'