Source code for jmb.core.admin.actions

# -*- coding: utf-8 -*-
""".. _action-forms:

.. image:: ../../images/actions.png


Extra Action Form
==================

Admin Actions_ in daily usage often require one or more
arguments. This extension provides an easy way to display a form to
fill in what needs to be passed to the actions. Namely:

#. Create a form that holds all the fields needed for any action and
   set ``action_form`` on your ExtendibleModelAdmin pointing to that
   form. 

   .. caution:: Set any additional field as ``required = False``
      otherwise all actions that don't define that will will not let
      the form validate and you'll get "No action selected". See below
      the syntax to declare a required field just for one action

#. Configure which fields are needed for any action
   (``action_form_fields`` dict) and set attribute
   ``action_form_fields`` on your ExtendibleModelAdmin

#. Modify ``change_list.html`` template to add 

    + templatetag to render ``admin/actions.html`` (inluded in
      ``{jmb.core}result_list``) 

    + javascript to toggle visibility of the fields

   this step is already done in *jmb.core* provided template


ExtendibleModelAdmin.action_form
--------------------------------

When ModelAdmin has actions enabled, Django creates a form and attaches it to 
attribute ``action_form``, thought is not officially documented.
This action will hold 

* the selected action

* the selected ids or ``select_across`` boolean to say all records
  where selected.

Entending and modifying that form we make available any fields defined
in it.  The selected row will be available as a queryset, as usual.

You can use :meth:`ExtendibleModelAdmin.get_action_form_instance` to
get an already validated form instance.

.. caution::

   if the form does not validate, you don't even get to the action and
   the error message will be: "No action selected".


This is used to specify a different form to add extra action fields.
To use this you need override class JumboActionForm. It's good idea
define these forms in admin_forms.py or in admin/forms.py::

    from jmb.core.admin import ExtendibleModelForm, JumboActionForm


    class NewsletterActionForm(JumboActionForm):
 
        action_form_fields = {    # fields_map?
           'clone': [],
           'send_newsletter': ['mailing_list'],
           'resend_newsletter': ['mailing_list:required'],
        }
        mailing_list = forms.ModelChoiceField(
            queryset=MailingList.objects.filter(status=1),
            label=_('Mailing list'),
            required=False,
            # This is automatically added if not specified. Used from JS to toggle extra action fields
            widget=forms.Select(attrs={'id': 'mailing_list', 'class': 'extra_action_field'})
        )


    class NewsletterAdmin(ExtendibleModelAdmin):

       def send_newsletter(self, request, queryset):
           form = self.get_form_action_instance(request)
           ...

       action_form = NewsletterActionForm

.. _action-form-fields:

ExtendibleModelAdmin.action_form_fields
---------------------------------------

A dictionary to specify action and its extra fields to complete operation::


key is the action and value can be [] if there is not any extra fields
or a list of extra fields.  ``required`` it's used to specify that
that field is required. Example: ``send_newsletter``:
``['mailing_list:required']``.  When user select ``send newsletter``
action is required specify ``mailing_list`` to whom to send newsletter.

ExtendibleModelAdmin.get_action_form_fields
--------------------------------------------

It's a method of ExtendibleModelAdmin and it used to redefine action
extra fields dictionary To use these methods we rewrote
changelist_view in ExtendibleModelAdmin adding get_action_form_fields
to extra_context and we overrode change_list.html template. 
API
===

.. autoclass:: JumboActionForm
   :members:

.. _Actions: https://docs.djangoproject.com/en/dev/ref/contrib/admin/actions/#admin-actions

"""

from django.contrib.admin import helpers

[docs]class JumboActionForm(helpers.ActionForm): """The default ActionForm with some logic to implement required fields according to selected action. """ def __init__(self, *args, **kwargs): self.non_actions_fields = ['action','select_across'] if 'action_form_fields' in kwargs: self.action_form_fields = kwargs.pop('action_form_fields') super(JumboActionForm, self).__init__(*args, **kwargs) for key, value in self.fields.iteritems(): if key not in self.non_actions_fields: self.fields[key].widget.attrs['id'] = key try: self.fields[key].widget.attrs['class'] = self.fields[key].widget.attrs['class'] + ' extra_action_field' except KeyError: self.fields[key].widget.attrs['class'] = 'extra_action_field'
[docs] def clean(self): """Check that any :required """ cleaned_data = super(JumboActionForm, self).clean() if ( hasattr(self, 'action_form_fields') and 'action' in self.cleaned_data ): extra_action = self.cleaned_data['action'] all_extra_actions_args_dict = self.action_form_fields if extra_action in all_extra_actions_args_dict: selected_action_extra_fields = all_extra_actions_args_dict[extra_action] error_flag = False for selected_action_arg in selected_action_extra_fields: required_flag = False field = selected_action_arg if ":" in selected_action_arg: field, required = selected_action_arg.split(":") if required and required == "required": required_flag = True if required_flag and not self.cleaned_data[field]: self.fields[field].error_messages[field + "_error"] = \ _("No %s selected." % field.replace("_", " ")) error_flag = True if error_flag: raise forms.ValidationError(_("")) return cleaned_data