Лучший шаблон для логики форм Django?

#django #django-forms

#django #django-forms

Вопрос:

Таким образом, стандартный шаблон для логики представления для форм Django:

 def contact(request):
    if request.method == 'POST': # If the form has been submitted...
        form = ContactForm(request.POST) # A form bound to the POST data
        if form.is_valid(): # All validation rules pass
            # Process the data in form.cleaned_data
            # ...
            return HttpResponseRedirect('/thanks/') # Redirect after POST
    else:
        form = ContactForm() # An unbound form

    return render_to_response('contact.html', {
        'form': form,
    })
  

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

Может ли кто-нибудь поделиться своими собственными более чистыми подходами, которые позволяют избежать вложенных IF и логики, которая зависит от аварийных случаев?

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

Ответ №1:

Одним из включенных представлений на основе классов является FormView (documentation). Два основных метода, которые вас интересуют, — это form_valid и form_invalid .

 from django.views.generic import FormView

from myapp.forms import MyForm

class MyView(FormView):

    template_name = 'edit_something.html'
    form_class = MyForm
    success_url = '/success/' # you should use `reverse`, but let's stay focused.

    def form_valid(self, form):
        """
        This is what's called when the form is valid.
        """

        return super(MyView, self).form_valid(form)

    def form_invalid(self, form):
        """
        This is what's called when the form is invalid.
        """

        return self.render_to_response(self.get_context_data(form=form))
  

В качестве альтернативы вы можете переопределить методы post , get или put и обрабатывать форму в соответствии с каждым типом запроса.

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

1. Я не думаю, что мой конкретный случай слишком точно подходит для любого из существующих классов, но чтение их исходного кода станет отличным источником вдохновения для лучшей структуры…

2. Каков ваш конкретный случай? Я нахожу, что предоставленные классы предлагают многое, и хитрость заключается в том, чтобы смешивать и сопоставлять различные из них, чтобы получить желаемый результат, а создание любых расширений минимально / просто.

3. Использование reverse в определении success_url вызвало у меня некоторые проблемы, особенно когда views.py импортируется до определения urlconf

4. Вместо того, чтобы делать это напрямую returning HttpResponseRedirect() в вашей версии, вам следует вместо этого использовать return super(MyView, self).form_valid(form) в конце вашего form_valid() и form_invalid() . Это позволит выполнять ваши операции с данными до того, как вы также запустите над ними версию form_valid() Django (которая ничего не делает, кроме возврата перенаправления). Таким образом, если Django когда-либо изменит то, что происходит внутри form_valid(), вы не удалили это полностью.

5. @synic: используйте reverse_lazy() вместо reverse().

Ответ №2:

Это самый короткий подход, который я нашел:

 def contact(request):
    # if it's POST request it'll have data else it'll  be unbound
    form = ContactForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        # Process the data in form.cleaned_data
        # ...
        return HttpResponseRedirect('/thanks/') # Redirect after POST

    return render_to_response('contact.html', { 'form': form, })
  

конечно, если вам не нравится вложенность, если бы вы могли попробовать перевернуть логику:

 def contact(request):
    template = 'contact.html'
    if request.method != 'POST': # GET return:
        return render_to_response(template, {'form': ContactForm()})

    form = ContactForm(request.POST)
    if not form.is_valid(): # FAIL return:
        return render_to_response(template, {'form': form})

    # here logic if user posted form and it's clean:

    return HttpResponseRedirect('/thanks/')
  

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

1. поддержано — я думаю, ваш первый пример — самый приятный способ создания форм в представлении Django. Обратите внимание, что вам не нужна if request.method == 'POST' проверка… форма не может быть допустимой, если вы передали None конструктору, это то же самое, что делать ContactForm()

Ответ №3:

Для сложных сценариев вам действительно следует рассмотреть возможность переопределения различных методов вашего класса form, которые участвуют в процессе проверки / очистки. BaseForm Класс определяет _post_clean() метод без поведения, который, в частности, предназначен для переопределения. Переопределение _clean() должным образом также довольно прямолинейно, вы могли бы сделать и это, если бы хотели что-то сделать до clean() вызова.

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

Ответ №4:

Вот что я пытался до сих пор, чтобы сделать просмотр функций более чистым:

  • Отдельные функции просмотра для GET / POST
  • Очевидно, что логика is_valid в форме как состояние в предыдущем сообщении.
  • Переопределение метода model save(), чтобы сделать часть form.is_valid() более чистой (вы можете скрыть другие связанные объекты, созданные внутри одного save())
  • Переместить общие части кода view-funcs в отдельную функцию.
  • Также, если ваша часть GET не загружена процессором или базой данных, вы можете сделать это выше, если req == POST .. другими словами, делайте это всегда.. Я, например, часто делаю: ctx[‘form’] = ContactForm() вверху .. и затем снова ContactForm (req.POST) внутри, если req == POST

НО в django 1.3 теперь есть представление на основе классов.. И это дает большую возможность расширить представление.. для создания миксинов.. не записывать сохранение снова и снова.
Я лично хочу попробовать и попробую написать материал django в новом стиле с помощью классов.

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

1. Мне было бы интересно увидеть версию стандартной логики обработки форм на основе классов. Также — не могли бы вы подробнее остановиться на своем третьем пункте?

2. Часто в form.is_valid(): используется что-то вроде этого: my_obj = form.save( commit = False ); my_obj.some_attr = datetime.now(); OtherObj = OtherModel( my_obj = my_obj_obj, … ); Если это обычная операция.. или просто для красоты вы можете скрыть все это в MyModel.save() или MyModel.custom_save()

Ответ №5:

Основным источником сложности в этих случаях является обработка GETs и POSTs в одной и той же функции просмотра. Разделите логику для каждого метода на его собственное представление, и все станет яснее.

Единственным дублированием будет рендеринг формы html, но эта конкретная часть является краткой и значимой для обоих случаев (т. Е. не шаблонной), так что это приемлемо.