Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop support for EOL Python and Django #151

Merged
merged 8 commits into from
Jan 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.6, 3.7, 3.8, 3.9]

steps:
- uses: actions/checkout@v1
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.1 on Python 3.6+/PyPy3
- simplejson >= 3.6.5, < 4


Expand Down
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
34 changes: 13 additions & 21 deletions advanced_filters/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,14 @@
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 +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

Expand Down Expand Up @@ -166,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):
Expand All @@ -184,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:
Expand All @@ -198,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

Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -294,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
Expand All @@ -305,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
Expand Down Expand Up @@ -364,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)
2 changes: 0 additions & 2 deletions advanced_filters/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
3 changes: 0 additions & 3 deletions advanced_filters/migrations/0002_advancedfilter_created_at.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


Expand Down
17 changes: 6 additions & 11 deletions advanced_filters/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,8 @@
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(object):
class CsrfExemptMixin:
"""
Exempts the view from CSRF requirements.
NOTE:
Expand All @@ -31,10 +26,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.
Expand Down Expand Up @@ -104,11 +99,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.
Expand All @@ -120,7 +115,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(
Expand Down
8 changes: 1 addition & 7 deletions advanced_filters/models.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down
5 changes: 2 additions & 3 deletions advanced_filters/q_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import base64
import time

import six
from django.db.models import Q
from django.core.serializers.base import SerializationError

Expand All @@ -25,7 +24,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.
Expand Down Expand Up @@ -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):
Expand Down
Loading