image library

created using VS Codey Copilot Agent with Claude Sonnet 4
This commit is contained in:
Tobias Brunner 2025-07-04 16:28:13 +02:00
parent bdf06863d2
commit 52dbe89582
No known key found for this signature in database
14 changed files with 1366 additions and 3 deletions

View file

@ -0,0 +1,149 @@
from django import forms
from django.utils.safestring import mark_safe
from django.utils.html import format_html
from django.urls import reverse
from ..models.images import ImageLibrary
class ImageLibraryWidget(forms.Select):
"""
Custom widget for selecting images from the library with thumbnails.
"""
def __init__(self, attrs=None, choices=(), show_thumbnails=True):
self.show_thumbnails = show_thumbnails
super().__init__(attrs, choices)
def format_value(self, value):
"""
Format the selected value for display.
"""
if value is None:
return ""
return str(value)
def render(self, name, value, attrs=None, renderer=None):
"""
Render the widget with thumbnails.
"""
if attrs is None:
attrs = {}
# Add CSS class for styling
attrs["class"] = attrs.get("class", "") + " image-library-select"
# Get all images for the select options
images = ImageLibrary.objects.all().order_by("name")
# Build choices with thumbnails
choices = [("", "--- Select an image ---")]
for image in images:
thumbnail_html = ""
if self.show_thumbnails and image.image:
thumbnail_html = format_html(
' <img src="{}" style="width: 20px; height: 20px; object-fit: cover; margin-left: 5px; vertical-align: middle;" />',
image.image.url,
)
choice_text = (
f"{image.name} ({image.get_category_display()}){thumbnail_html}"
)
choices.append((image.pk, choice_text))
# Build the select element
select_html = format_html(
'<select name="{}" id="{}"{}>{}</select>',
name,
attrs.get("id", ""),
self._build_attrs_string(attrs),
self._build_options(choices, value),
)
# Add preview area
preview_html = ""
if value:
try:
image = ImageLibrary.objects.get(pk=value)
preview_html = format_html(
'<div class="image-preview" style="margin-top: 10px;">'
'<img src="{}" style="max-width: 200px; max-height: 200px; border: 1px solid #ddd; border-radius: 4px;" />'
'<p style="margin-top: 5px; font-size: 12px; color: #666;">{} - {}x{} - {}</p>'
"</div>",
image.image.url,
image.name,
image.width or "?",
image.height or "?",
image.get_file_size_display(),
)
except ImageLibrary.DoesNotExist:
pass
# Add JavaScript for preview updates
js_html = format_html(
"<script>"
'document.addEventListener("DOMContentLoaded", function() {{'
' const select = document.getElementById("{}");\n'
' const previewDiv = select.parentNode.querySelector(".image-preview");\n'
' select.addEventListener("change", function() {{'
" const imageId = this.value;\n"
" if (imageId) {{"
' fetch("/admin/services/imagelibrary/" + imageId + "/preview/")'
" .then(response => response.json())"
" .then(data => {{"
" if (previewDiv) {{"
" previewDiv.innerHTML = data.html;\n"
" }}"
" }});\n"
" }} else {{"
" if (previewDiv) {{"
' previewDiv.innerHTML = "";\n'
" }}"
" }}"
" }});\n"
"}});\n"
"</script>",
attrs.get("id", ""),
)
return mark_safe(select_html + preview_html + js_html)
def _build_attrs_string(self, attrs):
"""
Build HTML attributes string.
"""
attr_parts = []
for key, value in attrs.items():
if key != "id": # id is handled separately
attr_parts.append(f'{key}="{value}"')
return " " + " ".join(attr_parts) if attr_parts else ""
def _build_options(self, choices, selected_value):
"""
Build option elements for the select.
"""
options = []
for value, text in choices:
selected = "selected" if str(value) == str(selected_value) else ""
options.append(f'<option value="{value}" {selected}>{text}</option>')
return "".join(options)
class ImageLibraryField(forms.ModelChoiceField):
"""
Custom form field for selecting images from the library.
"""
def __init__(self, queryset=None, widget=None, show_thumbnails=True, **kwargs):
if queryset is None:
queryset = ImageLibrary.objects.all()
if widget is None:
widget = ImageLibraryWidget(show_thumbnails=show_thumbnails)
super().__init__(queryset=queryset, widget=widget, **kwargs)
def label_from_instance(self, obj):
"""
Return the label for an image instance.
"""
return f"{obj.name} ({obj.get_category_display()})"