Ok, I apologize. Here is a fuller representation of what I'm doing. I had a hard time figuring out how much was enough versus too much. This is simplified, but I think it represents what I'm trying to do. For instance, don't pay too much attention to the save logic in the view, I haven't actually really worked on it much yet.
To restate my scenario, I want to carry information in hidden fields of a form that determine how the form is displayed and functions. I realize I do have access to the self.Initial values in the form itself but I don't think that there's easy access to those values in the template. The value that I want seems to be the value() method of the BoundField, but run through the to_python() method of the field. I actually solved this by adding a to_python() method to the BoundField that does just this, but I would like to solve this without modifying the Django code. The samples below do not reflect my added to_python() method.
# Model classes
class OrganizationItem(models.Model):
organization = models.ForeignKey(Organization)
item = models.ForeignKey(ListItem)
descr = models.TextField("Description", blank=True)
class ListItem(models.Model):
category = models.ForeignKey(ListCategory)
name = models.CharField("Name", max_length=200)
order = models.IntegerField("Order")
extraInfo = models.BooleanField("Requires extra information")
multiLineInfo = models.BooleanField("The display should use a multi-line edit box instead of a single line edit")
descrLabel = models.CharField("Extra information label", max_length=50,
blank=True)
class ListCategory(modelUtils.MyModelAudit):
catId = models.CharField("Category identifier", max_length=15,
primary_key=True)
name = models.CharField("Name", max_length=100)
order = models.IntegerField("Order")
group = models.ForeignKey(ListGroup)
organization = models.BooleanField("Should this be associated with an organization?")
twoColumn = models.BooleanField("Should the current list span 2 columns?")
class ListGroup(modelUtils.MyModelAudit):
name = models.CharField("Name", max_length=15)
descr = models.CharField("Description", max_length=100, blank=True)
order = models.IntegerField("Order")
# View example
def organizationAdd(request):
OrganizationItemFormSet = formset_factory(OrganizationItemForm)
if request.method == 'POST':
orgItemFormSet = OrganizationItemFormSet(request.POST)
orgItemFormSetIsValid = orgItemFormSet.is_valid()
if orgItemFormSetIsValid:
orgItemFormSet.save(commit=False)
else:
orgItemFormSet = OrganizationItemFormSet()
listItems = CreateItemsSection(orgItemFormSet, "organization", category__organization=True)
context = {
'listItems': listItems,
'listItemsManagementForm': orgItemFormSet.management_form}
return render_to_response('organizationEdit.html', context,
context_instance=RequestContext(request))
def CreateItemsSection(formset, idField="", objId=None, relation=None, **kwargs):
"""Construct the list of selectable items"""
qs = ListItem.objects.filter(**kwargs)
# Get the appliable list of groups
groups = qs.values(
"category__group", "category__group__descr"
).order_by(
"category__group__order"
).distinct()
# Convert the items relation into a dictionary of ListItem PK and XItems.descr
selectedData = {}
if relation is not None:
selectedData = dict([(item.item__id, item.descr) for item in relation])
retVal = []
# Track the current form to display when the formset is populated from a postback
formIndex = 0;
postedData = len(formset) > 0
# Assemble the information for each category group
for grp in groups:
# The current group's description
curGrpDescr = grp["category__group__descr"]
# Initialize the current group entry with a name and a list for the
# category entries
curGrp = {"group": curGrpDescr, "categories": []}
# Get the applicable list of catagories
categories = qs.filter(category__group=grp["category__group"]).values(
"category", "category__name", "category__twoColumn").order_by("category__order").distinct()
# These are used to determine when to start and end the table rows
startRow = True
endRow = False
# Assemble the category information
for cat in categories:
# The current category description (name field)
curCatDescr = cat["category__name"]
# Get the current category's items
listItems = qs.filter(category=cat["category"]).order_by("order")
# Should the category display span 2 columns
twoColumn = cat["category__twoColumn"]
# Build the checkboxes, each contains a formset form
items = []
for li in listItems:
if postedData:
form = formset.forms[formIndex]
formIndex += 1
else:
# Check if the item is selected items
isSelected = selectedData.has_key(
li.pk)
data = ""
if isSelected:
data = selectedData[
li.pk]
initialData = {
'selected': isSelected,
idField: objId,
'descr': data,
'extraRequired': li.extraInfo,
'multiLineInfo': li.multiLineInfo}
form = formset.add_form(initial=initialData)
items.append({
'descrLabel': li.descrLabel,
'category': cat["category"],
'order': li.order,
'form': form})
# If the category should span 2 columns, ensure the last category
# ends it's row and the current category both starts and ends this row
if twoColumn:
lastCatIndex = len(curGrp["categories"]) - 1
if lastCatIndex > -1:
curGrp["categories"][lastCatIndex]["endRow"] = True
startRow = True
endRow = True
# Append to the current group's list of categories
curCat = {
"category": curCatDescr,
"items": items,
"startRow": startRow,
"endRow": endRow,
"twoColumn": twoColumn}
curGrp["categories"].append(curCat)
# Set the default row managment for the next category
if not twoColumn:
startRow = not startRow
endRow = not endRow
else:
startRow = True
endRow = False
# Append the group to the return list
retVal.append(curGrp)
return retVal
# My custom formset for adding arbitrary forms to the set (I included this
# because I didn't think that the CreateItemsSection function the save method
# in the view would make sense without it.)
class XManagementForm(forms.formsets.ManagementForm):
"""
``ManagementForm`` is used to keep track of how many form instances
are displayed on the page. If adding new forms via javascript, you should
increment the count field of this form as well.
"""
def __init__(self, data=None, extra_fields=None, *args, **kwargs):
if extra_fields:
for f, tv in extra_fields.items():
self.base_fields[f] = tv[0](widget=forms.HiddenInput)
if kwargs.has_key('initial'):
kwargs['initial'][f] = tv[1]
super(XManagementForm, self).__init__(data, *args, **kwargs)
class BaseXFormSet(forms.formsets.BaseFormSet):
def __init__(self, *args, **kwargs):
self.added_forms = 0
super(BaseXFormSet, self).__init__(*args, **kwargs)
def _management_form(self):
"""Returns the ManagementForm instance for this FormSet."""
if self.data or self.files:
form = XManagementForm(self.data, auto_id=self.auto_id, prefix=self.prefix,
extra_fields=self.extra_fields)
#import pdb; pdb.set_trace()
if not form.is_valid():
raise forms.ValidationError('ManagementForm data is missing or has been tampered with')
else:
form = XManagementForm(auto_id=self.auto_id, prefix=self.prefix, extra_fields=self.extra_fields,
initial={
forms.formsets.TOTAL_FORM_COUNT: self.total_form_count(),
forms.formsets.INITIAL_FORM_COUNT: self.initial_form_count()
})
return form
management_form = property(_management_form)
def initial_form_count(self):
"""Returns the number of forms that are required/pre-populated in this FormSet."""
if not (self.data or self.files):
initial_forms = self.initial and len(self.initial) or 0
initial_forms += self.added_forms
if initial_forms > self.max_num > 0:
initial_forms = self.max_num
return initial_forms
return super(BaseXFormSet, self).initial_form_count()
def add_form(self, existing_form=None, **kwargs):
self.added_forms = self.added_forms + 1
l_curIdx = len(self.forms)
if existing_form:
l_new_form = existing_form
l_new_form.prefix = self.add_prefix(l_curIdx)
else:
l_new_form = self._construct_form(l_curIdx, **kwargs)
l_new_form.form_index = l_curIdx
self.forms.append(l_new_form)
return l_new_form
def save(self, commit=True):
if hasattr(self.form, "save") and callable(self.form.save):
for frm in self.forms:
frm.save(commit=commit)
else:
raise NotImplementedError('The form %s does not implement the save() method' % self.form.__class__.__name__)
def formset_factory(form, formset=BaseXFormSet, extra=0, can_order=False,
can_delete=False, max_num=0, extra_fields=None):
"""Return a FormSet for the given form class."""
attrs = {'form': form, 'extra': extra,
'can_order': can_order, 'can_delete': can_delete,
'max_num': max_num, 'extra_fields': extra_fields}
return type(form.__name__ + 'FormSet', (formset,), attrs)
# Template
{{ listItemsManagementForm }}
{% for group in listItems %}
{% for cat in group.categories %}
{% if cat.startRow %}<tr>{% endif %}
<td{% if cat.twoColumn %} colspan="2" {% endif %}>
{{ cat.category }}<br>