Nomad
Sept. 22, 2020, 8:39 p.m.

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

Django, forms

привет

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

  1. class MySettingsChangeView(LoginRequiredMixin, TemplateView):
  2. template_name = 'accounts/registration/settings_pass_status_change.html'
  3.  
  4. def get(self, request, *args, **kwargs):
  5. context = self.get_context_data(**kwargs)
  6. return render(request, self.template_name, context)
  7.  
  8. def get_context_data(self, **kwargs):
  9. extra_context = None
  10. context = super().get_context_data(**kwargs)
  11. context.update({
  12. 'pass_form': MyPasswordChangeForm,
  13. 'status_form': MyStatusChangeForm,
  14. **(self.extra_context or {})
  15. })
  16. return context
  17.  
  18. def post(self, request):
  19. post_data = request.POST or None
  20. if 'pass' in self.request.POST:
  21. print(" srabotal pass")
  22. cont = self.request.POST
  23. elif 'status' in self.request.POST:
  24. if not request.user.is_active or not request.user.phone_active:
  25. return redirect('/')
  26. else:
  27. cont = self.request.POST
  28. try:
  29. new_status = cont['user_status']
  30. except MultiValueDictKeyError:
  31. new_status = request.user.user_status
  32. curent_user = request.user.pk
  33.  
  34. object = get_object_or_404(User, pk=curent_user)
  35.  
  36. object.user_status = new_status
  37. object.save()
  38. return redirect('/dashboardc/home/')

вот формы:

  1. class MyPasswordChangeForm(PasswordChangeForm):
  2. pass
  3.  
  4. class MyStatusChangeForm(forms.Form):
  5. pass

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

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

  1. def clean(self):
  2. print("in clean(self)")
  3. cleaned_data = super().clean()
  4. return cleaned_data
  5.  
  6. def is_valid(self):
  7. print("in is_valid(self)")
  8.  
  9. def clean_recipients(self):
  10. print("in clean_recipients(self)")
  11. data = super(MyStatusChangeForm, self).clean()
  12. return data

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

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

3

Do you like it? Share on social networks!

13
Илья Чичак
  • Sept. 23, 2020, 2:55 p.m.
  • (edited)

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

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

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

    Nomad
    • Sept. 23, 2020, 3:20 p.m.

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

      Илья Чичак
      • Sept. 23, 2020, 3:47 p.m.
      • (edited)

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

      1. class MyPasswordChangeForm(forms.Form):
      2. old_password = forms.CharField(widget=forms.PasswordInput())
      3. new_password1 = forms.CharField(widget=forms.PasswordInput(), help_text="
      4. <ul>
      5. <li>YourYour password can’t be too similar to your other personal information.</li>
      6. <li>Your password must contain at least 8 characters.</li>
      7. <li>Your password can’t be a commonly used password.</li>
      8. <li>Your password can’t be entirely numeric.</li>
      9. </ul>", title="New password")
      10. new_password2 = forms.CharField(widget=forms.PasswordInput(), title="Repeat new Password")
      11.  
      12. def __init__(self, *args, **kwargs):
      13. self.user = kwargs.pop('user', None)
      14. super().__init__(*args, **kwargs)
      15.  
      16. def clean(self):
      17. cleaned_data = super().clean()
      18. if cleaned_data['new_password1'] != cleaned_data['new_password2']:
      19. self.add_error('new_password2', 'New password confirmation is not same to new password')
      20. if self.user and not self.user.check_password(cleaned_data['old_password']:
      21. self.add_error('old_password', 'Current password is not valid')
      22. return cleaned_data

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

      1. class MySettingsChangeView(views.View):
      2. def get_context_data(self, *args, **kwargs):
      3. context = super().get_context_data(*args, **kwargs)
      4. context.update({
      5. 'pass_form': MyPasswordChangeForm(self.request.POST or None),
      6. ...
      7. })
      8. return context

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

      1. ...
      2. <form>
      3. {% csrf_token %}
      4. {{ pass_form.non_fields_errors }}
      5. {{ pass_form.old_password }}
      6. {{ pass_form.new_password1 }}
      7. {{ pass_form.new_password2 }}
      8. <input type="submit">
      9. </form>
      10. ...

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

      1. class MySettingsChangeView(views.View):
      2. ...
      3. def post(self, *args, **kwargs):
      4. password_form = MyPasswordChangeForm(request.POST or None, user=user)
      5. if password_form.is_valid():
      6. self.request.user.set_password(password_form.cleaned_data['new_password1'])
      7. self.request.user.save()
      8. return redirect(...)
      9. return render(
      10. self.request,
      11. ...,
      12. self.get_context_data()
      13. )

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

        Nomad
        • Sept. 24, 2020, 2:26 p.m.

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

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

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

          Nomad
          • Sept. 24, 2020, 2:33 p.m.
          • (edited)

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

          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)
          ......

            Илья Чичак
            • Sept. 24, 2020, 4:42 p.m.
            • (edited)
            • The answer was marked as a solution.

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

            1. class MySettingsChangeView(views.View):
            2. def get_context_data(self, *args, **kwargs):
            3. context = super().get_context_data(*args, **kwargs)
            4. context.update({
            5. 'pass_form': None,
            6. 'status_form': None
            7. })
            8. return context
            9.  
            10. def get(self, *args, **kwargs):
            11. ...
            12. context = self.get_context_data()
            13. context['pass_form'] = MyPasswordChangeForm(user=self.request.user)
            14. context['status_form'] = MyStatusChangeForm()
            15. return render(self.request, '<template>', context)
            16.  
            17. def post(self, *args, **kwargs):
            18. password_form = MyPasswordChangeForm(self.request.POST or None, user=self.request.user)
            19. status_form = MyStatusChangeForm(self.request.POST or None)
            20. if password_form.is_valid():
            21. ...
            22. elif status_form.is_valid():
            23. ...
            24. context = self.get_context_data()
            25. context['pass_form'] = password_form
            26. context['status_form'] = status_form
            27. return render(self.request, '<template>', context)

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

              Nomad
              • Sept. 30, 2020, 8:33 p.m.

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

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

              1. # views.py
              2.  
              3. class MySettingsChangeView1(LoginRequiredMixin, TemplateView):
              4. template_name = 'accounts/registration/settings_pass_status_change.html'
              5.  
              6. def get_context_data(self, *args, **kwargs):
              7. context = super().get_context_data(*args, **kwargs)
              8. context.update({
              9. 'pass_form': None,
              10. 'status_form': None
              11. })
              12. return context
              13.  
              14. def get(self, request, *args, **kwargs):
              15. context = self.get_context_data(**kwargs)
              16. context['pass_form'] = MyPasswordChangeForm1(user=self.request.user)
              17. context['status_form'] = MyStatusChangeForm1(user=self.request.user)
              18. return render(request, self.template_name, context)
              19.  
              20. def post(self, request, *args, **kwargs):
              21. if 'pass' in self.request.POST:
              22. password_form = MyPasswordChangeForm1(request.POST or None, user=request.user)
              23. if password_form.is_valid():
              24. print("Password change")
              25. self.request.user.set_password(password_form.data['new_password1'])
              26. self.request.user.save()
              27. update_session_auth_hash(request, password_form.user)
              28. return redirect('/')
              29. else:
              30. context = self.get_context_data(**kwargs)
              31. context['pass_form'] = password_form
              32. context['status_form'] = MyStatusChangeForm1(user=request.user)
              33. return render(self.request, self.template_name, context)
              34.  
              35. if 'status' in self.request.POST:
              36. status_form = MyStatusChangeForm1(request.POST or None, user=request.user)
              37. if status_form and status_form.is_valid():
              38. print("Status change")
              39. if not request.user.is_active or not request.user.phone_active:
              40. return redirect('/')
              41. else:
              42. cont = self.request.POST
              43. try:
              44. new_status = cont['user_status']
              45. except MultiValueDictKeyError:
              46. new_status = request.user.user_status
              47. curent_user = request.user.pk
              48. object = get_object_or_404(User, pk=curent_user)
              49. object.user_status = new_status
              50. object.save()
              51. return redirect('/')
              52. else:
              53. context = self.get_context_data(**kwargs)
              54. context['status_form'] = status_form
              55. context['pass_form'] = MyPasswordChangeForm1(user=request.user)
              56. return render(self.request, self.template_name, context)
              57.  
              58. context = self.get_context_data()
              59. return render(self.request, self.template_name, context)
              1. # forms.py
              2.  
              3. class MyPasswordChangeForm1(forms.Form):
              4.  
              5. old_password = forms.CharField(strip=False, widget=forms.PasswordInput())
              6. new_password1 = forms.CharField(strip=False, widget=forms.PasswordInput())
              7. new_password2 = forms.CharField(strip=False, widget=forms.PasswordInput())
              8.  
              9. def __init__(self, *args, **kwargs):
              10. self.user = kwargs.pop('user', None)
              11. super().__init__(*args, **kwargs)
              12.  
              13. def clean_old_password(self):
              14. cleaned_data = super().clean()
              15. if cleaned_data:
              16. if self.user and not self.user.check_password(cleaned_data['old_password']):
              17. self.add_error('old_password', 'Current password is not valid')
              18. return cleaned_data
              19.  
              20. def clean(self):
              21. cleaned_data = super().clean()
              22. if cleaned_data:
              23. if ('new_password1' in cleaned_data) and ('new_password2' in cleaned_data):
              24. if cleaned_data['new_password1'] != cleaned_data['new_password2']:
              25. self.add_error('new_password2', 'New password confirmation is not same to new password')
              26. else:
              27. print("nu-s valori pu parole")
              28. # self.add_error('new_password1', 'New password must be')
              29. raise ValidationError("Please enter data", code='invalid')
              30. else:
              31. raise ValidationError("Please enter data", code='invalid')
              32. return cleaned_data
              33.  
              34. def is_valid(self):
              35. return self.is_bound and not self.errors
              36.  
              37.  
              38. class MyStatusChangeForm1(forms.Form):
              39.  
              40. user_status = forms.CharField()
              41.  
              42. def __init__(self, *args, **kwargs):
              43. self.user = kwargs.pop('user', None)
              44. super().__init__(*args, **kwargs)
              45.  
              46. def clean(self):
              47. cleaned_data = super().clean()
              48. if cleaned_data:
              49. if not ('user_status' in cleaned_data):
              50. self.add_error('user_status', 'need check status')
              51. else:
              52. raise ValidationError("Please enter data", code='invalid')
              53.  
              54. return cleaned_data
              55.  
              56. def is_valid(self):
              57. return self.is_bound and not self.errors
              58.  

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

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

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

                Илья Чичак
                • Oct. 1, 2020, 2:14 p.m.
                • (edited)

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

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

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

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

                  Nomad
                  • Oct. 1, 2020, 2:44 p.m.

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

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

                    Илья Чичак
                    • Oct. 1, 2020, 2:57 p.m.
                    1. class MyStatusChangeForm1(forms.Form):
                    2.  
                    3. user_status = forms.CharField()
                    4.  
                    5. def __init__(self, *args, **kwargs):
                    6. self.user = kwargs.pop('user', None)
                    7. super().__init__(*args, **kwargs)
                    8.  
                    9. def clean(self):
                    10. cleaned_data = super().clean()
                    11. if cleaned_data:
                    12. if not ('user_status' in cleaned_data):
                    13. self.add_error('user_status', 'need check status')
                    14. else:
                    15. raise ValidationError("Please enter data", code='invalid')
                    16.  
                    17. return cleaned_data
                    18.  
                    19. def is_valid(self): # <---- This one
                    20. return self.is_bound and not self.errors
                    21.  

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

                      Nomad
                      • Oct. 1, 2020, 3:04 p.m.

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

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

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

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

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

                        Илья Чичак
                        • Oct. 1, 2020, 3:13 p.m.
                        • (edited)

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

                        1. class ChangeProfileView(TemplateView):
                        2. template = 'accounts/registration/settings_pass_status_change.html'
                        3.  
                        4. def get_context_data(self, *args, **kwargs):
                        5. context = super().get_context_data(*args, **kwargs)
                        6. context.update({
                        7. 'pass_form': MyPasswordChangeForm(),
                        8. 'status_form': MyStatusChangeForm(),
                        9. })
                        10.  
                        11. class ProcessPasswordForm(ChangeProfileView):
                        12. def get(self, *args, **kwargs):
                        13. return redirect(reverse('<какой там урл у ChangeProfileView>'))
                        14.  
                        15. def post(self, *args, **kwargs):
                        16. form = MyPasswordChangeForm(self.request.POST, user=self.request.user)
                        17. if form.is_valid():
                        18. form.save()
                        19. return redirect(reverse('<какой там урл у ChangeProfileView>'))
                        20. context = self.get_context_data()
                        21. context.update({'pass_form': form})
                        22. return render(self.request, self.template, context)
                        23.  
                        24. # для MyStatusChangeForm все аналогично
                        25.  
                          Nomad
                          • Oct. 1, 2020, 3:22 p.m.

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

                            Comments

                            Only authorized users can post comments.
                            Please, Log in or Sign up
                            • Last comments
                            • AK
                              April 1, 2025, 11:41 a.m.
                              Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
                            • Evgenii Legotckoi
                              March 9, 2025, 9:02 p.m.
                              К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
                            • VP
                              March 9, 2025, 4:14 p.m.
                              Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
                            • ИМ
                              Nov. 22, 2024, 9:51 p.m.
                              Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
                            • Evgenii Legotckoi
                              Oct. 31, 2024, 11:37 p.m.
                              Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup