Skip to content

Commit

Permalink
Merge pull request #167 from modlinltd/release/2.0
Browse files Browse the repository at this point in the history
Release 2.0.0
  • Loading branch information
asfaltboy authored Jun 27, 2022
2 parents 985450e + 9e07b73 commit 5f56318
Show file tree
Hide file tree
Showing 29 changed files with 290 additions and 233 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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.7", "3.8", "3.9", "3.10", "pypy-3.7", "pypy-3.8"]

steps:
- uses: actions/checkout@v1
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ tests/db.sqlite*
/tests/local.db
/.venv
.vscode/settings.json
pyrightconfig.json
45 changes: 45 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -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
-------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ For release notes, see `Changelog <https://raw.githubusercontent.com/modlinltd/d
Requirements
============

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


Expand Down
2 changes: 1 addition & 1 deletion advanced_filters/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.4.0'
__version__ = '2.0.0'
46 changes: 23 additions & 23 deletions advanced_filters/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand All @@ -28,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 = "%s.%s" % (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:
Expand All @@ -49,14 +47,17 @@ 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)
self.original_change_list_template = "admin/change_list.html"
super().__init__(*args, **kwargs)
if self.change_list_template:
self.original_change_list_template = self.change_list_template
else:
self.original_change_list_template = "admin/change_list.html"
self.change_list_template = self.advanced_change_list_template
# add list filters to filters
self.list_filter = (AdvancedListFilters,) + tuple(self.list_filter)
Expand Down Expand Up @@ -103,8 +104,7 @@ def changelist_view(self, request, extra_context=None):
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):
Expand All @@ -123,20 +123,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

Expand All @@ -147,18 +147,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)


Expand Down
10 changes: 4 additions & 6 deletions advanced_filters/form_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

from django import forms

import six

logger = logging.getLogger('advanced_filters.form_helpers')

extra_spaces_pattern = re.compile(r'\s+')
Expand All @@ -29,7 +27,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()
Expand All @@ -40,7 +38,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".
Expand All @@ -55,9 +53,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
43 changes: 13 additions & 30 deletions advanced_filters/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,14 @@
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 six.moves import range, reduce
from functools import 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
Expand Down Expand Up @@ -84,7 +75,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

Expand Down Expand Up @@ -166,7 +157,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):
Expand All @@ -184,7 +175,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:
Expand All @@ -198,24 +189,16 @@ 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

@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,
Expand All @@ -234,7 +217,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',
Expand Down Expand Up @@ -294,7 +277,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
Expand All @@ -305,15 +288,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
Expand Down Expand Up @@ -364,4 +347,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)
Binary file added advanced_filters/locale/it/LC_MESSAGES/django.mo
Binary file not shown.
Loading

0 comments on commit 5f56318

Please sign in to comment.