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

fix: Remove user from autocomplete fields if (swapped) user model does not have search fields #1517

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
21 changes: 11 additions & 10 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,31 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.8, 3.9, '3.10', '3.11', '3.12']
python-version: [3.9, '3.10', '3.11', '3.12', '3.13']
requirements-file: [
django-4.2.txt,
django-5.0.txt,
django-5.1.txt,
django-5.2.txt,
django-main.txt,
]
custom-image-model: [false, true]
os: [
ubuntu-20.04,
ubuntu-latest,
]
exclude:
- requirements-file: django-5.0.txt
python-version: 3.8
- requirements-file: django-5.0.txt
python-version: 3.9
- requirements-file: django-5.1.txt
python-version: 3.8
- requirements-file: django-5.1.txt
python-version: 3.9
- requirements-file: django-main.txt
python-version: 3.8
- requirements-file: django-5.2.txt
python-version: 3.9
- requirements-file: django-main.txt
python-version: 3.9
- requirements-file: django-main.txt
python-version: 3.10
- requirements-file: django-main.txt
python-version: 3.11

steps:
- uses: actions/checkout@v1
Expand All @@ -40,7 +41,7 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: library prerequisites
run: sudo apt-get install python-dev libpq-dev libmagic1 gcc libxml2-dev libxslt1-dev libjpeg62 libopenjp2-7 -y
run: sudo apt-get install python-dev-is-python3 libpq-dev libmagic1 gcc libxml2-dev libxslt1-dev libjpeg62 libopenjp2-7 -y
- name: Install extra dependencies
run: pip install lxml
if: matrix.python-version == '3.10'
Expand All @@ -53,6 +54,6 @@ jobs:
run: echo "CUSTOM_IMAGE=custom_image.Image" >> $GITHUB_ENV
if: ${{ matrix.custom-image-model }}
- name: Run coverage
run: coverage run ./tests/settings.py
run: coverage run ./tests/settings.py
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v1
2 changes: 1 addition & 1 deletion filer/admin/fileadmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class FileAdmin(PrimitivePermissionAwareModelAdmin):
list_display = ('label',)
list_per_page = 10
search_fields = ['name', 'original_filename', 'sha1', 'description']
autocomplete_fields = ('owner',)
autocomplete_fields = ['owner']
readonly_fields = ('sha1', 'display_canonical')

form = FileAdminChangeFrom
Expand Down
36 changes: 27 additions & 9 deletions filer/admin/folderadmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from urllib.parse import quote as urlquote
from urllib.parse import unquote as urlunquote

from django import VERSION as DJANGO_VERSION
from django import forms
from django.conf import settings as django_settings
from django.contrib import messages
Expand Down Expand Up @@ -777,9 +778,15 @@ def delete_files_or_folders(self, request, files_queryset, folders_queryset):
n = files_queryset.count() + folders_queryset.count()
if n:
# delete all explicitly selected files
for f in files_queryset:
self.log_deletion(request, f, force_str(f))
f.delete()
if DJANGO_VERSION >= (5, 1):
self.log_deletions(request, files_queryset)
# Still need to delete files individually (not only the database entries)
for f in files_queryset:
f.delete()
else:
for f in files_queryset:
self.log_deletion(request, f, force_str(f))
f.delete()
# delete all files in all selected folders and their children
# This would happen automatically by ways of the delete
# cascade, but then the individual .delete() methods won't be
Expand All @@ -788,13 +795,24 @@ def delete_files_or_folders(self, request, files_queryset, folders_queryset):
for folder in folders_queryset:
folder_ids.add(folder.id)
folder_ids.update(folder.get_descendants_ids())
for f in File.objects.filter(folder__in=folder_ids):
self.log_deletion(request, f, force_str(f))
f.delete()
if DJANGO_VERSION >= (5, 1):
qs = File.objects.filter(folder__in=folder_ids)
self.log_deletions(request, qs)
# Still need to delete files individually (not only the database entries)
for f in qs:
f.delete()
else:
for f in File.objects.filter(folder__in=folder_ids):
self.log_deletion(request, f, force_str(f))
f.delete()
# delete all folders
for f in folders_queryset:
self.log_deletion(request, f, force_str(f))
f.delete()
if DJANGO_VERSION >= (5, 1):
self.log_deletions(request, files_queryset)
folders_queryset.delete()
else:
for f in folders_queryset:
self.log_deletion(request, f, force_str(f))
f.delete()
self.message_user(request, _("Successfully deleted %(count)d files and/or folders.") % {"count": n, })
# Return None to display the change list page again.
return None
Expand Down
14 changes: 14 additions & 0 deletions filer/admin/permissionadmin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from django import VERSION as django_version
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _

from .. import settings
Expand All @@ -19,6 +21,18 @@ class PermissionAdmin(admin.ModelAdmin):
class Media:
css = {'all': ['filer/css/admin_folderpermissions.css']}

def get_autocomplete_fields(self, request):
"""Remove "owner" from autocomplete_fields is User model has no search_fields"""

autocomplete_fields = super().get_autocomplete_fields(request)
if django_version >= (5, 0):
user_admin = self.admin_site.get_model_admin(get_user_model())
else:
user_admin = self.admin_site._registry[get_user_model()]
if not user_admin.get_search_fields(request):
autocomplete_fields.remove('user')
return autocomplete_fields

def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.prefetch_related("group", "folder")
Expand Down
14 changes: 14 additions & 0 deletions filer/admin/permissions.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
from django import VERSION as django_version
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.urls import reverse


class PrimitivePermissionAwareModelAdmin(admin.ModelAdmin):
def get_autocomplete_fields(self, request):
"""Remove "owner" from autocomplete_fields is User model has no search_fields"""

autocomplete_fields = super().get_autocomplete_fields(request)
if django_version >= (5, 0):
user_admin = self.admin_site.get_model_admin(get_user_model())
else:
user_admin = self.admin_site._registry[get_user_model()]
if not user_admin.get_search_fields(request) and 'owner' in autocomplete_fields:
autocomplete_fields.remove('owner')
return autocomplete_fields

def has_add_permission(self, request):
# we don't have a "add" permission... but all adding is handled
# by special methods that go around these permissions anyway
Expand Down
4 changes: 4 additions & 0 deletions tests/requirements/django-5.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-r base.txt

django>=5.2a1,<5.3
django_polymorphic>=3.1
8 changes: 6 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ envlist =
isort
docs
frontend
py{310,311,312}-{dj42,dj50,djmain}-{swap,noswap}
py{310,311,312}-{dj42,dj50,dj51,dj52}-{swap,noswap}
py{312,313,312}-{djmain}-{swap,noswap}

[gh-actions]
python =
3.10: py310
3.11: py311
3.12: py312
3.13: py313

skip_missing_interpreters=True

Expand All @@ -21,11 +23,13 @@ allowlist_externals =
deps =
dj42: -r tests/requirements/django-4.2.txt
dj50: -r tests/requirements/django-5.0.txt
dj51: -r tests/requirements/django-5.1.txt
dj52: -r tests/requirements/django-5.2.txt
djmain: -r tests/requirements/django-main.txt
commands =
{envpython} --version
{env:COMMAND:coverage} erase
{env:COMMAND:coverage} run setup.py test
{env:COMMAND:coverage} run tests/settings.py
{env:COMMAND:coverage} report
setenv =
swap: CUSTOM_IMAGE=custom_image.Image
Expand Down
Loading