extensions-website/files/admin.py
Oleg Komarov 3922f5d868 Version: drop file field, use files instead (#193)
Part of #74.

This PR drops Extension.file field, and introduces a cached_property Extension.file
as a temporary replacement for existing code references to the dropped field.

VersionFiles is renamed to VersionFile to match naming conventions.
All reads and writes go only via VersionFile cross-table, still assuming a single File
record per Version.

No functional changes are expected, except for one unfortunate side-effect in
recording object deletion via LogEntry: Version deletion is recorded twice:
1. during Extension deleting its Version objects via CASCADE
2. during post_delete for VersionFile when the check for the last file is done

After this change is deployed, we can start implementing multiple file uploads per
Version to support multi-platform builds (generated with `--split-platforms` option).

Reviewed-on: #193
Reviewed-by: Anna Sirota <annasirota@noreply.localhost>
2024-06-21 11:29:21 +02:00

187 lines
5.0 KiB
Python

import logging
from django.conf import settings
from django.contrib import admin
from django.template.loader import render_to_string
from .models import File, FileValidation
from common.admin import link_to
import files.signals
logger = logging.getLogger(__name__)
def schedule_scan(self, request, queryset):
"""Scan selected files."""
for instance in queryset:
files.signals.schedule_scan(instance)
def make_thumbnails(self, request, queryset):
"""Make thumbnails for selected files."""
for instance in queryset.filter(type__in=(File.TYPES.IMAGE, File.TYPES.VIDEO)):
files.tasks.make_thumbnails.task_function(file_id=instance.pk)
class FileValidationInlineAdmin(admin.StackedInline):
model = FileValidation
readonly_fields = ('date_created', 'date_modified', 'is_ok', 'results')
extra = 0
def _nope(self, request, obj):
return False
has_add_permission = _nope
has_change_permission = _nope
has_delete_permission = _nope
@admin.register(File)
class FileAdmin(admin.ModelAdmin):
class Media:
css = {'all': ('files/admin/file.css',)}
def get_queryset(self, *args, **kwargs):
q = super().get_queryset(*args, **kwargs)
return q.prefetch_related(*self.list_prefetch_related)
def thumbnails(self, obj):
if not obj or not (obj.is_image or obj.is_video):
return ''
try:
context = {'file': obj, 'MEDIA_URL': settings.MEDIA_URL}
return render_to_string('files/admin/thumbnails.html', context)
except Exception:
# Make sure any exception happening here is always logged
# (e.g. admin eats exceptions in ModelAdmin properties, making it hard to debug)
logger.exception('Failed to render thumbnails')
raise
def get_form(self, request, obj=None, **kwargs):
"""Override metadata help text depending on file type."""
if obj and (obj.is_image or obj.is_video):
help_text = 'Additional information about the file, e.g. existing thumbnails.'
kwargs.update({'help_texts': {'metadata': help_text}})
return super().get_form(request, obj, **kwargs)
date_hierarchy = 'date_created'
view_on_site = False
save_on_top = True
list_filter = (
'validation__is_ok',
'type',
'status',
'date_approved',
'date_created',
'date_modified',
'date_status_changed',
('version', admin.EmptyFieldListFilter),
('icon_of', admin.EmptyFieldListFilter),
('featured_image_of', admin.EmptyFieldListFilter),
('preview', admin.EmptyFieldListFilter),
)
list_display = (
'original_name',
link_to('user'),
'date_created',
'type',
'status',
link_to('version', 'version of'),
link_to('icon_of'),
link_to('featured_image_of'),
link_to('preview_set.extension', 'preview of'),
'is_ok',
)
list_select_related = (
'user',
'validation',
)
list_prefetch_related = (
'icon_of',
'featured_image_of',
'preview_set__extension',
'version',
'version__extension',
)
autocomplete_fields = ['user']
readonly_fields = (
'id',
'date_created',
'date_modified',
'date_approved',
'date_status_changed',
'size_bytes',
'thumbnails',
'thumbnail',
'type',
'original_hash',
'original_name',
'hash',
'content_type',
)
search_fields = (
'version__extension__slug',
'version__extension__name',
'extensions__slug',
'extensions__name',
'original_name',
'hash',
'source',
'user__email',
'user__full_name',
'user__username',
'icon_of__slug',
'icon_of__name',
'featured_image_of__slug',
'featured_image_of__name',
'preview__extension__slug',
'preview__extension__name',
)
fieldsets = (
(
None,
{
'fields': (
'id',
('source', 'thumbnails', 'thumbnail'),
('type', 'content_type', 'original_name'),
'status',
)
},
),
(
'Dates',
{
'fields': (
'date_created',
'date_modified',
'date_status_changed',
'date_approved',
)
},
),
(
'Details',
{
'fields': (
('hash', 'original_hash'),
'metadata',
'size_bytes',
'user',
),
},
),
)
inlines = [FileValidationInlineAdmin]
actions = [schedule_scan, make_thumbnails]
def is_ok(self, obj):
return obj.validation.is_ok if hasattr(obj, 'validation') else None
is_ok.boolean = True