As title says, how can I override it - specifically, how to change its widget from default TextInput to HiddenInput? And why isn't it hidden to begin with :S
I tried passing custom callback function as formfield callback to modelformset_factory but it only changed the default fields originating from model. I also tried specifying another widget by defining ORDER field in the form itself (the form that I give to modelformset_factory because it contain one additional field which gets added as expected) and to specify widgets dict in forms Meta class, but still no go - ORDER field gets rendered as visible TextInput:
<th><label for="id_form-0-ORDER">Order:</label></th><td><input name="form-0-ORDER" value="1" id="id_form-0-ORDER" type="text">
The reason why I (think) I need that order field is to change it dynamically with JS when I'm adding and removing random forms to/from the formset. I keep form-TOTAL_FORMS updated using JS too. When I submit such tinkered with formset it sometimes might have only one form with prefix form-1 or form-2 but not form-0. As I understand it, Django sees that there should only be one form (because of form-TOTAL_FORMS) and generates it looking for form-0 values in request.POST, so the actual submited data doesn't get used.
Am I doing something wrong or is there a better way to accomplish this? I still didn't check if ordering forms (setting form-1 ORDER value to 1 after form-0 with ORDER=1 has been removed from html) will solve the problem.
EDIT: Didn't know if answering the question myself was an accepted thing to do, so I'm doing this edit.
The answer - it is briefly mentioned in Django docs http://docs.djangoproject.com/en/1.2/topics/forms/formsets/#adding-additional-fields-to-a-formset, but somehow I missed it. So to override the default ORDER field which gets added to forms when using formsets with 'can_order', you have to override add_fields() method in Base(Model)FormSet subclass and change ORDER field to whatever you want. Here is the code I used:
class BaseAuthorFormSet(BaseModelFormSet):
# Overriden add_fields method which makes ORDER field hidden in forms.
def add_fields(self, form, index):
super(BaseAuthorFormSet, self).add_fields(form, index)
form.fields['ORDER'] = forms.IntegerField(required=False,
widget=forms.HiddenInput, initial=form.fields['ORDER'].initial)
However, this didn't solve my problem - that is to be able to remove random forms from HTML/DOM using JS and still have correctly working formset. So I ended up altering HTML even more - when the form which isn't the last one in the formset is removed, I change the numbers in the last form to those used in the removed one. By using this method, on submit all forms in the formset will be numbered from 0 to TOTAL_FORMS (the number of existing forms) and Django will create/save model instances correctly. JS function I used ('new_number' - the number of the removed form, 'elements' - DOM elements of all current forms in the formset):
function orderForms(new_number, elements)
{
var re1, re2, last, old_number, inputs, backup = [];
if (typeof elements != "undefined" && elements.length > 0) {
last = elements[elements.length - 1];
old_number = parseInt(last.id.match(/\d+$/ig));
// If removed author form was not the last,
// then switch the numbers used in the attributes.
if (old_number > new_number) {
re1 = /("[a-zA-Z_]+)\d+(")/ig;
re2 = /(form-)\d+/ig;
inputs = last.getElementsByTagName("input");
// Save current values of form's input fields.
for (i = 0; i < inputs.length; i++) {
if (inputs[i].type == "checkbox") {
backup.push(inputs[i].checked);
} else {
backup.push(inputs[i].value);
}
}
// Change the numbers in attributes by altering html code.
last.id = last.id.replace(old_number, new_number);
last.innerHTML = last.innerHTML.replace(re1, "$1" +
new_number + "$2").replace(re2, "$1" + new_number);
// Restore the values of form's input fields.
for (i = inputs.length; i > 0; i--) {
if (inputs[i - 1].type == "checkbox") {
inputs[i - 1].checked = backup.pop();
} else {
inputs[i - 1].value = backup.pop();
}
}
}
}
}