mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59:13 +00:00
newforms-admin: Changed the 'fields' option on ModelAdmin to 'fieldsets'. 'fields' is still valid, but should be a list of field names instead. Also, added some new hooks that were needed to support the change.
git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@6080 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
c05527dd60
commit
b5aa61a325
@ -36,28 +36,41 @@ def unquote(s):
|
|||||||
myappend('_' + item)
|
myappend('_' + item)
|
||||||
return "".join(res)
|
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
|
||||||
|
|
||||||
class AdminForm(object):
|
class AdminForm(object):
|
||||||
def __init__(self, form, fieldsets, prepopulated_fields):
|
def __init__(self, form, fieldsets, prepopulated_fields):
|
||||||
self.form, self.fieldsets = form, fieldsets
|
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()]
|
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):
|
def __iter__(self):
|
||||||
for fieldset in self.fieldsets:
|
for name, options in self.fieldsets:
|
||||||
yield BoundFieldset(self.form, fieldset)
|
yield Fieldset(self.form, name, **options)
|
||||||
|
|
||||||
def first_field(self):
|
def first_field(self):
|
||||||
for bf in self.form:
|
for bf in self.form:
|
||||||
return bf
|
return bf
|
||||||
|
|
||||||
def _media(self):
|
def _media(self):
|
||||||
media = self.form.media
|
media = self.form.media
|
||||||
for fs in self.fieldsets:
|
for fs in self:
|
||||||
media = media + fs.media
|
media = media + fs.media
|
||||||
return media
|
return media
|
||||||
media = property(_media)
|
media = property(_media)
|
||||||
|
|
||||||
class Fieldset(object):
|
class Fieldset(object):
|
||||||
def __init__(self, name=None, fields=(), classes=(), description=None):
|
def __init__(self, form, name=None, fields=(), classes=(), description=None):
|
||||||
|
self.form = form
|
||||||
self.name, self.fields = name, fields
|
self.name, self.fields = name, fields
|
||||||
self.classes = u' '.join(classes)
|
self.classes = u' '.join(classes)
|
||||||
self.description = description
|
self.description = description
|
||||||
@ -68,16 +81,12 @@ class Fieldset(object):
|
|||||||
return forms.Media(js=['%sjs/admin/CollapsedFieldsets.js' % settings.ADMIN_MEDIA_PREFIX])
|
return forms.Media(js=['%sjs/admin/CollapsedFieldsets.js' % settings.ADMIN_MEDIA_PREFIX])
|
||||||
return forms.Media()
|
return forms.Media()
|
||||||
media = property(_media)
|
media = property(_media)
|
||||||
|
|
||||||
class BoundFieldset(object):
|
|
||||||
def __init__(self, form, fieldset):
|
|
||||||
self.form, self.fieldset = form, fieldset
|
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for field in self.fieldset.fields:
|
for field in self.fields:
|
||||||
yield BoundFieldline(self.form, field)
|
yield Fieldline(self.form, field)
|
||||||
|
|
||||||
class BoundFieldline(object):
|
class Fieldline(object):
|
||||||
def __init__(self, form, field):
|
def __init__(self, form, field):
|
||||||
self.form = form # A django.forms.Form instance
|
self.form = form # A django.forms.Form instance
|
||||||
if isinstance(field, basestring):
|
if isinstance(field, basestring):
|
||||||
@ -87,12 +96,12 @@ class BoundFieldline(object):
|
|||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for i, field in enumerate(self.fields):
|
for i, field in enumerate(self.fields):
|
||||||
yield BoundField(self.form, field, is_first=(i == 0))
|
yield AdminField(self.form, field, is_first=(i == 0))
|
||||||
|
|
||||||
def errors(self):
|
def errors(self):
|
||||||
return u'\n'.join([self.form[f].errors.as_ul() for f in self.fields])
|
return u'\n'.join([self.form[f].errors.as_ul() for f in self.fields])
|
||||||
|
|
||||||
class BoundField(object):
|
class AdminField(object):
|
||||||
def __init__(self, form, field, is_first):
|
def __init__(self, form, field, is_first):
|
||||||
self.field = form[field] # A django.forms.BoundField instance
|
self.field = form[field] # A django.forms.BoundField instance
|
||||||
self.is_first = is_first # Whether this field is first on the line
|
self.is_first = is_first # Whether this field is first on the line
|
||||||
@ -116,10 +125,17 @@ class BaseModelAdmin(object):
|
|||||||
"""Functionality common to both ModelAdmin and InlineAdmin."""
|
"""Functionality common to both ModelAdmin and InlineAdmin."""
|
||||||
raw_id_fields = ()
|
raw_id_fields = ()
|
||||||
fields = None
|
fields = None
|
||||||
|
fieldsets = None
|
||||||
filter_vertical = ()
|
filter_vertical = ()
|
||||||
filter_horizontal = ()
|
filter_horizontal = ()
|
||||||
prepopulated_fields = {}
|
prepopulated_fields = {}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# TODO: This should really go in django.core.validation, but validation
|
||||||
|
# doesn't work on ModelAdmin classes yet.
|
||||||
|
if self.fieldsets and self.fields:
|
||||||
|
raise ImproperlyConfigured('Both fieldsets and fields is specified for %s.' % self.model)
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
"""
|
"""
|
||||||
Hook for specifying the form Field instance for a given database Field
|
Hook for specifying the form Field instance for a given database Field
|
||||||
@ -163,21 +179,22 @@ class BaseModelAdmin(object):
|
|||||||
# For any other type of field, just call its formfield() method.
|
# For any other type of field, just call its formfield() method.
|
||||||
return db_field.formfield(**kwargs)
|
return db_field.formfield(**kwargs)
|
||||||
|
|
||||||
def fieldsets(self, request):
|
def _fieldsets(self, request):
|
||||||
"""
|
if self.fieldsets:
|
||||||
Generator that yields Fieldset objects for use on add and change admin
|
return self.fieldsets
|
||||||
form pages.
|
if self.fields:
|
||||||
|
return [(None, {'fields': self.fields})]
|
||||||
|
# TODO: switch this to pull from the form, not the model
|
||||||
|
fields = [f.name for f in self.opts.fields + self.opts.many_to_many if f.editable and not isinstance(f, models.AutoField)]
|
||||||
|
return [(None, {'fields': fields})]
|
||||||
|
|
||||||
This default implementation looks at self.fields, but subclasses can
|
def fieldsets_add(self, request):
|
||||||
override this implementation and do something special based on the
|
"Hook for specifying fieldsets for the add form."
|
||||||
given HttpRequest object.
|
return list(self._fieldsets(request))
|
||||||
"""
|
|
||||||
if self.fields is None:
|
def fieldsets_change(self, request, obj):
|
||||||
default_fields = [f.name for f in self.opts.fields + self.opts.many_to_many if f.editable and not isinstance(f, models.AutoField)]
|
"Hook for specifying fieldsets for the change form."
|
||||||
yield Fieldset(fields=default_fields)
|
return list(self._fieldsets(request))
|
||||||
else:
|
|
||||||
for name, options in self.fields:
|
|
||||||
yield Fieldset(name, options['fields'], classes=options.get('classes', '').split(' '), description=options.get('description'))
|
|
||||||
|
|
||||||
class ModelAdmin(BaseModelAdmin):
|
class ModelAdmin(BaseModelAdmin):
|
||||||
"Encapsulates all admin options and functionality for a given model."
|
"Encapsulates all admin options and functionality for a given model."
|
||||||
@ -203,6 +220,7 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
for inline_class in self.inlines:
|
for inline_class in self.inlines:
|
||||||
inline_instance = inline_class(self.model, self.admin_site)
|
inline_instance = inline_class(self.model, self.admin_site)
|
||||||
self.inline_instances.append(inline_instance)
|
self.inline_instances.append(inline_instance)
|
||||||
|
super(ModelAdmin, self).__init__()
|
||||||
|
|
||||||
def __call__(self, request, url):
|
def __call__(self, request, url):
|
||||||
# Check that LogEntry, ContentType and the auth context processor are installed.
|
# Check that LogEntry, ContentType and the auth context processor are installed.
|
||||||
@ -243,14 +261,6 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
return forms.Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js])
|
return forms.Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js])
|
||||||
media = property(_media)
|
media = property(_media)
|
||||||
|
|
||||||
def fieldsets_add(self, request):
|
|
||||||
"Hook for specifying Fieldsets for the add form."
|
|
||||||
return list(self.fieldsets(request))
|
|
||||||
|
|
||||||
def fieldsets_change(self, request, obj):
|
|
||||||
"Hook for specifying Fieldsets for the change form."
|
|
||||||
return list(self.fieldsets(request))
|
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
def has_add_permission(self, request):
|
||||||
"Returns True if the given request has permission to add an object."
|
"Returns True if the given request has permission to add an object."
|
||||||
opts = self.opts
|
opts = self.opts
|
||||||
@ -413,19 +423,20 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
# Object list will give 'Permission Denied', so go back to admin home
|
# Object list will give 'Permission Denied', so go back to admin home
|
||||||
post_url = '../../../'
|
post_url = '../../../'
|
||||||
|
|
||||||
ModelForm = forms.form_for_model(model, formfield_callback=self.formfield_for_dbfield)
|
fields = flatten_fieldsets(self.fieldsets_add(request))
|
||||||
|
ModelForm = forms.form_for_model(model, fields=fields, formfield_callback=self.formfield_for_dbfield)
|
||||||
|
|
||||||
inline_formsets = []
|
inline_formsets = []
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = ModelForm(request.POST, request.FILES)
|
form = ModelForm(request.POST, request.FILES)
|
||||||
for FormSet in self.get_inline_formsets():
|
for FormSet in self.formsets_add(request):
|
||||||
inline_formset = FormSet(data=request.POST, files=request.FILES)
|
inline_formset = FormSet(data=request.POST, files=request.FILES)
|
||||||
inline_formsets.append(inline_formset)
|
inline_formsets.append(inline_formset)
|
||||||
if all_valid(inline_formsets) and form.is_valid():
|
if all_valid(inline_formsets) and form.is_valid():
|
||||||
return self.save_add(request, model, form, inline_formsets, '../%s/')
|
return self.save_add(request, model, form, inline_formsets, '../%s/')
|
||||||
else:
|
else:
|
||||||
form = ModelForm(initial=request.GET)
|
form = ModelForm(initial=request.GET)
|
||||||
for FormSet in self.get_inline_formsets():
|
for FormSet in self.formsets_add(request):
|
||||||
inline_formset = FormSet()
|
inline_formset = FormSet()
|
||||||
inline_formsets.append(inline_formset)
|
inline_formsets.append(inline_formset)
|
||||||
|
|
||||||
@ -436,7 +447,7 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
|
|
||||||
inline_admin_formsets = []
|
inline_admin_formsets = []
|
||||||
for inline, formset in zip(self.inline_instances, inline_formsets):
|
for inline, formset in zip(self.inline_instances, inline_formsets):
|
||||||
fieldsets = list(inline.fieldsets(request))
|
fieldsets = list(inline.fieldsets_add(request))
|
||||||
inline_admin_formset = InlineAdminFormSet(inline, formset, fieldsets)
|
inline_admin_formset = InlineAdminFormSet(inline, formset, fieldsets)
|
||||||
inline_admin_formsets.append(inline_admin_formset)
|
inline_admin_formsets.append(inline_admin_formset)
|
||||||
|
|
||||||
@ -474,12 +485,13 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
if request.POST and request.POST.has_key("_saveasnew"):
|
if request.POST and request.POST.has_key("_saveasnew"):
|
||||||
return self.add_view(request, form_url='../../add/')
|
return self.add_view(request, form_url='../../add/')
|
||||||
|
|
||||||
ModelForm = forms.form_for_instance(obj, formfield_callback=self.formfield_for_dbfield)
|
fields = flatten_fieldsets(self.fieldsets_change(request, obj))
|
||||||
|
ModelForm = forms.form_for_instance(obj, fields=fields, formfield_callback=self.formfield_for_dbfield)
|
||||||
|
|
||||||
inline_formsets = []
|
inline_formsets = []
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = ModelForm(request.POST, request.FILES)
|
form = ModelForm(request.POST, request.FILES)
|
||||||
for FormSet in self.get_inline_formsets():
|
for FormSet in self.formsets_change(request, obj):
|
||||||
inline_formset = FormSet(obj, request.POST, request.FILES)
|
inline_formset = FormSet(obj, request.POST, request.FILES)
|
||||||
inline_formsets.append(inline_formset)
|
inline_formsets.append(inline_formset)
|
||||||
|
|
||||||
@ -487,7 +499,7 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
return self.save_change(request, model, form, inline_formsets)
|
return self.save_change(request, model, form, inline_formsets)
|
||||||
else:
|
else:
|
||||||
form = ModelForm()
|
form = ModelForm()
|
||||||
for FormSet in self.get_inline_formsets():
|
for FormSet in self.formsets_change(request, obj):
|
||||||
inline_formset = FormSet(obj)
|
inline_formset = FormSet(obj)
|
||||||
inline_formsets.append(inline_formset)
|
inline_formsets.append(inline_formset)
|
||||||
|
|
||||||
@ -505,14 +517,14 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
#orig_list = func()
|
#orig_list = func()
|
||||||
#oldform.order_objects.extend(orig_list)
|
#oldform.order_objects.extend(orig_list)
|
||||||
|
|
||||||
adminForm = AdminForm(form, self.fieldsets_change(request, obj), self.prepopulated_fields)
|
adminForm = AdminForm(form, self.fieldsets_change(request, obj), self.prepopulated_fields)
|
||||||
media = self.media + adminForm.media
|
media = self.media + adminForm.media
|
||||||
for fs in inline_formsets:
|
for fs in inline_formsets:
|
||||||
media = media + fs.media
|
media = media + fs.media
|
||||||
|
|
||||||
inline_admin_formsets = []
|
inline_admin_formsets = []
|
||||||
for inline, formset in zip(self.inline_instances, inline_formsets):
|
for inline, formset in zip(self.inline_instances, inline_formsets):
|
||||||
fieldsets = list(inline.fieldsets(request))
|
fieldsets = list(inline.fieldsets_change(request, obj))
|
||||||
inline_admin_formset = InlineAdminFormSet(inline, formset, fieldsets)
|
inline_admin_formset = InlineAdminFormSet(inline, formset, fieldsets)
|
||||||
inline_admin_formsets.append(inline_admin_formset)
|
inline_admin_formsets.append(inline_admin_formset)
|
||||||
|
|
||||||
@ -626,9 +638,13 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
]
|
]
|
||||||
return render_to_response(template_list, extra_context, context_instance=template.RequestContext(request))
|
return render_to_response(template_list, extra_context, context_instance=template.RequestContext(request))
|
||||||
|
|
||||||
def get_inline_formsets(self):
|
def formsets_add(self, request):
|
||||||
for inline in self.inline_instances:
|
for inline in self.inline_instances:
|
||||||
yield inline.formset_class
|
yield inline.formset_add(request)
|
||||||
|
|
||||||
|
def formsets_change(self, request, obj):
|
||||||
|
for inline in self.inline_instances:
|
||||||
|
yield inline.formset_change(request, obj)
|
||||||
|
|
||||||
class InlineModelAdmin(BaseModelAdmin):
|
class InlineModelAdmin(BaseModelAdmin):
|
||||||
"""
|
"""
|
||||||
@ -648,23 +664,25 @@ class InlineModelAdmin(BaseModelAdmin):
|
|||||||
self.admin_site = admin_site
|
self.admin_site = admin_site
|
||||||
self.parent_model = parent_model
|
self.parent_model = parent_model
|
||||||
self.opts = self.model._meta
|
self.opts = self.model._meta
|
||||||
# TODO: pass a fields arg into forms.inline_formset if/when we have one
|
super(InlineModelAdmin, self).__init__()
|
||||||
self.formset_class = forms.inline_formset(parent_model, self.model, fk_name=self.fk_name, formfield_callback=self.formfield_for_dbfield, extra=self.extra)
|
|
||||||
|
|
||||||
def fieldsets(self, request):
|
def formset_add(self, request):
|
||||||
"""
|
"""Returns an InlineFormSet class for use in admin add views."""
|
||||||
Generator that yields Fieldset objects for use on add and change admin
|
fields = flatten_fieldsets(self.fieldsets_add(request))
|
||||||
form pages.
|
return forms.inline_formset(self.parent_model, self.model, fk_name=self.fk_name, fields=fields, formfield_callback=self.formfield_for_dbfield, extra=self.extra)
|
||||||
|
|
||||||
This default implementation looks at self.fields, but subclasses can
|
def formset_change(self, request, obj):
|
||||||
override this implementation and do something special based on the
|
"""Returns an InlineFormSet class for use in admin change views."""
|
||||||
given HttpRequest object.
|
fields = flatten_fieldsets(self.fieldsets_change(request, obj))
|
||||||
"""
|
return forms.inline_formset(self.parent_model, self.model, fk_name=self.fk_name, fields=fields, formfield_callback=self.formfield_for_dbfield, extra=self.extra)
|
||||||
if self.fields is None:
|
|
||||||
yield Fieldset(fields=self.formset_class.form_class.base_fields)
|
def _fieldsets(self, request):
|
||||||
else:
|
if self.fieldsets:
|
||||||
for name, options in self.fields:
|
return self.fieldsets
|
||||||
yield Fieldset(name, options['fields'], classes=options.get('classes', '').split(' '), description=options.get('description'))
|
if self.fields:
|
||||||
|
return [(None, {'fields': self.fields})]
|
||||||
|
fields = [f for f in self.formset_class(request).form_class.base_fields.keys()]
|
||||||
|
return [(None, {'fields': fields})]
|
||||||
|
|
||||||
class StackedInline(InlineModelAdmin):
|
class StackedInline(InlineModelAdmin):
|
||||||
template = 'admin/edit_inline_stacked.html'
|
template = 'admin/edit_inline_stacked.html'
|
||||||
@ -694,8 +712,8 @@ class InlineAdminFormSet(object):
|
|||||||
yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, None)
|
yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, None)
|
||||||
|
|
||||||
def fields(self):
|
def fields(self):
|
||||||
# TODO: this needs to respect the field order of self.fieldsets
|
for field_name in flatten_fieldsets(self.fieldsets):
|
||||||
return self.formset.form_class.base_fields.values()
|
yield self.formset.form_class.base_fields[field_name]
|
||||||
|
|
||||||
class InlineAdminForm(AdminForm):
|
class InlineAdminForm(AdminForm):
|
||||||
"""
|
"""
|
||||||
@ -707,12 +725,12 @@ class InlineAdminForm(AdminForm):
|
|||||||
super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields)
|
super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields)
|
||||||
|
|
||||||
def pk_field(self):
|
def pk_field(self):
|
||||||
return BoundField(self.form, self.formset._pk_field_name, False)
|
return AdminField(self.form, self.formset._pk_field_name, False)
|
||||||
|
|
||||||
def deletion_field(self):
|
def deletion_field(self):
|
||||||
from django.newforms.formsets import DELETION_FIELD_NAME
|
from django.newforms.formsets import DELETION_FIELD_NAME
|
||||||
return BoundField(self.form, DELETION_FIELD_NAME, False)
|
return AdminField(self.form, DELETION_FIELD_NAME, False)
|
||||||
|
|
||||||
def ordering_field(self):
|
def ordering_field(self):
|
||||||
from django.newforms.formsets import ORDERING_FIELD_NAME
|
from django.newforms.formsets import ORDERING_FIELD_NAME
|
||||||
return BoundField(self.form, ORDERING_FIELD_NAME, False)
|
return AdminField(self.form, ORDERING_FIELD_NAME, False)
|
||||||
|
@ -40,11 +40,11 @@
|
|||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% for bfset in adminform %}
|
{% for fieldset in adminform %}
|
||||||
<fieldset class="module aligned {{ bfset.fieldset.classes }}">
|
<fieldset class="module aligned {{ fieldset.classes }}">
|
||||||
{% if bfset.fieldset.name %}<h2>{{ bfset.fieldset.name }}</h2>{% endif %}
|
{% if fieldset.name %}<h2>{{ fieldset.name }}</h2>{% endif %}
|
||||||
{% if bfset.fieldset.description %}<div class="description">{{ bfset.fieldset.description }}</div>{% endif %}
|
{% if fieldset.description %}<div class="description">{{ fieldset.description }}</div>{% endif %}
|
||||||
{% for line in bfset %}
|
{% for line in fieldset %}
|
||||||
<div class="form-row{% if line.errors %} errors{% endif %}">
|
<div class="form-row{% if line.errors %} errors{% endif %}">
|
||||||
{{ line.errors }}
|
{{ line.errors }}
|
||||||
{% for field in line %}
|
{% for field in line %}
|
||||||
|
@ -25,8 +25,8 @@
|
|||||||
<tr class="{% cycle row1,row2 %}">
|
<tr class="{% cycle row1,row2 %}">
|
||||||
{{ inline_admin_form.pk_field.field }}
|
{{ inline_admin_form.pk_field.field }}
|
||||||
{% if inline_admin_formset.formset.deletable %}<td>{{ inline_admin_form.deletion_field.field }}</td>{% endif %}
|
{% if inline_admin_formset.formset.deletable %}<td>{{ inline_admin_form.deletion_field.field }}</td>{% endif %}
|
||||||
{% for bfset in inline_admin_form %}
|
{% for fieldset in inline_admin_form %}
|
||||||
{% for line in bfset %}
|
{% for line in fieldset %}
|
||||||
{% for field in line %}
|
{% for field in line %}
|
||||||
<td>{{ field.field }}</td>
|
<td>{{ field.field }}</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user