Django 1.x & form_for_model

by Larin

Интро

Давным-давно, когда деревья были большими, “джанга”  маленькой, а формы “новыми”, существовала такая функция: form_for_model. И на основе этой функции был даже создан сниппет, позволяющий динамический создавать форму по модели и менять “на лету” свойства созданной формы.

Однако, начиная с версии Django 1.0 данная функциональность была помечена как “deprecated” и уже в 1.0 beta 1 удалена.

Еще проще

В новой версии просто советуют использовать наследников класса ModelForm.

В одном из проектов мне понадобилось создавать формы по моделям, динамически. Моделей много, формы почти однотипные. Вот это “почти” и заставило искать решение – как просто изменить один (или несколько) виджетов у динамически созданной формы?

Вот, что у меня получилось:


def form_for_model(model, fields=None, widgets=None):
    """ Динамическое создание формы по моделе. """

    class Meta:
        pass
    setattr(Meta, 'model', model)

    if widgets:
        setattr(Meta, 'widgets', widgets)

    attrs = { '__module__': '', 'Meta': Meta, }

    if fields:
        attrs.update(fields)

    name = '%sForm' % model.__name__
    return type(name, (ModelForm,), attrs)

Пример использования:


#-- models.py
class Book(models.Model):
        title = models.CharField(maxlength = 100)
        description = models.TextField()
        author = models.ForeignKey(Author)

#-- views.py
def add_book(request):
    custom_fields = dict(
        owner = AutocompleteField()
    )

    custom_widgets = dict(
        name = forms.TextInput({"size": 70}),
        description = RichTextarea()
    )

    form_inst = form_for_model(Book, fields=custom_fields, widgets=custom_widgets)
    form = form_inst(request.POST or None)

    if form.is_valid():
        form.save()
    return locals()

Alpha

Код еще можно доработать до полной универсальности, чтобы можно было “повторить” конструкцию вида:


class PartialAuthorForm(ModelForm):
    class Meta:
        model = Author
        exclude = ('birth_date',)

но для моей задачи этого не требовалось.