Nomad22 сентября 2020 г. 10:39

MyForm(forms.Form): - непонятка

Django, forms

привет

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

class MySettingsChangeView(LoginRequiredMixin, TemplateView):
    template_name = 'accounts/registration/settings_pass_status_change.html'

    def get(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)
        return render(request, self.template_name, context)

    def get_context_data(self, **kwargs):
        extra_context = None
        context = super().get_context_data(**kwargs)
        context.update({
            'pass_form': MyPasswordChangeForm,
            'status_form': MyStatusChangeForm,
            **(self.extra_context or {})
        })
        return context

    def post(self, request):
        post_data = request.POST or None
        if 'pass' in self.request.POST:
            print(" srabotal pass")
            cont = self.request.POST
        elif 'status' in self.request.POST:
            if not request.user.is_active or not request.user.phone_active:
                return redirect('/')
            else:
                cont = self.request.POST
                try:
                    new_status = cont['user_status']
                except MultiValueDictKeyError:
                    new_status = request.user.user_status
                curent_user = request.user.pk

                object = get_object_or_404(User, pk=curent_user)

                object.user_status = new_status
                object.save()
        return redirect('/dashboardc/home/')

вот формы:

class MyPasswordChangeForm(PasswordChangeForm):
    pass

class MyStatusChangeForm(forms.Form):
    pass

форма MyStatusChangeForm состоит из 2 радио + сабмит

все работает гуд НО вся логика находится в представлении
я пытался в форме MyStatusChangeForm переопределить пару методов и для того чтобы понять что к ним стучатся написал по одному принту в теле НО такое осушение что все они в игноре, вот что я добавлял в класс формы:

def clean(self):
    print("in clean(self)")
    cleaned_data = super().clean()
    return cleaned_data

def is_valid(self):
    print("in is_valid(self)")

def clean_recipients(self):
    print("in clean_recipients(self)")
    data = super(MyStatusChangeForm, self).clean()
    return data

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

но вопрос в том почему эти методы не срабатывают??

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.
13

1) что в шаблоне?
2) в определении контекста:

    def get_context_data(self, **kwargs):
        extra_context = None
        context = super().get_context_data(**kwargs)
        context.update({
            'pass_form': MyPasswordChangeForm,  # формы надо инициализировать
            'status_form': MyStatusChangeForm,  # формы надо инициализировать
            **(self.extra_context or {})  # это тут не нужно - всегда None
        })
        return context

3) в методе post во вьюхе нет никакого использования форм, вот ничего и не происходит
4) зачем напрямую работать с данными из request.POST если можно в форме определить метод "save()" и дергать его или, на худой конец, работать с form.cleaned_data
5) я таки рекомендую разделить это на 2 вьюхи, чтобы каждая их них обрабатывала свою форму - меньше путаницы будет

  • 23 сентября 2020 г. 5:20

вот шаблон: https://dpaste.org/PhjO

1) Почитайте про формы - многое, что у вас в шалобне определено, можно решить с помощью определения класса формы:

class MyPasswordChangeForm(forms.Form):
    old_password = forms.CharField(widget=forms.PasswordInput())
    new_password1 = forms.CharField(widget=forms.PasswordInput(), help_text="
<ul>
<li>YourYour password can’t be too similar to your other personal information.</li>
<li>Your password must contain at least 8 characters.</li>
<li>Your password can’t be a commonly used password.</li>
<li>Your password can’t be entirely numeric.</li>
</ul>", title="New password")
    new_password2 = forms.CharField(widget=forms.PasswordInput(), title="Repeat new Password")

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user', None)
        super().__init__(*args, **kwargs)

    def clean(self):
        cleaned_data = super().clean()
        if cleaned_data['new_password1'] != cleaned_data['new_password2']:
            self.add_error('new_password2', 'New password confirmation is not same to new password')
        if self.user and not self.user.check_password(cleaned_data['old_password']:
            self.add_error('old_password', 'Current password is not valid')
        return cleaned_data

а работа с формой будет:

class MySettingsChangeView(views.View):
    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        context.update({
            'pass_form': MyPasswordChangeForm(self.request.POST or None),
            ...
        })
        return context

2) В шаблоне не используются формы из контекста. как вариант, что-то такое. Если используете bootstap - можно посмотреть на django-fancy-forms (или как-то так)

...
<form>
    {% csrf_token %}    
    {{ pass_form.non_fields_errors }}
    {{ pass_form.old_password }}
    {{ pass_form.new_password1 }}
    {{ pass_form.new_password2 }}
    <input type="submit">
</form>
...

3) во вьюхе надо бы как-то с формой взаимодействовать:

class MySettingsChangeView(views.View):
    ...
    def post(self, *args, **kwargs):
        password_form = MyPasswordChangeForm(request.POST or None, user=user)
        if password_form.is_valid():
            self.request.user.set_password(password_form.cleaned_data['new_password1'])
            self.request.user.save()
            return redirect(...)
        return render(
            self.request,
            ...,
            self.get_context_data()
        )

выше пример, на изусть не помню - надо почитать доку

скажите пожалуйста а в чем смысл конструктора из формы:

def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user', None)
        super().__init__(*args, **kwargs)

просто в конструкторе self.user = None

  • 24 сентября 2020 г. 4:33
  • (ред.)

последний вопрос снимаю
выдавал ошибку НО надо было в методе пост представления получить данные из формы вот так:

password_form = MyPasswordChangeForm1(user=request.user, data=request.POST or None)

т.е явно передать user=request.user

и все заработало

единственный вопрос остался, вы комент сделали, что в методе get_context_data из представления при добавлении форм в контекст, надо "# формы надо инициализировать"
вы имеите в виду это:

context.update({
'pass_form': MyPasswordChangeForm1(self.request.POST or None),
'status_form': MyStatusChangeForm1(self.request.POST or None)
})

если так, то нужна ли инициализация форм в контексте если в методе post того же представления рабораю с формой вот так:

password_form = MyPasswordChangeForm1(user=request.user, data=request.POST or None)
......

  • 24 сентября 2020 г. 6:42
  • (ред.)
  • Ответ был помечен как решение.

лично я бы сделал так:
в get_context_data я бы генерил основной контекст, в а методах get и post я бы уже добавлял туда формы, правильно инициализированные.
Т.е.

class MySettingsChangeView(views.View):
    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        context.update({
            'pass_form': None,
            'status_form': None
        })
        return context

    def get(self, *args, **kwargs):
        ...
        context = self.get_context_data()
        context['pass_form'] = MyPasswordChangeForm(user=self.request.user)
        context['status_form'] = MyStatusChangeForm()
        return render(self.request, '<template>', context)

    def post(self, *args, **kwargs):
        password_form = MyPasswordChangeForm(self.request.POST or None, user=self.request.user)
        status_form = MyStatusChangeForm(self.request.POST or None)
        if password_form.is_valid():
            ...
        elif status_form.is_valid():
            ...
        context = self.get_context_data()
        context['pass_form'] = password_form
        context['status_form'] = status_form
        return render(self.request, '<template>', context)

таким образом ошибки в форме правильно перенесутся в контектс и правильно можно будет их отобразить

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

вот мое решение:

# views.py

class MySettingsChangeView1(LoginRequiredMixin, TemplateView):
    template_name = 'accounts/registration/settings_pass_status_change.html'

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        context.update({
            'pass_form': None,
            'status_form': None
        })
        return context

    def get(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)
        context['pass_form'] = MyPasswordChangeForm1(user=self.request.user)
        context['status_form'] = MyStatusChangeForm1(user=self.request.user)
        return render(request, self.template_name, context)

    def post(self, request, *args, **kwargs):
        if 'pass' in self.request.POST:
            password_form = MyPasswordChangeForm1(request.POST or None, user=request.user)
            if password_form.is_valid():
                print("Password change")
                self.request.user.set_password(password_form.data['new_password1'])
                self.request.user.save()
                update_session_auth_hash(request, password_form.user)
                return redirect('/')
            else:
                context = self.get_context_data(**kwargs)
                context['pass_form'] = password_form
                context['status_form'] = MyStatusChangeForm1(user=request.user)
                return render(self.request, self.template_name, context)

        if 'status' in self.request.POST:
            status_form = MyStatusChangeForm1(request.POST or None, user=request.user)
            if status_form and status_form.is_valid():
                print("Status change")
                if not request.user.is_active or not request.user.phone_active:
                    return redirect('/')
                else:
                    cont = self.request.POST
                    try:
                        new_status = cont['user_status']
                    except MultiValueDictKeyError:
                        new_status = request.user.user_status
                    curent_user = request.user.pk
                    object = get_object_or_404(User, pk=curent_user)
                    object.user_status = new_status
                    object.save()
                return redirect('/')
            else:
                context = self.get_context_data(**kwargs)
                context['status_form'] = status_form
                context['pass_form'] = MyPasswordChangeForm1(user=request.user)
                return render(self.request, self.template_name, context)

        context = self.get_context_data()
        return render(self.request, self.template_name, context)
# forms.py

class MyPasswordChangeForm1(forms.Form):

    old_password = forms.CharField(strip=False, widget=forms.PasswordInput())
    new_password1 = forms.CharField(strip=False, widget=forms.PasswordInput())
    new_password2 = forms.CharField(strip=False, widget=forms.PasswordInput())

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user', None)
        super().__init__(*args, **kwargs)

    def clean_old_password(self):
        cleaned_data = super().clean()
        if cleaned_data:
            if self.user and not self.user.check_password(cleaned_data['old_password']):
                self.add_error('old_password', 'Current password is not valid')
        return cleaned_data

    def clean(self):
        cleaned_data = super().clean()
        if cleaned_data:
            if ('new_password1' in cleaned_data) and ('new_password2' in cleaned_data):
                if cleaned_data['new_password1'] != cleaned_data['new_password2']:
                    self.add_error('new_password2', 'New password confirmation is not same to new password')
            else:
                print("nu-s valori pu parole")
                # self.add_error('new_password1', 'New password must be')
                raise ValidationError("Please enter data", code='invalid')
        else:
            raise ValidationError("Please enter data", code='invalid')
        return cleaned_data

    def is_valid(self):
        return self.is_bound and not self.errors


class MyStatusChangeForm1(forms.Form):

    user_status = forms.CharField()

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user', None)
        super().__init__(*args, **kwargs)

    def clean(self):
        cleaned_data = super().clean()
        if cleaned_data:
            if not ('user_status' in cleaned_data):
                self.add_error('user_status', 'need check status')
        else:
            raise ValidationError("Please enter data", code='invalid')

        return cleaned_data

    def is_valid(self):
        return self.is_bound and not self.errors

баг в следующем: на хтмл странице 2 формы MyPasswordChangeForm1 , MyStatusChangeForm1
каждая из этих форм имеит свою собственную кнопку сaбмит

если я нажимаю на кнопку сабмит у любой формы НО без того чтобы заполнить форму (то есть формы невалидны) то появляются сообшения об ошибках над обеими формами а по моему мнению сообшение об ошибках должно появлятся только у той формы чей сабмит был нажат

может кто подсказать или направить как решить?

ну вообще, все логично.

Но чтобы они обрабатывались по отдельности - как я уже говорил в первом комменте, лучше было бы сделать 2 отдельные вьюхи. А у каждой формы в аттрибуте action должна быть своя вьюха. так получится и меньше сложность кода и тестируемость и все вот это

тоесть, получилось бы 3 вьюхи:
1. просто рендерит страницу + обратаывает только GET запрос
2. обрабатывает первую форму(вторую форму тоже рендерит без данных) + обрабатывает только POST запрос
3. обрабатывает вторую форму(первую рендерит без данных) + обрабатывает только POST запрос

кстати, не стоит переопределять метод is_valid() в формах

ведь метод is_valid() это метод форм

или вы намекаете что is_valid() можно в представлении переопределить? или его вообше не переопределять?

class MyStatusChangeForm1(forms.Form):

    user_status = forms.CharField()

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user', None)
        super().__init__(*args, **kwargs)

    def clean(self):
        cleaned_data = super().clean()
        if cleaned_data:
            if not ('user_status' in cleaned_data):
                self.add_error('user_status', 'need check status')
        else:
            raise ValidationError("Please enter data", code='invalid')

        return cleaned_data

    def is_valid(self):  # <---- This one
        return self.is_bound and not self.errors

По документации, для дополнительных проверок надо использовать метод clean, is_valid - лучше не трогать

  • 1 октября 2020 г. 5:04

то есть вы советуете создать 2 пары:

1 пара => первая форма + отдельное представление (обработчик) для данной формы
2 пара => вторая форма + отдельное представление (обработчик) для данной формы

плюс к этому, третье представление с методом get куда я просто в context добавляю эти 2 формы

а уже в этих формах в атрибуте action указывать url путь из urls.py к соответствующему представлению которое отвечает за обработку данной формы

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

ну поставить заглушку на метод get или редирект=)
если очень упрощенно и почти псевдокодом:

class ChangeProfileView(TemplateView):
    template = 'accounts/registration/settings_pass_status_change.html'

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        context.update({
            'pass_form': MyPasswordChangeForm(),
            'status_form': MyStatusChangeForm(),
        })

class ProcessPasswordForm(ChangeProfileView):
    def get(self, *args, **kwargs):
        return redirect(reverse('<какой там урл у ChangeProfileView>'))

    def post(self, *args, **kwargs):
        form = MyPasswordChangeForm(self.request.POST, user=self.request.user)
        if form.is_valid():
            form.save()
            return redirect(reverse('<какой там урл у ChangeProfileView>'))
        context = self.get_context_data()
        context.update({'pass_form': form})
        return render(self.request, self.template, context)

# для MyStatusChangeForm все аналогично

  • 1 октября 2020 г. 5:22

понятно
спасибо

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
Timeweb

Позвольте мне порекомендовать вам отличный хостинг, на котором расположен EVILEG.

В течение многих лет Timeweb доказывает свою стабильность.

Для проектов на Django рекомендую VDS хостинг

Посмотреть Хостинг
ГИ

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:60баллов,
  • Очки рейтинга-1
t

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:70баллов,
  • Очки рейтинга1
ЛД

C++ - Тест 003. Условия и циклы

  • Результат:57баллов,
  • Очки рейтинга-2
Последние комментарии
ВР

QML - Урок 016. База данных SQLite и работа с ней в QML Qt

Помогите, пожалуйста. У меня похожая задача, но я в qml слой долен передать не чистый запрос, а со сложной обработкой, поэтому у меня в С++ слое есть иерархия классов, которая имитирует бд и зап…
e
  • eviza
  • 16 ноября 2020 г. 4:32

Qt/C++ - Урок 083. Создание динамической библиотеки и подключение её в другой проект

здравствуйте! при компиляции библиотеки выскакивает окно особая программа( не удалось найти программу, укажите путь к ней), и в папке debug создается файл .dll, а .lib нет. подскажите…
ИБ

Шифрование данных в Qt алгоритмом RSA c открытым и закрытым ключами без привязки к OpenSSL

Библиотека подключилась нормально, только на выводе из первого примера выходит пустое сообщение, вместо "test message" просто "". Никаких ошибок не выдает.
VS

Qt WinAPI - Урок 002. Как сделать win инсталлятор для Qt-приложения?

Можно ли как-то однозначно (не проверяя) выяснить, запустится ли программа в windows 7? И как быть с разрядностью уже и просто в w10 (32\64)?
ДТ

Django - Урок 036. Как добавить аутентификацию через социальные сети. ВКонтакте

Возможно, автор прочитает. Делал авторизацию с помощью ВК по книге Дронова. Выдает ошибку "Backend not found". Стал гуглить, нашел вашу статью, вроде почти то же самое (оно и понятно, документа…
Сейчас обсуждают на форуме

Разный масштаб в формах и при запуске

Сврестайте все в один лэйаут (Выбирите окно и нажмити сверху на голубой квадратик из 9 голубфх квадратиков)

Обращение к ячейке таблицы

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

Отправка JSON

postData.addQueryItem("data", doc.toJson(QJsonDocument::Compact).toStdString().c_str()); а почему так? попробуйте эту строку убрать, а эту запись: { "Array": [ t…
ДК

QTableView не становится в редактирование

балин, вот я, конечно, "молодец". минус 5 часов, из-за того, что не поставил в модели флаги. Qt::ItemFlags UserModel::flags(const QModelIndex &index) const{ Qt::ItemFlags flags = Tre…

QSqlTableModel - Как добавить картинки в таблицу, чтобы они отражались в диалоговом окне, но не были частью модели

Ну тогда в этом столбце указывайте пути на несколько картинок
О нас
Услуги
© EVILEG 2015-2020
Рекомендует хостинг TIMEWEB