20 мая 2013 г.

django: минимальное количество formset inline

Захотелось мне в django, чтобы formset содержал не менее некоторого количества полностью заполненных форм. С нормальной валидацией где нужно, а не в середине вьюшки, например. На момент 1.4.x ничего особо удобного на этот счёт не было (возможно, это странно), хотя какие-то реквесты в багзилле я находил. На самом деле довольно просто ручками всё решается наследованием и переопределением InlineFormSet при конструировании FormSet через factory (а я так использую всегда).

Допустим, мы конструируем FormSet таким образом (обратите внимание на formset=...):
ConductingClazzRequestMorePersonFormSet = inlineformset_factory(ConductingClazzRequest, ConductingClazzRequestPerson, extra=1, formset=ConductingClazzRequestPersonFormsetBase, form=ConductingClazzRequestPersonForm)

Использовать понятно как во вьюшках:
formset = ConductingClazzRequestMorePersonFormSet(request.POST, instance=ccrequest)

Теперь осталось написать ConductingClazzRequestPersonFormsetBase, чтобы она при валидации проверяла количество форм. Здесь я проверяю, что есть как минимум один контакт. Надо учесть всякие удаляемые налету формы итд, чтобы не было сюрпризов в сложных случаях. А у меня таковые возникли, покуда конечный такой вид не состряпал (если не ошибаюсь, часть решения на SO нашёл):
class ConductingClazzRequestPersonFormsetBase(forms.models.BaseInlineFormSet):
    def clean(self):
        super(ConductingClazzRequestPersonFormsetBase, self).clean()
        initial_num = len(filter(lambda f: not self._should_delete_form(f), self.initial_forms))
        extra_num = len(filter(lambda f: f.has_changed() and not self._should_delete_form(f), self.extra_forms))
        if initial_num + extra_num < 1:
            raise ValidationError(_(u'request should be at least one person'))
Это всё прекрасно работает и в админке, надо только указать formset для Inline соответствующей МодельAdmin.
class ConductingClazzRequestPersonInline(admin.TabularInline):
    model = ConductingClazzRequestPerson
    extra = 1
    formset = ConductingClazzRequestMorePersonFormSet

Комментариев нет:

Отправить комментарий