Refactor for inline reqs. Added inline favorites.
This commit is contained in:
parent
dd55e37686
commit
4e23507363
3 changed files with 130 additions and 67 deletions
61
savepointradio/profiles/actions.py
Normal file
61
savepointradio/profiles/actions.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
from django.contrib import messages
|
||||||
|
|
||||||
|
from radio.models import Song
|
||||||
|
from .exceptions import MakeRequestError
|
||||||
|
from .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 ToggleFavoriteActionsMixin(object):
|
||||||
|
'''This allows a song to be [un]favorited directly from the admin page.'''
|
||||||
|
def get_inline_actions(self, request, obj=None):
|
||||||
|
actions = super().get_inline_actions(request=request, obj=obj)
|
||||||
|
actions.append('toggle_favorite')
|
||||||
|
return actions
|
||||||
|
|
||||||
|
def toggle_favorite(self, request, obj, parent_obj=None):
|
||||||
|
profile = RadioProfile.objects.get(user=request.user)
|
||||||
|
|
||||||
|
# This is to get around the M2M 'through' table
|
||||||
|
song = obj
|
||||||
|
if not isinstance(song, Song):
|
||||||
|
song = obj.song
|
||||||
|
|
||||||
|
found = profile.favorites.filter(pk=song.pk).exists()
|
||||||
|
if found:
|
||||||
|
profile.favorites.remove(song)
|
||||||
|
else:
|
||||||
|
profile.favorites.add(song)
|
||||||
|
|
||||||
|
status = 'removed from favorites' if found else 'added to favorites'
|
||||||
|
messages.success(request, 'Song successfully {}.'.format(status))
|
||||||
|
|
||||||
|
def get_toggle_favorite_label(self, obj):
|
||||||
|
return 'Toggle Favorite'
|
|
@ -1,13 +1,15 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from inline_actions.admin import InlineActionsModelAdminMixin
|
||||||
|
|
||||||
|
from radio.admin import BaseSongInline
|
||||||
from .models import RadioProfile, SongRequest
|
from .models import RadioProfile, SongRequest
|
||||||
|
|
||||||
|
|
||||||
class FavoriteInline(admin.TabularInline):
|
class FavoriteInline(BaseSongInline):
|
||||||
model = RadioProfile.favorites.through
|
model = RadioProfile.favorites.through
|
||||||
verbose_name = 'favorite'
|
verbose_name = 'favorite'
|
||||||
verbose_name_plural = 'favorites'
|
verbose_name_plural = 'favorites'
|
||||||
extra = 0
|
|
||||||
|
|
||||||
|
|
||||||
class RatingInline(admin.TabularInline):
|
class RatingInline(admin.TabularInline):
|
||||||
|
@ -18,7 +20,7 @@ class RatingInline(admin.TabularInline):
|
||||||
|
|
||||||
|
|
||||||
@admin.register(RadioProfile)
|
@admin.register(RadioProfile)
|
||||||
class ProfileAdmin(admin.ModelAdmin):
|
class ProfileAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin):
|
||||||
# Edit Form display
|
# Edit Form display
|
||||||
readonly_fields = ('created_date', 'modified_date')
|
readonly_fields = ('created_date', 'modified_date')
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
|
@ -37,6 +39,11 @@ class ProfileAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
inlines = [FavoriteInline, RatingInline]
|
inlines = [FavoriteInline, RatingInline]
|
||||||
|
|
||||||
|
class Media:
|
||||||
|
css = {
|
||||||
|
'all': ('css/remove_inline_object_names.css', )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@admin.register(SongRequest)
|
@admin.register(SongRequest)
|
||||||
class RequestAdmin(admin.ModelAdmin):
|
class RequestAdmin(admin.ModelAdmin):
|
||||||
|
|
|
@ -8,56 +8,20 @@ from inline_actions.admin import (
|
||||||
|
|
||||||
from .actions import change_items, publish_items, remove_items
|
from .actions import change_items, publish_items, remove_items
|
||||||
from .models import Album, Artist, Game, Song, Store
|
from .models import Album, Artist, Game, Song, Store
|
||||||
from profiles.exceptions import MakeRequestError
|
from profiles.actions import RequestSongActionMixin, ToggleFavoriteActionsMixin
|
||||||
from profiles.models import RadioProfile
|
|
||||||
|
|
||||||
|
|
||||||
class RequestSongActionMixin(object):
|
class BaseSongInline(RequestSongActionMixin,
|
||||||
'''This allows a song to be requested directly from an admin page.'''
|
ToggleFavoriteActionsMixin,
|
||||||
def get_inline_actions(self, request, obj=None):
|
InlineActionsMixin,
|
||||||
actions = super().get_inline_actions(request=request, obj=obj)
|
admin.TabularInline):
|
||||||
actions.append('request_song')
|
# This is VERY hacky and I hate it. I would love to find something
|
||||||
return actions
|
# 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 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'
|
|
||||||
verbose_name_plural = 'artists'
|
|
||||||
extra = 0
|
|
||||||
|
|
||||||
|
|
||||||
class StoreInline(admin.TabularInline):
|
|
||||||
model = Song.stores.through
|
|
||||||
verbose_name = 'data store'
|
|
||||||
verbose_name_plural = 'data stores'
|
|
||||||
extra = 0
|
|
||||||
|
|
||||||
|
|
||||||
class SongInline(RequestSongActionMixin,
|
|
||||||
InlineActionsMixin,
|
|
||||||
admin.TabularInline):
|
|
||||||
model = Song
|
|
||||||
readonly_fields = (
|
readonly_fields = (
|
||||||
'title',
|
'title',
|
||||||
'game',
|
'game',
|
||||||
|
@ -72,47 +36,77 @@ class SongInline(RequestSongActionMixin,
|
||||||
verbose_name_plural = 'related songs'
|
verbose_name_plural = 'related songs'
|
||||||
extra = 0
|
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):
|
def title(self, obj):
|
||||||
return obj.song.title
|
song = obj
|
||||||
|
if not isinstance(song, Song):
|
||||||
|
song = obj.song
|
||||||
|
return song.title
|
||||||
title.short_description = 'title'
|
title.short_description = 'title'
|
||||||
|
|
||||||
def game(self, obj):
|
def game(self, obj):
|
||||||
return obj.song.game
|
song = obj
|
||||||
|
if not isinstance(song, Song):
|
||||||
|
song = obj.song
|
||||||
|
return song.game
|
||||||
game.short_description = 'game'
|
game.short_description = 'game'
|
||||||
|
|
||||||
def album(self, obj):
|
def album(self, obj):
|
||||||
return obj.song.album
|
song = obj
|
||||||
|
if not isinstance(song, Song):
|
||||||
|
song = obj.song
|
||||||
|
return song.album
|
||||||
album.short_description = 'album'
|
album.short_description = 'album'
|
||||||
|
|
||||||
def artist_list(self, obj):
|
def artist_list(self, obj):
|
||||||
return ', '.join([a.full_name for a in obj.song.artists.all()])
|
song = obj
|
||||||
|
if not isinstance(song, Song):
|
||||||
|
song = obj.song
|
||||||
|
return ', '.join([a.full_name for a in song.artists.all()])
|
||||||
|
|
||||||
def _is_enabled(self, obj):
|
def _is_enabled(self, obj):
|
||||||
|
song = obj
|
||||||
|
if not isinstance(song, Song):
|
||||||
|
song = obj.song
|
||||||
return obj.song.is_enabled
|
return obj.song.is_enabled
|
||||||
_is_enabled.boolean = True
|
_is_enabled.boolean = True
|
||||||
|
|
||||||
def _is_published(self, obj):
|
def _is_published(self, obj):
|
||||||
|
song = obj
|
||||||
|
if not isinstance(song, Song):
|
||||||
|
song = obj.song
|
||||||
return obj.song.is_published
|
return obj.song.is_published
|
||||||
_is_published.boolean = True
|
_is_published.boolean = True
|
||||||
|
|
||||||
def _is_requestable(self, obj):
|
def _is_requestable(self, obj):
|
||||||
|
song = obj
|
||||||
|
if not isinstance(song, Song):
|
||||||
|
song = obj.song
|
||||||
return obj.song.is_requestable
|
return obj.song.is_requestable
|
||||||
_is_requestable.boolean = True
|
_is_requestable.boolean = True
|
||||||
|
|
||||||
|
|
||||||
|
class SongInline(BaseSongInline):
|
||||||
|
model = Song
|
||||||
|
|
||||||
|
|
||||||
|
class SongArtistInline(BaseSongInline):
|
||||||
|
model = Song.artists.through
|
||||||
|
|
||||||
|
|
||||||
|
class ArtistInline(admin.TabularInline):
|
||||||
|
model = Song.artists.through
|
||||||
|
verbose_name = 'artist'
|
||||||
|
verbose_name_plural = 'artists'
|
||||||
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
|
class StoreInline(admin.TabularInline):
|
||||||
|
model = Song.stores.through
|
||||||
|
verbose_name = 'data store'
|
||||||
|
verbose_name_plural = 'data stores'
|
||||||
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Album)
|
@admin.register(Album)
|
||||||
class AlbumAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin):
|
class AlbumAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin):
|
||||||
# Detail List display
|
# Detail List display
|
||||||
|
@ -249,6 +243,7 @@ class StoreAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
@admin.register(Song)
|
@admin.register(Song)
|
||||||
class SongAdmin(RequestSongActionMixin,
|
class SongAdmin(RequestSongActionMixin,
|
||||||
|
ToggleFavoriteActionsMixin,
|
||||||
InlineActionsModelAdminMixin,
|
InlineActionsModelAdminMixin,
|
||||||
admin.ModelAdmin):
|
admin.ModelAdmin):
|
||||||
formfield_overrides = {
|
formfield_overrides = {
|
||||||
|
|
Loading…
Reference in a new issue