From 62aafd8e3001b502bf7ee15ada53c6d440524a5d Mon Sep 17 00:00:00 2001 From: Brian Rosner Date: Mon, 18 Aug 2008 15:49:58 +0000 Subject: [PATCH] Moved admin form helpers to their own module to clean-up django/contrib/admin/options.py a little bit. git-svn-id: http://code.djangoproject.com/svn/django/trunk@8433 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/admin/helpers.py | 141 +++++++++++++++++++++++++++ django/contrib/admin/options.py | 162 ++------------------------------ django/contrib/admin/util.py | 12 +++ 3 files changed, 161 insertions(+), 154 deletions(-) create mode 100644 django/contrib/admin/helpers.py diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py new file mode 100644 index 0000000000..ba49f8b7cf --- /dev/null +++ b/django/contrib/admin/helpers.py @@ -0,0 +1,141 @@ + +from django import forms +from django.conf import settings +from django.utils.html import escape +from django.utils.safestring import mark_safe +from django.utils.encoding import force_unicode +from django.contrib.admin.util import flatten_fieldsets + +class AdminForm(object): + def __init__(self, form, fieldsets, prepopulated_fields): + self.form, self.fieldsets = form, fieldsets + self.prepopulated_fields = [{ + 'field': form[field_name], + 'dependencies': [form[f] for f in dependencies] + } for field_name, dependencies in prepopulated_fields.items()] + + def __iter__(self): + for name, options in self.fieldsets: + yield Fieldset(self.form, name, **options) + + def first_field(self): + for bf in self.form: + return bf + + def _media(self): + media = self.form.media + for fs in self: + media = media + fs.media + return media + media = property(_media) + +class Fieldset(object): + def __init__(self, form, name=None, fields=(), classes=(), description=None): + self.form = form + self.name, self.fields = name, fields + self.classes = u' '.join(classes) + self.description = description + + def _media(self): + if 'collapse' in self.classes: + return forms.Media(js=['%sjs/admin/CollapsedFieldsets.js' % settings.ADMIN_MEDIA_PREFIX]) + return forms.Media() + media = property(_media) + + def __iter__(self): + for field in self.fields: + yield Fieldline(self.form, field) + +class Fieldline(object): + def __init__(self, form, field): + self.form = form # A django.forms.Form instance + if isinstance(field, basestring): + self.fields = [field] + else: + self.fields = field + + def __iter__(self): + for i, field in enumerate(self.fields): + yield AdminField(self.form, field, is_first=(i == 0)) + + def errors(self): + return mark_safe(u'\n'.join([self.form[f].errors.as_ul() for f in self.fields]).strip('\n')) + +class AdminField(object): + def __init__(self, form, field, is_first): + self.field = form[field] # A django.forms.BoundField instance + self.is_first = is_first # Whether this field is first on the line + self.is_checkbox = isinstance(self.field.field.widget, forms.CheckboxInput) + + def label_tag(self): + classes = [] + if self.is_checkbox: + classes.append(u'vCheckboxLabel') + contents = force_unicode(escape(self.field.label)) + else: + contents = force_unicode(escape(self.field.label)) + u':' + if self.field.field.required: + classes.append(u'required') + if not self.is_first: + classes.append(u'inline') + attrs = classes and {'class': u' '.join(classes)} or {} + return self.field.label_tag(contents=contents, attrs=attrs) + +class InlineAdminFormSet(object): + """ + A wrapper around an inline formset for use in the admin system. + """ + def __init__(self, inline, formset, fieldsets): + self.opts = inline + self.formset = formset + self.fieldsets = fieldsets + + def __iter__(self): + for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()): + yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, original) + for form in self.formset.extra_forms: + yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, None) + + def fields(self): + for field_name in flatten_fieldsets(self.fieldsets): + yield self.formset.form.base_fields[field_name] + + def _media(self): + media = self.opts.media + self.formset.media + for fs in self: + media = media + fs.media + return media + media = property(_media) + +class InlineAdminForm(AdminForm): + """ + A wrapper around an inline form for use in the admin system. + """ + def __init__(self, formset, form, fieldsets, prepopulated_fields, original): + self.formset = formset + self.original = original + self.show_url = original and hasattr(original, 'get_absolute_url') + super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields) + + def pk_field(self): + return AdminField(self.form, self.formset._pk_field_name, False) + + def deletion_field(self): + from django.forms.formsets import DELETION_FIELD_NAME + return AdminField(self.form, DELETION_FIELD_NAME, False) + + def ordering_field(self): + from django.forms.formsets import ORDERING_FIELD_NAME + return AdminField(self.form, ORDERING_FIELD_NAME, False) + +class AdminErrorList(forms.util.ErrorList): + """ + Stores all errors for the form/formsets in an add/change stage view. + """ + def __init__(self, form, inline_formsets): + if form.is_bound: + self.extend(form.errors.values()) + for inline_formset in inline_formsets: + self.extend(inline_formset.non_form_errors()) + for errors_in_inline_form in inline_formset.errors: + self.extend(errors_in_inline_form.values()) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 408c4add2e..8b9417bc3b 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -4,7 +4,8 @@ from django.forms.models import modelform_factory, inlineformset_factory from django.forms.models import BaseInlineFormSet from django.contrib.contenttypes.models import ContentType from django.contrib.admin import widgets -from django.contrib.admin.util import quote, unquote, get_deleted_objects +from django.contrib.admin import helpers +from django.contrib.admin.util import quote, unquote, flatten_fieldsets, get_deleted_objects from django.core.exceptions import PermissionDenied from django.db import models, transaction from django.http import Http404, HttpResponse, HttpResponseRedirect @@ -26,94 +27,6 @@ get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '') class IncorrectLookupParameters(Exception): pass -def flatten_fieldsets(fieldsets): - """Returns a list of field names from an admin fieldsets structure.""" - field_names = [] - for name, opts in fieldsets: - for field in opts['fields']: - # type checking feels dirty, but it seems like the best way here - if type(field) == tuple: - field_names.extend(field) - else: - field_names.append(field) - return field_names - -class AdminForm(object): - def __init__(self, form, fieldsets, prepopulated_fields): - self.form, self.fieldsets = form, fieldsets - self.prepopulated_fields = [{ - 'field': form[field_name], - 'dependencies': [form[f] for f in dependencies] - } for field_name, dependencies in prepopulated_fields.items()] - - def __iter__(self): - for name, options in self.fieldsets: - yield Fieldset(self.form, name, **options) - - def first_field(self): - for bf in self.form: - return bf - - def _media(self): - media = self.form.media - for fs in self: - media = media + fs.media - return media - media = property(_media) - -class Fieldset(object): - def __init__(self, form, name=None, fields=(), classes=(), description=None): - self.form = form - self.name, self.fields = name, fields - self.classes = u' '.join(classes) - self.description = description - - def _media(self): - from django.conf import settings - if 'collapse' in self.classes: - return forms.Media(js=['%sjs/admin/CollapsedFieldsets.js' % settings.ADMIN_MEDIA_PREFIX]) - return forms.Media() - media = property(_media) - - def __iter__(self): - for field in self.fields: - yield Fieldline(self.form, field) - -class Fieldline(object): - def __init__(self, form, field): - self.form = form # A django.forms.Form instance - if isinstance(field, basestring): - self.fields = [field] - else: - self.fields = field - - def __iter__(self): - for i, field in enumerate(self.fields): - yield AdminField(self.form, field, is_first=(i == 0)) - - def errors(self): - return mark_safe(u'\n'.join([self.form[f].errors.as_ul() for f in self.fields]).strip('\n')) - -class AdminField(object): - def __init__(self, form, field, is_first): - self.field = form[field] # A django.forms.BoundField instance - self.is_first = is_first # Whether this field is first on the line - self.is_checkbox = isinstance(self.field.field.widget, forms.CheckboxInput) - - def label_tag(self): - classes = [] - if self.is_checkbox: - classes.append(u'vCheckboxLabel') - contents = force_unicode(escape(self.field.label)) - else: - contents = force_unicode(escape(self.field.label)) + u':' - if self.field.field.required: - classes.append(u'required') - if not self.is_first: - classes.append(u'inline') - attrs = classes and {'class': u' '.join(classes)} or {} - return self.field.label_tag(contents=contents, attrs=attrs) - class BaseModelAdmin(object): """Functionality common to both ModelAdmin and InlineAdmin.""" raw_id_fields = () @@ -587,13 +500,13 @@ class ModelAdmin(BaseModelAdmin): formset = FormSet(instance=self.model()) formsets.append(formset) - adminForm = AdminForm(form, list(self.get_fieldsets(request)), self.prepopulated_fields) + adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)), self.prepopulated_fields) media = self.media + adminForm.media inline_admin_formsets = [] for inline, formset in zip(self.inline_instances, formsets): fieldsets = list(inline.get_fieldsets(request)) - inline_admin_formset = InlineAdminFormSet(inline, formset, fieldsets) + inline_admin_formset = helpers.InlineAdminFormSet(inline, formset, fieldsets) inline_admin_formsets.append(inline_admin_formset) media = media + inline_admin_formset.media @@ -604,7 +517,7 @@ class ModelAdmin(BaseModelAdmin): 'show_delete': False, 'media': mark_safe(media), 'inline_admin_formsets': inline_admin_formsets, - 'errors': AdminErrorList(form, formsets), + 'errors': helpers.AdminErrorList(form, formsets), 'root_path': self.admin_site.root_path, } context.update(extra_context or {}) @@ -664,13 +577,13 @@ class ModelAdmin(BaseModelAdmin): formset = FormSet(instance=obj) formsets.append(formset) - adminForm = AdminForm(form, self.get_fieldsets(request, obj), self.prepopulated_fields) + adminForm = helpers.AdminForm(form, self.get_fieldsets(request, obj), self.prepopulated_fields) media = self.media + adminForm.media inline_admin_formsets = [] for inline, formset in zip(self.inline_instances, formsets): fieldsets = list(inline.get_fieldsets(request, obj)) - inline_admin_formset = InlineAdminFormSet(inline, formset, fieldsets) + inline_admin_formset = helpers.InlineAdminFormSet(inline, formset, fieldsets) inline_admin_formsets.append(inline_admin_formset) media = media + inline_admin_formset.media @@ -682,7 +595,7 @@ class ModelAdmin(BaseModelAdmin): 'is_popup': request.REQUEST.has_key('_popup'), 'media': mark_safe(media), 'inline_admin_formsets': inline_admin_formsets, - 'errors': AdminErrorList(form, formsets), + 'errors': helpers.AdminErrorList(form, formsets), 'root_path': self.admin_site.root_path, } context.update(extra_context or {}) @@ -868,62 +781,3 @@ class StackedInline(InlineModelAdmin): class TabularInline(InlineModelAdmin): template = 'admin/edit_inline/tabular.html' - -class InlineAdminFormSet(object): - """ - A wrapper around an inline formset for use in the admin system. - """ - def __init__(self, inline, formset, fieldsets): - self.opts = inline - self.formset = formset - self.fieldsets = fieldsets - - def __iter__(self): - for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()): - yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, original) - for form in self.formset.extra_forms: - yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, None) - - def fields(self): - for field_name in flatten_fieldsets(self.fieldsets): - yield self.formset.form.base_fields[field_name] - - def _media(self): - media = self.opts.media + self.formset.media - for fs in self: - media = media + fs.media - return media - media = property(_media) - -class InlineAdminForm(AdminForm): - """ - A wrapper around an inline form for use in the admin system. - """ - def __init__(self, formset, form, fieldsets, prepopulated_fields, original): - self.formset = formset - self.original = original - self.show_url = original and hasattr(original, 'get_absolute_url') - super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields) - - def pk_field(self): - return AdminField(self.form, self.formset._pk_field_name, False) - - def deletion_field(self): - from django.forms.formsets import DELETION_FIELD_NAME - return AdminField(self.form, DELETION_FIELD_NAME, False) - - def ordering_field(self): - from django.forms.formsets import ORDERING_FIELD_NAME - return AdminField(self.form, ORDERING_FIELD_NAME, False) - -class AdminErrorList(forms.util.ErrorList): - """ - Stores all errors for the form/formsets in an add/change stage view. - """ - def __init__(self, form, inline_formsets): - if form.is_bound: - self.extend(form.errors.values()) - for inline_formset in inline_formsets: - self.extend(inline_formset.non_form_errors()) - for errors_in_inline_form in inline_formset.errors: - self.extend(errors_in_inline_form.values()) diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py index 6a585b2919..c3f567d9dd 100644 --- a/django/contrib/admin/util.py +++ b/django/contrib/admin/util.py @@ -43,6 +43,18 @@ def unquote(s): myappend('_' + item) return "".join(res) +def flatten_fieldsets(fieldsets): + """Returns a list of field names from an admin fieldsets structure.""" + field_names = [] + for name, opts in fieldsets: + for field in opts['fields']: + # type checking feels dirty, but it seems like the best way here + if type(field) == tuple: + field_names.extend(field) + else: + field_names.append(field) + return field_names + def _nest_help(obj, depth, val): current = obj for i in range(depth):