Регистрация в Django не возвращает сообщения об ошибках

Регистрация для этого проекта Django 4.0.3 работает хорошо, за исключением того, что не отображаются сообщения об ошибках и содержимое ранее заполненных полей формы. Другими словами, регистрация работает с заполненными формами user_form и profile_form, и база данных обновляется, как ожидалось.

Что не работает

  • При несовпадении паролей ошибка не возвращается, а страница возвращает пустую форму (у меня сложилось впечатление, что при передаче форм обратно в виде контекстного словаря данные полей будут сохранены - не уверен, что я что-то упустил в этом отношении)
  • .
  • Проверка ошибок в forms.py для обеспечения уникального email также не работает (я широко использую проверку ошибок форм во всем проекте). Однако, учитывая, что ошибка не выдает ошибку несоответствия пароля, я склонен полагать, что оба этих симптома имеют одну и ту же причину.
  • При недействительной регистрации (несовпадение пароля или неоригинальный адрес электронной почты) возвращаются пустые формы. Тот факт, что валидация форм не работает должным образом, наводит на мысль, что в представлении регистрации есть что-то изначально неправильное. Однако, после того, как я рассматривал это в течение нескольких дней, я надеюсь, что светлые умы Stack Overflow смогут указать мне правильное направление...
  • .

Я включаю все соответствующие фрагменты кода в ожидании того, что они будут запрошены для поддержки диагностики. Учитывая, что единственный элемент регистрации пользователя, который не работает, это неспособность возвращать сообщения об ошибках и сохранять содержимое полей формы, то, что я включил, вполне может быть излишним. И я не удивлюсь, если я что-то упустил. Если это так, пожалуйста, прокомментируйте любые подобные пожелания, и я обновлю вопрос.

Сниппеты кода

settings.py

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

# Password validation
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

models.py

Я добавил связь один-к-одному между встроенной моделью User и моей собственной моделью Profile. Я знаю, что модель User может быть расширена, но я выбрал модель Profile (которую Django Documentation признает приемлемой). Я опустил константу COUNTRY_CHOICES, поскольку, по моим оценкам, эти 200 с лишним строк кода не будут поддерживать диагностику.

from django.db import models
from django.contrib.auth.models import User
from phone_field import PhoneField
# ...

# ####################### CHOICE CONSTANTS #######################
PRONOUN_CHOICES = [
    ('---', '---'),
    ('She/her', 'She/her'),
    ('He/him', 'He/him'),
    ('They/them', 'They/them'),
    ('Rather not say', 'Rather not say'),
]
NOTIFICATION_PREFERENCE = [
    ('None', 'None'),
    ('Email', 'EMail'),
    ('Text/SMS', 'Text/SMS'),
]
# Omitted COUNTRY_CHOICES to save space
# ...

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    pronoun = models.CharField(max_length=20, choices=PRONOUN_CHOICES, default='---')
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    full_name = models.CharField(max_length=200, blank=True, null=True)
    address_1 = models.CharField(max_length=100)
    address_2 = models.CharField(max_length=100, blank=True, null=True)
    city = models.CharField(max_length=50)
    state_province_county = models.CharField(max_length=100, verbose_name='State/Province/County')
    postal_zip_code = models.CharField(max_length=10, verbose_name='postal/zip code')
    country = models.CharField(max_length=100, choices=COUNTRY_CHOICES, default='---')
    phone = PhoneField(blank=True, help_text='Contact phone number')
    notification_preference = models.CharField(
        max_length=10,
        choices=NOTIFICATION_PREFERENCE,
        default='Email',
        help_text='''If you would like to receive booking confirmation notifications from us please select your preference.'''
    )

    def __str__(self):
        return f'{self.first_name} {self.last_name}'

forms.py

Я использую forms.py для проверки уникальности электронной почты. Я добавил это, потому что оригинальный подход (вдохновленный Corey Schafer), похоже, не предотвращал дублирование адресов электронной почты.

from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm

from .models import Profile


# ############ Profile form validation ############
def profile_form_validation(form, form_type):
    cleaned_data = super(form_type, form).clean()
    if not cleaned_data.get('phone') and cleaned_data.get('notification_preference') == 'Text/SMS':
        form.add_error(
            'phone',
            'A phone number must be entered in order to be able to receive text messages on your phone.'
        )
    return


# ############ Forms ############
class UserRegisterForm(UserCreationForm):
    email = forms.EmailField()

    class Meta:
        model = User
        fields = [
            'username',
            'email',
            'password1',
            'password2',
        ]

    def clean(self):
        cleaned_data = super(UserRegisterForm, self).clean()
        if User.objects.filter(email=cleaned_data.get('email')).exists():
            self.add_error(
                'email',
                'That email address is already associated with an account. Please use another email address.',
            )
        return self.cleaned_data


class ProfileUpdateForm(forms.ModelForm):

    class Meta:
        model = Profile
        fields = [
            'pronoun',
            'first_name',
            'last_name',
            'address_1',
            'address_2',
            'city',
            'state_province_county',
            'postal_zip_code',
            'country',
            'phone',
            'notification_preference',
        ]

    def clean(self):
        profile_form_validation(self, ProfileUpdateForm)

        return self.cleaned_data

views.py

Моя интуиция подсказывает, что причина проблемы кроется в представлении для обработки регистрации. Обратите внимание, что:

  • Я использую функцию login() для автоматического входа пользователя в систему после завершения регистрации.
  • Автологин работает, если оба пароля совпадают и введенный адрес электронной почты уникален.
  • Перенаправление на "события" работает.
  • Единственная неработающая функциональность - это невозможность вернуть сообщения об ошибках или сохранить ранее заполненные поля (за исключением пароля1 и пароля2).
from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth import login

from .models import Profile

from .forms import (
    UserRegisterForm,
    ProfileUpdateForm,
)


def register(request):
    if request.method == 'POST':
        user_form = UserRegisterForm(request.POST)
        profile_form = ProfileUpdateForm(request.POST)

        if user_form.is_valid() and profile_form.is_valid():
            # Process user form
            user = user_form.save(commit=False)
            user.save()
            # Process profile form
            profile_form.instance.user = user
            profile_form.instance.full_name = f'{profile_form.instance.first_name} {profile_form.instance.last_name}'
            profile_form.save()
            # Log in user after registration
            login(request, user)
            messages.success(request, f'Your account (username:{username}) has been created. '
                                      f'You are now logged in!')
            return redirect('events')

        else:
            user_form = UserRegisterForm()
            profile_form = ProfileUpdateForm()

        return render(request, 'users/register.html', {'user_form': user_form, 'profile_form': profile_form})

    else:
        user_form = UserRegisterForm()
        profile_form = ProfileUpdateForm()
        return render(request, 'users/register.html', {'user_form': user_form, 'profile_form': profile_form})

urls.py

Включение urls.py, вероятно, является чрезмерным убийством.

from django.contrib.auth import views as auth_views
from django.urls import path
# ...

urlpatterns = [
    # ...
    path('register/', user_views.register, name='register'),
    # ...
]

register.html

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

Я не уверен, как я это пропустил, но, очевидно, я повторно создавал формы с веткой else. Лицо-ладонь

Я отвечаю на вопрос в надежде, что кто-то другой сэкономит время.

Исправленный views.py

from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth import login

from .models import Profile

from .forms import (
    UserRegisterForm,
    ProfileUpdateForm,
)


def register(request):
    if request.method == 'POST':
        user_form = UserRegisterForm(request.POST)
        profile_form = ProfileUpdateForm(request.POST)

        if user_form.is_valid() and profile_form.is_valid():
            # Process user form
            user = user_form.save(commit=False)
            user.save()
            # Process profile form
            profile_form.instance.user = user
            profile_form.instance.full_name = f'{profile_form.instance.first_name} {profile_form.instance.last_name}'
            profile_form.save()
            # Log in user after registration
            login(request, user)
            messages.success(request, f'Your account (username:{username}) has been created. '
                                      f'You are now logged in!')
            return redirect('events')

        return render(request, 'users/register.html', {'user_form': user_form, 'profile_form': profile_form})

    else:
        user_form = UserRegisterForm()
        profile_form = ProfileUpdateForm()
        return render(request, 'users/register.html', {'user_form': user_form, 'profile_form': profile_form})

Мораль этой истории такова: если вы проверяете формы, нет необходимости возвращать эти формы, когда формы не возвращаются как действительные!

Вернуться на верх