.
This commit is contained in:
@@ -11,6 +11,9 @@
|
|||||||
<div class="level-item">
|
<div class="level-item">
|
||||||
<a class="button is-light" href="{% url 'cards:list' %}">Back to cards</a>
|
<a class="button is-light" href="{% url 'cards:list' %}">Back to cards</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="level-item">
|
||||||
|
<a class="button is-link" href="{% url 'cards:backgrounds_download_all' %}">Download all backgrounds</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<h2 class="title is-4">Cards overview — bulk edit</h2>
|
<h2 class="title is-4">Cards overview — bulk edit{% if card_count %} ({{ card_count }} card{{ card_count|pluralize }}){% endif %}</h2>
|
||||||
<p class="subtitle is-6">Edit multiple cards inline and save changes in one go.</p>
|
<p class="subtitle is-6">Edit multiple cards inline and save changes in one go.</p>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -17,6 +17,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% if card_count is not None %}
|
||||||
|
<div class="level-item">
|
||||||
|
<span class="tag is-light">{{ card_count }} card{% if card_count > 1 %}s{% endif %}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div id="htmx-form"></div>
|
<div id="htmx-form"></div>
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ urlpatterns = [
|
|||||||
path('preview/<int:pk>/', views.preview_card, name='preview'),
|
path('preview/<int:pk>/', views.preview_card, name='preview'),
|
||||||
path('backgrounds/', views.background_list, name='backgrounds'),
|
path('backgrounds/', views.background_list, name='backgrounds'),
|
||||||
path('backgrounds/create/', views.background_create, name='background_create'),
|
path('backgrounds/create/', views.background_create, name='background_create'),
|
||||||
|
path('backgrounds/download-all/', views.backgrounds_download_all, name='backgrounds_download_all'),
|
||||||
path('backgrounds/<int:pk>/activate/', views.background_activate, name='background_activate'),
|
path('backgrounds/<int:pk>/activate/', views.background_activate, name='background_activate'),
|
||||||
path('backgrounds/<int:pk>/delete/', views.background_delete, name='background_delete'),
|
path('backgrounds/<int:pk>/delete/', views.background_delete, name='background_delete'),
|
||||||
path('overview/', views.cards_overview, name='overview'),
|
path('overview/', views.cards_overview, name='overview'),
|
||||||
|
|||||||
+38
-2
@@ -1,6 +1,8 @@
|
|||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django.http import HttpResponse, HttpResponseBadRequest
|
from django.http import HttpResponse, HttpResponseBadRequest
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
import io
|
||||||
|
import zipfile
|
||||||
import os
|
import os
|
||||||
from .models import Dinosaur, BackgroundImage
|
from .models import Dinosaur, BackgroundImage
|
||||||
from .forms import DinosaurBulkFormSet
|
from .forms import DinosaurBulkFormSet
|
||||||
@@ -62,6 +64,9 @@ def card_list(request):
|
|||||||
'current_order': order,
|
'current_order': order,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Total (or filtered) card count for UI summary
|
||||||
|
context['card_count'] = qs.count()
|
||||||
|
|
||||||
if is_htmx(request):
|
if is_htmx(request):
|
||||||
return render(request, 'cards/_cards_list.html', context)
|
return render(request, 'cards/_cards_list.html', context)
|
||||||
return render(request, 'cards/list.html', context)
|
return render(request, 'cards/list.html', context)
|
||||||
@@ -158,6 +163,35 @@ def background_list(request):
|
|||||||
return render(request, 'cards/backgrounds_list.html', {'backgrounds': backgrounds, 'form': form})
|
return render(request, 'cards/backgrounds_list.html', {'backgrounds': backgrounds, 'form': form})
|
||||||
|
|
||||||
|
|
||||||
|
def backgrounds_download_all(request):
|
||||||
|
"""Provide a ZIP archive download containing all uploaded background images.
|
||||||
|
|
||||||
|
Creates the ZIP in-memory and returns it as an attachment. Skips missing
|
||||||
|
files gracefully.
|
||||||
|
"""
|
||||||
|
backgrounds = BackgroundImage.objects.all()
|
||||||
|
buffer = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(buffer, 'w', compression=zipfile.ZIP_DEFLATED) as zf:
|
||||||
|
for bg in backgrounds:
|
||||||
|
if not getattr(bg, 'image', None):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
filepath = bg.image.path
|
||||||
|
except Exception:
|
||||||
|
# If storage backend doesn't expose a local path, skip.
|
||||||
|
continue
|
||||||
|
if not os.path.exists(filepath):
|
||||||
|
continue
|
||||||
|
arcname = os.path.basename(bg.image.name) if getattr(bg.image, 'name', None) else f'background-{bg.pk}'
|
||||||
|
# Prefix with id to avoid duplicate names
|
||||||
|
arcname = f"{bg.pk}_{arcname}"
|
||||||
|
zf.write(filepath, arcname)
|
||||||
|
buffer.seek(0)
|
||||||
|
resp = HttpResponse(buffer.getvalue(), content_type='application/zip')
|
||||||
|
resp['Content-Disposition'] = 'attachment; filename=backgrounds.zip'
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
def background_create(request):
|
def background_create(request):
|
||||||
if request.method != 'POST':
|
if request.method != 'POST':
|
||||||
return HttpResponseBadRequest('Only POST allowed')
|
return HttpResponseBadRequest('Only POST allowed')
|
||||||
@@ -222,7 +256,8 @@ def cards_overview(request):
|
|||||||
qs = Dinosaur.objects.order_by('name')
|
qs = Dinosaur.objects.order_by('name')
|
||||||
# Use a modelformset so the template renders the management form
|
# Use a modelformset so the template renders the management form
|
||||||
formset = DinosaurBulkFormSet(queryset=qs)
|
formset = DinosaurBulkFormSet(queryset=qs)
|
||||||
return render(request, 'cards/bulk_edit.html', {'formset': formset, 'backgrounds': backgrounds})
|
card_count = qs.count()
|
||||||
|
return render(request, 'cards/bulk_edit.html', {'formset': formset, 'backgrounds': backgrounds, 'card_count': card_count})
|
||||||
|
|
||||||
|
|
||||||
def cards_bulk_update(request):
|
def cards_bulk_update(request):
|
||||||
@@ -243,7 +278,8 @@ def cards_bulk_update(request):
|
|||||||
return redirect('cards:overview')
|
return redirect('cards:overview')
|
||||||
# If invalid, re-render the overview with errors
|
# If invalid, re-render the overview with errors
|
||||||
backgrounds = BackgroundImage.objects.all()
|
backgrounds = BackgroundImage.objects.all()
|
||||||
return render(request, 'cards/bulk_edit.html', {'formset': formset, 'backgrounds': backgrounds})
|
card_count = qs.count()
|
||||||
|
return render(request, 'cards/bulk_edit.html', {'formset': formset, 'backgrounds': backgrounds, 'card_count': card_count})
|
||||||
|
|
||||||
|
|
||||||
def cards_bulk_create_images(request):
|
def cards_bulk_create_images(request):
|
||||||
|
|||||||
BIN
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 6.9 MiB |
Reference in New Issue
Block a user