Pull to refresh

Comments 37

Красавэлла! На этой неделе нужна была django-робокасса и django-вконтакте, а тут такое… Приятно читать такое попивая кофеек )
Спасибо тебе добрый человек. Низкий поклон от изучающего Джанго! :)

PS: Если есть еще чем полезным поделиться для новичков — будем только рады (думаю начинающие джангисты меня поддержат)
На этой неделе надо были графики %) Курил pyOFC2.
Но спасибо большое, как новичек в джангопитоне узнал много нового.
> long_description = open('README.rst').read()

Я обычно ещё вот так делаю:

long_description = open('README.rst').read() + open('CHANGES.rst').read()
Еще больше сподвигли меня изучать Python) Завтра иду за книгой)
Начните с онлайн документации с оффсайта.
«request.POST or None» — отличная идея.

Конечно, никуда без django-annoying. А еще полезны:
https://bitbucket.org/jespern/django-piston/overview
https://bitbucket.org/offline/django-publicauth/overview
https://bitbucket.org/jezdez/django-robots/overview
https://bitbucket.org/david/django-storages/overview
https://bitbucket.org/zuber/django-newtagging/overview
https://bitbucket.org/Kizlum/django-lightsearch/overview
https://bitbucket.org/slav0nic/django-flickrstorage/overview
UFO landed and left these words here
@render_to, @ajax_request
Redirect exception
AutoOneToOneField, JSONField
HttpResponseReload

вроде мелочи, а без них потом сложно, если привыкнуть
относительно request.POST or None — если вы захотите делать вручную редактирование модели (то есть автозаполнять форму, основываясь на данных модели) — то в вашем варианте придется переписывать всю логику (в противном случае форма берет все данные из модели и сразу считает себя валидной, до POST-запроса дело не доходит). а в первом варианте будет достаточно заменить ContactForm на ContactForm(model). думаю, здесь именно такая логика, почему джанго в доках дает именно первый, развернутый вариант.

так что этот шорткат имеет смысл только в том случае, если вы совершенно точно хотите один раз отправить форму и навсегда про нее забыть (обращения с контактной формы на сайте, правда, наверное, подходят под это определение).
def project_edit(request, slug):
    project = get_object_or_404(Project, slug=slug)

    form = ProjectEditingForm(request.POST or None, instance=project)
    if form.is_valid():
        project = form.save()
        return redirect(project)

    return direct_to_template('projects/edit.html', {
        'project': project, 
        'form': form
    })


Это рабочий код. С повторным показом формы, правильными начальными данными, ошибками валидации и редиректом на project.get_absolute_url в случае успеха. Никаких ограничений фишка с request.POST or None не накладывает. Поразбирайтесь повнимательнее, как фишка работает.
о, ну чудненько! отдельная передача модели как instance (кстати, туда любые данные ведь можно передать — или только модель? меня смущает синаксис model = form.save()) — совершенно верное решение в плане архитектуры.
Какие есть параметры конструктора у формы, такие и можно данные передать) Я тут пример привел для стандартной джанговской ModelForm:

class ProjectEditingForm(forms.ModelForm):
    class Meta:
        model = Project


параметр конструктора instance для формы все равно ведь нужно дублировать при стандартном подходе в обеих ветках условия: unbound-форма берет из instance начальные значения, bound-форма узнает, куда все сохранять.

В случае с обычной формой (forms.Form) начальные значения (initial) ни на что не влияют, когда форма — bound, поэтому их точно так же можно передавать. Да и вообще, в большинстве случаев по логике требуются одинаковые параметры в конструкторе и там, и там.

Насчет ограничений — про «никаких ограничений» я погорячился) Правильней будет сказать, «можно почти всегда использовать» или «в большинстве случаев упрощает код».

Фишку с request.POST or None не выйдет использовать, если почему-то нужно 2 совсем разные формы в bound и unbound варианте (не знаю, зачем и когда это может понадобиться).

Еще 1 вариант есть, когда она не будет работать — если по совсем пустому POST-запросу нужно что-то делать (именно из-за этого случая проверяют request.method == 'POST', а не просто if request.POST).
Две разные формы могут понадобиться, если в них есть цепь последовательных <select>, а объектов в базе слишком много, чтобы их грузить все сразу. вот и получается, что в unbound ...field.queryset=SomeModel.objects.none()
а в bound
...field.queryset=SomeModel.objects.all()

Может, подскажете какой-нибудь другой подход? Этот не нравится, но ничего другого не придумалось
Блин, это не рабочий код, direct_to_template первым параметром request принимает) Копировал из кода, использующего @render_to, поэтому ошибся.
форма берет все данные из модели и сразу считает себя валидной

Данные для предварительного заполнения скармливаются форме через параметр initial — тогда она не будет bound, и ещё не будет почём зря выполняться процедура валидации.

form = MyForm(request.POST or None, initial = {'qwe': 'asd', 'foo': 'bar'})

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

return direct_to_template( ... )
Если после этого сразу непонятно, как работает код ContactForm(request.POST or None), то разберитесь в качестве упражнения, расписывать не буду. Это простая и полезная идиома.

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

если контоллерная логика такая же простая как в примере — используйте create-update-delete-generic-views — будет еще короче. :)

ваш же код это грязный хак, а не «полезная идеома», т.к. в корне меняет семантику проверки. проверяя request.POST, вы делаете допущение, что при POST запросе (request.method == 'POST'), тело HTTP сообщения (request.POST) не может быть пустым. это так:

допустим, форма предлагает выбрать один из вариантов контактов (список полей type=«radio»). если не выбрать ни одного и сделать POST, то тело POST запроса будет пустым и после перезагрузки страницы посетитель увидит снова оригинальную форму, вместо формы с сообщением об ошибке.

т.е. для какой-то конкретной формы, при данных условиях это может работать. но в общем случае — нет. вот поэтому
В документации и обучающих примерах по django обычно пишут вьюхи вот так
Если Вы все делаете «по науке» и используете защиту от CSRF, то в каждой форме есть еще CSRF-токен, и запрос POST не будет пустым никогда. Так что это не грязный хак и это будет работать в общем случае.

Если же защита от CSRF не используется, то у сайта есть проблемы посерьезные, чем отсутствие ошибок валидации в тех (редких?) случаях, когда у кнопки submit не прописан name.
какие именно проблемы? я пока вижу только одну проблему — в пресловутом сниппете нужно учитывать массу плохо контролируемых нюансов внешней среды (csrf, содержание html-формы, ajax, возможности http-клиента). а это чревато появлением ошибок на этапе развития проекта (отключив csrf вы рискуете уронить бизнес-логику).

учите писать правильно, а не экономить строчки кода. даже если «все под контролем». :)

csrf же это не «наука», а инженерное решение, которое имеет вполне конкретную область применимости: работает только для html-форм, требует поддержку кук от браузера. (кстати, именно из-за привязанности к кукам, csrf не обеспечивает защиту на под-доменах). полезная штука, в своей нише, и не нужная во всех остальных случаях. не понимаю, ради чего его вытащили из contrib в core и переворотили четверть джанги. )

«общий случай», где используются Forms (кстати, намеренно абстрагированные от request) — гораздо шире. forms часто используются для обработки данных, полученных через ajax — здесь csrf бесполезен, в принципе (кстати, и не обрабатывается django); с помощью forms удобно делать валидацию при создании api сайта. здесь csrf так же не нужен…
CSRF-защиту включили по умолчанию, чтобы не было ситуаций, как в mail.ru) Если защита от чего-то отключена по умолчанию, то это не защита — ей никто не будет пользоваться, а если и будет, то нет никаких гарантий, что пользоваться будут последовательно. Поэтому imho это правильное решение, так же, как и автоэкранирование в шаблонах, например. Автоэкранирование — это точно такое же инженерное решение, которое полезно в своей нише и не нужно в остальных случаях, но это большой плюс, что оно включено по умолчанию. Это снижает ментальную нагрузку с программиста — ему не нужно думать, как сделать приложение защищенным, ему нужно думать в тех случаях, когда защиту нужно по каким-то причинам отключать.

Вы назвали csrf, ajax, содержание форм и возможности http-клиента «неконтролируемыми особенностями внешней среды». Я считаю вполне позволительным и допустимым то, что csrf (как и автоэкранирование в шаблонах) подразумевается включенным (а, значит, содержание форм и возможности http клиентов тоже сразу автоматически соответствуют условиям, при которых способ работает).

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

— отключена защита от CSRF
— обработчики ajax-запросов, которые должны выполнять какие-то действия при пустых запросах, и при этом занимаются обработкой как POST, так и GET
— обработчики api-запросов, которые должны что-то делать при пустых запросах и при этом занимаются обработкой как POST, так и GET
— у пользователя не работают cookie (а, значит, авторизация и тд у него тоже не работает)

Насчет ajax api и стороннего API. В случае ajax и стороннего API unbound-формы ведь не используются обычно. Там во вьюхах и так нет 2 веток условий c созданием bound или unbound формы, поэтому там и фишка эта бессмысленна. unbound-формы нужны для показа пользователю HTML с начальными значениями. Могу представить, как формы можно использовать для отдачи начальных значений через тот же json, но это опять же, imho, очень маргинальный вариант.

Я ничего не упускаю? Тут единственный интересный вариант — это использование одной и той же вьюхи как для ajax, так и для обычной работы в ситуации, когда на клиенте js собирает запрос для отправки «вручную», а не подхватывает просто данные из формы автоматически (так csrf-токен подхватится), и при этом вообще не шлет ничего для полей, у которых пустое значение (так кто-то пишет в реальной жизни? как это вообще написать, для каждого поля ставить проверку?)

Мне кажется, что в большинстве случаев способ работает и полезен, поэтому его рекомендую, но предлагаю разобраться, как он работает. Конечно, нужно понимать, когда его можно использовать, а когда нет, тут Вы абсолютно правы.

Конечная цель всего этого — упростить поддержку кода. И мы тут, очевидно, разошлись в оценках) Может, из-за того, что код разный имеем в виду. Мне кажется, что из-за полполпроцента случаев (которые, imho, довольно явно отличаются от обычных, и их нетрудно заметить) усложнять код почти всех вьюх (вводить дополнительный уровень вложенности и дублировать параметры конструктора) неправильно.
автоэкранирование в шаблонах, например.

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

а view-функции из другой области. они предназначены для обработки http запросов. поэтому было бы просто замечательно, если бы django предоставляла аналогичные средства, так же упрощающие поддержку семантики этого протокола (в понимании rfc2616)…

в реальности все совершенно иначе. view-фукнция является точкой входа для обработки любого запроса к данному URI. и вся мудреная логика формирования ответа целиком лежит на совести пользовательского кода (в том числе соответствие ответа типу сообщения GET, POST, PUT, ..). заморчки с диспетчеризацией данных для формы, по типу запроса, это прямое следствие джанговской архитектуры. вот решение этой задачи (на что сейчас все прикладники кладут, по понятным причинам) было бы приятно видеть на уровне фреймворка. сравните:

# первая попавшаяся под руку хрень -)
$ curl -svX PUT msdn.microsoft.com/ru-ru/default.aspx > /dev/null
(HTTP/1.1 411 Length Required)

# django
$ curl -svX PUT docs.djangoproject.com/en/1.2/ > /dev/null
(HTTP/1.0 200 OK)

впрочем, не так давно, появилась определенная надежда на class-based views, ожидаемые в следующем релизе. они могли бы предоставлять нужный функционал по умолчанию… (правда это нехилый такой лисапед — классная реализация уже давно есть в django-piston).

а csrf это вообще фича уровня протокола приложения. в contrib ей самое место. :)

в принципе, я понял Вашу точку зрения, но принять не могу. :)
Спасибо за разъяснение direct_to_template! а то меня эти контексты замучали)

> form = ContactForm(request.POST or None)
с формами я к такому же решению пришел
Могу посоветовать еще недавно появившийся полезный сайтик с расширениями django: djangopackages.com/. Особенно там нравится колонка «Using This»
я кстати для ещё большего сокращения пользую annoying pypi.python.org/pypi/django-annoying/0.7.4
и получается что-то вроде
@render_to('contact.html')
def contact(request):
    form = ContactForm(request.POST or None)
    if form.is_valid():
        # обрабатываем данные. Например, делаем form.save()
        # ...
        return redirect('url_name', param1=value)
    return {'form': form} 

Я согласен с Иваном, в том плане, что @render_to скорее вреден, чем полезен. Хотя в django и есть аналогичная безделушка, в api шаблонизатора simple_tag

Но в общем случае, декораторы часто используются в качестве адаптеров, поэтому отличие сигнатуры декорируемой функции это не аргумент. :)
По первому пункту, думаю, не стоит так делать.
«Explicit is better than implicit.»
«Readability counts.»
По-моему, в данном случае магии нет и код покороче более читабельный.
А direct_to_template разве не request первым аргументом принимает?
Тоже хотел спросить, без request первым параметром не работает :) Или это можно как-то обойти?
Ограничений по количеству обращений у google charts image api нет, там просят только связаться с ними, если > 200тыс обращений в день будет, чтобы они нагрузку распределили. Так что такие графики можно не только в админке использовать.


ещё бы, зачем им упускать халявный источник структурированной пользователем информации.
подскажи как яндекс.карты в админку запихать?
Only those users with full accounts are able to leave comments. Log in, please.