From 83e425349b2adb7a08266c17d57c8bc5e4103cef Mon Sep 17 00:00:00 2001 From: bkfox Date: Tue, 7 Jun 2016 17:25:38 +0200 Subject: [PATCH] work on admin interface --- cms/admin.py | 51 +++++++++++++++++++++++++++++++- cms/models.py | 11 +++---- programs/admin.py | 74 +++++++++++++++++++++++++++++++---------------- website/admin.py | 12 ++++---- website/forms.py | 10 +++---- website/models.py | 4 ++- 6 files changed, 118 insertions(+), 44 deletions(-) diff --git a/cms/admin.py b/cms/admin.py index 8236be2..2cc6419 100644 --- a/cms/admin.py +++ b/cms/admin.py @@ -4,7 +4,6 @@ from django.utils.translation import ugettext as _, ugettext_lazy import aircox.cms.models as models - class PostAdmin(admin.ModelAdmin): list_display = [ 'title', 'date', 'author', 'published', 'post_tags'] list_editable = [ 'published' ] @@ -42,6 +41,56 @@ class CommentAdmin(admin.ModelAdmin): return post.content[:256] content_slice.short_description = _('content') + +class PostInline(admin.StackedInline): + extra = 1 + max_num = 1 + verbose_name = _('Post') + + fieldsets = [ + (None, { + 'fields': ['title', 'content', 'image', 'tags'] + }), + (None, { + 'fields': ['date', 'published', 'author'] + }) + ] + + +def inject_related_inline(post_model, prepend = False, inline = None): + """ + Create an inline class and inject it into the related model admin class. + Clean-up bound attributes. + """ + class InlineModel(PostInline): + model = post_model + verbose_name = _('Related post') + + inline = inline or InlineModel + + # remove bound attributes + for none, dic in inline.fieldsets: + if not dic.get('fields'): + continue + dic['fields'] = [ v for v in dic['fields'] + if v not in post_model._relation.bindings.keys() ] + + inject_inline(post_model._meta.get_field('related').rel.to, + inline, prepend) + +def inject_inline(model, inline, prepend = False): + registry = admin.site._registry + if not model in registry: + return TypeError('{} not in admin registry'.format(model.__name__)) + + inlines = list(registry[model].inlines) or [] + if prepend: + inlines.insert(0, inline) + else: + inlines.append(inline) + registry[model].inlines = inlines + + admin.site.register(models.Article, PostAdmin) admin.site.register(models.Comment, CommentAdmin) diff --git a/cms/models.py b/cms/models.py index 6fff903..41696b3 100644 --- a/cms/models.py +++ b/cms/models.py @@ -288,7 +288,8 @@ class RelatedPostBase (models.base.ModelBase): post = model.objects.filter(related = instance) if post.count(): post = post[0] - elif rel.auto_create: + elif rel.auto_create(instance) if callable(rel.auto_create) else \ + rel.auto_create: post = model(related = instance) else: return @@ -375,12 +376,12 @@ class RelatedPost (Post, metaclass = RelatedPostBase): * post_to_rel: auto update related object when post is updated * rel_to_post: auto update the post when related object is updated * thread_model: generated by the metaclass, points to the RelatedPost - model generated for the bindings.thread object. + model generated for the bindings.thread object. * field_args: dict of arguments to pass to the ForeignKey constructor, - such as: ForeignKey(related_model, **field_args) + such as: ForeignKey(related_model, **field_args) * auto_create: automatically create a RelatedPost for each new item of - the related object and init it with bounded values. Use signals - '', ''. + the related object and init it with bounded values. Use 'post_save' + signal. If auto_create is callable, use `auto_create(related_object)`. Be careful with post_to_rel! * There is no check of permissions when related object is synchronised diff --git a/programs/admin.py b/programs/admin.py index 7df2872..7c389f7 100755 --- a/programs/admin.py +++ b/programs/admin.py @@ -11,22 +11,22 @@ from aircox.programs.models import * # # Inlines # -class SoundInline (admin.TabularInline): +class SoundInline(admin.TabularInline): model = Sound -class ScheduleInline (admin.TabularInline): +class ScheduleInline(admin.TabularInline): model = Schedule extra = 1 -class StreamInline (admin.TabularInline): +class StreamInline(admin.TabularInline): fields = ['delay', 'begin', 'end'] model = Stream extra = 1 # from suit.admin import SortableTabularInline, SortableModelAdmin -#class TrackInline (SortableTabularInline): +#class TrackInline(SortableTabularInline): # fields = ['artist', 'name', 'tags', 'position'] # form = TrackForm # model = Track @@ -34,7 +34,7 @@ class StreamInline (admin.TabularInline): # extra = 10 -class NameableAdmin (admin.ModelAdmin): +class NameableAdmin(admin.ModelAdmin): fields = [ 'name' ] list_display = ['id', 'name'] @@ -43,7 +43,7 @@ class NameableAdmin (admin.ModelAdmin): @admin.register(Sound) -class SoundAdmin (NameableAdmin): +class SoundAdmin(NameableAdmin): fields = None list_display = ['id', 'name', 'duration', 'type', 'mtime', 'good_quality', 'removed', 'public'] fieldsets = [ @@ -55,17 +55,17 @@ class SoundAdmin (NameableAdmin): @admin.register(Stream) -class StreamAdmin (admin.ModelAdmin): +class StreamAdmin(admin.ModelAdmin): list_display = ('id', 'program', 'delay', 'begin', 'end') @admin.register(Station) -class StationAdmin (NameableAdmin): +class StationAdmin(NameableAdmin): fields = NameableAdmin.fields + [ 'active', 'public', 'fallback' ] @admin.register(Program) -class ProgramAdmin (NameableAdmin): - def schedule (self, obj): +class ProgramAdmin(NameableAdmin): + def schedule(self, obj): return Schedule.objects.filter(program = obj).count() > 0 schedule.boolean = True schedule.short_description = _("Schedule") @@ -76,7 +76,7 @@ class ProgramAdmin (NameableAdmin): inlines = [ ScheduleInline, StreamInline ] # SO#8074161 - #def get_form (self, request, obj=None, **kwargs): + #def get_form(self, request, obj=None, **kwargs): #if obj: # if Schedule.objects.filter(program = obj).count(): # self.inlines.remove(StreamInline) @@ -84,22 +84,38 @@ class ProgramAdmin (NameableAdmin): # self.inlines.remove(ScheduleInline) #return super().get_form(request, obj, **kwargs) + +class DiffusionInline(admin.StackedInline): + model = Diffusion + extra = 0 + fields = ['type', 'start', 'end'] + @admin.register(Diffusion) -class DiffusionAdmin (admin.ModelAdmin): - def archives (self, obj): +class DiffusionAdmin(admin.ModelAdmin): + def archives(self, obj): sounds = [ str(s) for s in obj.get_archives()] return ', '.join(sounds) if sounds else '' - def conflicts (self, obj): + def conflicts(self, obj): if obj.type == Diffusion.Type.unconfirmed: return ', '.join([ str(d) for d in obj.get_conflicts()]) return '' - list_display = ('id', 'type', 'start', 'end', 'program', 'initial', 'archives', 'conflicts') + def end_time(self, obj): + return obj.end.strftime('%H:%M') + end_time.short_description = _('end') + + def first(self, obj): + return obj.initial.start if obj.initial else '' + + list_display = ('id', 'program', 'start', 'end_time', 'type', 'first', 'archives', 'conflicts') list_filter = ('type', 'start', 'program') - list_editable = ('type', 'start', 'end') + list_editable = ('type',) + ordering = ('-start', 'id') fields = ['type', 'start', 'end', 'initial', 'program', 'sounds'] + inlines = [ DiffusionInline ] + def get_form(self, request, obj=None, **kwargs): if request.user.has_perm('aircox_program.programming'): @@ -111,32 +127,40 @@ class DiffusionAdmin (admin.ModelAdmin): self.readonly_fields += ['program', 'sounds'] return super().get_form(request, obj, **kwargs) + def get_object(self, *args, **kwargs): + """ + We want rerun to redirect to the given object. + """ + obj = super().get_object(*args, **kwargs) + if obj.initial: + obj = obj.initial + return obj + def get_queryset(self, request): - qs = super(DiffusionAdmin, self).get_queryset(request) - if ('_changelist_filters' in request.GET or \ - 'type__exact' in request.GET) and \ - str(Diffusion.Type.unconfirmed) in request.GET['type__exact']: + qs = super().get_queryset(request) + if request.GET and '_changelist_filters' in request.GET and \ + request.GET.get('type__exact') == Diffusion.Type.unconfirmed: return qs return qs.exclude(type = Diffusion.Type.unconfirmed) @admin.register(Log) -class LogAdmin (admin.ModelAdmin): +class LogAdmin(admin.ModelAdmin): list_display = ['id', 'date', 'source', 'comment', 'related_object'] list_filter = ['date', 'source', 'related_type'] @admin.register(Schedule) -class ScheduleAdmin (admin.ModelAdmin): - def program_name (self, obj): +class ScheduleAdmin(admin.ModelAdmin): + def program_name(self, obj): return obj.program.name program_name.short_description = _('Program') - def day (self, obj): + def day(self, obj): return obj.date.strftime('%A') day.short_description = _('Day') - def rerun (self, obj): + def rerun(self, obj): return obj.initial != None rerun.short_description = _('Rerun') rerun.boolean = True diff --git a/website/admin.py b/website/admin.py index ad86605..8455809 100644 --- a/website/admin.py +++ b/website/admin.py @@ -7,19 +7,17 @@ import aircox.website.models as models import aircox.website.forms as forms -class TrackInline (SortableTabularInline): +class TrackInline(SortableTabularInline): fields = ['artist', 'name', 'tags', 'position'] form = forms.TrackForm model = programs.Track sortable = 'position' extra = 10 - -class DiffusionPostAdmin(cms.RelatedPostAdmin): - inlines = [TrackInline] - - admin.site.register(models.Program, cms.RelatedPostAdmin) -admin.site.register(models.Diffusion, DiffusionPostAdmin) +admin.site.register(models.Diffusion, cms.RelatedPostAdmin) + +cms.inject_inline(programs.Diffusion, TrackInline, True) +cms.inject_related_inline(models.Diffusion, True) diff --git a/website/forms.py b/website/forms.py index 32e9453..f632eee 100644 --- a/website/forms.py +++ b/website/forms.py @@ -1,7 +1,7 @@ from django import forms -import autocomplete_light.shortcuts as al -from autocomplete_light.contrib.taggit_field import TaggitWidget +#import autocomplete_light.shortcuts as al +#from autocomplete_light.contrib.taggit_field import TaggitWidget import aircox.programs.models as programs @@ -11,9 +11,9 @@ class TrackForm (forms.ModelForm): model = programs.Track fields = ['artist', 'name', 'tags', 'position'] widgets = { - 'artist': al.TextWidget('TrackArtistAutocomplete'), - 'name': al.TextWidget('TrackNameAutocomplete'), - 'tags': TaggitWidget('TagAutocomplete'), +# 'artist': al.TextWidget('TrackArtistAutocomplete'), +# 'name': al.TextWidget('TrackNameAutocomplete'), +# 'tags': TaggitWidget('TagAutocomplete'), } diff --git a/website/models.py b/website/models.py index b08221a..6a28506 100644 --- a/website/models.py +++ b/website/models.py @@ -33,7 +33,9 @@ class Diffusion (RelatedPost): } } rel_to_post = True - auto_create = True + + def auto_create(object): + return not object.initial def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)