Adust the as_table tag to render a pick link if appropriate
The templates are adjusted to pass on a 'reference' value, which
is the url_name of the view from which the entity should be picked.
The as_table (and related) function(s) construct and then pass on
this argument and enable takes_contex so that we have access to the
context of the enclosing template.
We only extract ReferenceProperties that end with '_link_id' since
that is how all RP's are currently named. It is not possible to
create a field with the same name as the RP, as GAE will try to
interpret it's contents as the key of an entity before even calling
any function we can override.
Patch by: Sverre Rabbelier
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
from django.contrib.contenttypes.models import ContentType
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):
try:
fieldset_name, fieldset_options = self.fieldsets[0]
field_name = fieldset_options['fields'][0]
if not isinstance(field_name, basestring):
field_name = field_name[0]
return self.form[field_name]
except (KeyError, IndexError):
pass
try:
return iter(self.form).next()
except StopIteration:
return None
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
if original is not None:
self.original.content_type_id = ContentType.objects.get_for_model(original).pk
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())