solovyov.net

Django newforms

· · django

Сама по себе Джанга замечательна именно тем, что многие, очень многие вещи в ней делать намного проще, чем где-либо ещё. То есть практически любой компонент сделан так, что использовать его невероятно удобно. Но вот обработка форм (в Джанге ею занимались так называемые манипуляторы) была… не слишком удобная для меня лично. ;-)

И где-то примерно в январе в trunk‘е Джанги появилась новая подсистема - newforms. Она позволяет многие вещи упростить очень сильно, и она сделана именно в духе самого фреймворка в том смысле, что она действительно проще и удобнее чего-либо другого. Самая большая проблема новых формочек ;) заключается в том, что документации как таковой по ней не было вообще - только доктесты (хотя они были достаточно хорошо написаны, чтобы понять начала пользования системой).

Чуть погодя появилась документация, которая толком не давала хороших примеров работы с newforms, и я в это время большинство форм (которые у меня не получались достаточно простые, чтобы можно было легко применить form_for_model/form_for_instance, которые генерируют форму из модели автоматом) делал руками. Чуть позже я узнал (из форумов и исходников Джанги), каким образом можно с помощью form_for_model создать такую форму, которая не полностью повторяет модель (сначала callback_function, а вот не так давно добавили параметр fields).

Но вот недавно с помощью форумов softwaremaniacs.org и тех самых исходников Джанги я-таки разобрался с созданием довольно сложных форм (когда нужно создать модель со ссылкой на текущего залогинившегося пользователя). Чтобы долго не распинаться, просто покажу код:

BaseRoomForm = forms.form_for_model(Room, 
    fields=('name', 'description', 'image', 'products'))

class RoomForm(BaseRoomForm):
    def __init__(self, user, *args, **kwargs):
        super(RoomForm, self).__init__(*args, **kwargs)
        self.fields['image'] = forms.Field(label=u'Image', widget=forms.FileInput())
        self.user = user

    def save(self):
        room = forms.save_instance(self, Room(user=self.user), 
            fields=('name', 'description', 'products'))
        room.save_image_file(self.cleaned_data['image']['filename'],
            self.cleaned_data['image']['content'])
        return room

Вот так вот замечательно можно наследоваться от формы, созданной с помощью form_for_model. :) Спасибо Ивану за подсказку. :)

Собственно, основная штука здесь - это save_instance. Ему надо передать инстанс, который будет сохраняться - а так как в данном случае происходит создание, то вот я и передаю новосозданный объект. :) fields я передаю, чтоб избежать передачи картинки в save_instance - он их ещё нормально обрабатывать не умеет. Вызов этой формы происходит примерно так:

if request.POST:
    data = request.POST.copy()
    data.update(request.FILES)
    form = RoomForm(request.user, data)
    if form.is_valid():
        room = form.save()
else:
    form = RoomForm(request.user)

Тут, конечно, надо учитывать, что форма непроста ещё и тем, что картинка передаётся. Без неё всё было бы проще (просто form = RoomForm(request.user, request.POST)). Возникает только один вопрос - таким ведь образом form_for_instance никак не используешь. Тут нужно просто заменить процесс создания формы form = RoomForm(request.user) на form = RoomForm(request.user, initial=room.__dict__) - передать начальные значения.

Хотелось бы ещё отметить одну штуку, которую сегодня нашёл Илья - если в модели есть ForeignKey, то form_for_model абсолютно корректно для него создаёт SelectBox, корректно заполняет его значениями, но не устанавливает стартовое значение, которое передаётся в initial. Решение довольно простое, но с одной загвоздкой - нужно в init класса установить initial нужного поля из набора base_fields в id. Т.е., если бы пользователя можно было бы выбирать, получилось бы что-то вроде этого:

self.base_fields['user'].initial = room.user.id

Я так до конца и не разобрался, почему оно не ловит само и почему никак не помогает установка initial в fields['user']. :)

Вот так вкратце и обстоят дела. Есть, конечно, некоторые недочёты и недоработки, однако в общем это всё невероятно облегчает мне жизнь. Чего и всем остальным желаю! :)