Skip to content

Commit 5f56318

Browse files
authored
Merge pull request #167 from modlinltd/release/2.0
Release 2.0.0
2 parents 985450e + 9e07b73 commit 5f56318

29 files changed

+290
-233
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
runs-on: ubuntu-latest
99
strategy:
1010
matrix:
11-
python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9]
11+
python-version: ["3.7", "3.8", "3.9", "3.10", "pypy-3.7", "pypy-3.8"]
1212

1313
steps:
1414
- uses: actions/checkout@v1

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ tests/db.sqlite*
1818
/tests/local.db
1919
/.venv
2020
.vscode/settings.json
21+
pyrightconfig.json

CHANGELOG.rst

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,51 @@
11
Changelog
22
=========
33

4+
2.0.0 - Support Django 3.2 and 4.0
5+
----------------------------------
6+
7+
**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.**
8+
9+
Changes since 1.4.0:
10+
11+
Features
12+
~~~~~~~~
13+
14+
- Add support for python 3.10 and Django 4.0 (Merge 7bfb5b6)
15+
- Add compiled IT translation (Merge e39395f)
16+
17+
Bug fixes
18+
~~~~~~~~~
19+
20+
- Don't add empty form to AdvancedFilterFormSet.forms (Merge 7bfb5b6)
21+
22+
Other
23+
~~~~~
24+
25+
- Drop support for EOL Python 2.7 and 3.5 (Merge dfeb005)
26+
- Drop support for EOL Django 3.0 (Merge dfeb005)
27+
- Drop support for EOL Django up to 2.2 (Merge dfeb005)
28+
- Upgrade Python syntax with pyupgrade --py36-plus (Merge dfeb005)
29+
- Remove six (Merge dfeb005)
30+
- Remove unused import (Merge dfeb005)
31+
- Drop support for python 3.6 (Merge 7bfb5b6)
32+
- Correct support matrix (Merge 7bfb5b6)
33+
- Simplify url path import (Merge 7bfb5b6)
34+
- Remove standalone clean env from tox envlist (Merge 7bfb5b6)
35+
- Remove unused cached_property import (Merge 7bfb5b6)
36+
- Add Django 3.2 to classifiers (#163)
37+
- f-string for model_name string interpolation (Merge dfeb005)
38+
- remove unsupported django 3.1 from tox matrix (Merge 7bfb5b6)
39+
- update README and remove Django 3.1 classifier
40+
41+
Contributors
42+
~~~~~~~~~~~~
43+
44+
- Fabrizio Corallini
45+
- Dmytro Litvinov
46+
- Hugo van Kemenade
47+
- Pavel Savchenko
48+
449
1.4.0 - Latvian translation and minor fixes
550
-------------------------------------------
651

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ For release notes, see `Changelog <https://raw.githubusercontent.com/modlinltd/d
2626
Requirements
2727
============
2828

29-
- Django >= 1.9 (Django 1.9 - 3.1 on Python 2/3/PyPy3)
29+
- Django 2.2, >= 3.2 on Python 3.6+/PyPy3
3030
- simplejson >= 3.6.5, < 4
3131

3232

advanced_filters/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '1.4.0'
1+
__version__ = '2.0.0'

advanced_filters/admin.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,11 @@
55
from django.contrib.admin.utils import unquote
66
from django.http import HttpResponseRedirect
77
from django.shortcuts import resolve_url
8+
from django.utils.translation import gettext_lazy as _
89

910
from .forms import AdvancedFilterForm
1011
from .models import AdvancedFilter
1112

12-
# django < 1.9 support
13-
from django import VERSION
14-
if VERSION >= (2, 0):
15-
from django.utils.translation import gettext_lazy as _
16-
else:
17-
from django.utils.translation import ugettext_lazy as _
18-
1913

2014
logger = logging.getLogger('advanced_filters.admin')
2115

@@ -28,16 +22,20 @@ class AdvancedListFilters(admin.SimpleListFilter):
2822

2923
def lookups(self, request, model_admin):
3024
if not model_admin:
31-
raise Exception('Cannot use AdvancedListFilters without a '
32-
'model_admin')
33-
model_name = "%s.%s" % (model_admin.model._meta.app_label,
34-
model_admin.model._meta.object_name)
25+
raise Exception(
26+
"Cannot use AdvancedListFilters without a model_admin"
27+
)
28+
model_name = (
29+
f"{model_admin.model._meta.app_label}."
30+
f"{model_admin.model._meta.object_name}"
31+
)
3532
return AdvancedFilter.objects.filter_by_user(request.user).filter(
3633
model=model_name).values_list('id', 'title')
3734

3835
def queryset(self, request, queryset):
3936
if self.value():
4037
filters = AdvancedFilter.objects.filter(id=self.value())
38+
advfilter = None
4139
if hasattr(filters, 'first'):
4240
advfilter = filters.first()
4341
if not advfilter:
@@ -49,14 +47,17 @@ def queryset(self, request, queryset):
4947
return queryset
5048

5149

52-
class AdminAdvancedFiltersMixin(object):
50+
class AdminAdvancedFiltersMixin:
5351
""" Generic AdvancedFilters mixin """
5452
advanced_change_list_template = "admin/advanced_filters.html"
5553
advanced_filter_form = AdvancedFilterForm
5654

5755
def __init__(self, *args, **kwargs):
58-
super(AdminAdvancedFiltersMixin, self).__init__(*args, **kwargs)
59-
self.original_change_list_template = "admin/change_list.html"
56+
super().__init__(*args, **kwargs)
57+
if self.change_list_template:
58+
self.original_change_list_template = self.change_list_template
59+
else:
60+
self.original_change_list_template = "admin/change_list.html"
6061
self.change_list_template = self.advanced_change_list_template
6162
# add list filters to filters
6263
self.list_filter = (AdvancedListFilters,) + tuple(self.list_filter)
@@ -103,8 +104,7 @@ def changelist_view(self, request, extra_context=None):
103104
if response:
104105
return response
105106

106-
return super(AdminAdvancedFiltersMixin, self
107-
).changelist_view(request, extra_context=extra_context)
107+
return super().changelist_view(request, extra_context=extra_context)
108108

109109

110110
class AdvancedFilterAdmin(admin.ModelAdmin):
@@ -123,20 +123,20 @@ def save_model(self, request, new_object, *args, **kwargs):
123123
if new_object and not new_object.pk:
124124
new_object.created_by = request.user
125125

126-
super(AdvancedFilterAdmin, self).save_model(
126+
super().save_model(
127127
request, new_object, *args, **kwargs)
128128

129129
def change_view(self, request, object_id, form_url='', extra_context=None):
130-
orig_response = super(AdvancedFilterAdmin, self).change_view(
130+
orig_response = super().change_view(
131131
request, object_id, form_url, extra_context)
132132
if '_save_goto' in request.POST:
133133
obj = self.get_object(request, unquote(object_id))
134134
if obj:
135135
app, model = obj.model.split('.')
136-
path = resolve_url('admin:%s_%s_changelist' % (
136+
path = resolve_url('admin:{}_{}_changelist'.format(
137137
app, model.lower()))
138138
url = "{path}{qparams}".format(
139-
path=path, qparams="?_afilter={id}".format(id=object_id))
139+
path=path, qparams=f"?_afilter={object_id}")
140140
return HttpResponseRedirect(url)
141141
return orig_response
142142

@@ -147,18 +147,18 @@ def user_has_permission(user):
147147

148148
def get_queryset(self, request):
149149
if self.user_has_permission(request.user):
150-
return super(AdvancedFilterAdmin, self).get_queryset(request)
150+
return super().get_queryset(request)
151151
else:
152152
return self.model.objects.filter_by_user(request.user)
153153

154154
def has_change_permission(self, request, obj=None):
155155
if obj is None:
156-
return super(AdvancedFilterAdmin, self).has_change_permission(request)
156+
return super().has_change_permission(request)
157157
return self.user_has_permission(request.user) or obj in self.model.objects.filter_by_user(request.user)
158158

159159
def has_delete_permission(self, request, obj=None):
160160
if obj is None:
161-
return super(AdvancedFilterAdmin, self).has_delete_permission(request)
161+
return super().has_delete_permission(request)
162162
return self.user_has_permission(request.user) or obj in self.model.objects.filter_by_user(request.user)
163163

164164

advanced_filters/form_helpers.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
from django import forms
55

6-
import six
7-
86
logger = logging.getLogger('advanced_filters.form_helpers')
97

108
extra_spaces_pattern = re.compile(r'\s+')
@@ -29,7 +27,7 @@ def to_python(self, value):
2927
>>> assert field.to_python('and,me') == '(and|me)'
3028
>>> assert field.to_python('and,me;too') == '(and|me;too)'
3129
"""
32-
res = super(VaryingTypeCharField, self).to_python(value)
30+
res = super().to_python(value)
3331
split_res = res.split(self._default_separator)
3432
if not res or len(split_res) < 2:
3533
return res.strip()
@@ -40,7 +38,7 @@ def to_python(self, value):
4038
return res
4139

4240

43-
class CleanWhiteSpacesMixin(object):
41+
class CleanWhiteSpacesMixin:
4442
"""
4543
This mixin, when added to any form subclass, adds a clean method which
4644
strips repeating spaces in and around each string value of "clean_data".
@@ -55,9 +53,9 @@ def clean(self):
5553
>>> assert form.is_valid()
5654
>>> assert form.cleaned_data == {'some_field': 'a weird value'}
5755
"""
58-
cleaned_data = super(CleanWhiteSpacesMixin, self).clean()
56+
cleaned_data = super().clean()
5957
for k in self.cleaned_data:
60-
if isinstance(self.cleaned_data[k], six.string_types):
58+
if isinstance(self.cleaned_data[k], str):
6159
cleaned_data[k] = re.sub(extra_spaces_pattern, ' ',
6260
self.cleaned_data[k] or '').strip()
6361
return cleaned_data

advanced_filters/forms.py

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,14 @@
1414
from django.db.models import Q
1515
from django.db.models.fields import DateField
1616
from django.forms.formsets import formset_factory, BaseFormSet
17-
from django.utils.functional import cached_property
18-
from six.moves import range, reduce
17+
from functools import reduce
1918
from django.utils.text import capfirst
19+
from django.utils.translation import gettext_lazy as _
2020

2121
from .models import AdvancedFilter
2222
from .form_helpers import CleanWhiteSpacesMixin, VaryingTypeCharField
2323

24-
# django < 1.9 support
25-
from django import VERSION
26-
if VERSION >= (2, 0):
27-
from django.utils.translation import gettext_lazy as _
28-
else:
29-
from django.utils.translation import ugettext_lazy as _
3024

31-
32-
# django < 1.9 support
33-
USE_VENDOR_DIR = VERSION >= (1, 9)
3425
logger = logging.getLogger('advanced_filters.forms')
3526

3627
# select2 location can be modified via settings
@@ -84,7 +75,7 @@ def _build_field_choices(self, fields):
8475
Iterate over passed model fields tuple and update initial choices.
8576
"""
8677
return tuple(sorted(
87-
[(fquery, capfirst(fname)) for fquery, fname in fields.items()],
78+
((fquery, capfirst(fname)) for fquery, fname in fields.items()),
8879
key=lambda f: f[1].lower())
8980
) + self.FIELD_CHOICES
9081

@@ -166,7 +157,7 @@ def set_range_value(self, data):
166157
data['value'] = (dtfrom, dtto)
167158

168159
def clean(self):
169-
cleaned_data = super(AdvancedFilterQueryForm, self).clean()
160+
cleaned_data = super().clean()
170161
if cleaned_data.get('operator') == "range":
171162
if ('value_from' in cleaned_data and
172163
'value_to' in cleaned_data):
@@ -184,7 +175,7 @@ def make_query(self, *args, **kwargs):
184175
return query
185176

186177
def __init__(self, model_fields={}, *args, **kwargs):
187-
super(AdvancedFilterQueryForm, self).__init__(*args, **kwargs)
178+
super().__init__(*args, **kwargs)
188179
self.FIELD_CHOICES = self._build_field_choices(model_fields)
189180
self.fields['field'].choices = self.FIELD_CHOICES
190181
if not self.fields['field'].initial:
@@ -198,24 +189,16 @@ class AdvancedFilterFormSet(BaseFormSet):
198189

199190
def __init__(self, *args, **kwargs):
200191
self.model_fields = kwargs.pop('model_fields', {})
201-
super(AdvancedFilterFormSet, self).__init__(*args, **kwargs)
192+
super().__init__(*args, **kwargs)
202193
if self.forms:
203194
form = self.forms[0]
204195
self.fields = form.visible_fields()
205196

206197
def get_form_kwargs(self, index):
207-
kwargs = super(AdvancedFilterFormSet, self).get_form_kwargs(index)
198+
kwargs = super().get_form_kwargs(index)
208199
kwargs['model_fields'] = self.model_fields
209200
return kwargs
210201

211-
@cached_property
212-
def forms(self):
213-
# override the original property to include `model_fields` argument
214-
forms = [self._construct_form(i, model_fields=self.model_fields)
215-
for i in range(self.total_form_count())]
216-
forms.append(self.empty_form) # add initial empty form
217-
return forms
218-
219202

220203
AFQFormSet = formset_factory(
221204
AdvancedFilterQueryForm, formset=AdvancedFilterFormSet,
@@ -234,7 +217,7 @@ class Meta:
234217

235218
class Media:
236219
required_js = [
237-
'admin/js/%sjquery.min.js' % ('vendor/jquery/' if USE_VENDOR_DIR else ''),
220+
'admin/js/vendor/jquery/jquery.min.js',
238221
'advanced-filters/jquery_adder.js',
239222
'orig_inlines%s.js' % ('' if settings.DEBUG else '.min'),
240223
'magnific-popup/jquery.magnific-popup.js',
@@ -294,7 +277,7 @@ def __init__(self, *args, **kwargs):
294277
self._filter_fields = filter_fields or getattr(
295278
model_admin, 'advanced_filter_fields', ())
296279

297-
super(AdvancedFilterForm, self).__init__(*args, **kwargs)
280+
super().__init__(*args, **kwargs)
298281

299282
# populate existing or empty forms formset
300283
data = None
@@ -305,15 +288,15 @@ def __init__(self, *args, **kwargs):
305288
self.initialize_form(instance, self._model, data, extra_form)
306289

307290
def clean(self):
308-
cleaned_data = super(AdvancedFilterForm, self).clean()
291+
cleaned_data = super().clean()
309292
if not self.fields_formset.is_valid():
310293
logger.debug(
311294
"Errors validating advanced query filters: %s",
312295
pformat([(f.errors, f.non_field_errors())
313296
for f in self.fields_formset.forms]))
314297
raise forms.ValidationError("Error validating filter forms")
315-
cleaned_data['model'] = "%s.%s" % (self._model._meta.app_label,
316-
self._model._meta.object_name)
298+
cleaned_data['model'] = "{}.{}".format(self._model._meta.app_label,
299+
self._model._meta.object_name)
317300
return cleaned_data
318301

319302
@property
@@ -364,4 +347,4 @@ def initialize_form(self, instance, model, data=None, extra=None):
364347
def save(self, commit=True):
365348
self.instance.query = self.generate_query()
366349
self.instance.model = self.cleaned_data.get('model')
367-
return super(AdvancedFilterForm, self).save(commit)
350+
return super().save(commit)
1.96 KB
Binary file not shown.

0 commit comments

Comments
 (0)