Nomad
NomadҚыр. 22, 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
Илья Чичак
  • Қыр. 23, 2020, 4:55 Т.Ж.
  • (өңделген)

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 вьюхи, чтобы каждая их них обрабатывала свою форму - меньше путаницы будет

    Nomad
    • Қыр. 23, 2020, 5:20 Т.Ж.

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

      Илья Чичак
      • Қыр. 23, 2020, 5:47 Т.Ж.
      • (өңделген)

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

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

        Nomad
        • Қыр. 24, 2020, 4:26 Т.Ж.

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

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

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

          Nomad
          • Қыр. 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)
            

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

              Nomad
              • Қыр. 30, 2020, 10:33 Т.Ж.

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

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

              # 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бмит

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

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

                Илья Чичак
                • Қаз. 1, 2020, 4:14 Т.Ж.
                • (өңделген)

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

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

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

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

                  Nomad
                  • Қаз. 1, 2020, 4:44 Т.Ж.

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

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

                    Илья Чичак
                    • Қаз. 1, 2020, 4:57 Т.Ж.
                    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 - лучше не трогать

                      Nomad
                      • Қаз. 1, 2020, 5:04 Т.Ж.

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

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

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

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

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

                        Илья Чичак
                        • Қаз. 1, 2020, 5:13 Т.Ж.
                        • (өңделген)

                        ну поставить заглушку на метод 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 все аналогично
                        
                        
                          Nomad
                          • Қаз. 1, 2020, 5:22 Т.Ж.

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

                            Пікірлер

                            Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
                            Кіріңіз немесе Тіркеліңіз
                            AD

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

                            • Нәтиже:50ұпай,
                            • Бағалау ұпайлары-4
                            m
                            • molni99
                            • Қаз. 26, 2024, 1:37 Т.Ж.

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

                            • Нәтиже:80ұпай,
                            • Бағалау ұпайлары4
                            m
                            • molni99
                            • Қаз. 26, 2024, 1:29 Т.Ж.

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

                            • Нәтиже:20ұпай,
                            • Бағалау ұпайлары-10
                            Соңғы пікірлер
                            ИМ
                            Игорь МаксимовҚар. 22, 2024, 11:51 Т.Ж.
                            Django - Оқулық 017. Теңшелген Django кіру беті Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
                            Evgenii Legotckoi
                            Evgenii LegotckoiҚаз. 31, 2024, 2:37 Т.Қ.
                            Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
                            A
                            ALO1ZEҚаз. 19, 2024, 8:19 Т.Ж.
                            Qt Creator көмегімен fb3 файл оқу құралы Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
                            ИМ
                            Игорь МаксимовҚаз. 5, 2024, 7:51 Т.Ж.
                            Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
                            d
                            dblas5Шілде 5, 2024, 11:02 Т.Ж.
                            QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                            Енді форумда талқылаңыз
                            m
                            moogoҚар. 22, 2024, 7:17 Т.Ж.
                            Mosquito Spray System Effective Mosquito Systems for Backyard | Eco-Friendly Misting Control Device & Repellent Spray - Moogo ; Upgrade your backyard with our mosquito-repellent device! Our misters conce…
                            Evgenii Legotckoi
                            Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
                            добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
                            t
                            tonypeachey1Қар. 15, 2024, 6:04 Т.Ж.
                            google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
                            NSProject
                            NSProjectМаусым 4, 2022, 3:49 Т.Ж.
                            Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…

                            Бізді әлеуметтік желілерде бақылаңыз