Admin actions в качестве views
Долгое время в джанговской админке не было встроенной поддержки для массовых действий над записями. К примеру как только не приходилось извращаться для хотя бы чуть более удобного удаления объектов.
В версии 1.1 джангонавтам привалило счастье - групповые операции. Удаление кучки объектов теперь есть прямо из коробки. При этом, как обычно это бывает в django, использовать новый механизм легко и приятно - написание простенького action укладывается в две-три строчки. Больше того, action может не только как-то молча обрабатывать группу объектов, но и возвращать HttpResponse, в котором пользователь может проделать дополнительные действия.
Документация в качестве примера предлагает возвращать HttpResponseRedirect и передавать список обрабатываемых объектов в виде GET-параметра полноценной view для последующей самостоятельной работы. Однако, ничего не мешает использовать наш action в качестве view-функции.
Допустим у нас есть магазин, в котором товары разбиты по категориям:
class Category(models.Model):
name = models.CharField(u'Наименование', max_length=100)
def __unicode__(self):
return self.name
class Product(models.Model):
category = models.ForeignKey(Category, verbose_name=u'Категория')
name = models.CharField(u'Наименование', max_length=100)
price = models.DecimalField(u'Цена', max_digits=15, decimal_places=2)
Время от времени пользователю хочется перенести пачку товаров из одной категории в другую (к примеру разбили «Молоко» на «Молоко настоящее»/«Молочные продукты» и надо раскидать товары согласно указаниям санэпидстанции и угрызениям своей совести). Чтобы человек не мучался, выставляя каждому такому товару новую категорию, мы и напишем нехитрый action.
class CategoryForm(forms.Form):
category = forms.ModelChoiceField(queryset=Category.objects.all())
class ProductAdmin(admin.ModelAdmin):
list_display = ('name', 'price', 'category',)
actions = ['set_category_action']
def set_category_action(self, request, queryset):
if 'do_action' in request.POST:
form = CategoryForm(request.POST)
if form.is_valid():
queryset.update(category=form.cleaned_data['category'])
# Ничего не возвращаем, это вернет нас на список товаров
return
else:
form = CategoryForm()
return render_to_response(
'admin/shop/set_category.html',
{'title': u'Укажите категорию, в которую надо переместить товары',
'objects': queryset,
'form': form},
context_instance=RequestContext(request))
set_category_action.short_description = u'Переместить в категорию'
Поддерживающий это дело set_category.html прост. Через hidden-поля action и _selected_action мы заставляем админку опять вызвать наш action, но на этот раз в POST попадают две дополнительные переменные: do_action (флажок, что мы пришли из формы) и category (та категория, которую надо назначить).
{% extends "admin/base_site.html" %}
{% block content %}
<form action="" method="post">
<input type="hidden" name="action" value="set_category_action">
<input type="hidden" name="do_action" value="yes">
<div>
{{ form.category }}
<input type="submit" class="default" style="float: none" value="Переместить">
{{ form.category.errors }}
</div>
<h2>Товары для перемещения</h2>
<ul>
{% for object in objects %}
<li>
<a href="{{ object.pk }}/">{{ object.name }}</a> - {{ object.category }}
<input type="hidden" name="_selected_action" value="{{ object.pk }}">
</li>
{% endfor %}
</ul>
</form>
{% endblock %}

Вот таким простым образом мы избавились от необходимости писать лишний view, самостоятельно выдергивать в нем список объектов, придумывать url и делать редирект на change list.
[...] Пытаюсь написать admin action вот по этому примеру:http://djangonaut.ru/2009/05/03/admin-actions-as-views/Когда нажимаю на кнопку “Переместить”, срабатывает csrf, [...]
Admin actions
4 Июнь 10 at 1:44