Files
labhelper/boxes/admin.py
2026-01-20 00:18:05 +01:00

180 lines
5.1 KiB
Python

import json
from django import forms
from django.contrib import admin
from django.contrib.admin import SimpleListFilter
from django.http import JsonResponse
from django.urls import path
from django.utils.html import format_html
from django.views.decorators.http import require_POST
from .models import Box, BoxType, Facet, Tag, Thing, ThingFile, ThingLink
class BoxFilter(SimpleListFilter):
"""Custom filter for boxes using pk to avoid spaces in aliases."""
title = 'box'
parameter_name = 'box__pk'
def lookups(self, request, model_admin):
boxes = Box.objects.select_related('box_type').order_by('sort_order')
return [(box.pk, str(box)) for box in boxes]
def queryset(self, request, queryset):
if self.value():
return queryset.filter(box__pk=self.value())
return queryset
class TagsFilter(SimpleListFilter):
"""Custom filter for tags using pk to avoid spaces in aliases."""
title = 'tags'
parameter_name = 'tags__pk'
def lookups(self, request, model_admin):
tags = Tag.objects.select_related('facet').order_by('facet__name', 'name')
return [(tag.pk, str(tag)) for tag in tags]
def queryset(self, request, queryset):
if self.value():
return queryset.filter(tags__pk=self.value())
return queryset
@admin.register(BoxType)
class BoxTypeAdmin(admin.ModelAdmin):
"""Admin configuration for BoxType model."""
list_display = ('name', 'width', 'height', 'length')
search_fields = ('name',)
@admin.register(Box)
class BoxAdmin(admin.ModelAdmin):
"""Admin configuration for Box model."""
ordering = ['sort_order']
list_display = ('id', 'box_type', 'sort_order')
list_filter = ('box_type',)
search_fields = ('id',)
change_list_template = 'admin/boxes/box/change_list.html'
def get_urls(self):
urls = super().get_urls()
custom_urls = [
path('reorder/', self.admin_site.admin_view(self.reorder_view), name='boxes_box_reorder'),
]
return custom_urls + urls
def reorder_view(self, request):
"""Handle AJAX reorder requests."""
if request.method != 'POST':
return JsonResponse({'error': 'POST required'}, status=405)
try:
data = json.loads(request.body)
order = data.get('order', [])
for index, pk in enumerate(order):
Box.objects.filter(pk=pk).update(sort_order=index)
return JsonResponse({'status': 'ok'})
except (json.JSONDecodeError, KeyError) as e:
return JsonResponse({'error': str(e)}, status=400)
class ThingFileInline(admin.TabularInline):
"""Inline admin for Thing files."""
model = ThingFile
extra = 1
fields = ('title', 'file')
class ThingLinkInline(admin.TabularInline):
"""Inline admin for Thing links."""
model = ThingLink
extra = 1
fields = ('title', 'url')
class ThingAdmin(admin.ModelAdmin):
"""Admin configuration for Thing model."""
list_display = ('name', 'box')
list_filter = (BoxFilter, TagsFilter)
search_fields = ('name', 'description')
filter_horizontal = ('tags',)
inlines = [ThingFileInline, ThingLinkInline]
admin.site.register(Thing, ThingAdmin)
@admin.register(ThingFile)
class ThingFileAdmin(admin.ModelAdmin):
"""Admin configuration for ThingFile model."""
list_display = ('thing', 'title', 'uploaded_at')
list_filter = ('thing',)
search_fields = ('title',)
class ColorInput(forms.TextInput):
"""Color picker widget using HTML5 color input."""
input_type = 'color'
class FacetAdminForm(forms.ModelForm):
"""Form for Facet model with color picker widget."""
class Meta:
model = Facet
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['color'].widget = ColorInput(attrs={'type': 'color', 'style': 'height: 50px; width: 100px;'})
@admin.register(Facet)
class FacetAdmin(admin.ModelAdmin):
"""Admin configuration for Facet model."""
form = FacetAdminForm
list_display = ('name', 'color_preview', 'cardinality')
search_fields = ('name',)
prepopulated_fields = {'slug': ('name',)}
list_filter = ('cardinality',)
def color_preview(self, obj):
return format_html('<span style="color: {}; font-weight: bold;">■ {}</span>', obj.color, obj.color)
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
"""Admin configuration for Tag model."""
list_display = ('__str__', 'facet_with_color')
list_filter = ('facet',)
search_fields = ('name', 'facet__name')
def facet_with_color(self, obj):
if obj.facet:
return format_html('<span style="color: {}; font-weight: bold;">{}</span>', obj.facet.color, obj.facet.name)
return '-'
facet_with_color.short_description = 'Facet'
@admin.register(ThingLink)
class ThingLinkAdmin(admin.ModelAdmin):
"""Admin configuration for ThingLink model."""
list_display = ('thing', 'title', 'url', 'uploaded_at')
list_filter = ('thing',)
search_fields = ('title', 'url')