22 января 2010 в 20:40

Работа с формами в Django перевод

image

Формы являются одной из самых вкусных вещей в Django (после моделей, админки, url-маршрутизации и т.д.). Перед вами небольшое, но ёмкое руководство по их использованию, представляющее собой разбор 8 часто встречающихся ситуаций при программировании форм.

  • Задача. Проверка и отображение простейшей формы.
    Copy Source | Copy HTML<br/>class UserForm(forms.Form):<br/>    username = forms.CharField()<br/>    joined_on = forms.DateField() <br/>

    Этот код отвечает за отображение двух текстовых полей, за данные, которые содержатся в них, а так же за корректность введенной даты.


>

  • Задача. Создание формы, значения которой зависят от какого-то конкретного параметра. Например, вам может потребоваться отобразить значения, относящиеся к определённому субдомену.
    Copy Source | Copy HTML<br/>class UserForm(forms.Form):<br/>    username = forms.CharField()<br/>    plan = forms.ModelChoiceField(queryset = Plan.objects.none())<br/> <br/>    def __init__(self, subdomain, *args, **kwargs):<br/>        self.default_username = default_username<br/>        super(UserForm, self).__init__(*args, **kwargs)<br/>        self.fields['plan'].queryset = Plan.objects.filter(subdomain = subdomain) <br/>

    Здесь в методе __init__ мы переопределяем стандартный queryset поля plan. Подобным образом можно переопределять абсолютно любые атрибуты. Внимательный читатель Заметим также, что выборка происходит лишь после вызова super(UserForm, self).__init__(*args, **kwargs).


>

  • Задача. Многократное использование формы. Обработка данных с помощью cleaned_data.
    Copy Source | Copy HTML<br/>class UserForm(forms.Form):<br/>    username = forms.CharField()<br/> <br/>    def save(self):<br/>        data = self.cleaned_data<br/>        user = User.objects.create(username = data['username'])<br/>        #create a profile<br/>        UserProfile.objects.create(user = user, ...some more data...) <br/>

    Вы можете назвать этот метод как угодно, но обычно его называют save, чтобы сохранить сходство с ModelForm.


>

  • Задача. Создание формы с полями, которые имеют собственный вид проверки.
    Copy Source | Copy HTML<br/>class UserForm(forms.Form):<br/>    username = forms.CharField()<br/> <br/>    def clean_username(self):<br/>        data = self.cleaned_data<br/>        try:<br/>            User.objects.get(username = data['username'])<br/>        except User.DoesNotExist:<br/>            return data['username']<br/>        raise forms.ValidationError('This username is already taken.') <br/>

    Здесь идёт проверка на уникальность имени пользователя.


>

  • Задача. Создание формы с перекрёстной проверкой полей.
    Copy Source | Copy HTML<br/>class UserForm(forms.Form):<br/>    username = forms.CharField()<br/> <br/>    password1 = forms.PasswordField()<br/>    password2 = forms.PasswordField()<br/> <br/>    def clean(self):<br/>        data = self.cleaned_data<br/>        if "password1" in data and "password2" in data and data["password1"] != data["password2"]:<br/>            raise forms.ValudationError("Passwords must be same") <br/>

    Пароли в двух полях проверяются на идентичность друг другу.


>

  • Задача. Создание формы, поля которой зависят от некого значения в базе данных. Например, вам может потребоваться отображать разные формы для разных пользователей.
    Copy Source | Copy HTML<br/>def get_user_form_for_user(user):<br/>        class UserForm(forms.Form):<br/>            username = forms.CharField()<br/>            fields = user.get_profile().all_field()<br/>            #Use field to find what to show. <br/>

    Выходом в данном случае будет — создавать форму динамически.


>

  • Задача. Создание HTML формы, работающей с несколькими моделями.
    Copy Source | Copy HTML<br/>#in forms.py<br/>class UserForm(forms.ModelForm):<br/>    class Meta:<br/>        model = User<br/>        fields = ["username", "email"]<br/> <br/>class UserProfileForm(forms.ModelForm):<br/>    class Meta:<br/>        model = UserProfile<br/> <br/>#in views.py<br/>def add_user(request):<br/>    ...<br/>    if request.method == "POST":<br/>        uform = UserForm(data = request.POST)<br/>        pform = UserProfileForm(data = request.POST)<br/>        if uform.is_valid() and pform.is_valid():<br/>            user = uform.save()<br/>            profile = pform.save(commit = False)<br/>            profile.user = user<br/>            profile.save()<br/>            ....<br/>    ...<br/> <br/>#in template<br/><form method="post"><br/>    {{ uform.as_p }}<br/>    {{ pform.as_p }}<br/>    <input type="submit" ...><br/></form> <br/>



>

  • Задача. Использование нескольких форм одного типа на одной странице. Например, при создании опроса вам может понадобиться отобразить все вопросы на одной странице.
    Copy Source | Copy HTML<br/>#IN views.py<br/>def survey(request, survey_slug)<br/>    ...<br/>    questions = survey.questions.all()<br/>    question_forms = []<br/>    for question in questions:<br/>        qform = QuestionForm(question=question, prefix = question.slug)<br/>        question_forms.append(qform)<br/>        ...<br/>    if request.method == "POST":<br/>        for question in questions:<br/>            qform = QuestionForm(question=question, prefix = question.slug, data = request.POST)<br/>        #Validate and do save action<br/>        ...<br/>    ... <br/>

    В этой ситуации нам очень поможет атрибут prefix.


Подробнее о формах, разумеется, вы можете прочитать в документации.
Перевод: uswaretech.com
Александр Жебрак @fata1ex
карма
206,0
рейтинг 0,0
Похожие публикации
Самое читаемое Разработка

Комментарии (33)

  • НЛО прилетело и опубликовало эту надпись здесь
    • +8
      Почти все эти плюшки есть в любом веб-фреймворке
    • НЛО прилетело и опубликовало эту надпись здесь
      • НЛО прилетело и опубликовало эту надпись здесь
        • +4
          И, что с того? Всеравно по ряду причин Yii Django не конкурент ни разу. Весовая категория не та.
          • НЛО прилетело и опубликовало эту надпись здесь
          • 0
            Я чего-то недопонимаю, может… Почему вы сравниваете фреймворки у которых разные назначения?
            • 0
              Чёрт пора спать… Yii а не yui, как я прочитал )
            • НЛО прилетело и опубликовало эту надпись здесь
              • +4
                Язык бы еще нормальный, был бы вообще дзен!
              • 0
                Написал же, что ошибся, что вообще не то прочитал — подумал js фреймворк сравнивают с джангой
              • +1
                Да причём тут вообще Yii? Я понимаю ещё Zend с Yii сравнивать, но всё-таки разные же языки?
        • НЛО прилетело и опубликовало эту надпись здесь
  • +2
    Пара советов: save() можно возвращать объект, который сохраняется/создается, иногда это удобно. Для форм User и UserProfile посмотрите как работает инлайн. Для нескольких форм одного типа есть замечательная штука Formset.

    Вообще писателям форм настоятельно рекомендую читать прямо исходник django.forms чтобы знать чего ждать, особенно в части рендринга, и как строить workaround-ы.
    • 0
      Обратите внимание, это перевод. formset здесь специально не затрагивался, это новичковая статья. Ознакомление, так сказать.
      • +2
        Наш человек учится на ошибках… Американец же на компьютерах…

        Из моего опыта я сделал вывод, что книга для чайников способна дать образование чайника. Нужно заведомо учить правильному мышлению, методике поиска и выбора правильного средства решения задачи, правильной структуры данных. Иначе пхпизм не изыдет и все будет делаться в циклах в ручном режиме, а логику сваливать в шаблоны.

        Учитесь сразу высшему пилотажу, я верю в человечество.
        • +1
          Вы очень правы
          • +3
            Знаете, что вас хотел бы попросить…
            Вот глядя в код человек далекий от технологии, которая фактически построена на магии, ничего не поймет. Для него этот декларативный вид формы ничего не скажет.

            Написали бы вы как оно работает все. Логику построения вью. Секция пост или гет, случай is_ajax(). Почему действие направленное на коммит идет постом и зачем нужен редирект. Затем физику процесса как данные пост парсятся QueryDict (его плюсы… как работает getlist)… Потом механизмы валидации… Магический метод clean_. Как работают префиксы. Как прохакать в ините сделать обязательными некоторую группу полей выборочно в зависимости от выбранной радиокнопки, а может и добавлять поля динамически.

            На мое удивление, люди приходящие с пхп или дотнета либо долго отказываются понимать как оно все элегантно работает, либо их охватывает нескрываемое благоговение.

            Раскройте физику процесса — с удовольствием почитаю и покоментирую.
            • 0
              Боюсь, что вы сильно переоцениваете мои способности на данный момент, но я постараюсь отыскать и описать что-нибудь более низкоуровневое.

              Насчет благоговения — может так оно и надо? На то они и языки с высоким уровнем абстракции, что иногда и без понимания физики можно добиться успеха в своём деле. А потом уже начинаешь интересоваться «а как это оно там»

              Упоминание пхп в топике про питон чревато появлением нервного тика :)
  • 0
    Это всё можно найти в документации. А вот реально часто встречающаяся задача, для которой в Django, кажется, нет решения: форма загрузки файлов наподобие веб-интерфейса любого почтовика, то есть с динамическими полями, создаваемыми скриптом.
    • 0
      На чистом Django тут, наверное, не выехать => jquery + django
    • +4
      Со стороны django для поддержки этого есть formset'ы. Django отвечает за серверную часть, а не за клиентскую.
      • 0
        formset'ы поддерживают только повторение формы целиком (http://docs.djangoproject.com/en/dev/topics/forms/formsets/), если я не ошибаюсь. В случае необходимости создания формы для заполнения описания товара + списка магазинов, где он продается по разным ценам приходится использовать Javascript, но это ещё полбеды, отказаться от автоматической админки и на стороне сервера писать достаточно большую ручную валидацию для MyForm.__init__(*args, **kwargs).
        • +1
          Да, форма повторяется целиком. Другое дело, что никто не мешает форму в формсете указать свою, например, такую, которая содержит только нужные поля. Список магазинов с ценами (или чего там) — это таблица, в которой у элементов есть FK на товар, так? Т.е. можно определить свою форму для элементов этой таблицы и использовать формсет (который инлайн- и model-).

          Я может как-то задачу не так понял, или не до конца?

          Да, а для какого-нибудь добавления строк на клиенте в любом случае придется писать js.
    • 0
      Решение есть, легко находится с помощью google.
  • 0
    Есть ли у кого-то решение для формы, для такой структуры моделей:
    объект, дочерний объект (fk), дочерний объект дочернего объекта (fk)?

    Сложность в том, что при сохранении новой серии объектов,
    мы еще не знаем их id, поэтому не можем нормально использовать inline_formsets.
  • 0
    И пост лучше перенести в блог Django, а то сейчас налетят всякие «а вот у нас в PHP...»
    • 0
      Всегда думал, что блог, который называется «Django Framework» и есть блог Django

      Или вы имеете в виду закрыть доступ _не читателям_?
      • 0
        Показалось, что сначала топик был опубликован не в нем. Так всё ok, конечно.
    • 0
      php — это язык программирования, а не диагноз. да синтаксис у python значительно лучше, но это не значит, что нельзя архитектурные подходы подсматривать, тем более у нас открытых аналогов django даже рядом нет. воспетый выше yii — это _первый_ фреймворк на котором можно писать.
      • НЛО прилетело и опубликовало эту надпись здесь
  • +1
    PasswordField в 1.2 уже нету.
    password = forms.CharField(widget=forms.PasswordInput(render_value=False))
  • 0
    Ну, как бы, это все и так очевидно. Зачем статья писалась — непонятно.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.