Source code for jmb.core.admin.options

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

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

JumboActionForm
---------------
In jmb.core.admin.options we add a new class that subclasses helpers.ActionForm.
For now we do not add any code.

ExtendibleModelAdmin.action_form
--------------------------------
by default an ActionForm is dinamically created for your model. 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.options import JumboActionForm

    class NewsletterActionForm(JumboActionForm):
        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'})
        )

.. _action-form-fields:

ExtendibleModelAdmin.action_form_fields
---------------------------------------
A dictionary to specify action and its extra fields to complete operation::

    class NewsletterAdmin(ExtendibleModelAdmin):
       action_form_fields = {
           'clone': [],
           'make_ready_to_send': [],
           'make_cancel_sending': [],
           'send_newsletter': ['mailing_list'],
           'send_in_test_newsletter': ['mailing_list'],
           'resend_newsletter': ['mailing_list'],
           'resend_all_newsletter': ['mailing_list'],
        }

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 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
-------------------------------------------
is 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.
In template we have added this javascript code::

    {% if action_form_fields %}
    <script type="text/javascript">
        $(document).ready(function($) {
            $('select.extra_action_field').hide();
            $('select.extra_action_field').parent().hide();
            $('select[name=action]').change(function(e){
                var option_value = this.value;
                {% for action, extra_fields in action_form_fields.items %}
                    if (this.value == '{{ action }}'){
                        {% if extra_fields != None %}
                            {% for field in extra_fields %}
                                $('#{{ field }}').parent().show();
                                $('#{{ field }}').show();
                            {% endfor %}
                        {% else %}
                            $('select.extra_action_field').hide();
                            $('select.extra_action_field').parent().hide();
                        {% endif %}
                    }
                {% endfor %}
            });
        });
    </script>
    {% endif %}
"""

import re
from functools import partial, reduce, update_wrapper

from django import forms
from django import template
from django.core.urlresolvers import reverse
from django.db import models, transaction, router
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext as ugt
from django.contrib.admin.util import unquote, lookup_field, display_for_field, get_deleted_objects
from django.utils.encoding import force_unicode, smart_unicode
from django.shortcuts import render_to_response
from django.http import Http404, HttpResponseRedirect, HttpResponse

from django.utils.html import escape, strip_tags
from django.views.decorators.csrf import csrf_protect
from django.conf import settings
from django.utils.decorators import method_decorator
from django.core.exceptions import PermissionDenied, ValidationError
from django.utils.http import urlquote
from django.contrib.admin.util import flatten_fieldsets
from django.contrib import messages
from django.conf.urls import patterns, url

from jmb.core.utils.functions import render_csv, render_xls
from jmb.core.db.utils import clone
from ajax_inlines import AjaxParentModelAdmin
from django.contrib.admin import helpers

csrf_protect_m = method_decorator(csrf_protect)

# GET parameter for the URL to return to after change/add views. This lets the
# admin save the state of the changelist page through the change/add page.
#RETURN_GET_PARAM = '_return_to'

def set_active(modeladmin, request, queryset):
    rows_updated = 0
    for obj in queryset:
        obj.status = 1
        obj.save()
        rows_updated += 1
    if rows_updated == 1:
        message_bit = ugt("1 object was")
    else:
        message_bit = ugt("%(row)s objects were") % {'row': rows_updated}
    modeladmin.message_user(request, ugt("%(msg)s successfully actived.") % \
                                                  {'msg': message_bit})
set_active.short_description = _("Set %(verbose_name_plural)s as active")
admin.site.add_action(set_active)


def set_disactive(modeladmin, request, queryset):
    rows_updated = 0
    for obj in queryset:
        obj.status = 0
        obj.save()
        rows_updated += 1
    if rows_updated == 1:
        message_bit = ugt("1 object was")
    else:
        message_bit = ugt("%(row)s objects were") % {'row': rows_updated}
    modeladmin.message_user(request, ugt("%(msg)s successfully disactived.") % \
                                                  {'msg': message_bit})
set_disactive.short_description = _("Set %(verbose_name_plural)s as disactive")
admin.site.add_action(set_disactive)


def _export_data(modeladmin, request=None, queryset=None):
    """
    ritorna un generatore di liste contenenti i valori degli item del queryset
    :arg modeladmin: the modeladmin. 
    :arg request: the request, not used
    :arg queryset: the queryset or a list of
    """
    if hasattr(modeladmin, 'list_display_csv'):
        fields_list = modeladmin.list_display_csv
    else:
        fields_list = modeladmin.list_display

    plural_name = ugt(modeladmin.model._meta.verbose_name_plural)

    title = list(fields_list)
    title.append('\n')
    if 'action_checkbox' in title:
        title.remove('action_checkbox')
    yield title

    for obj in queryset:
        row = []
        for field_name in fields_list:
                
            if field_name == 'action_checkbox':
                continue
            field, attr, value = lookup_field(field_name, obj, modeladmin)
            if not field:
                # try to interpret it as a modeladmin's function
                if hasattr(modeladmin, field_name):
                    result_repr = getattr(modeladmin, field_name)(obj)
                elif hasattr(obj, field_name):
                    result_repr = getattr(obj, field_name)()
            elif value is None:
                if isinstance(field, (models.DecimalField, models.IntegerField, 
                                      models.FloatField, models.NullBooleanField)):
                    result_repr = ''
                elif field is None:
                    result_repr = strip_tags(smart_unicode(value))
            else:
                if isinstance(field, (models.NullBooleanField, models.BooleanField)):
                    result_repr = False and _('No') or _('Yes')
                else:
                    result_repr = display_for_field(value, field)

            # Tolgo eventuali 'a capo'
            row.append(unicode(result_repr))
        yield row

class ExportData(object):
    def __init__(self, modeladmin, request=None, queryset=None):
        """
        ritorna un generatore di liste contenenti i valori degli item del queryset
        :arg modeladmin: the modeladmin. 
        :arg request: the request, not used
        :arg queryset: the queryset or a list of
        """
        # Attempt to split into a simple part that uses modelaldimn that is difficult to 
        # serialize and another that can be serialized so as to use it when passing over
        # to celery
        self.fields_list = self.get_field_list()

        if hasattr(modeladmin, 'list_display_csv'):
            fields_list = modeladmin.list_display_csv
        else:
            fields_list = modeladmin.list_display

        plural_name = ugt(modeladmin.model._meta.verbose_name_plural)

        title = list(fields_list)
        title.append('\n')
        if 'action_checkbox' in title:
            title.remove('action_checkbox')
        yield title

        for obj in queryset:
            row = []
            for field_name in fields_list:

                if field_name == 'action_checkbox':
                    continue
                field, attr, value = lookup_field(field_name, obj, modeladmin)
                if not field:
                    # try to interpret it as a modeladmin's function
                    if hasattr(modeladmin, field_name):
                        result_repr = getattr(modeladmin, field_name)(obj)
                elif value is None:
                    if isinstance(field, (models.DecimalField, models.IntegerField, 
                                          models.FloatField, models.NullBooleanField)):
                        result_repr = ''
                    elif field is None:
                        result_repr = strip_tags(smart_unicode(value))
                else:
                    if isinstance(field, (models.NullBooleanField, models.BooleanField)):
                        result_repr = False and _('No') or _('Yes')
                    else:
                        result_repr = display_for_field(value, field)

                # Tolgo eventuali 'a capo'
                row.append(unicode(result_repr))
            yield row



def clone_action(modeladmin, request, queryset):
    verbose_name = ugt(modeladmin.model._meta.verbose_name)
    verbose_name_plural = ugt(modeladmin.model._meta.verbose_name_plural)
    cloned = 0
    not_cloned = 0

    for obj in queryset:
        # TODO: da trasformare in funzione
        cloned_obj, result = clone(obj)
        # TODO: da fixare in funzione di cosa restituisce la funzione
        if result != True:
            not_cloned += 1
            messages.error(request, _("%(name)s not cloned: %(id)d") % {
                    'name': verbose_name,
                    'id': obj.pk
                 }
            )
            continue
        else:
            cloned += 1

    if not_cloned:
        name = verbose_name
        if not_cloned > 1:
            name = verbose_name_plural
        messages.error(request, _("%(name)s not cloned: %(number)s") % {'name': name, 'number': not_cloned})
    if cloned:
        name = verbose_name
        if cloned > 1:
            name = verbose_name_plural
        messages.info(request, _("%(name)s cloned: %(number)s") % {'name': name, 'number': cloned})
    else:
        name = verbose_name_plural
        messages.error(request, _("%(name)s cloned: %(number)s") % {'name': name, 'number': cloned})
clone_action.short_description = _("Clone selected %(verbose_name_plural)s")
#admin.site.add_action(clone_action)

[docs]def export_csv(modeladmin, request, queryset): """ action per esportazione elementi selezionati changelist_view in .csv """ plural_name = ugt(modeladmin.model._meta.verbose_name_plural) return render_csv(_export_data(modeladmin, request, queryset), '%s.csv' % plural_name)
export_csv.short_description = _("Export selected %(verbose_name_plural)s in csv") admin.site.add_action(export_csv)
[docs]def export_xls(modeladmin, request, queryset): """ action per esportazione elementi selezionati changelist_view in .xls """ plural_name = ugt(modeladmin.model._meta.verbose_name_plural) return render_xls(_export_data(modeladmin, request, queryset), '%s.xls' % plural_name)
export_xls.short_description = _("Export selected %(verbose_name_plural)s in xls") admin.site.add_action(export_xls) class SettingsAdmin(admin.ModelAdmin): fieldsets = ( (_('general admin'), {'fields': (('name', 'type'), 'value', 'description')}), ) list_display = ('name', 'value', 'type', 'description') list_display_links = ('name', ) list_per_page = 30 class JumboActionForm(helpers.ActionForm): 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 self.fields[key].widget.attrs['class'] = 'extra_action_field' def clean(self): 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 class ExtendibleModelAdmin(AjaxParentModelAdmin): class Media: js = ['jmb/js/collapsed_stacked_inlines.js', #'jmb/js/detail_fancybox.js' ] #: tabs: show tabs even if they're empty tabs_show_empty = True #: tabs: show tab lables sticky on top of the page tabs_sticky_on_top = False no_display_links = True list_per_page = 20 save_on_top = True use_fancybox = False father_foreignkey_fancybox = None def get_action_form_fields(self, request): """ Return a :ref:`dictionary <action-form-fields>` to specify action and its extra fields to complete operation """ try: return self.action_form_fields except: return None def get_list_display_links(self, request, list_display): return ('get_edit_icon',) def get_readonly_fields(self, request, obj=None): readonly = () fields = [] if self.declared_fieldsets: fields = flatten_fieldsets(self.declared_fieldsets) if 'creator' in fields: readonly = readonly + ('creator',) if 'date_create' in fields: readonly = readonly + ('date_create',) if 'last_modifier' in fields: readonly = readonly + ('last_modifier',) if 'date_last_modify' in fields: readonly = readonly + ('date_last_modify',) return self.readonly_fields + readonly def get_changelist_instance(self, request): """Return a chagelist instance suitable to find out the real queryset represented bu result_list This function can be used in actions when the queryset is much faster then using the list of single ids """ kw = dict(model=self.model, list_display=self.list_display, list_display_links=self.list_display_links, list_filter=self.list_filter, date_hierarchy=self.date_hierarchy, search_fields=self.search_fields, list_select_related=self.list_select_related, list_per_page=self.list_per_page, list_max_show_all=self.list_max_show_all, list_editable=self.list_editable, model_admin=self) return self.get_changelist(request)(request, **kw) def _getobj(self, request, object_id): opts = self.model._meta app_label = opts.app_label try: obj = self.queryset(request).get(pk=unquote(object_id)) except self.model.DoesNotExist: obj = None if obj is None: raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.verbose_name), 'key': escape(object_id)}) return obj def _wrap(self, view): def wrapper(*args, **kwargs): return self.admin_site.admin_view(view)(*args, **kwargs) return update_wrapper(wrapper, view) def _view_name(self, name): info = self.model._meta.app_label, self.model._meta.module_name, name return '%s_%s_%s' % info def has_change_permission(self, request, obj=None): has_change_permission = super(ExtendibleModelAdmin, self).has_change_permission(request, obj) if obj is None: if ( has_change_permission or request.user.has_perm(self.model._meta.app_label + '.' + 'view_%s' % self.model._meta.module_name) or request.user.has_perm(self.model._meta.app_label + '.' + 'list_%s' % self.model._meta.module_name) ): return True return False else: if hasattr(obj, "can_be_modified"): if obj.can_be_modified(): return True else: return False return has_change_permission def has_delete_permission(self, request, obj=None): has_delete_permission = super(ExtendibleModelAdmin, self).has_delete_permission(request, obj) if hasattr(obj, "can_be_delete"): if obj.can_be_delete(): return True else: return False return has_delete_permission def get_value_obj(self, obj, attribute): value = None try: value = getattr(obj, attribute) except: try: if hasattr(self, attribute): value = getattr(self, attribute)(obj) except Exception, e: pass return value def detail_view(self, request, object_id, **kwargs): obj = self._getobj(request, object_id) opts = self.model._meta verbose_name = opts.verbose_name app_label = opts.app_label.lower() object_name = opts.object_name.lower() detail_fields = [] d_values = {} #ricordarsi di aggiungere nel progetto #(r'^comments/', include('django.contrib.comments.urls')), template_name = kwargs.get('template_name', 'detail.html') if hasattr(self, 'detail_display'): for f in self.detail_display: # Se e' una lista aggiunga tt i suoi campi ai detail_fields if type(f) == type([]) or type(f) == type(()): detail_fields.append(f) for subf in f: d_values[subf] = self.get_value_obj(obj, subf) # Se la funzione e' presente nell'admin la chiamo e aggiungo il # valore ai detail fields elif hasattr(self, f): detail_fields.append([f]) d_values[f] = getattr(self, f)(obj) # altrimenti cerco il valore dell'oggetto in maniera normale else: detail_fields.append([f]) d_values[f] = self.get_value_obj(obj, f) context = { 'title': _('Detail %s') % force_unicode(verbose_name), 'object_id': object_id, 'obj': obj, 'is_popup': request.REQUEST.has_key('_popup'), 'app_label': app_label, 'opts': opts, 'detail_fields': detail_fields, 'd_values': d_values, 'has_change_permission': self.has_change_permission(request, obj), } context_instance = template.RequestContext(request, current_app=self.admin_site.name) return render_to_response([ "admin/%s/%s/%s" % (app_label, object_name, template_name), "admin/%s/%s" % (app_label, template_name), "admin/%s" % template_name ], context, context_instance=context_instance) #nuovo nome, retrocompatibilità detail = detail_view @csrf_protect_m @transaction.commit_on_success def import_data_view(self, request, form_url='', extra_context=None, **kwargs): "The 'import data' admin view for this model." from jmb.core.admin.forms import ImportDataForm from jmb.core.admin.import_data import ImportData from django.contrib import messages model = self.model opts = model._meta app_label = opts.app_label verbose_name = force_unicode(opts.verbose_name) # if not self.has_change_permission(request, obj): # raise PermissionDenied template_name = kwargs.get('template_name', 'import_data.html') breadcrumbs = ( (_('Home'), '/admin/'), (_(app_label), '/admin/%s/' % app_label), (_(verbose_name), '/admin/%s/%s/' % (app_label, verbose_name)), ) if request.method == 'POST': form = ImportDataForm(request.POST, request.FILES) if form.is_valid(): file_source = form.cleaned_data['import_file'] import_data = ImportData( file_contents=file_source.read(), auto=True, model=model ) import_data.read() if import_data.messages['inserted'] > 0: messages.success(request, "%s righe inserite correttamente" % import_data.messages['inserted']) if import_data.messages['modified'] > 0: messages.warning(request, "%s righe modificate correttamente" % import_data.messages['modified']) if import_data.messages['errors']: for error in import_data.messages['errors']: messages.error(request, error) else: messages.error(request, "Correggi l'errore qui sotto") else: form = ImportDataForm() context = { 'title': _('Import %s') % verbose_name, 'is_popup': "_popup" in request.REQUEST, 'app_label': opts.app_label, 'breadcrumbs': breadcrumbs, 'import_form': form } context.update(extra_context or {}) context_instance = template.RequestContext(request, current_app=self.admin_site.name) return render_to_response( [ "admin/%s/%s/%s" % (opts.app_label, opts.object_name.lower(), template_name), "admin/%s/%s" % (opts.app_label, template_name), "admin/%s" % template_name ], context, context_instance=context_instance ) @csrf_protect_m @transaction.commit_on_success def export_data_view(self, request, form_url='', extra_context=None, **kwargs): "The 'export data' admin view for this model." from django.contrib import messages from jmb.core.admin.forms import ExportDataForm from jmb.core.admin import tasks model = self.model opts = model._meta app_label = opts.app_label verbose_name = force_unicode(opts.verbose_name) # if not self.has_change_permission(request, obj): # raise PermissionDenied template_name = kwargs.get('template_name', 'export_data.html') breadcrumbs = ( (_('Home'), '/admin/'), (_(app_label), '/admin/%s/' % app_label), (_(verbose_name), '/admin/%s/%s/' % (app_label, verbose_name)), ) if request.method == 'POST': form = ExportDataForm(request.POST, request.FILES) if form.is_valid(): return tasks.create_xls(model) else: messages.error(request, "Correggi l'errore qui sotto") else: form = ExportDataForm() context = { 'title': _('Export %s') % verbose_name, 'is_popup': "_popup" in request.REQUEST, 'app_label': opts.app_label, 'breadcrumbs': breadcrumbs, 'export_form': form } context.update(extra_context or {}) context_instance = template.RequestContext(request, current_app=self.admin_site.name) return render_to_response( [ "admin/%s/%s/%s" % (opts.app_label, opts.object_name.lower(), template_name), "admin/%s/%s" % (opts.app_label, template_name), "admin/%s" % template_name ], context, context_instance=context_instance ) @csrf_protect_m def changelist_view(self, request, extra_context=None): extra_context = extra_context or {} extra_context['is_popup']= request.GET.get("_popup", False) extra_context['action_form_fields'] = self.get_action_form_fields(request) return super(ExtendibleModelAdmin, self).changelist_view(request, extra_context) def get_urls(self): urls = super(ExtendibleModelAdmin, self).get_urls() def wrap(view): def wrapper(*args, **kwargs): return self.admin_site.admin_view(view)(*args, **kwargs) return update_wrapper(wrapper, view) info = self.model._meta.app_label, self.model._meta.module_name my_urls = patterns( '', url( r'^(.+)/detail/$', self._wrap(self.detail), name=self._view_name('detail') ), url( r'^import_data/$', self.admin_site.admin_view(self.import_data_view), name='%s_%s_import_data' % info ), url( r'export_data/$', self.admin_site.admin_view(self.export_data_view), name="%s_%s_export_data" % info ), ) return my_urls + urls def response_action(self, request, queryset): """ Handle an admin action. This is called if a request is POSTed to the changelist; it returns an HttpResponse if the action was handled, and None otherwise. """ # There can be multiple action forms on the page (at the top # and bottom of the change list, for example). Get the action # whose button was pushed. try: action_index = int(request.POST.get('index', 0)) except ValueError: action_index = 0 # Construct the action form. data = request.POST.copy() data.pop(helpers.ACTION_CHECKBOX_NAME, None) data.pop("index", None) # Use the action whose button was pushed try: data.update({'action': data.getlist('action')[action_index]}) except IndexError: # If we didn't get an action from the chosen form that's invalid # POST data, so by deleting action it'll fail the validation check # below. So no need to do anything here pass if hasattr(self, 'action_form_fields') and self.action_form_fields: action_form = self.action_form(data, auto_id=None, action_form_fields=self.action_form_fields) else: action_form = self.action_form(data, auto_id=None) action_form.fields['action'].choices = self.get_action_choices(request) # If the form's valid we can handle the action. if action_form.is_valid(): action = action_form.cleaned_data['action'] select_across = action_form.cleaned_data['select_across'] func = self.get_actions(request)[action][0] # Get the list of selected PKs. If nothing's selected, we can't # perform an action on it, so bail. Except we want to perform # the action explicitly on all objects. selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME) if not selected and not select_across: # Reminder that something needs to be selected or nothing will happen msg = _("Items must be selected in order to perform " "actions on them. No items have been changed.") self.message_user(request, msg, messages.WARNING) return None if not select_across: # Perform the action only on the selected objects queryset = queryset.filter(pk__in=selected) response = func(self, request, queryset) # Actions may return an HttpResponse-like object, which will be # used as the response from the POST. If not, we'll be a good # little HTTP citizen and redirect back to the changelist page. if isinstance(response, HttpResponse): return response else: return HttpResponseRedirect(request.get_full_path()) else: msg = "" if hasattr(self, 'action_form_fields') and self.action_form_fields: if 'action' in action_form.cleaned_data: selected_action = action_form.cleaned_data['action'] if selected_action in self.action_form_fields: selected_action_args = self.action_form_fields[selected_action] for selected_action_argument in selected_action_args: if ":" in selected_action_argument: field, required = selected_action_argument.split(":") msg = action_form.fields[field].error_messages[field+"_error"] self.message_user(request, msg, messages.WARNING) if not msg: msg = _("No action selected.") self.message_user(request, msg, messages.WARNING) return None # def get_edit_icon(self, obj): # opts = self.model._meta # can_be_modified = False # # Se il modello ha la funzione can_be_modified la chiamo per fare # # i controlli relativi a quel modello # if hasattr(obj, "can_be_modified"): # if obj.can_be_modified(): # can_be_modified = True # # Se non ce l'ha non faccio controlli e visualizzo l'icona # else: # can_be_modified = True # request = get_request() # if can_be_modified: # dj_reverse_url = reverse('admin:%s_%s_change' % (opts.app_label, opts.object_name.lower()), args=(obj.pk,)) # ## print "Debug: Puo essere modificato", obj # return "<a href=%(reverse_url)s><img src='%(url)sjmb/images/edit.gif' alt='%(window_title)s' title='%(window_title)s'/></a>" % { # 'reverse_url': "%s" % (dj_reverse_url, ), # 'url':settings.STATIC_URL, # 'window_title':ugt("Edit %s" % opts.object_name.lower()) # } # return "" # get_edit_icon.short_description = _("E") # get_edit_icon.allow_tags = True # def get_delete_icon(self, obj): # opts = self.model._meta # can_be_delete = False # # Se il modello ha la funzione can_be_modified la chiamo per fare # # i controlli relativi a quel modello # if hasattr(obj, "can_be_delete"): # if obj.can_be_delete(): # can_be_delete = True # # Se non ce l'ha non faccio controlli e visualizzo l'icona # else: # can_be_delete = True # if can_be_delete: # param = "" # if self.use_fancybox: # param='?_popup=1&nobuttons=1' # return "<a href=%(reverse_url)s%(param)s><img src='%(url)sjmb/images/del.gif' alt='%(window_title)s' title='%(window_title)s'/></a>" % { # 'reverse_url':reverse('admin:%s_%s_delete' % (opts.app_label, opts.object_name.lower()), args=(obj.pk,)), # 'param': param, # 'url':settings.STATIC_URL, # 'window_title':ugt("Delete %s" % opts.object_name.lower()) # } # return "" # get_delete_icon.short_description = _("D") # get_delete_icon.allow_tags = True def get_detail_icon(self, obj): opts = self.model._meta app_label = opts.app_label.lower() object_name = opts.object_name.lower() return """ <a class='iframe' href=%(reverse_url)s?_popup=1&nobuttons=1> <img src='%(url)sjmb/images/search.png' alt='%(window_title)s' title='%(window_title)s'/> </a>""" % { 'reverse_url':reverse('%s:%s_%s_detail' % ( self.admin_site.name, app_label, object_name), args=(obj.pk,) ), 'url':settings.STATIC_URL, 'window_title':ugt("Detail %s" % object_name) } get_detail_icon.allow_tags = True get_detail_icon.short_description = _("V") def get_status_icon(self, obj): return obj.status get_status_icon.short_description = _("ST") get_status_icon.boolean = True get_status_icon.admin_order_field = 'status' def get_changelist_queryset(self, request): """ Return a queryset that was correctly filtered by any filter (q= and filterset_class) """ # stolen from changelist_view in Django 1.5 opts = self.model._meta app_label = opts.app_label if not self.has_change_permission(request, None): raise PermissionDenied list_display = self.get_list_display(request) list_display_links = self.get_list_display_links(request, list_display) list_filter = self.get_list_filter(request) ChangeList = self.get_changelist(request) cl = ChangeList(request, self.model, list_display, list_display_links, list_filter, self.date_hierarchy, self.search_fields, self.list_select_related, self.list_per_page, self.list_max_show_all, self.list_editable, self) try: return cl.queryset except AttributeError: # pre Django 1.6 return cl.query_set class OrderedModelAdmin(admin.ModelAdmin): def _view_name(self, name): info = self.model._meta.app_label, self.model._meta.module_name, name return '%s_%s_%s' % info def _wrap(self, view): def wrapper(*args, **kwargs): return self.admin_site.admin_view(view)(*args, **kwargs) return update_wrapper(wrapper, view) def get_urls(self): from django.conf.urls import patterns, url urls = super(OrderedModelAdmin, self).get_urls() my_urls = patterns('', url(r'^(.+)/up/$', self._wrap(self.up), name=self._view_name('up')), url(r'^(.+)/down/$', self._wrap(self.down), name=self._view_name('down')), ) return my_urls + urls def up(self, request, id): node = self.model._default_manager.get(pk=id) node.up() try: redirect_to = request.META['HTTP_REFERER'] except: redirect_to = '../../' return HttpResponseRedirect(redirect_to) def down(self, request, id): node = self.model._default_manager.get(pk=id) node.down() try: redirect_to = request.META['HTTP_REFERER'] except: redirect_to = '../../' return HttpResponseRedirect(redirect_to) def move_actions(self, node): info = self.admin_site.name, self.model._meta.app_label, self.model._meta.module_name data = [] if not node.is_first(): # up node data.append(u'<a href="%s" class="nodes-up">%s</a>' % (reverse('%s:%s_%s_up' % info, node.id), _('up'))) if not node.is_last() and not node.is_first(): data.append(u'<span style="font-weight:normal"> | </span>') if not node.is_last(): # down node data.append(u'<a href="%s" class="nodes-down">%s</a>' % (reverse('%s:%s_%s_down' % info, node.id), _('down'))) return u''.join(data) move_actions.short_description = _('move') move_actions.allow_tags = True