Skip to content

Instantly share code, notes, and snippets.

@mariocesar
Last active December 28, 2023 19:17
Show Gist options
  • Save mariocesar/8adc56de00104e90bac7 to your computer and use it in GitHub Desktop.
Save mariocesar/8adc56de00104e90bac7 to your computer and use it in GitHub Desktop.
Django admin decorator to create a confirmation form action, like the default delete action works
from .models import Post, Category
from .decorators import action_form
class PostCategoryForm(forms.Form):
title = 'Update category for the selected posts'
myfile = forms.FileField()
category = forms.ModelChoiceField(queryset=Category.objects.all())
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
actions = ['change_category']
@action_form(PostCategoryForm)
def change_category(self, request, queryset, form):
category = form.cleaned_data['category']
return queryset.update(category=category)
import functools
from django.contrib.admin import helpers
from django.template.response import TemplateResponse
def action_form(form_class=None):
def decorator(func):
@functools.wraps(func)
def wrapper(self, request, queryset):
form = form_class()
if 'confirm' in request.POST and request.POST:
form = form_class(request.POST, request.FILES)
if form.is_valid():
obj_count = func(self, request, queryset, form)
self.message_user(request, '%s objects updated' % obj_count)
return None
context = dict(
self.admin_site.each_context(request),
title=form_class.title,
action=func.__name__,
opts=self.model._meta,
queryset=queryset, form=form,
action_checkbox_name=helpers.ACTION_CHECKBOX_NAME)
return TemplateResponse(request, 'admin/form_action_confirmation.html', context)
wrapper.short_description = form_class.title
return wrapper
return decorator
{% extends "admin/base_site.html" %}
{% load i18n l10n admin_urls %}
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation
delete-selected-confirmation{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
&rsaquo; {{ title }}
</div>
{% endblock %}
{% block content %}
<ul style="padding: 0">
{% for object in queryset.all %}
<li style="list-style: none; float: left; margin: 5px">
{{ object }}
</li>
{% endfor %}
</ul>
<hr>
<br>
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
<fieldset class="module aligned">
{% for obj in queryset.all %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}"/>
{% endfor %}
<div class="form-row">
{{ form }}
</div>
</fieldset>
<div class="submit-row">
<input type="hidden" name="action" value="{{ action }}"/>
<input type="submit" name="confirm" value="{% trans "Confirm" %}"/>
<a href="#" onclick="window.history.back(); return false;"
class="button cancel-link">{% trans "No, take me back" %}</a>
</div>
</form>
{% endblock %}
@vuducmanh11
Copy link

vuducmanh11 commented Dec 28, 2023

I do same your instructions but exist values not autofill in confirmation form, right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment