--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/parts/django/docs/ref/contrib/admin/actions.txt Sat Jan 08 11:20:57 2011 +0530
@@ -0,0 +1,351 @@
+=============
+Admin actions
+=============
+
+.. versionadded:: 1.1
+
+.. currentmodule:: django.contrib.admin
+
+The basic workflow of Django's admin is, in a nutshell, "select an object,
+then change it." This works well for a majority of use cases. However, if you
+need to make the same change to many objects at once, this workflow can be
+quite tedious.
+
+In these cases, Django's admin lets you write and register "actions" -- simple
+functions that get called with a list of objects selected on the change list
+page.
+
+If you look at any change list in the admin, you'll see this feature in
+action; Django ships with a "delete selected objects" action available to all
+models. For example, here's the user module from Django's built-in
+:mod:`django.contrib.auth` app:
+
+.. image:: _images/user_actions.png
+
+.. warning::
+
+ The "delete selected objects" action uses :meth:`QuerySet.delete()
+ <django.db.models.QuerySet.delete>` for efficiency reasons, which has an
+ important caveat: your model's ``delete()`` method will not be called.
+
+ If you wish to override this behavior, simply write a custom action which
+ accomplishes deletion in your preferred manner -- for example, by calling
+ ``Model.delete()`` for each of the selected items.
+
+ For more background on bulk deletion, see the documentation on :ref:`object
+ deletion <topics-db-queries-delete>`.
+
+Read on to find out how to add your own actions to this list.
+
+Writing actions
+===============
+
+The easiest way to explain actions is by example, so let's dive in.
+
+A common use case for admin actions is the bulk updating of a model. Imagine a
+simple news application with an ``Article`` model::
+
+ from django.db import models
+
+ STATUS_CHOICES = (
+ ('d', 'Draft'),
+ ('p', 'Published'),
+ ('w', 'Withdrawn'),
+ )
+
+ class Article(models.Model):
+ title = models.CharField(max_length=100)
+ body = models.TextField()
+ status = models.CharField(max_length=1, choices=STATUS_CHOICES)
+
+ def __unicode__(self):
+ return self.title
+
+A common task we might perform with a model like this is to update an
+article's status from "draft" to "published". We could easily do this in the
+admin one article at a time, but if we wanted to bulk-publish a group of
+articles, it'd be tedious. So, let's write an action that lets us change an
+article's status to "published."
+
+Writing action functions
+------------------------
+
+First, we'll need to write a function that gets called when the action is
+trigged from the admin. Action functions are just regular functions that take
+three arguments:
+
+ * The current :class:`ModelAdmin`
+ * An :class:`~django.http.HttpRequest` representing the current request,
+ * A :class:`~django.db.models.QuerySet` containing the set of objects
+ selected by the user.
+
+Our publish-these-articles function won't need the :class:`ModelAdmin` or the
+request object, but we will use the queryset::
+
+ def make_published(modeladmin, request, queryset):
+ queryset.update(status='p')
+
+.. note::
+
+ For the best performance, we're using the queryset's :ref:`update method
+ <topics-db-queries-update>`. Other types of actions might need to deal
+ with each object individually; in these cases we'd just iterate over the
+ queryset::
+
+ for obj in queryset:
+ do_something_with(obj)
+
+That's actually all there is to writing an action! However, we'll take one
+more optional-but-useful step and give the action a "nice" title in the admin.
+By default, this action would appear in the action list as "Make published" --
+the function name, with underscores replaced by spaces. That's fine, but we
+can provide a better, more human-friendly name by giving the
+``make_published`` function a ``short_description`` attribute::
+
+ def make_published(modeladmin, request, queryset):
+ queryset.update(status='p')
+ make_published.short_description = "Mark selected stories as published"
+
+.. note::
+
+ This might look familiar; the admin's ``list_display`` option uses the
+ same technique to provide human-readable descriptions for callback
+ functions registered there, too.
+
+Adding actions to the :class:`ModelAdmin`
+-----------------------------------------
+
+Next, we'll need to inform our :class:`ModelAdmin` of the action. This works
+just like any other configuration option. So, the complete ``admin.py`` with
+the action and its registration would look like::
+
+ from django.contrib import admin
+ from myapp.models import Article
+
+ def make_published(modeladmin, request, queryset):
+ queryset.update(status='p')
+ make_published.short_description = "Mark selected stories as published"
+
+ class ArticleAdmin(admin.ModelAdmin):
+ list_display = ['title', 'status']
+ ordering = ['title']
+ actions = [make_published]
+
+ admin.site.register(Article, ArticleAdmin)
+
+That code will give us an admin change list that looks something like this:
+
+.. image:: _images/article_actions.png
+
+That's really all there is to it! If you're itching to write your own actions,
+you now know enough to get started. The rest of this document just covers more
+advanced techniques.
+
+Advanced action techniques
+==========================
+
+There's a couple of extra options and possibilities you can exploit for more
+advanced options.
+
+Actions as :class:`ModelAdmin` methods
+--------------------------------------
+
+The example above shows the ``make_published`` action defined as a simple
+function. That's perfectly fine, but it's not perfect from a code design point
+of view: since the action is tightly coupled to the ``Article`` object, it
+makes sense to hook the action to the ``ArticleAdmin`` object itself.
+
+That's easy enough to do::
+
+ class ArticleAdmin(admin.ModelAdmin):
+ ...
+
+ actions = ['make_published']
+
+ def make_published(self, request, queryset):
+ queryset.update(status='p')
+ make_published.short_description = "Mark selected stories as published"
+
+Notice first that we've moved ``make_published`` into a method and renamed the
+`modeladmin` parameter to `self`, and second that we've now put the string
+``'make_published'`` in ``actions`` instead of a direct function reference. This
+tells the :class:`ModelAdmin` to look up the action as a method.
+
+Defining actions as methods gives the action more straightforward, idiomatic
+access to the :class:`ModelAdmin` itself, allowing the action to call any of the
+methods provided by the admin.
+
+.. _custom-admin-action:
+
+For example, we can use ``self`` to flash a message to the user informing her
+that the action was successful::
+
+ class ArticleAdmin(admin.ModelAdmin):
+ ...
+
+ def make_published(self, request, queryset):
+ rows_updated = queryset.update(status='p')
+ if rows_updated == 1:
+ message_bit = "1 story was"
+ else:
+ message_bit = "%s stories were" % rows_updated
+ self.message_user(request, "%s successfully marked as published." % message_bit)
+
+This make the action match what the admin itself does after successfully
+performing an action:
+
+.. image:: _images/article_actions_message.png
+
+Actions that provide intermediate pages
+---------------------------------------
+
+By default, after an action is performed the user is simply redirected back
+to the original change list page. However, some actions, especially more
+complex ones, will need to return intermediate pages. For example, the
+built-in delete action asks for confirmation before deleting the selected
+objects.
+
+To provide an intermediary page, simply return an
+:class:`~django.http.HttpResponse` (or subclass) from your action. For
+example, you might write a simple export function that uses Django's
+:doc:`serialization functions </topics/serialization>` to dump some selected
+objects as JSON::
+
+ from django.http import HttpResponse
+ from django.core import serializers
+
+ def export_as_json(modeladmin, request, queryset):
+ response = HttpResponse(mimetype="text/javascript")
+ serializers.serialize("json", queryset, stream=response)
+ return response
+
+Generally, something like the above isn't considered a great idea. Most of the
+time, the best practice will be to return an
+:class:`~django.http.HttpResponseRedirect` and redirect the user to a view
+you've written, passing the list of selected objects in the GET query string.
+This allows you to provide complex interaction logic on the intermediary
+pages. For example, if you wanted to provide a more complete export function,
+you'd want to let the user choose a format, and possibly a list of fields to
+include in the export. The best thing to do would be to write a small action
+that simply redirects to your custom export view::
+
+ from django.contrib import admin
+ from django.contrib.contenttypes.models import ContentType
+ from django.http import HttpResponseRedirect
+
+ def export_selected_objects(modeladmin, request, queryset):
+ selected = request.POST.getlist(admin.ACTION_CHECKBOX_NAME)
+ ct = ContentType.objects.get_for_model(queryset.model)
+ return HttpResponseRedirect("/export/?ct=%s&ids=%s" % (ct.pk, ",".join(selected)))
+
+As you can see, the action is the simple part; all the complex logic would
+belong in your export view. This would need to deal with objects of any type,
+hence the business with the ``ContentType``.
+
+Writing this view is left as an exercise to the reader.
+
+.. _adminsite-actions:
+
+Making actions available site-wide
+----------------------------------
+
+.. method:: AdminSite.add_action(action[, name])
+
+ Some actions are best if they're made available to *any* object in the admin
+ site -- the export action defined above would be a good candidate. You can
+ make an action globally available using :meth:`AdminSite.add_action()`. For
+ example::
+
+ from django.contrib import admin
+
+ admin.site.add_action(export_selected_objects)
+
+ This makes the `export_selected_objects` action globally available as an
+ action named `"export_selected_objects"`. You can explicitly give the action
+ a name -- good if you later want to programatically :ref:`remove the action
+ <disabling-admin-actions>` -- by passing a second argument to
+ :meth:`AdminSite.add_action()`::
+
+ admin.site.add_action(export_selected_objects, 'export_selected')
+
+.. _disabling-admin-actions:
+
+Disabling actions
+-----------------
+
+Sometimes you need to disable certain actions -- especially those
+:ref:`registered site-wide <adminsite-actions>` -- for particular objects.
+There's a few ways you can disable actions:
+
+Disabling a site-wide action
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. method:: AdminSite.disable_action(name)
+
+ If you need to disable a :ref:`site-wide action <adminsite-actions>` you can
+ call :meth:`AdminSite.disable_action()`.
+
+ For example, you can use this method to remove the built-in "delete selected
+ objects" action::
+
+ admin.site.disable_action('delete_selected')
+
+ Once you've done the above, that action will no longer be available
+ site-wide.
+
+ If, however, you need to re-enable a globally-disabled action for one
+ particular model, simply list it explicitly in your ``ModelAdmin.actions``
+ list::
+
+ # Globally disable delete selected
+ admin.site.disable_action('delete_selected')
+
+ # This ModelAdmin will not have delete_selected available
+ class SomeModelAdmin(admin.ModelAdmin):
+ actions = ['some_other_action']
+ ...
+
+ # This one will
+ class AnotherModelAdmin(admin.ModelAdmin):
+ actions = ['delete_selected', 'a_third_action']
+ ...
+
+
+Disabling all actions for a particular :class:`ModelAdmin`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you want *no* bulk actions available for a given :class:`ModelAdmin`, simply
+set :attr:`ModelAdmin.actions` to ``None``::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ actions = None
+
+This tells the :class:`ModelAdmin` to not display or allow any actions,
+including any :ref:`site-wide actions <adminsite-actions>`.
+
+Conditionally enabling or disabling actions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. method:: ModelAdmin.get_actions(request)
+
+ Finally, you can conditionally enable or disable actions on a per-request
+ (and hence per-user basis) by overriding :meth:`ModelAdmin.get_actions`.
+
+ This returns a dictionary of actions allowed. The keys are action names, and
+ the values are ``(function, name, short_description)`` tuples.
+
+ Most of the time you'll use this method to conditionally remove actions from
+ the list gathered by the superclass. For example, if I only wanted users
+ whose names begin with 'J' to be able to delete objects in bulk, I could do
+ the following::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ ...
+
+ def get_actions(self, request):
+ actions = super(MyModelAdmin, self).get_actions(request)
+ if request.user.username[0].upper() != 'J':
+ del actions['delete_selected']
+ return actions
+
+