From 6daedf60d8300bb7f8fb4bbe1ca7172eed28c611 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 26 Aug 2021 22:44:29 +0300 Subject: [PATCH 01/21] Drop support for EOL Python 2.7 and 3.5 --- .github/workflows/test.yml | 2 +- .../tests/test_get_field_choices_view.py | 13 ++----------- setup.py | 6 +++--- tox.ini | 6 ------ 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3473365..7d52064 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9] + python-version: [3.6, 3.7, 3.8, 3.9] steps: - uses: actions/checkout@v1 diff --git a/advanced_filters/tests/test_get_field_choices_view.py b/advanced_filters/tests/test_get_field_choices_view.py index 28947b3..2e6a5a7 100644 --- a/advanced_filters/tests/test_get_field_choices_view.py +++ b/advanced_filters/tests/test_get_field_choices_view.py @@ -39,6 +39,8 @@ def assert_view_error(client, error, exception=None, **view_kwargs): NO_APP_INSTALLED_ERROR = "No installed app with label 'foo'." +ARGUMENT_LENGTH_ERROR = "not enough values to unpack (expected 2, got 1)" +MISSING_FIELD_ERROR = "SalesRep has no field named 'baz'" if django.VERSION < (1, 11): NO_MODEL_ERROR = "App 'reps' doesn't have a 'foo' model." @@ -46,17 +48,6 @@ def assert_view_error(client, error, exception=None, **view_kwargs): NO_MODEL_ERROR = "App 'reps' doesn't have a 'Foo' model." -if sys.version_info >= (3, 5): - ARGUMENT_LENGTH_ERROR = "not enough values to unpack (expected 2, got 1)" -else: - ARGUMENT_LENGTH_ERROR = "need more than 1 value to unpack" - -if sys.version_info < (3,) and django.VERSION < (1, 11): - MISSING_FIELD_ERROR = "SalesRep has no field named u'baz'" -else: - MISSING_FIELD_ERROR = "SalesRep has no field named 'baz'" - - def test_invalid_view_kwargs(client): assert_view_error(client, "GetFieldChoices view requires 2 arguments") assert_view_error( diff --git a/setup.py b/setup.py index dfdec2b..baa9f1d 100644 --- a/setup.py +++ b/setup.py @@ -70,6 +70,7 @@ def get_full_description(): zip_safe=False, author='Pavel Savchenko', author_email='pavel@modlinltd.com', + python_requires='>=3.6', classifiers=[ 'Environment :: Web Environment', 'Framework :: Django', @@ -77,13 +78,12 @@ def get_full_description(): 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3 :: Only', 'Framework :: Django', 'Framework :: Django :: 1.9', 'Framework :: Django :: 1.10', diff --git a/tox.ini b/tox.ini index 1cb6e29..99a1136 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,5 @@ [tox] envlist = - py27-django{19,110,111} - py35-django{19,110,111,20,21,22} py36-django{111,20,21,22,30,31} py37-django{111,20,21,22,30,31} py38-django{22,30,31} @@ -43,8 +41,6 @@ commands = [travis] python = - 2.7: py27 - 3.5: py35 3.6: py36 3.7: py37 3.9: py39 @@ -64,8 +60,6 @@ DJANGO = [gh-actions] python = - 2.7: py27 - 3.5: py35 3.6: py36 3.7: py37 3.8: py38 From a470d4aa097ccaa8309807fae56d192859e697d6 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 26 Aug 2021 22:50:32 +0300 Subject: [PATCH 02/21] Drop support for EOL Django up to 2.2 --- README.rst | 2 +- advanced_filters/admin.py | 8 +----- advanced_filters/forms.py | 12 ++------- advanced_filters/models.py | 8 +----- .../tests/test_admin_change_form.py | 6 +---- advanced_filters/tests/test_creation.py | 6 +---- advanced_filters/tests/test_forms.py | 13 ++-------- .../tests/test_get_field_choices_view.py | 12 ++------- advanced_filters/tests/test_usage.py | 6 +---- setup.py | 5 ---- tests/customers/models.py | 8 +----- tox.ini | 26 ++----------------- 12 files changed, 15 insertions(+), 97 deletions(-) diff --git a/README.rst b/README.rst index b68ab6d..fc7c707 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ For release notes, see `Changelog = 1.9 (Django 1.9 - 3.0 on Python 2/3/PyPy3) +- Django 2.2, >= 3.1 on Python 3.6+/PyPy3 - simplejson >= 3.6.5, < 4 diff --git a/advanced_filters/admin.py b/advanced_filters/admin.py index 693d5be..e3908c6 100644 --- a/advanced_filters/admin.py +++ b/advanced_filters/admin.py @@ -5,17 +5,11 @@ from django.contrib.admin.utils import unquote from django.http import HttpResponseRedirect from django.shortcuts import resolve_url +from django.utils.translation import gettext_lazy as _ from .forms import AdvancedFilterForm from .models import AdvancedFilter -# django < 1.9 support -from django import VERSION -if VERSION >= (2, 0): - from django.utils.translation import gettext_lazy as _ -else: - from django.utils.translation import ugettext_lazy as _ - logger = logging.getLogger('advanced_filters.admin') diff --git a/advanced_filters/forms.py b/advanced_filters/forms.py index efb50d1..9945edc 100644 --- a/advanced_filters/forms.py +++ b/advanced_filters/forms.py @@ -17,20 +17,12 @@ from django.utils.functional import cached_property from six.moves import range, reduce from django.utils.text import capfirst +from django.utils.translation import gettext_lazy as _ from .models import AdvancedFilter from .form_helpers import CleanWhiteSpacesMixin, VaryingTypeCharField -# django < 1.9 support -from django import VERSION -if VERSION >= (2, 0): - from django.utils.translation import gettext_lazy as _ -else: - from django.utils.translation import ugettext_lazy as _ - -# django < 1.9 support -USE_VENDOR_DIR = VERSION >= (1, 9) logger = logging.getLogger('advanced_filters.forms') # select2 location can be modified via settings @@ -234,7 +226,7 @@ class Meta: class Media: required_js = [ - 'admin/js/%sjquery.min.js' % ('vendor/jquery/' if USE_VENDOR_DIR else ''), + 'admin/js/vendor/jquery/jquery.min.js', 'advanced-filters/jquery_adder.js', 'orig_inlines%s.js' % ('' if settings.DEBUG else '.min'), 'magnific-popup/jquery.magnific-popup.js', diff --git a/advanced_filters/models.py b/advanced_filters/models.py index 33f1b28..0e4f835 100644 --- a/advanced_filters/models.py +++ b/advanced_filters/models.py @@ -1,16 +1,10 @@ from django.conf import settings from django.db import models from django.db.models import Q +from django.utils.translation import gettext_lazy as _ from .q_serializer import QSerializer -# django < 1.9 support -from django import VERSION -if VERSION >= (2, 0): - from django.utils.translation import gettext_lazy as _ -else: - from django.utils.translation import ugettext_lazy as _ - class UserLookupManager(models.Manager): def filter_by_user(self, user): diff --git a/advanced_filters/tests/test_admin_change_form.py b/advanced_filters/tests/test_admin_change_form.py index 540376e..fd4c248 100644 --- a/advanced_filters/tests/test_admin_change_form.py +++ b/advanced_filters/tests/test_admin_change_form.py @@ -1,15 +1,11 @@ import pytest from django.contrib.auth.models import Permission from django.db.models import Q +from django.urls import reverse from ..models import AdvancedFilter from .factories import AdvancedFilterFactory -try: - from django.urls import reverse -except ImportError: # Django < 2.0 - from django.core.urlresolvers import reverse - URL_NAME_CHANGE = "admin:advanced_filters_advancedfilter_change" URL_NAME_ADD = "admin:advanced_filters_advancedfilter_add" URL_NAME_CLIENT_CHANGELIST = "admin:customers_client_changelist" diff --git a/advanced_filters/tests/test_creation.py b/advanced_filters/tests/test_creation.py index de3cab0..0384613 100644 --- a/advanced_filters/tests/test_creation.py +++ b/advanced_filters/tests/test_creation.py @@ -1,13 +1,9 @@ import pytest from django.contrib.auth.models import Permission +from django.urls import reverse_lazy from ..models import AdvancedFilter -try: - from django.urls import reverse_lazy -except ImportError: # Django < 2.0 - from django.core.urlresolvers import reverse_lazy - URL_CLIENT_CHANGELIST = reverse_lazy("admin:customers_client_changelist") diff --git a/advanced_filters/tests/test_forms.py b/advanced_filters/tests/test_forms.py index 7cb2e7f..cca3c82 100644 --- a/advanced_filters/tests/test_forms.py +++ b/advanced_filters/tests/test_forms.py @@ -106,17 +106,8 @@ def test_make_query(self): assert isinstance(q, Q) assert isinstance(q.children, list) assert q.connector == 'AND' - if django.VERSION >= (2, 0): - # django 2+ flattens nested empty Query - assert q.negated - assert q.children[0] == ('fname__iexact', 'john') - else: - # django <2 has a parent Query that stays default - assert not q.negated - subquery = q.children[0] - assert isinstance(subquery, Q) - assert subquery.negated - assert subquery.children[0] == ('fname__iexact', 'john') + assert q.negated + assert q.children[0] == ('fname__iexact', 'john') def test_invalid_existing_query(self): Rep = get_user_model() diff --git a/advanced_filters/tests/test_get_field_choices_view.py b/advanced_filters/tests/test_get_field_choices_view.py index 2e6a5a7..e126326 100644 --- a/advanced_filters/tests/test_get_field_choices_view.py +++ b/advanced_filters/tests/test_get_field_choices_view.py @@ -8,13 +8,9 @@ import pytest from django.utils import timezone from django.utils.encoding import force_str +from django.urls import reverse from tests.factories import ClientFactory -try: - from django.urls import reverse -except ImportError: # Django < 2.0 - from django.core.urlresolvers import reverse - URL_NAME = "afilters_get_field_choices" @@ -41,11 +37,7 @@ def assert_view_error(client, error, exception=None, **view_kwargs): NO_APP_INSTALLED_ERROR = "No installed app with label 'foo'." ARGUMENT_LENGTH_ERROR = "not enough values to unpack (expected 2, got 1)" MISSING_FIELD_ERROR = "SalesRep has no field named 'baz'" - -if django.VERSION < (1, 11): - NO_MODEL_ERROR = "App 'reps' doesn't have a 'foo' model." -else: - NO_MODEL_ERROR = "App 'reps' doesn't have a 'Foo' model." +NO_MODEL_ERROR = "App 'reps' doesn't have a 'Foo' model." def test_invalid_view_kwargs(client): diff --git a/advanced_filters/tests/test_usage.py b/advanced_filters/tests/test_usage.py index 0155093..edca0d8 100644 --- a/advanced_filters/tests/test_usage.py +++ b/advanced_filters/tests/test_usage.py @@ -1,17 +1,13 @@ import pytest from django.contrib.auth.models import Permission from django.db.models import Q +from django.urls import reverse from tests.factories import ClientFactory, SalesRepFactory from ..admin import AdvancedListFilters from ..models import AdvancedFilter from .factories import AdvancedFilterFactory -try: - from django.urls import reverse -except ImportError: # Django < 2.0 - from django.core.urlresolvers import reverse - URL_NAME_CLIENT_CHANGELIST = "admin:customers_client_changelist" diff --git a/setup.py b/setup.py index baa9f1d..559a5e9 100644 --- a/setup.py +++ b/setup.py @@ -85,11 +85,6 @@ def get_full_description(): 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3 :: Only', 'Framework :: Django', - 'Framework :: Django :: 1.9', - 'Framework :: Django :: 1.10', - 'Framework :: Django :: 1.11', - 'Framework :: Django :: 2.0', - 'Framework :: Django :: 2.1', 'Framework :: Django :: 2.2', 'Framework :: Django :: 3.0', 'Framework :: Django :: 3.1', diff --git a/tests/customers/models.py b/tests/customers/models.py index f4bf75f..31238aa 100644 --- a/tests/customers/models.py +++ b/tests/customers/models.py @@ -1,13 +1,7 @@ from django.contrib.auth.models import AbstractBaseUser from django.db import models from django.utils import timezone - -# django < 1.9 support -from django import VERSION -if VERSION >= (2, 0): - from django.utils.translation import gettext_lazy as _ -else: - from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ class Client(AbstractBaseUser): diff --git a/tox.ini b/tox.ini index 99a1136..dce1023 100644 --- a/tox.ini +++ b/tox.ini @@ -1,14 +1,7 @@ [tox] envlist = - py36-django{111,20,21,22,30,31} - py37-django{111,20,21,22,30,31} - py38-django{22,30,31} - py39-django{22,30,31} - ; py36-django{111,20,21,22,30,31,32} - ; py37-django{111,20,21,22,30,31,32} - ; py38-django{22,30,31,32} - ; py39-django{22,30,31,32} - pypy3-django{111,20,21,22,30,31} + py{36,37,38,39,py3}-django{22,30,31} + ; py{36,37,38,39}-django{22,30,31,32} report [pycodestyle] @@ -19,11 +12,6 @@ usedevelop = true passenv = TRAVIS TRAVIS_* deps = -rtest-reqs.txt - django19: Django>=1.9,<1.10 - django110: Django>=1.10,<1.11 - django111: Django>=1.11,<1.12 - django20: Django>=2.0,<2.1 - django21: Django>=2.1,<2.2 django22: Django>=2.2,<3.0 django30: Django>=3.0,<3.1 django31: Django>=3.1,<3.2 @@ -48,11 +36,6 @@ python = [travis:env] DJANGO = - 1.9: django19 - 1.10: django110 - 1.11: django111 - 2.0: django20 - 2.1: django21 2.2: django22 3.0: django30 3.1: django31 @@ -68,11 +51,6 @@ python = [gh-actions:env] DJANGO = - 1.9: django19 - 1.10: django110 - 1.11: django111 - 2.0: django20 - 2.1: django21 2.2: django22 3.0: django30 3.1: django31 From cc3467713304a6a8a2859592054f0944b4ffdad3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 26 Aug 2021 23:00:50 +0300 Subject: [PATCH 03/21] Upgrade Python syntax with pyupgrade --py36-plus --- advanced_filters/admin.py | 25 +++++++++---------- advanced_filters/form_helpers.py | 8 +++--- advanced_filters/forms.py | 22 ++++++++-------- advanced_filters/migrations/0001_initial.py | 2 -- .../0002_advancedfilter_created_at.py | 3 --- advanced_filters/mixins.py | 12 ++++----- advanced_filters/q_serializer.py | 2 +- advanced_filters/tests/test_creation.py | 2 +- advanced_filters/tests/test_forms.py | 14 +++++------ .../tests/test_get_field_choices_view.py | 2 +- advanced_filters/tests/test_q_serializer.py | 4 +-- setup.py | 6 ++--- tests/customers/migrations/0001_initial.py | 2 -- tests/factories.py | 2 +- tests/reps/migrations/0001_initial.py | 2 -- 15 files changed, 49 insertions(+), 59 deletions(-) diff --git a/advanced_filters/admin.py b/advanced_filters/admin.py index e3908c6..56a6e6f 100644 --- a/advanced_filters/admin.py +++ b/advanced_filters/admin.py @@ -24,8 +24,8 @@ def lookups(self, request, model_admin): if not model_admin: raise Exception('Cannot use AdvancedListFilters without a ' 'model_admin') - model_name = "%s.%s" % (model_admin.model._meta.app_label, - model_admin.model._meta.object_name) + model_name = "{}.{}".format(model_admin.model._meta.app_label, + model_admin.model._meta.object_name) return AdvancedFilter.objects.filter_by_user(request.user).filter( model=model_name).values_list('id', 'title') @@ -43,13 +43,13 @@ def queryset(self, request, queryset): return queryset -class AdminAdvancedFiltersMixin(object): +class AdminAdvancedFiltersMixin: """ Generic AdvancedFilters mixin """ advanced_change_list_template = "admin/advanced_filters.html" advanced_filter_form = AdvancedFilterForm def __init__(self, *args, **kwargs): - super(AdminAdvancedFiltersMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if self.change_list_template: self.original_change_list_template = self.change_list_template else: @@ -98,8 +98,7 @@ def changelist_view(self, request, extra_context=None): extra_context=extra_context) if response: return response - return super(AdminAdvancedFiltersMixin, self - ).changelist_view(request, extra_context=extra_context) + return super().changelist_view(request, extra_context=extra_context) class AdvancedFilterAdmin(admin.ModelAdmin): @@ -118,20 +117,20 @@ def save_model(self, request, new_object, *args, **kwargs): if new_object and not new_object.pk: new_object.created_by = request.user - super(AdvancedFilterAdmin, self).save_model( + super().save_model( request, new_object, *args, **kwargs) def change_view(self, request, object_id, form_url='', extra_context=None): - orig_response = super(AdvancedFilterAdmin, self).change_view( + orig_response = super().change_view( request, object_id, form_url, extra_context) if '_save_goto' in request.POST: obj = self.get_object(request, unquote(object_id)) if obj: app, model = obj.model.split('.') - path = resolve_url('admin:%s_%s_changelist' % ( + path = resolve_url('admin:{}_{}_changelist'.format( app, model.lower())) url = "{path}{qparams}".format( - path=path, qparams="?_afilter={id}".format(id=object_id)) + path=path, qparams=f"?_afilter={object_id}") return HttpResponseRedirect(url) return orig_response @@ -142,18 +141,18 @@ def user_has_permission(user): def get_queryset(self, request): if self.user_has_permission(request.user): - return super(AdvancedFilterAdmin, self).get_queryset(request) + return super().get_queryset(request) else: return self.model.objects.filter_by_user(request.user) def has_change_permission(self, request, obj=None): if obj is None: - return super(AdvancedFilterAdmin, self).has_change_permission(request) + return super().has_change_permission(request) return self.user_has_permission(request.user) or obj in self.model.objects.filter_by_user(request.user) def has_delete_permission(self, request, obj=None): if obj is None: - return super(AdvancedFilterAdmin, self).has_delete_permission(request) + return super().has_delete_permission(request) return self.user_has_permission(request.user) or obj in self.model.objects.filter_by_user(request.user) diff --git a/advanced_filters/form_helpers.py b/advanced_filters/form_helpers.py index 681be77..61b3b49 100644 --- a/advanced_filters/form_helpers.py +++ b/advanced_filters/form_helpers.py @@ -29,7 +29,7 @@ def to_python(self, value): >>> assert field.to_python('and,me') == '(and|me)' >>> assert field.to_python('and,me;too') == '(and|me;too)' """ - res = super(VaryingTypeCharField, self).to_python(value) + res = super().to_python(value) split_res = res.split(self._default_separator) if not res or len(split_res) < 2: return res.strip() @@ -40,7 +40,7 @@ def to_python(self, value): return res -class CleanWhiteSpacesMixin(object): +class CleanWhiteSpacesMixin: """ This mixin, when added to any form subclass, adds a clean method which strips repeating spaces in and around each string value of "clean_data". @@ -55,9 +55,9 @@ def clean(self): >>> assert form.is_valid() >>> assert form.cleaned_data == {'some_field': 'a weird value'} """ - cleaned_data = super(CleanWhiteSpacesMixin, self).clean() + cleaned_data = super().clean() for k in self.cleaned_data: - if isinstance(self.cleaned_data[k], six.string_types): + if isinstance(self.cleaned_data[k], str): cleaned_data[k] = re.sub(extra_spaces_pattern, ' ', self.cleaned_data[k] or '').strip() return cleaned_data diff --git a/advanced_filters/forms.py b/advanced_filters/forms.py index 9945edc..d77d7b8 100644 --- a/advanced_filters/forms.py +++ b/advanced_filters/forms.py @@ -15,7 +15,7 @@ from django.db.models.fields import DateField from django.forms.formsets import formset_factory, BaseFormSet from django.utils.functional import cached_property -from six.moves import range, reduce +from six.moves import reduce from django.utils.text import capfirst from django.utils.translation import gettext_lazy as _ @@ -76,7 +76,7 @@ def _build_field_choices(self, fields): Iterate over passed model fields tuple and update initial choices. """ return tuple(sorted( - [(fquery, capfirst(fname)) for fquery, fname in fields.items()], + ((fquery, capfirst(fname)) for fquery, fname in fields.items()), key=lambda f: f[1].lower()) ) + self.FIELD_CHOICES @@ -158,7 +158,7 @@ def set_range_value(self, data): data['value'] = (dtfrom, dtto) def clean(self): - cleaned_data = super(AdvancedFilterQueryForm, self).clean() + cleaned_data = super().clean() if cleaned_data.get('operator') == "range": if ('value_from' in cleaned_data and 'value_to' in cleaned_data): @@ -176,7 +176,7 @@ def make_query(self, *args, **kwargs): return query def __init__(self, model_fields={}, *args, **kwargs): - super(AdvancedFilterQueryForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.FIELD_CHOICES = self._build_field_choices(model_fields) self.fields['field'].choices = self.FIELD_CHOICES if not self.fields['field'].initial: @@ -190,13 +190,13 @@ class AdvancedFilterFormSet(BaseFormSet): def __init__(self, *args, **kwargs): self.model_fields = kwargs.pop('model_fields', {}) - super(AdvancedFilterFormSet, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if self.forms: form = self.forms[0] self.fields = form.visible_fields() def get_form_kwargs(self, index): - kwargs = super(AdvancedFilterFormSet, self).get_form_kwargs(index) + kwargs = super().get_form_kwargs(index) kwargs['model_fields'] = self.model_fields return kwargs @@ -286,7 +286,7 @@ def __init__(self, *args, **kwargs): self._filter_fields = filter_fields or getattr( model_admin, 'advanced_filter_fields', ()) - super(AdvancedFilterForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # populate existing or empty forms formset data = None @@ -297,15 +297,15 @@ def __init__(self, *args, **kwargs): self.initialize_form(instance, self._model, data, extra_form) def clean(self): - cleaned_data = super(AdvancedFilterForm, self).clean() + cleaned_data = super().clean() if not self.fields_formset.is_valid(): logger.debug( "Errors validating advanced query filters: %s", pformat([(f.errors, f.non_field_errors()) for f in self.fields_formset.forms])) raise forms.ValidationError("Error validating filter forms") - cleaned_data['model'] = "%s.%s" % (self._model._meta.app_label, - self._model._meta.object_name) + cleaned_data['model'] = "{}.{}".format(self._model._meta.app_label, + self._model._meta.object_name) return cleaned_data @property @@ -356,4 +356,4 @@ def initialize_form(self, instance, model, data=None, extra=None): def save(self, commit=True): self.instance.query = self.generate_query() self.instance.model = self.cleaned_data.get('model') - return super(AdvancedFilterForm, self).save(commit) + return super().save(commit) diff --git a/advanced_filters/migrations/0001_initial.py b/advanced_filters/migrations/0001_initial.py index 2fd49d9..dd25c59 100644 --- a/advanced_filters/migrations/0001_initial.py +++ b/advanced_filters/migrations/0001_initial.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.9.4 on 2016-03-07 23:02 -from __future__ import unicode_literals from django.conf import settings from django.db import migrations, models diff --git a/advanced_filters/migrations/0002_advancedfilter_created_at.py b/advanced_filters/migrations/0002_advancedfilter_created_at.py index 092e2b9..8d1cfea 100644 --- a/advanced_filters/migrations/0002_advancedfilter_created_at.py +++ b/advanced_filters/migrations/0002_advancedfilter_created_at.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.db import models, migrations diff --git a/advanced_filters/mixins.py b/advanced_filters/mixins.py index 12b69a8..68e38b5 100644 --- a/advanced_filters/mixins.py +++ b/advanced_filters/mixins.py @@ -22,7 +22,7 @@ import six -class CsrfExemptMixin(object): +class CsrfExemptMixin: """ Exempts the view from CSRF requirements. NOTE: @@ -31,10 +31,10 @@ class CsrfExemptMixin(object): @method_decorator(csrf_exempt) def dispatch(self, *args, **kwargs): - return super(CsrfExemptMixin, self).dispatch(*args, **kwargs) + return super().dispatch(*args, **kwargs) -class AccessMixin(object): +class AccessMixin: """ 'Abstract' mixin that gives access mixins the same customizable functionality. @@ -104,11 +104,11 @@ def dispatch(self, request, *args, **kwargs): if not request.user.is_staff: return self.handle_no_permission(request) - return super(StaffuserRequiredMixin, self).dispatch( + return super().dispatch( request, *args, **kwargs) -class JSONResponseMixin(object): +class JSONResponseMixin: """ A mixin that allows you to easily serialize simple data such as a dict or Django models. @@ -120,7 +120,7 @@ class JSONResponseMixin(object): def get_content_type(self): if (self.content_type is not None and not isinstance(self.content_type, - (six.string_types, six.text_type))): + ((str,), str))): raise ImproperlyConfigured( '{0} is missing a content type. Define {0}.content_type, ' 'or override {0}.get_content_type().'.format( diff --git a/advanced_filters/q_serializer.py b/advanced_filters/q_serializer.py index c0bc668..9f2dac9 100644 --- a/advanced_filters/q_serializer.py +++ b/advanced_filters/q_serializer.py @@ -25,7 +25,7 @@ def dt2ts(obj): return time.mktime(obj.timetuple()) if isinstance(obj, date) else obj -class QSerializer(object): +class QSerializer: """ A Q object serializer base class. Pass base64=True when initializing to Base-64 encode/decode the returned/passed string. diff --git a/advanced_filters/tests/test_creation.py b/advanced_filters/tests/test_creation.py index 0384613..af1805c 100644 --- a/advanced_filters/tests/test_creation.py +++ b/advanced_filters/tests/test_creation.py @@ -79,6 +79,6 @@ def test_create_form_valid(user, client, good_data, query): created_filter = AdvancedFilter.objects.order_by("pk").last() url = res["location"] - assert url.endswith("%s?_afilter=%s" % (URL_CLIENT_CHANGELIST, created_filter.pk)) + assert url.endswith(f"{URL_CLIENT_CHANGELIST}?_afilter={created_filter.pk}") assert list(created_filter.query.children[0]) == query diff --git a/advanced_filters/tests/test_forms.py b/advanced_filters/tests/test_forms.py index cca3c82..4445e76 100644 --- a/advanced_filters/tests/test_forms.py +++ b/advanced_filters/tests/test_forms.py @@ -219,8 +219,8 @@ def setUp(self): created_by=self.user) def _create_query_form_data(self, form_number=0, data=None, **kwargs): - form_data = dict(('form-%d-%s' % (form_number, k), v) - for k, v in (data or self.formset_data).items()) + form_data = {'form-%d-%s' % (form_number, k): v + for k, v in (data or self.formset_data).items()} form_data.update(self.mgmg_form_data) form_data.update(dict(title='baz filter')) form_data.update(kwargs) @@ -236,9 +236,9 @@ def test_failed_validation(self): **self.default_error) assert form.non_field_errors() == self.default_non_field_err assert form.fields_formset.errors == [ - {'operator': [u'This field is required.'], - 'field': [u'This field is required.'], - 'value': [u'This field is required.']}] + {'operator': ['This field is required.'], + 'field': ['This field is required.'], + 'value': ['This field is required.']}] def test_invalid_field_validation(self): form = AdvancedFilterForm(self._create_query_form_data(), instance=self.af, @@ -279,7 +279,7 @@ def test_remove_existing_query(self): class TestAdminInitialization(CommonFormTest): def setUp(self): - super(TestAdminInitialization, self).setUp() + super().setUp() self.fdata = self._create_query_form_data(form_number=0, data={ 'field': 'groups__name', 'negate': False, 'operator': 'iexact', 'value': 'bar'}) @@ -323,7 +323,7 @@ def test_field_resolution(self): def test_create_instance_with_modeladmin(self): form = AdvancedFilterForm(data=self.fdata, model_admin=self.rep_model_admin) - assert form.is_valid(), 'errors: %s, %s' % (form.errors, form.fields_formset.errors) + assert form.is_valid(), f'errors: {form.errors}, {form.fields_formset.errors}' instance = form.save(commit=False) instance.created_by = self.user assert isinstance(instance, AdvancedFilter) diff --git a/advanced_filters/tests/test_get_field_choices_view.py b/advanced_filters/tests/test_get_field_choices_view.py index e126326..d4aa954 100644 --- a/advanced_filters/tests/test_get_field_choices_view.py +++ b/advanced_filters/tests/test_get_field_choices_view.py @@ -140,7 +140,7 @@ def test_choices_no_date_fields_support(user, client, settings): def test_choices_has_null(user, client, settings): settings.ADVANCED_FILTERS_MAX_CHOICES = 4 named_users = ClientFactory.create_batch(2, assigned_to=user) - names = [None] + sorted(set([nu.first_name for nu in named_users])) + names = [None] + sorted({nu.first_name for nu in named_users}) assert len(named_users) == 2 ClientFactory.create_batch(2, assigned_to=user, first_name=None) view_url = reverse( diff --git a/advanced_filters/tests/test_q_serializer.py b/advanced_filters/tests/test_q_serializer.py index 41bae32..9912a31 100644 --- a/advanced_filters/tests/test_q_serializer.py +++ b/advanced_filters/tests/test_q_serializer.py @@ -8,7 +8,7 @@ class QSerializerTest(TestCase): correct_query = { 'children': [('test', 1234)], - 'connector': u'AND', + 'connector': 'AND', 'negated': False, } @@ -28,7 +28,7 @@ def test_jsondump_q(self): def test_deserialize_q(self): qres = self.s.deserialize({ 'children': [('test', 1234)], - 'connector': u'AND', + 'connector': 'AND', 'negated': False, 'subtree_parents': [] }) diff --git a/setup.py b/setup.py index 559a5e9..56c8214 100644 --- a/setup.py +++ b/setup.py @@ -35,11 +35,11 @@ def get_full_description(): readme = 'README.rst' changelog = 'CHANGELOG.rst' base = os.path.dirname(__file__) - with io.open(os.path.join(base, readme), encoding='utf-8') as readme: + with open(os.path.join(base, readme), encoding='utf-8') as readme: README = readme.read() - with io.open(os.path.join(base, changelog), encoding='utf-8') as changelog: + with open(os.path.join(base, changelog), encoding='utf-8') as changelog: CHANGELOG = changelog.read() - return '%s\n%s' % (README, CHANGELOG) + return f'{README}\n{CHANGELOG}' # allow setup.py to be run from any path diff --git a/tests/customers/migrations/0001_initial.py b/tests/customers/migrations/0001_initial.py index e6f6867..de6f1c5 100644 --- a/tests/customers/migrations/0001_initial.py +++ b/tests/customers/migrations/0001_initial.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.9.4 on 2016-03-13 22:23 -from __future__ import unicode_literals from django.conf import settings from django.db import migrations, models diff --git a/tests/factories.py b/tests/factories.py index 8fb039d..d5b2eb3 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -15,7 +15,7 @@ class Meta: @classmethod def _prepare(cls, create, **kwargs): password = kwargs.pop('password', None) - user = super(SalesRepFactory, cls)._prepare(create, **kwargs) + user = super()._prepare(create, **kwargs) if password: user.set_password(password) if create: diff --git a/tests/reps/migrations/0001_initial.py b/tests/reps/migrations/0001_initial.py index f6a2407..099a1e5 100644 --- a/tests/reps/migrations/0001_initial.py +++ b/tests/reps/migrations/0001_initial.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.9.4 on 2016-03-13 22:23 -from __future__ import unicode_literals import django.contrib.auth.models import django.core.validators From 5f2fc23e35ec1bf18be8ec72b04a97fa59ebb8ba Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 26 Aug 2021 23:01:45 +0300 Subject: [PATCH 04/21] Remove six --- advanced_filters/form_helpers.py | 2 -- advanced_filters/forms.py | 2 +- advanced_filters/mixins.py | 5 ----- advanced_filters/q_serializer.py | 3 +-- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/advanced_filters/form_helpers.py b/advanced_filters/form_helpers.py index 61b3b49..8fbe474 100644 --- a/advanced_filters/form_helpers.py +++ b/advanced_filters/form_helpers.py @@ -3,8 +3,6 @@ from django import forms -import six - logger = logging.getLogger('advanced_filters.form_helpers') extra_spaces_pattern = re.compile(r'\s+') diff --git a/advanced_filters/forms.py b/advanced_filters/forms.py index d77d7b8..e14794f 100644 --- a/advanced_filters/forms.py +++ b/advanced_filters/forms.py @@ -15,7 +15,7 @@ from django.db.models.fields import DateField from django.forms.formsets import formset_factory, BaseFormSet from django.utils.functional import cached_property -from six.moves import reduce +from functools import reduce from django.utils.text import capfirst from django.utils.translation import gettext_lazy as _ diff --git a/advanced_filters/mixins.py b/advanced_filters/mixins.py index 68e38b5..3a5be15 100644 --- a/advanced_filters/mixins.py +++ b/advanced_filters/mixins.py @@ -16,11 +16,6 @@ from django.utils.encoding import force_text as force_string from django.views.decorators.csrf import csrf_exempt -try: - from django.utils import six -except ImportError: - import six - class CsrfExemptMixin: """ diff --git a/advanced_filters/q_serializer.py b/advanced_filters/q_serializer.py index 9f2dac9..322cfcc 100644 --- a/advanced_filters/q_serializer.py +++ b/advanced_filters/q_serializer.py @@ -3,7 +3,6 @@ import base64 import time -import six from django.db.models import Q from django.core.serializers.base import SerializationError @@ -121,7 +120,7 @@ def dumps(self, obj): raise SerializationError string = json.dumps(self.serialize(obj), default=dt2ts) if self.b64_enabled: - return base64.b64encode(six.b(string)).decode("utf-8") + return base64.b64encode(string.encode("latin-1")).decode("utf-8") return string def loads(self, string, raw=False): From a8952dfd7f8e1886cf97a7c39fb0e3f1f63e4db1 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 27 Aug 2021 10:46:22 +0300 Subject: [PATCH 05/21] Drop support for EOL Django 3.0 --- setup.py | 1 - tox.ini | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 56c8214..92b2f1a 100644 --- a/setup.py +++ b/setup.py @@ -86,7 +86,6 @@ def get_full_description(): 'Programming Language :: Python :: 3 :: Only', 'Framework :: Django', 'Framework :: Django :: 2.2', - 'Framework :: Django :: 3.0', 'Framework :: Django :: 3.1', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', diff --git a/tox.ini b/tox.ini index dce1023..73ab869 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = - py{36,37,38,39,py3}-django{22,30,31} - ; py{36,37,38,39}-django{22,30,31,32} + py{36,37,38,39,py3}-django{22,31} + ; py{36,37,38,39}-django{22,31,32} report [pycodestyle] @@ -13,7 +13,6 @@ passenv = TRAVIS TRAVIS_* deps = -rtest-reqs.txt django22: Django>=2.2,<3.0 - django30: Django>=3.0,<3.1 django31: Django>=3.1,<3.2 ; django32: Django>=3.2,<3.3 @@ -37,7 +36,6 @@ python = [travis:env] DJANGO = 2.2: django22 - 3.0: django30 3.1: django31 ; 3.2: django32 @@ -52,7 +50,6 @@ python = [gh-actions:env] DJANGO = 2.2: django22 - 3.0: django30 3.1: django31 ; 3.2: django32 From 48e772a634f1f8feccdf43dbac2b4354f1d30b25 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 27 Aug 2021 11:22:30 +0300 Subject: [PATCH 06/21] Remove unused import --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 92b2f1a..fd1272f 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,3 @@ -import io import os import sys From a36aa8ac6e7a19b278c782c6e37b35cc48982027 Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Sun, 23 Jan 2022 13:19:35 +0000 Subject: [PATCH 07/21] feat: f-string for model_name string interpolation --- advanced_filters/admin.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/advanced_filters/admin.py b/advanced_filters/admin.py index 488baff..fce4831 100644 --- a/advanced_filters/admin.py +++ b/advanced_filters/admin.py @@ -22,16 +22,20 @@ class AdvancedListFilters(admin.SimpleListFilter): def lookups(self, request, model_admin): if not model_admin: - raise Exception('Cannot use AdvancedListFilters without a ' - 'model_admin') - model_name = "{}.{}".format(model_admin.model._meta.app_label, - model_admin.model._meta.object_name) + raise Exception( + "Cannot use AdvancedListFilters without a model_admin" + ) + model_name = ( + f"{model_admin.model._meta.app_label}." + f"{model_admin.model._meta.object_name}" + ) return AdvancedFilter.objects.filter_by_user(request.user).filter( model=model_name).values_list('id', 'title') def queryset(self, request, queryset): if self.value(): filters = AdvancedFilter.objects.filter(id=self.value()) + advfilter = None if hasattr(filters, 'first'): advfilter = filters.first() if not advfilter: From de5453415425627e89cd13ee2b55f39cc6751f6f Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Sun, 23 Jan 2022 13:31:39 +0000 Subject: [PATCH 08/21] Drop support for python 3.6 --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index fd1272f..5dffb85 100644 --- a/setup.py +++ b/setup.py @@ -78,7 +78,6 @@ def get_full_description(): 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', From f1e8b329de4ee8a2c06a495ebc248ddf6438c4f9 Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Sun, 23 Jan 2022 13:32:00 +0000 Subject: [PATCH 09/21] Add support for python 3.10 and Django 4.0 + start testing against these versions --- setup.py | 2 ++ tox.ini | 27 ++++++--------------------- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/setup.py b/setup.py index 5dffb85..61cbf74 100644 --- a/setup.py +++ b/setup.py @@ -81,10 +81,12 @@ def get_full_description(): 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3 :: Only', 'Framework :: Django', 'Framework :: Django :: 2.2', 'Framework :: Django :: 3.1', + 'Framework :: Django :: 4.0', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', ], diff --git a/tox.ini b/tox.ini index 82a1810..c7f387c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,6 @@ [tox] envlist = - py{36,37,38,39,py3}-django{22,31} - ; py{36,37,38,39}-django{22,31,32} + py{37,38,39,310,py3}-django{22,32,40} report clean @@ -10,12 +9,11 @@ max-line-length = 120 [testenv] usedevelop = true -passenv = TRAVIS TRAVIS_* deps = -rtest-reqs.txt django22: Django>=2.2,<3.0 - django31: Django>=3.1,<3.2 - ; django32: Django>=3.2,<3.3 + django32: Django>=3.2,<3.3 + django40: Django>=4.0,<4.1 depends = !report: clean @@ -27,32 +25,19 @@ commands = pytest --cov-append . pycodestyle --exclude=urls.py,migrations,.ropeproject -v advanced_filters -[travis] -python = - 3.6: py36 - 3.7: py37 - 3.9: py39 - pypy3: pypy3 - -[travis:env] -DJANGO = - 2.2: django22 - 3.1: django31 - ; 3.2: django32 - [gh-actions] python = - 3.6: py36 3.7: py37 3.8: py38 3.9: py39 + 3.10: py310 pypy3: pypy3 [gh-actions:env] DJANGO = 2.2: django22 - 3.1: django31 - ; 3.2: django32 + 3.2: django32 + 4.0: django40 [testenv:report] deps = coverage From 4a22fc1188adde27e9477c88a247d0eba7e6dd0f Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Sun, 23 Jan 2022 13:37:30 +0000 Subject: [PATCH 10/21] fixup! Add support for python 3.10 and Django 4.0 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7d52064..5683966 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: ["3.7", "3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v1 From b923e728ef75d7267256b9981d059317f7f4b106 Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Sun, 23 Jan 2022 13:47:57 +0000 Subject: [PATCH 11/21] Correct support matrix + Use the right python/django support matrix https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django + Correctly test on pypy https://github.com/ymyzk/tox-gh-actions#advanced-examples https://github.com/actions/setup-python#available-versions-of-pypy --- .github/workflows/test.yml | 2 +- tox.ini | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5683966..76317ed 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.7", "3.8", "3.9", "3.10", "pypy-3.7", "pypy-3.8"] steps: - uses: actions/checkout@v1 diff --git a/tox.ini b/tox.ini index c7f387c..09ffa1f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,8 @@ [tox] envlist = - py{37,38,39,310,py3}-django{22,32,40} + py{37,py37}-django{22,31,32} + py{38,39}-django{22,32,40} + py310-django{32,40} report clean @@ -31,7 +33,8 @@ python = 3.8: py38 3.9: py39 3.10: py310 - pypy3: pypy3 + pypy-3.7: pypy37 + pypy-3.8: pypy38 [gh-actions:env] DJANGO = From 84e2440d8dc26a21334d058b9e528dad2551577f Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Sun, 23 Jan 2022 15:00:23 +0000 Subject: [PATCH 12/21] Don't add empty form to AdvancedFilterFormSet.forms We should still be able to use empty_form fields in javascript, via the InlineAdminFormSet helper --- advanced_filters/forms.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/advanced_filters/forms.py b/advanced_filters/forms.py index e14794f..ab22e45 100644 --- a/advanced_filters/forms.py +++ b/advanced_filters/forms.py @@ -200,14 +200,6 @@ def get_form_kwargs(self, index): kwargs['model_fields'] = self.model_fields return kwargs - @cached_property - def forms(self): - # override the original property to include `model_fields` argument - forms = [self._construct_form(i, model_fields=self.model_fields) - for i in range(self.total_form_count())] - forms.append(self.empty_form) # add initial empty form - return forms - AFQFormSet = formset_factory( AdvancedFilterQueryForm, formset=AdvancedFilterFormSet, From 90412bc47f1b6c62af0cca4e633fc130f8e00b74 Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Sun, 23 Jan 2022 15:15:16 +0000 Subject: [PATCH 13/21] Simplify url path import django.conf.urls => django.conf url => path Docs: https://docs.djangoproject.com/en/2.2/ref/urls/#django.urls.path https://docs.djangoproject.com/en/4.0/releases/4.0/#features-removed-in-4-0 --- advanced_filters/urls.py | 6 +++--- tests/test_project/urls.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/advanced_filters/urls.py b/advanced_filters/urls.py index a8fd76e..4fc8320 100644 --- a/advanced_filters/urls.py +++ b/advanced_filters/urls.py @@ -1,14 +1,14 @@ -from django.conf.urls import url +from django.urls import path from advanced_filters.views import GetFieldChoices urlpatterns = [ - url(r'^field_choices/(?P.+)/(?P.+)/?', + path('field_choices///', GetFieldChoices.as_view(), name='afilters_get_field_choices'), # only to allow building dynamically - url(r'^field_choices/$', + path('field_choices/', GetFieldChoices.as_view(), name='afilters_get_field_choices'), ] diff --git a/tests/test_project/urls.py b/tests/test_project/urls.py index f3c5556..e249e36 100644 --- a/tests/test_project/urls.py +++ b/tests/test_project/urls.py @@ -1,7 +1,7 @@ -from django.conf.urls import include, url +from django.urls import include, path from django.contrib import admin urlpatterns = [ - url(r'^admin/', admin.site.urls), - url(r'^advanced_filters/', include('advanced_filters.urls')) + path('admin/', admin.site.urls), + path('advanced_filters/', include('advanced_filters.urls')) ] From a873b48325e83f8075ba5931e9f3ca44c11767a5 Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Sun, 23 Jan 2022 15:16:18 +0000 Subject: [PATCH 14/21] Remove standalone clean env from tox envlist This causes conflict when running tox in all envs locally, it'll be used on-demand as dependency of other envs --- .gitignore | 1 + tox.ini | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index dd56309..7190a6b 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ tests/db.sqlite* /tests/local.db /.venv .vscode/settings.json +pyrightconfig.json diff --git a/tox.ini b/tox.ini index 09ffa1f..8d2eb39 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,6 @@ envlist = py{38,39}-django{22,32,40} py310-django{32,40} report - clean [pycodestyle] max-line-length = 120 From 981b6f016740dbc9111f8dc4a8f01149ceb4c702 Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Sun, 23 Jan 2022 15:17:33 +0000 Subject: [PATCH 15/21] fix: remove unsupported django 3.1 from tox matrix EOLed --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 8d2eb39..25371e0 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = - py{37,py37}-django{22,31,32} - py{38,39}-django{22,32,40} + py{37,py37}-django{22,32} + py{38,py38,39}-django{22,32,40} py310-django{32,40} report From cb165c8af5dad489236cde2929846e85d86436f1 Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Sun, 23 Jan 2022 15:18:37 +0000 Subject: [PATCH 16/21] Remove unused cached_property import --- advanced_filters/forms.py | 1 - 1 file changed, 1 deletion(-) diff --git a/advanced_filters/forms.py b/advanced_filters/forms.py index ab22e45..0ffe761 100644 --- a/advanced_filters/forms.py +++ b/advanced_filters/forms.py @@ -14,7 +14,6 @@ from django.db.models import Q from django.db.models.fields import DateField from django.forms.formsets import formset_factory, BaseFormSet -from django.utils.functional import cached_property from functools import reduce from django.utils.text import capfirst from django.utils.translation import gettext_lazy as _ From e949bba4898c77162f4dd626a0b0413c7a803100 Mon Sep 17 00:00:00 2001 From: Fabrizio Corallini Date: Mon, 7 Jun 2021 09:22:04 +0200 Subject: [PATCH 17/21] fix italian traslation --- .../locale/it/LC_MESSAGES/django.po | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 advanced_filters/locale/it/LC_MESSAGES/django.po diff --git a/advanced_filters/locale/it/LC_MESSAGES/django.po b/advanced_filters/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000..910d6ca --- /dev/null +++ b/advanced_filters/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,146 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-01-25 09:49+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" +"%100>=11 && n%100<=14)? 2 : 3);\n" + +#: admin.py:24 +msgid "Advanced filters" +msgstr "Filtri avanzati" + +#: admin.py:81 +msgid "Advanced filter added successfully." +msgstr "Filtro aggiunto correttamente." + +#: forms.py:58 +msgid "Equals" +msgstr "Uguale" + +#: forms.py:59 +msgid "Contains" +msgstr "Contiene" + +#: forms.py:60 +msgid "One of" +msgstr "Uno di" + +#: forms.py:61 +msgid "DateTime Range" +msgstr "Intervallo data e ora" + +#: forms.py:62 +msgid "Is NULL" +msgstr "è Nullo" + +#: forms.py:63 +msgid "Is TRUE" +msgstr "è Vero" + +#: forms.py:64 +msgid "Is FALSE" +msgstr "è Falso" + +#: forms.py:65 +msgid "Less Than" +msgstr "Minore di" + +#: forms.py:66 +msgid "Greater Than" +msgstr "Maggiore di" + +#: forms.py:67 +msgid "Less Than or Equal To" +msgstr "Minore o uguale a" + +#: forms.py:68 +msgid "Greater Than or Equal To" +msgstr "Maggiore o uguale a" + +#: forms.py:72 +msgid "Or (mark an or between blocks)" +msgstr "O (inserisci un or tra i blocchi)" + +#: forms.py:76 +msgid "Field" +msgstr "Campo" + +#: forms.py:79 +msgid "Operator" +msgstr "Operatore" + +#: forms.py:81 +msgid "Value" +msgstr "Valore" + +#: forms.py:83 +msgid "Value from" +msgstr "Valore da" + +#: forms.py:85 +msgid "Value to" +msgstr "Valore a" + +#: forms.py:86 +msgid "Negate" +msgstr "Negazione" + +#: models.py:18 templates/admin/advanced_filters.html:16 +msgid "Advanced Filter" +msgstr "Filtro avanzato" + +#: models.py:19 +msgid "Advanced Filters" +msgstr "Filtri avanzati" + +#: models.py:22 +msgid "Title" +msgstr "Titolo" + +#: templates/admin/advanced_filters.html:16 +msgid "Edit" +msgstr "Modifica" + +#: templates/admin/advanced_filters.html:28 +msgid "Create advanced filter" +msgstr "Crea filtro avanzato" + +#: templates/admin/advanced_filters.html:66 +msgid "Save" +msgstr "Salva" + +#: templates/admin/advanced_filters.html:67 +#: templates/admin/advanced_filters/change_form.html:52 +msgid "Save & Filter Now!" +msgstr "Salva e filtra!" + +#: templates/admin/advanced_filters.html:68 +msgid "Cancel" +msgstr "Cancella" + +#: templates/admin/advanced_filters/change_form.html:16 +msgid "Change advanced filter" +msgstr "Cambia filtro avanzato" + +#: templates/admin/common_js_init.html:14 +msgid "Add another filter" +msgstr "Aggiungi un altro filtro" + +#: templates/admin/common_js_init.html:15 +msgid "Remove" +msgstr "Elimina" \ No newline at end of file From 2f187837449fc69a480b6d8854b27181db353442 Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Sun, 23 Jan 2022 15:48:03 +0000 Subject: [PATCH 18/21] Add compiled IT translation --- advanced_filters/locale/it/LC_MESSAGES/django.mo | Bin 0 -> 2006 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 advanced_filters/locale/it/LC_MESSAGES/django.mo diff --git a/advanced_filters/locale/it/LC_MESSAGES/django.mo b/advanced_filters/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..b7340cffd70c0752958c651c5d6b3a400f16a915 GIT binary patch literal 2006 zcmZvc&u<$=6vqb$6wI$ue*bFp8oH!sP4sbw7`~wK7>H#U2B7wxQLWsWrA#OcU@qOz_+!9va`OMpWzjvPhI5G9P zz(u%U;QKBiPBQPtg6leX0{jVl6#N}L4*m(g0{-LV>3f8@4{{NF3Oo(o4{kX5JopIY zAvgto1hW3e;Jx6d;6vbN;4|P?Alv@{9s_@LybeA9`B(5s@HdeC{Ox!XWV>77OW-jC z~S63OTIYQ=Xp~O zLz1ycca)``j#6duXivsn6;kDY>&`w6(u1b>LD2gu4|5qmwE9^58D?2jo zE10+#F6N4fQ>kO9StZk>?^p4#tp|!)Y%YROry}@ZB%?%Bw2DHpmf2Zq!~Y!u8B3Wa zwT-AIR4Las1Ku<`wHmYC>I9;O%*VEeyE_aUs*emrGgf4JqB&GnrpAh#j8QZa2urn{TWiIy6eJta1!TWH4hlp5yZv}o%zQesO+Bezn|n!)%1rABO6^S3(ZE5tH~tw+E&I%#08b8g04`bTn}g|sF$m?v*p#*R0B=JPrJ#wtd*OGD(7st?oG zhY>#dB6WA9O;qZ4+7<8AM32+zDeDDs*MvImm*~{CPIDX4$jZp87&}Nx6b~~o@fQ|p zy!B_}*9v*xFVM^k@iss07YcNS@&t9r%YO0T)snx!+sq=rR4CGwD`S)8!onO(3eLp$ z^ZY8)TlU|an4g|Id=9?|a}aaMhlev%go8Kc7IUK9@9R#WW z_hb|q3S}w@$K6U%NBeuaD@6w<07n+Chep&TQZiOis3&_vqfCdS*#C|y7*NBT2L2VY z4GuCab$nnq-rZ)!lXe-$R*6oGB=q61Vfttme-X;+q?@%76PH>^I-9rdj-C@iqz5{d dn7C-qttf&2W30^P%^(X59HKj*%!8d2{{sAW&nN%@ literal 0 HcmV?d00001 From 6f2c54574e7b5e7fd101c96fb78b71fea92614c1 Mon Sep 17 00:00:00 2001 From: Dmytro Litvinov Date: Mon, 24 Jan 2022 09:25:39 +0200 Subject: [PATCH 19/21] Add Django 3.2 to classifiers (#163) Co-authored-by: Pavel Savchenko --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 61cbf74..aa59a9e 100644 --- a/setup.py +++ b/setup.py @@ -86,6 +86,7 @@ def get_full_description(): 'Framework :: Django', 'Framework :: Django :: 2.2', 'Framework :: Django :: 3.1', + 'Framework :: Django :: 3.2', 'Framework :: Django :: 4.0', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', From 3911d2eb112f5731546ee2832449436fdb39e37c Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Mon, 27 Jun 2022 07:48:55 +0100 Subject: [PATCH 20/21] docs: update README and remove Django 3.1 classifier --- README.rst | 2 +- setup.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.rst b/README.rst index fc7c707..49c32fe 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ For release notes, see `Changelog = 3.1 on Python 3.6+/PyPy3 +- Django 2.2, >= 3.2 on Python 3.6+/PyPy3 - simplejson >= 3.6.5, < 4 diff --git a/setup.py b/setup.py index aa59a9e..761d3a5 100644 --- a/setup.py +++ b/setup.py @@ -85,7 +85,6 @@ def get_full_description(): 'Programming Language :: Python :: 3 :: Only', 'Framework :: Django', 'Framework :: Django :: 2.2', - 'Framework :: Django :: 3.1', 'Framework :: Django :: 3.2', 'Framework :: Django :: 4.0', 'Topic :: Internet :: WWW/HTTP', From 9e07b73a2b34715fe5ce3c455f6827bc0d078247 Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Mon, 27 Jun 2022 08:03:15 +0100 Subject: [PATCH 21/21] Chore: bump version and update changelog --- CHANGELOG.rst | 45 ++++++++++++++++++++++++++++++++++++ advanced_filters/__init__.py | 2 +- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f102cf3..c528541 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,51 @@ Changelog ========= +2.0.0 - Support Django 3.2 and 4.0 +---------------------------------- + +**BREAKING CHANGE:** This release is the first 2.x release, and drops support for EOL python and Django versions, all feature development will be done against 2.X branch.** + +Changes since 1.4.0: + +Features +~~~~~~~~ + +- Add support for python 3.10 and Django 4.0 (Merge 7bfb5b6) +- Add compiled IT translation (Merge e39395f) + +Bug fixes +~~~~~~~~~ + +- Don't add empty form to AdvancedFilterFormSet.forms (Merge 7bfb5b6) + +Other +~~~~~ + +- Drop support for EOL Python 2.7 and 3.5 (Merge dfeb005) +- Drop support for EOL Django 3.0 (Merge dfeb005) +- Drop support for EOL Django up to 2.2 (Merge dfeb005) +- Upgrade Python syntax with pyupgrade --py36-plus (Merge dfeb005) +- Remove six (Merge dfeb005) +- Remove unused import (Merge dfeb005) +- Drop support for python 3.6 (Merge 7bfb5b6) +- Correct support matrix (Merge 7bfb5b6) +- Simplify url path import (Merge 7bfb5b6) +- Remove standalone clean env from tox envlist (Merge 7bfb5b6) +- Remove unused cached_property import (Merge 7bfb5b6) +- Add Django 3.2 to classifiers (#163) +- f-string for model_name string interpolation (Merge dfeb005) +- remove unsupported django 3.1 from tox matrix (Merge 7bfb5b6) +- update README and remove Django 3.1 classifier + +Contributors +~~~~~~~~~~~~ + +- Fabrizio Corallini +- Dmytro Litvinov +- Hugo van Kemenade +- Pavel Savchenko + 1.4.0 - Latvian translation and minor fixes ------------------------------------------- diff --git a/advanced_filters/__init__.py b/advanced_filters/__init__.py index 96e3ce8..afced14 100644 --- a/advanced_filters/__init__.py +++ b/advanced_filters/__init__.py @@ -1 +1 @@ -__version__ = '1.4.0' +__version__ = '2.0.0'