Changeset View
Changeset View
Standalone View
Standalone View
films/admin/films.py
| import logging | |||||
| from django import forms | |||||
| from django.contrib import admin | from django.contrib import admin | ||||
| from django.contrib.auth import get_permission_codename | |||||
| from django.db.models.query import QuerySet | from django.db.models.query import QuerySet | ||||
| from django.http.request import HttpRequest | from django.http.request import HttpRequest | ||||
| from django.shortcuts import redirect | |||||
| from django.urls import reverse | |||||
| from films.models import assets, collections, films | from films.models import assets, collections, films | ||||
| from static_assets.models import static_assets | |||||
| from common import mixins | from common import mixins | ||||
| logger = logging.getLogger(__name__) | |||||
| @admin.register(assets.Asset) | @admin.register(assets.Asset) | ||||
| class AssetAdmin(mixins.ThumbnailMixin, admin.ModelAdmin): | class AssetAdmin(mixins.ThumbnailMixin, admin.ModelAdmin): | ||||
| # asset slugs aren't currently in use and were prepopulate | # asset slugs aren't currently in use and were prepopulate | ||||
| # during import from previous version of Blender Cloud | # during import from previous version of Blender Cloud | ||||
| # prepopulated_fields = {'slug': ('name',)} | # prepopulated_fields = {'slug': ('name',)} | ||||
| readonly_fields = ['date_created', 'slug'] | readonly_fields = ['date_created', 'slug'] | ||||
| list_display = ['view_thumbnail', '__str__', 'order', 'film', 'collection'] | list_display = ['view_thumbnail', '__str__', 'order', 'film', 'collection'] | ||||
| ▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | |||||
| @admin.register(films.Film) | @admin.register(films.Film) | ||||
| class FilmAdmin(mixins.ViewOnSiteMixin, admin.ModelAdmin): | class FilmAdmin(mixins.ViewOnSiteMixin, admin.ModelAdmin): | ||||
| search_fields = ['title', 'slug'] | search_fields = ['title', 'slug'] | ||||
| list_display = ('title', 'view_link') | list_display = ('title', 'view_link') | ||||
| prepopulated_fields = {'slug': ('title',)} | prepopulated_fields = {'slug': ('title',)} | ||||
| inlines = (FilmCrewInlineAdmin,) | inlines = (FilmCrewInlineAdmin,) | ||||
| class AssetFromFileInline( | |||||
| mixins.AdminUserDefaultMixin, mixins.ViewOnSiteMixin, admin.StackedInline | |||||
| ): | |||||
| class _Form(forms.ModelForm): | |||||
| def __init__(self, *args, **kwargs): | |||||
| """Unrequire required fields: they will be filled in based on StaticAsset form.""" | |||||
| super().__init__(*args, **kwargs) | |||||
| for name in ('name', 'category'): | |||||
| self.fields[name].required = False | |||||
| def get_initial_for_field(self, field, name, **kwargs): | |||||
| initial = super().get_initial_for_field(field, name, **kwargs) | |||||
| prepopulated_fields = { | |||||
| 'is_published': bool, | |||||
| 'is_featured': bool, | |||||
| 'collection': int, | |||||
| 'film': int, | |||||
| } | |||||
| if ( | |||||
| self.request.method == 'GET' | |||||
| and name in prepopulated_fields | |||||
| and name in self.request.GET | |||||
| ): | |||||
| try: | |||||
| type_ = prepopulated_fields[name] | |||||
| initial = type_(self.request.GET[name]) | |||||
| except ValueError: | |||||
| logger.exception('Invalid initial parameter for %s', name) | |||||
| return initial | |||||
| form = _Form | |||||
| model = assets.Asset | |||||
| # Changes title of the inline formset | |||||
| verbose_name_plural = 'Film asset details' | |||||
| # Changes title of each separate inline inside the inline formset | |||||
| verbose_name = 'Describe this film asset' | |||||
| fieldsets = ( | |||||
| (None, {'fields': (('name', 'view_link'), 'description')}), | |||||
| (None, {'fields': (('film', 'collection'),)}), | |||||
| (None, {'fields': (('is_published', 'is_featured', 'is_free'), 'contains_blend_file')}), | |||||
| (None, {'fields': (('category', 'tags'),)}), | |||||
| (None, {'fields': ('date_created',)}), | |||||
| ) | |||||
| extra = 1 | |||||
| max_num = 1 | |||||
| readonly_fields = ('date_created', 'view_link') | |||||
| autocomplete_fields = ('collection', 'film') | |||||
| def get_formset(self, request, obj=None, **kwargs): | |||||
| """Pass request to the inline form.""" | |||||
| formset = super().get_formset(request, obj, **kwargs) | |||||
| formset.form.request = request | |||||
| return formset | |||||
| class NewAsset(static_assets.StaticAsset): | |||||
| """Same as the other StaticAsset, but allows us to create a different admin form for it. | |||||
| N.B.: a proxy model also adds an empty migration, a new ContentType and new set of permissions. | |||||
| All three are useless but unavoidable. | |||||
| """ | |||||
| class Meta: | |||||
| proxy = True | |||||
| @admin.register(NewAsset) | |||||
| class NewAssetAdmin(mixins.AdminUserDefaultMixin, admin.ModelAdmin): | |||||
| def has_add_permission(self, request): | |||||
| """Inherit permission from the parent Asset model. | |||||
| Proxy models require new permissions to be created, they don't | |||||
| inherit parent model's permissions. | |||||
| See https://code.djangoproject.com/ticket/11154 for more details. | |||||
| """ | |||||
| opts = assets.Asset._meta | |||||
| codename = get_permission_codename('add', opts) | |||||
| return request.user.has_perm("%s.%s" % (opts.app_label, codename)) | |||||
| def has_change_permission(self, request, obj=None): | |||||
| """Inherit permission from the parent Asset model.""" | |||||
| opts = assets.Asset._meta | |||||
| codename = get_permission_codename('change', opts) | |||||
| return request.user.has_perm("%s.%s" % (opts.app_label, codename)) | |||||
| def has_delete_permission(self, request, obj=None): | |||||
| """Inherit permission from the parent Asset model.""" | |||||
| opts = assets.Asset._meta | |||||
| codename = get_permission_codename('delete', opts) | |||||
| return request.user.has_perm("%s.%s" % (opts.app_label, codename)) | |||||
| def has_module_permission(self, request): | |||||
| """Don't show this model in the admin sections. | |||||
| It's only needed for adding and editing, not listing. | |||||
| """ | |||||
| return False | |||||
| class _Form(forms.ModelForm): | |||||
| def __init__(self, data=None, files=None, **kwargs): | |||||
| """Make source file required.""" | |||||
| super().__init__(data, files, **kwargs) | |||||
| for name in ('source',): | |||||
| self.fields[name].required = True | |||||
| form = _Form | |||||
| model = NewAsset | |||||
| inlines = [AssetFromFileInline] | |||||
| fieldsets = ( | |||||
| ( | |||||
| 'Upload a file', | |||||
| {'fields': ('source',)}, | |||||
| ), | |||||
| ) | |||||
| def save_related(self, request, form, formsets, change): | |||||
| """Fill film asset fields based on the newly uploaded file.""" | |||||
| if not change: | |||||
| static_asset: static_assets.StaticAsset = form.instance | |||||
| assert isinstance(static_asset, NewAsset), static_asset.__class__ | |||||
| inline_form = None | |||||
| for formset in formsets: | |||||
| inline_form = formset.forms[0] | |||||
| assert isinstance(inline_form, AssetFromFileInline.form), inline_form.__class__ | |||||
| film_asset: assets.Asset = inline_form.instance | |||||
| assert isinstance(film_asset, assets.Asset), film_asset.__class__ | |||||
| # Set name based on original file name | |||||
| if not film_asset.name: | |||||
| film_asset.name = static_asset.original_filename | |||||
| # Set category (this might not be useful at all FIXME) | |||||
| if not film_asset.category: | |||||
| category = assets.AssetCategory.production_file | |||||
| if static_asset.source_type == static_assets.StaticAssetFileTypeChoices.image: | |||||
| category = assets.AssetCategory.artwork | |||||
| film_asset.category = category | |||||
| super().save_related(request, form, formsets, change) | |||||
| def response_add(self, request, obj, post_url_continue=None): | |||||
| """Redirect to editing the newly uploaded asset in the same form.""" | |||||
| return redirect(reverse('admin:films_newasset_change', kwargs={'object_id': obj.pk})) | |||||
| def response_change(self, request, obj, post_url_continue=None): | |||||
| """Redirect to editing the newly uploaded asset in the same form.""" | |||||
| return redirect(reverse('admin:films_newasset_change', kwargs={'object_id': obj.pk})) | |||||