Skip to content

Commit

Permalink
feature: Drop Python 3.7 support, Django 2.2, 3.0 and 3.1 support (dj…
Browse files Browse the repository at this point in the history
…ango-cms#1363)

* Updater to py38 dj32, update tox.ini and github actions

* Fix isort

* Update readme
  • Loading branch information
fsbraun authored Jun 19, 2023
1 parent 43434f7 commit 2780b6b
Show file tree
Hide file tree
Showing 26 changed files with 118 additions and 149 deletions.
22 changes: 0 additions & 22 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,11 @@ jobs:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11' ]
requirements-file: [
django-2.2.txt,
django-3.2.txt,
django-4.0.txt,
django-4.1.txt,
django-4.2.txt,
]
exclude:
- python-version: 3.7
requirements-file: django-4.0.txt
- python-version: 3.7
requirements-file: django-4.1.txt
- python-version: 3.7
requirements-file: django-4.2.txt
- python-version: 3.9
requirements-file: django-2.2.txt
- python-version: 3.10
requirements-file: django-2.2.txt
- python-version: 3.10
requirements-file: django-3.0.txt
- python-version: 3.10
requirements-file: django-3.1.txt
- python-version: 3.11
requirements-file: django-2.2.txt
- python-version: 3.11
requirements-file: django-3.0.txt
- python-version: 3.11
requirements-file: django-3.1.txt
os: [
ubuntu-20.04,
]
Expand Down
9 changes: 3 additions & 6 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Django Filer
============

|pypi| |build| |python| |django| |coverage|
|pypi| |python| |django| |coverage|

**django Filer** is a file management application for django that makes
handling of files and images a breeze.
Expand Down Expand Up @@ -52,12 +52,9 @@ for all the details on how to install, configure and use django-filer.

.. |pypi| image:: https://badge.fury.io/py/django-filer.svg
:target: http://badge.fury.io/py/django-filer
.. |build| image:: https://travis-ci.org/django-cms/django-filer.svg?branch=master
:target: https://travis-ci.org/django-cms/django-filer
.. |coverage| image:: https://codecov.io/gh/django-cms/django-filer/branch/master/graph/badge.svg
:target: https://codecov.io/gh/django-cms/django-filer

.. |python| image:: https://img.shields.io/badge/python-3.7+-blue.svg
.. |python| image:: https://img.shields.io/badge/python-3.8+-blue.svg
:target: https://pypi.org/project/django-filer/
.. |django| image:: https://img.shields.io/badge/django-2.2,%203.2,%204.1-blue.svg
.. |django| image:: https://img.shields.io/badge/django-3.2+-blue.svg
:target: https://www.djangoproject.com/
2 changes: 0 additions & 2 deletions filer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,3 @@
"""

__version__ = '2.3rc1'

default_app_config = 'filer.apps.FilerConfig'
34 changes: 17 additions & 17 deletions filer/admin/clipboardadmin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.contrib import admin, messages
from django.forms.models import modelform_factory
from django.http import JsonResponse
from django.urls import re_path
from django.urls import path
from django.utils.translation import gettext_lazy as _
from django.views.decorators.csrf import csrf_exempt

Expand Down Expand Up @@ -37,21 +37,21 @@ class ClipboardAdmin(admin.ModelAdmin):

def get_urls(self):
return [
re_path(r'^operations/paste_clipboard_to_folder/$',
self.admin_site.admin_view(views.paste_clipboard_to_folder),
name='filer-paste_clipboard_to_folder'),
re_path(r'^operations/discard_clipboard/$',
self.admin_site.admin_view(views.discard_clipboard),
name='filer-discard_clipboard'),
re_path(r'^operations/delete_clipboard/$',
self.admin_site.admin_view(views.delete_clipboard),
name='filer-delete_clipboard'),
re_path(r'^operations/upload/(?P<folder_id>[0-9]+)/$',
ajax_upload,
name='filer-ajax_upload'),
re_path(r'^operations/upload/no_folder/$',
ajax_upload,
name='filer-ajax_upload'),
path('operations/paste_clipboard_to_folder/',
self.admin_site.admin_view(views.paste_clipboard_to_folder),
name='filer-paste_clipboard_to_folder'),
path('operations/discard_clipboard/',
self.admin_site.admin_view(views.discard_clipboard),
name='filer-discard_clipboard'),
path('operations/delete_clipboard/',
self.admin_site.admin_view(views.delete_clipboard),
name='filer-delete_clipboard'),
path('operations/upload/<int:folder_id>/',
ajax_upload,
name='filer-ajax_upload'),
path('operations/upload/no_folder/',
ajax_upload,
name='filer-ajax_upload'),
] + super().get_urls()

def get_model_perms(self, *args, **kwargs):
Expand Down Expand Up @@ -144,7 +144,7 @@ def ajax_upload(request, folder_id=None):
data['original_image'] = file_obj.url
return JsonResponse(data)
else:
form_errors = '; '.join(['%s: %s' % (
form_errors = '; '.join(['{}: {}'.format(
field,
', '.join(errors)) for field, errors in list(
uploadform.errors.items())
Expand Down
6 changes: 3 additions & 3 deletions filer/admin/fileadmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def response_change(self, request, obj):
else:
url = reverse(
'admin:filer-directory_listing-unfiled_images')
url = "{0}{1}".format(
url = "{}{}".format(
url,
admin_url_params_encoded(request),
)
Expand Down Expand Up @@ -130,7 +130,7 @@ def delete_view(self, request, object_id, extra_context=None):
kwargs={'folder_id': parent_folder.id})
else:
url = reverse('admin:filer-directory_listing-unfiled_images')
url = "{0}{1}".format(
url = "{}{}".format(
url,
admin_url_params_encoded(request)
)
Expand All @@ -153,7 +153,7 @@ def get_model_perms(self, request):
def display_canonical(self, instance):
canonical = instance.canonical_url
if canonical:
return mark_safe('<a href="%s">%s</a>' % (canonical, canonical))
return mark_safe('<a href="{}">{}</a>'.format(canonical, canonical))
else:
return '-'
display_canonical.allow_tags = True
Expand Down
77 changes: 38 additions & 39 deletions filer/admin/folderadmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from django.db.models import OuterRef, Subquery
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import re_path, reverse
from django.urls import path, reverse
from django.utils.encoding import force_str
from django.utils.html import escape, format_html
from django.utils.safestring import mark_safe
Expand Down Expand Up @@ -131,7 +131,7 @@ def response_change(self, request, obj):
kwargs={'folder_id': obj.parent.id})
else:
url = reverse('admin:filer-directory_listing-root')
url = "{0}{1}".format(
url = "{}{}".format(
url,
admin_url_params_encoded(request),
)
Expand Down Expand Up @@ -184,7 +184,7 @@ def delete_view(self, request, object_id, extra_context=None):
kwargs={'folder_id': parent_folder.id})
else:
url = reverse('admin:filer-directory_listing-root')
url = "{0}{1}".format(
url = "{}{}".format(
url,
admin_url_params_encoded(request),
)
Expand All @@ -204,35 +204,35 @@ def get_urls(self):
return [
# we override the default list view with our own directory listing
# of the root directories
re_path(r'^$',
self.admin_site.admin_view(self.directory_listing),
name='filer-directory_listing-root'),

re_path(r'^last/$',
self.admin_site.admin_view(self.directory_listing),
{'viewtype': 'last'},
name='filer-directory_listing-last'),

re_path(r'^(?P<folder_id>\d+)/list/$',
self.admin_site.admin_view(self.directory_listing),
name='filer-directory_listing'),

re_path(r'^(?P<folder_id>\d+)/make_folder/$',
self.admin_site.admin_view(views.make_folder),
name='filer-directory_listing-make_folder'),
re_path(r'^make_folder/$',
self.admin_site.admin_view(views.make_folder),
name='filer-directory_listing-make_root_folder'),

re_path(r'^images_with_missing_data/$',
self.admin_site.admin_view(self.directory_listing),
{'viewtype': 'images_with_missing_data'},
name='filer-directory_listing-images_with_missing_data'),

re_path(r'^unfiled_images/$',
self.admin_site.admin_view(self.directory_listing),
{'viewtype': 'unfiled_images'},
name='filer-directory_listing-unfiled_images'),
path('',
self.admin_site.admin_view(self.directory_listing),
name='filer-directory_listing-root'),

path('last/',
self.admin_site.admin_view(self.directory_listing),
{'viewtype': 'last'},
name='filer-directory_listing-last'),

path('<int:folder_id>/list/',
self.admin_site.admin_view(self.directory_listing),
name='filer-directory_listing'),

path('<int:folder_id>/make_folder/',
self.admin_site.admin_view(views.make_folder),
name='filer-directory_listing-make_folder'),
path('make_folder/',
self.admin_site.admin_view(views.make_folder),
name='filer-directory_listing-make_root_folder'),

path('images_with_missing_data/',
self.admin_site.admin_view(self.directory_listing),
{'viewtype': 'images_with_missing_data'},
name='filer-directory_listing-images_with_missing_data'),

path('unfiled_images/',
self.admin_site.admin_view(self.directory_listing),
{'viewtype': 'unfiled_images'},
name='filer-directory_listing-unfiled_images'),
] + super().get_urls()

# custom views
Expand All @@ -252,10 +252,10 @@ def directory_listing(self, request, folder_id=None, viewtype=None):
self.get_queryset(request).get(id=last_folder_id)
except self.model.DoesNotExist:
url = reverse('admin:filer-directory_listing-root')
url = "%s%s" % (url, admin_url_params_encoded(request))
url = "{}{}".format(url, admin_url_params_encoded(request))
else:
url = reverse('admin:filer-directory_listing', kwargs={'folder_id': last_folder_id})
url = "%s%s" % (url, admin_url_params_encoded(request))
url = "{}{}".format(url, admin_url_params_encoded(request))
return HttpResponseRedirect(url)
elif folder_id is None:
folder = FolderRoot()
Expand Down Expand Up @@ -518,7 +518,7 @@ def owner_search_fields(self):

def get_owner_filter_lookups(self):
return [
'owner__{field}__icontains'.format(field=field)
f'owner__{field}__icontains'
for field in self.owner_search_fields
]

Expand Down Expand Up @@ -828,7 +828,7 @@ def _format_callback(self, obj, user, admin_site, perms_needed):
else:
# Don't display link to edit, because it either has no
# admin or is edited inline.
return '%s: %s' % (capfirst(opts.verbose_name), force_str(obj))
return '{}: {}'.format(capfirst(opts.verbose_name), force_str(obj))

def _check_copy_perms(self, request, files_queryset, folders_queryset):
try:
Expand Down Expand Up @@ -882,8 +882,7 @@ def _list_all_destination_folders_recursive(self, request, folders_queryset, cur
# We do not allow copying/moving back to the folder itself
enabled = (allow_self or fo != current_folder) and fo.has_add_children_permission(request)
yield (fo, (mark_safe(("&nbsp;&nbsp;" * level) + force_str(fo)), enabled))
for c in self._list_all_destination_folders_recursive(request, folders_queryset, current_folder, fo.children.all(), allow_self, level + 1):
yield c
yield from self._list_all_destination_folders_recursive(request, folders_queryset, current_folder, fo.children.all(), allow_self, level + 1)

def _list_all_destination_folders(self, request, folders_queryset, current_folder, allow_self):
root_folders = self.get_queryset(request).filter(parent__isnull=True).order_by('name')
Expand Down Expand Up @@ -1062,7 +1061,7 @@ def _get_available_name(self, destination, name):
count = itertools.count(1)
original = name
while destination.contains_folder(name):
name = "%s_%s" % (original, next(count))
name = "{}_{}".format(original, next(count))
return name

def _copy_folder(self, folder, destination, suffix, overwrite):
Expand Down
2 changes: 1 addition & 1 deletion filer/admin/permissionadmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Media:
css = {'all': ['filer/css/admin_folderpermissions.css']}

def get_queryset(self, request):
qs = super(PermissionAdmin, self).get_queryset(request)
qs = super().get_queryset(request)
return qs.prefetch_related("group", "folder")

def get_model_perms(self, request):
Expand Down
2 changes: 1 addition & 1 deletion filer/admin/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def admin_url_params_encoded(request, first_separator='?', params=None):
)
if not params:
return ''
return '{0}{1}'.format(first_separator, params)
return f'{first_separator}{params}'


class AdminContext(dict):
Expand Down
6 changes: 3 additions & 3 deletions filer/admin/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def paste_clipboard_to_folder(request):
if not redirect:
redirect = request.POST.get('redirect_to', '')
return HttpResponseRedirect(
'{0}?order_by=-modified_at{1}'.format(
'{}?order_by=-modified_at{}'.format(
redirect,
admin_url_params_encoded(request, first_separator='&'),
)
Expand All @@ -107,7 +107,7 @@ def discard_clipboard(request):
clipboard = Clipboard.objects.get(id=request.POST.get('clipboard_id'))
tools.discard_clipboard(clipboard)
return HttpResponseRedirect(
'{0}{1}'.format(
'{}{}'.format(
request.POST.get('redirect_to', ''),
admin_url_params_encoded(request, first_separator='&'),
)
Expand All @@ -124,7 +124,7 @@ def delete_clipboard(request):
clipboard = Clipboard.objects.get(id=request.POST.get('clipboard_id'))
tools.delete_clipboard(clipboard)
return HttpResponseRedirect(
'{0}{1}'.format(
'{}{}'.format(
request.POST.get('redirect_to', ''),
admin_url_params_encoded(request, first_separator='&'),
)
Expand Down
2 changes: 1 addition & 1 deletion filer/fields/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def render(self, name, value, attrs=None, renderer=None):
hidden_input = super(ForeignKeyRawIdWidget, self).render(name, value, attrs) # grandparent super
context = {
'hidden_input': hidden_input,
'lookup_url': '%s%s' % (related_url, lookup_url),
'lookup_url': '{}{}'.format(related_url, lookup_url),
'change_url': change_url,
'object': obj,
'lookup_name': name,
Expand Down
4 changes: 2 additions & 2 deletions filer/fields/folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def render(self, name, value, attrs=None, renderer=None):
# API to determine the ID dynamically.
context = {
'hidden_input': hidden_input,
'lookup_url': '%s%s' % (related_url, url),
'lookup_url': '{}{}'.format(related_url, url),
'lookup_name': name,
'span_id': css_id_description_txt,
'object': obj,
Expand Down Expand Up @@ -115,7 +115,7 @@ def __init__(self, **kwargs):
if "to" in kwargs.keys(): # pragma: no cover
old_to = get_model_label(kwargs.pop("to"))
if old_to.lower() != dfl.lower():
msg = "%s can only be a ForeignKey to %s; %s passed" % (
msg = "{} can only be a ForeignKey to {}; {} passed".format(
self.__class__.__name__, dfl, old_to
)
warnings.warn(msg, SyntaxWarning)
Expand Down
6 changes: 3 additions & 3 deletions filer/fields/multistorage_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def __set__(self, instance, value):
# To prevent recalculating upon reassignment of the same file, update only if value is
# different than the previous one.
if prev_assigned and value != previous_file:
callback_attr = '{}_data_changed'.format(self.field.name)
callback_attr = f'{self.field.name}_data_changed'
if hasattr(instance, callback_attr):
getattr(instance, callback_attr)()

Expand Down Expand Up @@ -158,8 +158,8 @@ def value_to_string(self, obj):
payload_file.seek(0)
encoded_string = base64.b64encode(payload_file.read()).decode('utf-8')
return value, encoded_string
except IOError:
warnings.warn('The payload for "%s" is missing. No such file on disk: %s!' % (obj.original_filename, self.storage.location))
except OSError:
warnings.warn('The payload for "{}" is missing. No such file on disk: {}!'.format(obj.original_filename, self.storage.location))
return value

def to_python(self, value):
Expand Down
Loading

0 comments on commit 2780b6b

Please sign in to comment.