#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, но эта конкретная часть является краткой и значимой для обоих случаев (т. Е. не шаблонной), так что это приемлемо.