LINUX.ORG.RU

Экспертам по Django

 , ,


0

1

Все доброго времени суток.

Заранее извиняюсь за пафосный заголовок и, возможно, ламерский вопрос.

Проблема связана с обработкой форм при использовании Django.

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

Для одной страницы все просто: создаем экземпляр класса Form, описываем необходимые нам поля, виджеты; во вьюху, рендерящую главную страницу добавляем контекст для рендеринга нашей формы входа а также код валидации этой формы и выборки данных из нее.

def index(request):
    if request.method == "POST":
        form = LoginForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            user = authenticate(username=username, password=password)
            if user is not None:
                if user.is_active:
                    login(request, user)
            else:
                return HttpResponse(u"Ошибка авторизации!")
    else:
        form = LoginForm()
    return render(request, "pages/index.djhtml", {"form": form})

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

Может вообще забить на django'вский механизм обработки форм и просто обрабатывать данные из POST? Но чувствую, что не тру это.

Кто что подскажет?


Ответ на: комментарий от vurdalak

Хм. Сомневаюсь, что это хорошая идея. Я использую классические function-based views. Следовательно, добавить экшена ко вьюхам можно только с помощью декорирования. А это попахивает костылями.

djnoob
() автор топика
Ответ на: комментарий от djnoob

Я использую классические function-based views.

Ты хочешь избежать дублирования функционала, но при этом не хочешь вызывать один и тот же метод из всех мест. Это решается наследованием, но ООП ты тоже не хочешь.

vurdalak ★★★★★
()
Ответ на: комментарий от vurdalak

Да. В проекте не используются CBV.

Решил просто вынести функционал обработки формы ввода в отдельную функцию, принимающую в качестве аргументов - request а возвращающую объект from.

Затем просто добавляю результат в контекст каждой и вьюх. Это сильный костыль)?

djnoob
() автор топика
Ответ на: збс упрлс от heilkitty

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

djnoob
() автор топика

появляется в результате нажатия на некоторую кнопку (скажем, в хедере сайта), а хедер наследуется всеми страницами.

Раз у тебя хедер наследуется всеми страницами, то все вью, которые рендерят эти страницы, должны уметь рендерить форму и знать о ней (вставлять csrf токен, к примеру, в форму). Следовательно, каждая вьюшка должна иметь код создания формы. А это достигается наследованием. Класс вью для этого и созданы. Вообще, конечно, такая архитектура - в любой странице форма - несколько гемморна и, с моей точки зрения, не правильна. Желательно делать отдельную страницу для логина, отдельную для поиска, отдельную для ошибок и т.д. Сам я тоже так кодил - на одной странице были и поиск и логин, но лучше не смешивать (а если смешивать, то давать имя сабмит кнопке и/или разводить форму в разные url).

menangen ★★★★★
()
Ответ на: комментарий от menangen

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

0) Создаем экземпляр класса Form:

class LoginForm(forms.Form):
    username = forms.CharField(
        label=u'Имя пользователя', min_length=3, max_length=32,
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    password = forms.CharField(
        label=u'Пароль', min_length=6, max_length=32,
        widget=forms.PasswordInput(attrs={'class': 'form-control'}),
    )

1) Определяем шаблон формы:

<form action="{% url "accounts:login" %}?next={{ current_url }}" method="POST">
  {% csrf_token %}
  <div class="form-group">
    {{ form.username.errors }}
    <label for="id_username">Имя пользователя:</label>
    {{ form.username }}
  </div>
  <div class="form-group">
    {{ form.password.errors }}
    <label for="id_password">Пароль:</label>
    {{ form.password }}
  </div>
  <div class="checkbox">
    <label>
      <input type="checkbox"> Запомнить
    </label>
  </div>
  <button type="submit" class="btn btn-default">Войти</button>
</form>

2) Ренедрим фому как inclusion_tag:

def login_form(request_path):
    return {"form": LoginForm(), "current_url": request_path}

register.inclusion_tag("units/forms/form_signin.djhtml")(login_form)

3) Обрабатываем форму

def auth_login(request):
    """
    :param request:
    :return:
    """
    if request.method == "POST":
        form = LoginForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            user = authenticate(username=username, password=password)
            if user is not None:
                if user.is_active:
                    login(request, user)
                    return HttpResponseRedirect(request.GET.get("next"))
            else:
                return HttpResponse(u"Ошибка авторизации!")
    return HttpResponseRedirect(request.GET.get("next"))

Но, как вы понимаете, валидация работать не будет(. К сожалению, я не нашел адекватного способа базовыми средствами решить данную проблему. Буду писать отдельные шаблоны - тогда все встанет на свои места).

djnoob
() автор топика
Последнее исправление: djnoob (всего исправлений: 2)
Ответ на: комментарий от djnoob

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

zz ★★★★
()
Ответ на: комментарий от zz

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

djnoob
() автор топика

если форма находится в модальном окне

то у клиента гарантировано есть Javascript. Следовательно, можно делать AJAX-формы, которые будет валидировать Django Rest Framework и код обработки формы будет ровно в одном месте. В чём проблемы?

x3al ★★★★★
()
Ответ на: комментарий от x3al

Впрочем, и с классическими формами: action формы — на единственный вид с проверкой, который по успешной валидации отправляет назад. По неуспешной — можно придумать, что делать, например, добавлять в GET URL возврата, если осторожно (можно случайно сделать себе XSS).

x3al ★★★★★
()

Для нежелающих в ООП и контекстные процессоры

# функция для формирования общего контекста всех обычных вьюх
def common_context():
    context = {
        ...
        'login_form': LoginForm()
    }
    return context

# обычная вьюха, перед ответом контекст дополняется общим
dev some_view(request):
    ...
    context = context.update(common_context())
    return render(request, template, context)

А post-запрос на action url формы обрабатывать отдельной вьюхой

shatsky ★★
()
Ответ на: комментарий от shatsky

Для нежелающих в ООП и контекстные процессоры

Не учи детей плохому.

heilkitty ★★
()
Ответ на: комментарий от heilkitty

Правильнее, наверное, вот так: http://stackoverflow.com/a/3124885

На самой странице рендерить форму через LoginForm не надо, этим пусть занимается login view, на остальных страницах достаточно прописать html.

heilkitty ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.