Mix of Python and Django utility functions, classed etc.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

172 lines
7.0 KiB

from django import forms
from django.contrib import admin
from django.contrib.admin.widgets import ForeignKeyRawIdWidget
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils.translation import ngettext, gettext_lazy as _
class AdminActionBase:
action_name = None
options_template_name = 'admin/action_forms/admin_action_base.html'
title = None
queryset_action_label = None
action_button_label = None
def __init__(self, action_name=None):
if action_name:
self.action_name = action_name
def apply(self, queryset, form):
raise NotImplementedError
def get_message(self, count):
raise NotImplementedError
def get_failure_message(self, count, failure_count):
raise NotImplementedError
class BaseForm(forms.Form):
_selected_action = forms.CharField(widget=forms.MultipleHiddenInput)
def get_form_class(self, modeladmin, request, queryset):
"""
Example:
class CustomForm(BaseForm)
chosen_target = forms.ModelChoiceField(
label=_("Choose target itembundle"),
queryset=ItemBundle.objects.exclude(pk__in=queryset),
widget=ForeignKeyRawIdWidget(modeladmin.model._meta.get_field('parent').rel, modeladmin.admin_site),
empty_label=_("Root level"), required=False)
return CustomForm
"""
raise NotImplementedError
def __call__(self, modeladmin, request, queryset):
form_class = self.get_form_class(modeladmin, request, queryset)
form = None
if 'apply' in request.POST:
form = form_class(request.POST)
if form.is_valid():
queryset_count = queryset.count()
count = self.apply(queryset, form)
failure_count = queryset_count - count
if failure_count > 0:
message = self.get_failure_message(form, count, failure_count)
else:
message = self.get_message(form, count)
modeladmin.message_user(request, message)
return HttpResponseRedirect(request.get_full_path())
if 'cancel' in request.POST:
return HttpResponseRedirect(request.get_full_path())
if not form:
form = form_class(initial={
'_selected_action': request.POST.getlist(
admin.ACTION_CHECKBOX_NAME),
})
return render(request, self.options_template_name, context={
'action_name': self.action_name,
'title': self.title,
'queryset_action_label': self.queryset_action_label,
'action_button_label': self.action_button_label,
'queryset': queryset,
'action_form': form,
'opts': modeladmin.model._meta,
})
class TargetActionBase(AdminActionBase):
target_model = None
related_field_name = None
def get_form_class(self, modeladmin, request, queryset):
class ChooseTargetForm(AdminActionBase.BaseForm):
chosen_target = forms.ModelChoiceField(
label=_("Choose {}".format(self.target_model._meta.verbose_name)),
queryset=self.target_model.objects.exclude(pk__in=queryset),
widget=ForeignKeyRawIdWidget(
modeladmin.model._meta.get_field(self.related_field_name).rel,
modeladmin.admin_site
),
)
return ChooseTargetForm
def get_target(self, form):
return form.cleaned_data['chosen_target']
def get_message(self, form, count):
chosen_target = form.cleaned_data['chosen_target']
target_name = chosen_target.name
return ngettext(
'Successfully added %(count)d %(verbose_name)s to %(target)s.',
'Successfully added %(count)d %(verbose_name_plural)s to %(target)s.',
count) % {
'count': count,
'verbose_name': self.target_model._meta.verbose_name,
'verbose_name_plural': self.target_model._meta.verbose_name_plural,
'target': target_name}
def get_failure_message(self, form, count, failure_count):
chosen_target = form.cleaned_data['chosen_target']
target_name = chosen_target.name
return ngettext(
'Adding %(count)d %(verbose_name)s to %(target)s, %(failure_count)s failed or skipped.',
'Adding %(count)d %(verbose_name_plural)s to %(target)s, %(failure_count)s failed or skipped.',
count) % {
'count': count,
'verbose_name': self.target_model._meta.verbose_name,
'verbose_name_plural': self.target_model._meta.verbose_name_plural,
'target': target_name,
'failure_count': failure_count}
class ChangeForeignKeyAction(AdminActionBase):
# Subclass::
# action_name = 'change_fieldname_action'
# field_name_label = _("<field name>")
# title = _("Change <field name>")
# queryset_action_label = _("For the following items the <field name> will be changed:")
# action_button_label = _("Change <field name>")
def apply(self, queryset, form):
raise NotImplementedError("apply must be implemented by the subclass.")
# Subclass::
# new_value = form.cleaned_data['new_value']
# return change_fieldname_state(queryset, new_value)
def get_value_choices_queryset(self, modeladmin, request, queryset):
raise NotImplementedError("get_value_choices_queryset must be implemented by the subclass.")
def get_form_class(self, modeladmin, request, queryset):
class ChooseValueForm(AdminActionBase.BaseForm):
new_value = forms.ModelChoiceField(
label=_("Choose %(field_name)s" % {'field_name': self.field_name_label}),
queryset=self.get_value_choices_queryset(modeladmin, request, queryset),
required=True)
return ChooseValueForm
def get_message(self, form, count):
new_value = form.cleaned_data['new_value']
return ngettext(
'Successfully changed %(field_name)s of %(count)d item to %(new_value)s.',
'Successfully changed %(field_name)s of %(count)d items to %(new_value)s.',
count) % {
'field_name': self.field_name_label,
'count': count,
'new_value': new_value}
def get_failure_message(self, form, count, failure_count):
new_value = form.cleaned_data['new_value']
return ngettext(
'Successfully changed %(field_name)s of %(count)d item to %(new_value)s, %(failure_count)s failed or skipped.',
'Successfully changed %(field_name)s of %(count)d items to %(new_value)s, %(failure_count)s failed or skipped.',
count) % {
'field_name': self.field_name_label,
'count': count,
'new_value': new_value,
'failure_count': failure_count}