From 7b3ed3d127e866262492f5e89a656f671dd10c4a Mon Sep 17 00:00:00 2001 From: Josh W Date: Thu, 13 Feb 2020 11:33:10 -0500 Subject: [PATCH] Expanded song requests to artists, game, & album. --- savepointradio/radio/admin.py | 138 +++++++++++++++--- .../static/css/remove_inline_object_names.css | 7 + 2 files changed, 126 insertions(+), 19 deletions(-) create mode 100644 savepointradio/radio/static/css/remove_inline_object_names.css diff --git a/savepointradio/radio/admin.py b/savepointradio/radio/admin.py index 508aada..6fda859 100644 --- a/savepointradio/radio/admin.py +++ b/savepointradio/radio/admin.py @@ -2,7 +2,9 @@ from django.contrib import admin, messages from django.db import models from django.forms import TextInput -from inline_actions.admin import InlineActionsModelAdminMixin +from inline_actions.admin import ( + InlineActionsMixin, InlineActionsModelAdminMixin +) from .actions import change_items, publish_items, remove_items from .models import Album, Artist, Game, Song, Store @@ -10,6 +12,34 @@ from profiles.exceptions import MakeRequestError from profiles.models import RadioProfile +class RequestSongActionMixin(object): + '''This allows a song to be requested directly from an admin page.''' + def get_inline_actions(self, request, obj=None): + actions = super().get_inline_actions(request=request, obj=obj) + actions.append('request_song') + return actions + + def request_song(self, request, obj, parent_obj=None): + profile = RadioProfile.objects.get(user=request.user) + + # This is to get around the M2M 'through' table on Artists + song = obj + if not isinstance(song, Song): + song = obj.song + + try: + profile.make_request(song) + except MakeRequestError as e: + return messages.error( + request, + 'Unable to request the song: {}'.format(str(e)) + ) + return messages.success(request, 'Successfully queued song.') + + def get_request_song_label(self, obj): + return 'Request Song' + + class ArtistInline(admin.TabularInline): model = Song.artists.through verbose_name = 'artist' @@ -24,12 +54,72 @@ class StoreInline(admin.TabularInline): extra = 0 +class SongInline(RequestSongActionMixin, + InlineActionsMixin, + admin.TabularInline): + model = Song + readonly_fields = ( + 'title', + 'game', + 'album', + 'artist_list', + '_is_enabled', + '_is_published', + '_is_requestable' + ) + fields = readonly_fields + verbose_name = 'related song' + verbose_name_plural = 'related songs' + extra = 0 + + def artist_list(self, obj): + return ', '.join([a.full_name for a in obj.artists.all()]) + +class SongArtistInline(SongInline): + model = Song.artists.through + + # This is VERY hacky and I hate it. I would love to find something + # a little more DRY. There's this: + # https://gist.github.com/cauethenorio/9db40c59cf406bf328fd + # . . .but it seems to be aimed at ModelAdmins, not Inlines. Maybe a + # refactor is in order when I understand the underlying functionality + # down the road. + + def title(self, obj): + return obj.song.title + title.short_description = 'title' + + def game(self, obj): + return obj.song.game + game.short_description = 'game' + + def album(self, obj): + return obj.song.album + album.short_description = 'album' + + def artist_list(self, obj): + return ', '.join([a.full_name for a in obj.song.artists.all()]) + + def _is_enabled(self, obj): + return obj.song.is_enabled + _is_enabled.boolean = True + + def _is_published(self, obj): + return obj.song.is_published + _is_published.boolean = True + + def _is_requestable(self, obj): + return obj.song.is_requestable + _is_requestable.boolean = True + + @admin.register(Album) -class AlbumAdmin(admin.ModelAdmin): +class AlbumAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin): # Detail List display list_display = ('title', '_is_enabled', '_is_published') search_fields = ['title'] actions = ['publish_albums'] + inlines = [SongInline] # Edit Form display readonly_fields = ('created_date', 'modified_date') @@ -51,9 +141,14 @@ class AlbumAdmin(admin.ModelAdmin): publish_items(request, queryset) publish_albums.short_description = "Publish selected albums" + class Media: + css = { + 'all': ('css/remove_inline_object_names.css', ) + } + @admin.register(Artist) -class ArtistAdmin(admin.ModelAdmin): +class ArtistAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin): # Detail List display list_display = ('first_name', 'alias', @@ -62,6 +157,7 @@ class ArtistAdmin(admin.ModelAdmin): '_is_published') search_fields = ['first_name', 'alias', 'last_name'] actions = ['publish_artists'] + inlines = [SongArtistInline] # Edit Form display readonly_fields = ('created_date', 'modified_date') @@ -83,13 +179,19 @@ class ArtistAdmin(admin.ModelAdmin): publish_items(request, queryset) publish_artists.short_description = "Publish selected artists" + class Media: + css = { + 'all': ('css/remove_inline_object_names.css', ) + } + @admin.register(Game) -class GameAdmin(admin.ModelAdmin): +class GameAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin): # Detail List display list_display = ('title', '_is_enabled', '_is_published') search_fields = ['title'] actions = ['publish_games'] + inlines = [SongInline] # Edit Form display readonly_fields = ('created_date', 'modified_date') @@ -111,6 +213,11 @@ class GameAdmin(admin.ModelAdmin): publish_items(request, queryset) publish_games.short_description = "Publish selected games" + class Media: + css = { + 'all': ('css/remove_inline_object_names.css', ) + } + @admin.register(Store) class StoreAdmin(admin.ModelAdmin): @@ -141,7 +248,9 @@ class StoreAdmin(admin.ModelAdmin): @admin.register(Song) -class SongAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin): +class SongAdmin(RequestSongActionMixin, + InlineActionsModelAdminMixin, + admin.ModelAdmin): formfield_overrides = { models.TextField: {'widget': TextInput(attrs={'size': 160, })}, } @@ -154,6 +263,7 @@ class SongAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin): '_is_enabled', '_is_published', '_is_requestable') + list_select_related = True search_fields = ['title'] actions = ['publish_songs', 'add_game', 'remove_game', @@ -194,7 +304,6 @@ class SongAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin): }) ) inlines = [ArtistInline, StoreInline] - inline_actions = ['request_song'] def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == 'active_store': @@ -238,16 +347,7 @@ class SongAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin): publish_items(request, queryset) publish_songs.short_description = "Publish selected songs" - # Inline Admin Actions - - def request_song(self, request, obj, parent_obj=None): - profile = RadioProfile.objects.get(user=request.user) - try: - profile.make_request(obj) - except MakeRequestError as e: - return messages.error( - request, - 'Unable to request the song: {}'.format(str(e)) - ) - return messages.success(request, 'Successfully queued song.') - request_song.short_description = 'Request Song' + class Media: + css = { + 'all': ('css/remove_inline_object_names.css', ) + } diff --git a/savepointradio/radio/static/css/remove_inline_object_names.css b/savepointradio/radio/static/css/remove_inline_object_names.css new file mode 100644 index 0000000..f8f5eda --- /dev/null +++ b/savepointradio/radio/static/css/remove_inline_object_names.css @@ -0,0 +1,7 @@ +.inline-group .tabular tr.has_original td { + padding-top: 8px; +} + +.original { + display: none; +} \ No newline at end of file