code quality

This commit is contained in:
bkfox
2023-03-13 17:47:00 +01:00
parent 934817da8a
commit 112770eddf
162 changed files with 4798 additions and 4069 deletions

View File

@ -7,3 +7,18 @@ from .program import ProgramAdmin, ScheduleAdmin, StreamAdmin
from .sound import SoundAdmin, TrackAdmin
from .station import StationAdmin
__all__ = (
"filters",
"ArticleAdmin",
"DiffusionAdmin",
"EpisodeAdmin",
"LogAdmin",
"PageAdmin",
"StaticPageAdmin",
"ProgramAdmin",
"ScheduleAdmin",
"StreamAdmin",
"SoundAdmin",
"TrackAdmin",
"StationAdmin",
)

View File

@ -1,17 +1,12 @@
import copy
from django.contrib import admin
from ..models import Article
from .page import PageAdmin
__all__ = ['ArticleAdmin']
__all__ = ["ArticleAdmin"]
@admin.register(Article)
class ArticleAdmin(PageAdmin):
search_fields = PageAdmin.search_fields + ('parent__title',)
search_fields = PageAdmin.search_fields + ("parent__title",)
# TODO: readonly field

View File

@ -1,78 +1,83 @@
from adminsortable2.admin import SortableAdminBase
from django.contrib import admin
from django.forms import ModelForm
from django.utils.translation import gettext as _
from adminsortable2.admin import SortableAdminBase
from ..models import Episode, Diffusion
from ..models import Diffusion, Episode
from .page import PageAdmin
from .sound import SoundInline, TrackInline
class DiffusionBaseAdmin:
fields = ('type', 'start', 'end', 'schedule')
readonly_fields = ('schedule',)
fields = ("type", "start", "end", "schedule")
readonly_fields = ("schedule",)
def get_readonly_fields(self, request, obj=None):
fields = super().get_readonly_fields(request, obj)
if not request.user.has_perm('aircox_program.scheduling'):
fields = fields + ('program', 'start', 'end')
if not request.user.has_perm("aircox_program.scheduling"):
fields = fields + ("program", "start", "end")
return [field for field in fields if field in self.fields]
@admin.register(Diffusion)
class DiffusionAdmin(DiffusionBaseAdmin, admin.ModelAdmin):
def start_date(self, obj):
return obj.local_start.strftime('%Y/%m/%d %H:%M')
start_date.short_description = _('start')
return obj.local_start.strftime("%Y/%m/%d %H:%M")
start_date.short_description = _("start")
def end_date(self, obj):
return obj.local_end.strftime('%H:%M')
end_date.short_description = _('end')
return obj.local_end.strftime("%H:%M")
list_display = ('episode', 'start_date', 'end_date', 'type', 'initial')
list_filter = ('type', 'start', 'program')
list_editable = ('type',)
ordering = ('-start', 'id')
end_date.short_description = _("end")
fields = ('type', 'start', 'end', 'initial', 'program', 'schedule')
readonly_fields = ('schedule',)
list_display = ("episode", "start_date", "end_date", "type", "initial")
list_filter = ("type", "start", "program")
list_editable = ("type",)
ordering = ("-start", "id")
fields = ("type", "start", "end", "initial", "program", "schedule")
readonly_fields = ("schedule",)
class DiffusionInline(DiffusionBaseAdmin, admin.TabularInline):
model = Diffusion
fk_name = 'episode'
fk_name = "episode"
extra = 0
def has_add_permission(self, request, obj):
return request.user.has_perm('aircox_program.scheduling')
return request.user.has_perm("aircox_program.scheduling")
class EpisodeAdminForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['parent'].required = True
self.fields["parent"].required = True
@admin.register(Episode)
class EpisodeAdmin(SortableAdminBase, PageAdmin):
form = EpisodeAdminForm
list_display = PageAdmin.list_display
list_filter = tuple(f for f in PageAdmin.list_filter
if f != 'pub_date') + ('diffusion__start', 'pub_date')
search_fields = PageAdmin.search_fields + ('parent__title',)
list_filter = tuple(
f for f in PageAdmin.list_filter if f != "pub_date"
) + (
"diffusion__start",
"pub_date",
)
search_fields = PageAdmin.search_fields + ("parent__title",)
# readonly_fields = ('parent',)
inlines = [TrackInline, SoundInline, DiffusionInline]
def add_view(self, request, object_id, form_url='', context=None):
def add_view(self, request, object_id, form_url="", context=None):
context = context or {}
context['init_app'] = True
context['init_el'] = '#inline-tracks'
context["init_app"] = True
context["init_el"] = "#inline-tracks"
return super().change_view(request, object_id, form_url, context)
def change_view(self, request, object_id, form_url='', context=None):
def change_view(self, request, object_id, form_url="", context=None):
context = context or {}
context['init_app'] = True
context['init_el'] = '#inline-tracks'
context["init_app"] = True
context["init_el"] = "#inline-tracks"
return super().change_view(request, object_id, form_url, context)

View File

@ -1,63 +1,86 @@
from django.db import models
from django.contrib.admin import filters
from django.utils.translation import gettext_lazy as _
from django.db import models
from django.utils.http import urlencode
from django.utils.translation import gettext_lazy as _
__all__ = ('DateFieldFilter', 'DateTimeField')
__all__ = ("DateFieldFilter", "DateTimeFieldFilter")
class DateFieldFilter(filters.FieldListFilter):
""" Display date input """
template = 'admin/aircox/filters/date_filter.html'
input_type = 'date'
"""Display date input."""
template = "admin/aircox/filters/date_filter.html"
input_type = "date"
def __init__(self, field, request, params, model, model_admin, field_path):
self.field_generic = '%s__' % field_path
self.date_params = {k: v for k, v in params.items()
if k.startswith(self.field_generic)}
self.field_generic = "%s__" % field_path
self.date_params = {
k: v for k, v in params.items() if k.startswith(self.field_generic)
}
exact_lookup = 'date' if isinstance(field, models.DateTimeField) else 'exact'
exact_lookup = (
"date" if isinstance(field, models.DateTimeField) else "exact"
)
# links as: (label, param, input_type|None, value)
self.links = [(_('Exact'), self.field_generic + exact_lookup, self.input_type),
(_('Since'), self.field_generic + 'gte', self.input_type),
(_('Until'), self.field_generic + 'lte', self.input_type)]
self.links = [
(_("Exact"), self.field_generic + exact_lookup, self.input_type),
(_("Since"), self.field_generic + "gte", self.input_type),
(_("Until"), self.field_generic + "lte", self.input_type),
]
if field.null:
self.links.insert(0, (_('None'), self.field_generic + 'isnull', None, '1'))
self.query_attrs = {k:v for k,v in request.GET.items()
if k not in self.date_params}
self.links.insert(
0, (_("None"), self.field_generic + "isnull", None, "1")
)
self.query_attrs = {
k: v for k, v in request.GET.items() if k not in self.date_params
}
self.query_string = urlencode(self.query_attrs)
super().__init__(field, request, params, model, model_admin, field_path)
super().__init__(
field, request, params, model, model_admin, field_path
)
def expected_parameters(self):
return [link[1] for link in self.links]
def choices(self, changelist):
yield {'label': _('Any'),
'type': None,
'query_string': self.query_string}
yield {
"label": _("Any"),
"type": None,
"query_string": self.query_string,
}
for link in self.links:
value = len(link) > 3 and link[3] or self.date_params.get(link[1])
yield {
'label': link[0], 'name': link[1], 'value': value,
'type': link[2],
'query_attrs': self.query_attrs,
'query_string': urlencode({link[1]: value}) + '&' + self.query_string
if value else self.query_string,
"label": link[0],
"name": link[1],
"value": value,
"type": link[2],
"query_attrs": self.query_attrs,
"query_string": urlencode({link[1]: value})
+ "&"
+ self.query_string
if value
else self.query_string,
}
class DateTimeFieldFilter(DateFieldFilter):
""" Display datetime input """
input_type = 'datetime-local'
"""Display datetime input."""
input_type = "datetime-local"
filters.FieldListFilter.register(
lambda f: isinstance(f, models.DateField), DateFieldFilter, take_priority=True)
lambda f: isinstance(f, models.DateField),
DateFieldFilter,
take_priority=True,
)
filters.FieldListFilter.register(
lambda f: isinstance(f, models.DateTimeField), DateTimeFieldFilter, take_priority=True)
lambda f: isinstance(f, models.DateTimeField),
DateTimeFieldFilter,
take_priority=True,
)

View File

@ -2,12 +2,10 @@ from django.contrib import admin
from ..models import Log
__all__ = ['LogAdmin']
__all__ = ["LogAdmin"]
@admin.register(Log)
class LogAdmin(admin.ModelAdmin):
list_display = ['id', 'date', 'station', 'source', 'type', 'comment']
list_filter = ['date', 'source', 'station']
list_display = ["id", "date", "station", "source", "type", "comment"]
list_filter = ["date", "source", "station"]

View File

@ -1,23 +1,22 @@
class UnrelatedInlineMixin:
"""
Inline class that can be included in an admin change view whose model
is not directly related to inline's model.
"""
"""Inline class that can be included in an admin change view whose model is
not directly related to inline's model."""
view_model = None
parent_model = None
parent_fk = ''
parent_fk = ""
def __init__(self, parent_model, admin_site):
self.view_model = parent_model
super().__init__(self.parent_model, admin_site)
def get_parent(self, view_obj):
""" Get formset's instance from `obj` of AdminSite's change form. """
"""Get formset's instance from `obj` of AdminSite's change form."""
field = self.parent_model._meta.get_field(self.parent_fk).remote_field
return getattr(view_obj, field.name, None)
def save_parent(self, parent, view_obj):
""" Save formset's instance. """
"""Save formset's instance."""
setattr(parent, self.parent_fk, view_obj)
parent.save()
return parent
@ -25,6 +24,7 @@ class UnrelatedInlineMixin:
def get_formset(self, request, obj):
ParentFormSet = super().get_formset(request, obj)
inline = self
class FormSet(ParentFormSet):
view_obj = None
@ -37,6 +37,5 @@ class UnrelatedInlineMixin:
def save(self):
inline.save_parent(self.instance, self.view_obj)
return super().save()
return FormSet

View File

@ -1,74 +1,82 @@
from copy import deepcopy
from adminsortable2.admin import SortableInlineAdminMixin
from django.contrib import admin
from django.http import QueryDict
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from adminsortable2.admin import SortableInlineAdminMixin
from ..models import Category, Comment, NavItem, Page, StaticPage
__all__ = ('CategoryAdmin', 'PageAdmin', 'NavItemInline')
__all__ = ("CategoryAdmin", "PageAdmin", "NavItemInline")
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ['pk', 'title', 'slug']
list_editable = ['title', 'slug']
search_fields = ['title']
fields = ['title', 'slug']
list_display = ["pk", "title", "slug"]
list_editable = ["title", "slug"]
search_fields = ["title"]
fields = ["title", "slug"]
prepopulated_fields = {"slug": ("title",)}
class BasePageAdmin(admin.ModelAdmin):
list_display = ('cover_thumb', 'title', 'status', 'parent')
list_display_links = ('cover_thumb', 'title')
list_editable = ('status',)
list_filter = ('status',)
list_display = ("cover_thumb", "title", "status", "parent")
list_display_links = ("cover_thumb", "title")
list_editable = ("status",)
list_filter = ("status",)
prepopulated_fields = {"slug": ("title",)}
# prepopulate fields using changelist's filters
prepopulated_filters = ('parent',)
prepopulated_filters = ("parent",)
search_fields = ('title',)
search_fields = ("title",)
fieldsets = [
('', {
'fields': ['title', 'slug', 'cover', 'content'],
}),
(_('Publication Settings'), {
'fields': ['status', 'parent'],
}),
(
"",
{
"fields": ["title", "slug", "cover", "content"],
},
),
(
_("Publication Settings"),
{
"fields": ["status", "parent"],
},
),
]
change_form_template = 'admin/aircox/page_change_form.html'
change_form_template = "admin/aircox/page_change_form.html"
def cover_thumb(self, obj):
return mark_safe('<img src="{}"/>'.format(obj.cover.icons['64'])) \
if obj.cover else ''
return (
mark_safe('<img src="{}"/>'.format(obj.cover.icons["64"]))
if obj.cover
else ""
)
def get_changeform_initial_data(self, request):
data = super().get_changeform_initial_data(request)
filters = QueryDict(request.GET.get('_changelist_filters', ''))
data['parent'] = filters.get('parent', None)
filters = QueryDict(request.GET.get("_changelist_filters", ""))
data["parent"] = filters.get("parent", None)
return data
def _get_common_context(self, query, extra_context=None):
extra_context = extra_context or {}
parent = query.get('parent', None)
extra_context['parent'] = None if parent is None else \
Page.objects.get_subclass(id=parent)
parent = query.get("parent", None)
extra_context["parent"] = (
None if parent is None else Page.objects.get_subclass(id=parent)
)
return extra_context
def render_change_form(self, request, context, *args, **kwargs):
if context['original'] and not 'parent' in context:
context['parent'] = context['original'].parent
if context["original"] and "parent" not in context:
context["parent"] = context["original"].parent
return super().render_change_form(request, context, *args, **kwargs)
def add_view(self, request, form_url='', extra_context=None):
filters = QueryDict(request.GET.get('_changelist_filters', ''))
def add_view(self, request, form_url="", extra_context=None):
filters = QueryDict(request.GET.get("_changelist_filters", ""))
extra_context = self._get_common_context(filters, extra_context)
return super().add_view(request, form_url, extra_context)
@ -78,31 +86,33 @@ class BasePageAdmin(admin.ModelAdmin):
class PageAdmin(BasePageAdmin):
change_list_template = 'admin/aircox/page_change_list.html'
change_list_template = "admin/aircox/page_change_list.html"
list_display = BasePageAdmin.list_display + ('category',)
list_editable = BasePageAdmin.list_editable + ('category',)
list_filter = BasePageAdmin.list_filter + ('category', 'pub_date')
search_fields = BasePageAdmin.search_fields + ('category__title',)
list_display = BasePageAdmin.list_display + ("category",)
list_editable = BasePageAdmin.list_editable + ("category",)
list_filter = BasePageAdmin.list_filter + ("category", "pub_date")
search_fields = BasePageAdmin.search_fields + ("category__title",)
fieldsets = deepcopy(BasePageAdmin.fieldsets)
fieldsets[0][1]['fields'].insert(fieldsets[0][1]['fields'].index('slug') + 1, 'category')
fieldsets[1][1]['fields'] += ('featured', 'allow_comments')
fieldsets[0][1]["fields"].insert(
fieldsets[0][1]["fields"].index("slug") + 1, "category"
)
fieldsets[1][1]["fields"] += ("featured", "allow_comments")
@admin.register(StaticPage)
class StaticPageAdmin(BasePageAdmin):
list_display = BasePageAdmin.list_display + ('attach_to',)
list_display = BasePageAdmin.list_display + ("attach_to",)
fieldsets = deepcopy(BasePageAdmin.fieldsets)
fieldsets[1][1]['fields'] += ('attach_to',)
fieldsets[1][1]["fields"] += ("attach_to",)
@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display = ('page_title', 'date', 'nickname')
list_filter = ('date',)
search_fields = ('page__title', 'nickname')
list_display = ("page_title", "date", "nickname")
list_filter = ("date",)
search_fields = ("page__title", "nickname")
def page_title(self, obj):
return obj.page.title

View File

@ -1,5 +1,3 @@
from copy import copy
from django.contrib import admin
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
@ -14,20 +12,20 @@ class ScheduleInlineForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.initial:
self.fields['date'].disabled = True
self.fields['frequency'].disabled = True
self.fields["date"].disabled = True
self.fields["frequency"].disabled = True
class ScheduleInline(admin.TabularInline):
model = Schedule
form = ScheduleInlineForm
readonly_fields = ('timezone',)
readonly_fields = ("timezone",)
extra = 1
class StreamInline(admin.TabularInline):
model = Stream
fields = ['delay', 'begin', 'end']
fields = ["delay", "begin", "end"]
extra = 1
@ -39,20 +37,23 @@ class ProgramAdmin(PageAdmin):
schedule.boolean = True
schedule.short_description = _("Schedule")
list_display = PageAdmin.list_display + ('schedule', 'station', 'active')
list_filter = PageAdmin.list_filter + ('station', 'active')
prepopulated_fields = {'slug': ('title',)}
search_fields = ('title',)
list_display = PageAdmin.list_display + ("schedule", "station", "active")
list_filter = PageAdmin.list_filter + ("station", "active")
prepopulated_fields = {"slug": ("title",)}
search_fields = ("title",)
inlines = [ScheduleInline, StreamInline]
def get_fieldsets(self, request, obj=None):
fields = super().get_fieldsets(request, obj)
if request.user.has_perm('aircox.program.scheduling'):
if request.user.has_perm("aircox.program.scheduling"):
fields = fields + [
(_('Program Settings'), {
'fields': ['active', 'station', 'sync'],
})
(
_("Program Settings"),
{
"fields": ["active", "station", "sync"],
},
)
]
return fields
@ -61,26 +62,32 @@ class ProgramAdmin(PageAdmin):
class ScheduleAdmin(admin.ModelAdmin):
def program_title(self, obj):
return obj.program.title
program_title.short_description = _('Program')
program_title.short_description = _("Program")
def freq(self, obj):
return obj.get_frequency_verbose()
freq.short_description = _('Day')
list_filter = ['frequency', 'program']
list_display = ['program_title', 'freq', 'time', 'timezone', 'duration',
'initial']
list_editable = ['time', 'duration', 'initial']
freq.short_description = _("Day")
list_filter = ["frequency", "program"]
list_display = [
"program_title",
"freq",
"time",
"timezone",
"duration",
"initial",
]
list_editable = ["time", "duration", "initial"]
def get_readonly_fields(self, request, obj=None):
if obj:
return ['program', 'date', 'frequency']
return ["program", "date", "frequency"]
else:
return []
@admin.register(Stream)
class StreamAdmin(admin.ModelAdmin):
list_display = ('id', 'program', 'delay', 'begin', 'end')
list_display = ("id", "program", "delay", "begin", "end")

View File

@ -1,40 +1,48 @@
import math
from adminsortable2.admin import SortableAdminBase
from django.contrib import admin
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin
from ..models import Sound, Track
class TrackInline(admin.TabularInline):
template = 'admin/aircox/playlist_inline.html'
template = "admin/aircox/playlist_inline.html"
model = Track
extra = 0
fields = ('position', 'artist', 'title', 'tags', 'album', 'year', 'info')
fields = ("position", "artist", "title", "tags", "album", "year", "info")
list_display = ['artist', 'album', 'title', 'tags', 'related']
list_filter = ['artist', 'album', 'title', 'tags']
list_display = ["artist", "album", "title", "tags", "related"]
list_filter = ["artist", "album", "title", "tags"]
class SoundTrackInline(TrackInline):
fields = TrackInline.fields + ('timestamp',)
fields = TrackInline.fields + ("timestamp",)
class SoundInline(admin.TabularInline):
model = Sound
fields = ['type', 'name', 'audio', 'duration', 'is_good_quality',
'is_public', 'is_downloadable']
readonly_fields = ['type', 'audio', 'duration', 'is_good_quality']
fields = [
"type",
"name",
"audio",
"duration",
"is_good_quality",
"is_public",
"is_downloadable",
]
readonly_fields = ["type", "audio", "duration", "is_good_quality"]
extra = 0
max_num = 0
def audio(self, obj):
return mark_safe('<audio src="{}" controls></audio>'
.format(obj.file.url))
audio.short_description = _('Audio')
return mark_safe(
'<audio src="{}" controls></audio>'.format(obj.file.url)
)
audio.short_description = _("Audio")
def get_queryset(self, request):
return super().get_queryset(request).available()
@ -43,63 +51,99 @@ class SoundInline(admin.TabularInline):
@admin.register(Sound)
class SoundAdmin(SortableAdminBase, admin.ModelAdmin):
fields = None
list_display = ['id', 'name', 'related',
'type', 'duration', 'is_public', 'is_good_quality',
'is_downloadable', 'audio']
list_filter = ('type', 'is_good_quality', 'is_public')
list_editable = ['name', 'is_public', 'is_downloadable']
search_fields = ['name', 'program__title']
fieldsets = [
(None, {'fields': ['name', 'file', 'type', 'program', 'episode']}),
(None, {'fields': ['duration', 'is_public', 'is_downloadable',
'is_good_quality', 'mtime']}),
list_display = [
"id",
"name",
"related",
"type",
"duration",
"is_public",
"is_good_quality",
"is_downloadable",
"audio",
]
readonly_fields = ('file', 'duration', 'type')
list_filter = ("type", "is_good_quality", "is_public")
list_editable = ["name", "is_public", "is_downloadable"]
search_fields = ["name", "program__title"]
fieldsets = [
(None, {"fields": ["name", "file", "type", "program", "episode"]}),
(
None,
{
"fields": [
"duration",
"is_public",
"is_downloadable",
"is_good_quality",
"mtime",
]
},
),
]
readonly_fields = ("file", "duration", "type")
inlines = [SoundTrackInline]
def related(self, obj):
# TODO: link to episode or program edit
return obj.episode.title if obj.episode else\
obj.program.title if obj.program else ''
related.short_description = _('Program / Episode')
return (
obj.episode.title
if obj.episode
else obj.program.title
if obj.program
else ""
)
related.short_description = _("Program / Episode")
def audio(self, obj):
return mark_safe('<audio src="{}" controls></audio>'
.format(obj.file.url)) \
if obj.type != Sound.TYPE_REMOVED else ''
audio.short_description = _('Audio')
return (
mark_safe('<audio src="{}" controls></audio>'.format(obj.file.url))
if obj.type != Sound.TYPE_REMOVED
else ""
)
def add_view(self, request, form_url='', context=None):
audio.short_description = _("Audio")
def add_view(self, request, form_url="", context=None):
context = context or {}
context['init_app'] = True
context['init_el'] = '#inline-tracks'
context['track_timestamp'] = True
context["init_app"] = True
context["init_el"] = "#inline-tracks"
context["track_timestamp"] = True
return super().add_view(request, form_url, context)
def change_view(self, request, object_id, form_url='', context=None):
def change_view(self, request, object_id, form_url="", context=None):
context = context or {}
context['init_app'] = True
context['init_el'] = '#inline-tracks'
context['track_timestamp'] = True
context["init_app"] = True
context["init_el"] = "#inline-tracks"
context["track_timestamp"] = True
return super().change_view(request, object_id, form_url, context)
@admin.register(Track)
class TrackAdmin(admin.ModelAdmin):
def tag_list(self, obj):
return u", ".join(o.name for o in obj.tags.all())
return ", ".join(o.name for o in obj.tags.all())
list_display = ['pk', 'artist', 'title', 'tag_list', 'episode',
'sound', 'ts']
list_editable = ['artist', 'title']
list_filter = ['artist', 'title', 'tags']
list_display = [
"pk",
"artist",
"title",
"tag_list",
"episode",
"sound",
"ts",
]
list_editable = ["artist", "title"]
list_filter = ["artist", "title", "tags"]
search_fields = ['artist', 'title']
search_fields = ["artist", "title"]
fieldsets = [
(_('Playlist'), {'fields': ['episode', 'sound', 'position',
'timestamp']}),
(_('Info'), {'fields': ['artist', 'title', 'info', 'tags']}),
(
_("Playlist"),
{"fields": ["episode", "sound", "position", "timestamp"]},
),
(_("Info"), {"fields": ["artist", "title", "info", "tags"]}),
]
# TODO on edit: readonly_fields = ['episode', 'sound']
@ -107,10 +151,10 @@ class TrackAdmin(admin.ModelAdmin):
def ts(self, obj):
ts = obj.timestamp
if ts is None:
return ''
return ""
h = math.floor(ts / 3600)
m = math.floor((ts - h) / 60)
s = ts-h*3600-m*60
return '{:0>2}:{:0>2}:{:0>2}'.format(h, m, s)
s = ts - h * 3600 - m * 60
return "{:0>2}:{:0>2}:{:0>2}".format(h, m, s)
ts.short_description = _('timestamp')
ts.short_description = _("timestamp")

View File

@ -1,11 +1,10 @@
from django.contrib import admin
from adminsortable2.admin import SortableAdminBase
from django.contrib import admin
from ..models import Port, Station
from .page import NavItemInline
__all__ = ['PortInline', 'StationAdmin']
__all__ = ["PortInline", "StationAdmin"]
class PortInline(admin.StackedInline):
@ -15,7 +14,5 @@ class PortInline(admin.StackedInline):
@admin.register(Station)
class StationAdmin(SortableAdminBase, admin.ModelAdmin):
prepopulated_fields = {'slug': ('name',)}
prepopulated_fields = {"slug": ("name",)}
inlines = (PortInline, NavItemInline)