formsetで外部から状態を注入したい場合。

こんな感じに書くと、何も考えずに使えて便利かもしれない。

from django.conf import settings
settings.configure()

import django.forms as forms
from django.forms.formsets import  BaseFormSet, formset_factory

class ExtraArgsForm(forms.Form):
    def __init__(self, data=None, extra_args=None, **kwargs):
        super(ExtraArgsForm, self).__init__(data=data, **kwargs)
        if extra_args:
            for k, v in extra_args.items():
                setattr(self, k, v)

class ExtraArgsFormSet(BaseFormSet):
    def __init__(self, data=None, extra_args=None, **kwargs):
        self._extra_args = extra_args
        super(ExtraArgsFormSet, self).__init__(data=data, **kwargs)
        

    def _construct_form(self, i, **kwargs):
        """
        Instantiates and returns the i-th form instance in a formset.
        """
        defaults = {'auto_id': self.auto_id, 
                    'prefix': self.add_prefix(i), 
                    "extra_args": self._extra_args
                    }
        if self.is_bound:
            defaults['data'] = self.data
            defaults['files'] = self.files
        if self.initial:
            try:
                defaults['initial'] = self.initial[i]
            except IndexError:
                pass
        # Allow extra forms to be empty.
        if i >= self.initial_form_count():
            defaults['empty_permitted'] = True
        defaults.update(kwargs)
        form = self.form(**defaults)
        self.add_fields(form, i)
        return form

def extra_args_formset_factory(form, formset=ExtraArgsFormSet, extra=1, can_order=False,
                               can_delete=False, max_num=None):
    return formset_factory(form, formset=formset, extra=extra, can_order=can_order, 
                    can_delete=can_delete, max_num=max_num)

使ってみる。

class SumForm(ExtraArgsForm):
    x = forms.IntegerField()
    y = forms.IntegerField()

    def clean(self):
        data = self.cleaned_data
        if data["x"] + data["y"] != self.sum:
            raise forms.ValidationError('%d + %d is not %d' % (data["x"], data["y"], self.sum))
        return data

SumFormSet = extra_args_formset_factory(SumForm)
sum_form = SumFormSet(initial=[dict(x=10, y=90)], extra_args=dict(sum=100))

params={"form-TOTAL_FORMS" : "1", 
 "form-INITIAL_FORMS" : "1", 
 "form-MAX_NUM_FORMS" : None, 
 "form-0-x" :"10", 
 "form-0-y" :"90"}

sum_form = SumFormSet(params, extra_args=dict(sum=100))
print sum_form.is_valid()
print sum_form.cleaned_data

sum_form = SumFormSet(params, extra_args=dict(sum=99))
print sum_form.is_valid()
print sum_form.errors

# True
# [{'y': 90, 'x': 10}]
# False
# [{'__all__': [u'10 + 90 is not 99']}]