Django 5.1 UserCreationForm не позволяет вводить пустые пароли

Я обновляю приложение Django 3.0 до версии 5.1 и медленно перехожу от одного минорного релиза к другому. Пока все хорошо.

Однако после обновления с Django 5.0 до 5.1 я увидел изменение поведения на странице «Создать нового пользователя», которая раньше разрешала пустые пароли, при этом генерировался случайный пароль, если он не был введен.

Теперь я больше не могу отправить пустой пароль. Я получаю ошибку «обязательное поле» для обоих полей пароля, хотя оба поля явно установлены как required=False.

Я знаю, что в Django 5.1 произошли некоторые изменения (5.1.0, 5.1.1) в классе UserCreationForm. Я попробовал использовать AdminUserCreationForm вместо UserCreationForm и установить для поля usable_password значение None, но оно все равно не позволяет вводить пустые пароли, как раньше.

Есть идеи?

Окружение
Python 3.12.8
Django 5.1.5

Упрощенный код

class SignupForm(AdminUserCreationForm): # previously using UserCreationForm

    usable_password = None # Newly added

    # Form fields
    sharedaccountflag = forms.ChoiceField(
                            label='Cuenta compartida', 
                            required=True, 
                            choices=BOOLEAN_CHOICES
                        )

    # Constructor
    def __init__(self, *args, **kwargs):
                
        # Call base class constructor (i.e. SignupForm)
        super(SignupForm, self).__init__(*args, **kwargs)

        # Set password fields as optional
        self.fields['password1'].required=False
        self.fields['password2'].required=False

        # Set form helper properties
        self.helper = FormHelper()
        setFormHelper(self.helper) # set form method (POST / GET) and styling
        self.helper.form_tag = False

        # Set form layout
        self.helper.layout = Layout(
            Fieldset(
                'password1',
                'password2',
                'sharedaccountflag'
            )
        )

    # Specify model and which fields to include in form
    class Meta:
        model = get_user_model()
        fields = ('password1', 'password2', 'sharedaccountflag')

Снимок экрана
enter image description here

Да. В новых версиях Django исходный код изменился, соответственно изменилось и поведение класса BaseUserCreationForm. Теперь поля password1 и password2 создаются с помощью статического метода SetPasswordMixin.create_password_fields(), и по умолчанию они принимают значение required=False. Это можно легко проверить здесь. Но даже если поля необязательны, метод validate_passwords всегда вызывается , в методе clean и проверяет, что поля не пусты. Например, когда вы вызываете что-то вроде этого form.is_valid(), будет вызван clean.

Если вам нужно поведение, в котором разрешены пустые пароли, то, учитывая required=False, вы можете определить пользовательский validate_passwords метод, подобный приведенному ниже, который позволит вам создавать пользователей с пустыми паролями:

from django.contrib.auth import forms


class CustomUserCreationForm(forms.UserCreationForm):  
    def validate_passwords(  
            self,  
            password1_field_name: str = "password1",  
            password2_field_name: str = "password2"):  
  
        def is_password_field_required_and_not_valid(field_name: str) -> bool:  
            is_required = self.fields[field_name].required  
            cleaned_value = self.cleaned_data.get(field_name)  
            is_field_has_errors = field_name in self.errors  
            return (  
                is_required  
                and not cleaned_value  
                and not is_field_has_errors  
            )  
  
        if is_password_field_required_and_not_valid(password1_field_name):  
            error = ValidationError(  
                self.fields[password1_field_name].error_messages["required"],  
                code="required",  
            )  
            self.add_error(password1_field_name, error)  
  
        if is_password_field_required_and_not_valid(password2_field_name):  
            error = ValidationError(  
                self.fields[password2_field_name].error_messages["required"],  
                code="required",  
            )  
            self.add_error(password2_field_name, error)  
  
        password1 = self.cleaned_data.get(password1_field_name)  
        password2 = self.cleaned_data.get(password2_field_name)  
        if password1 != password2:  
            error = ValidationError(  
                self.error_messages["password_mismatch"],  
                code="password_mismatch",  
            )  
            self.add_error(password2_field_name, error)

p.s. Я не уверен, так ли это было задумано, возможно, просто допущена ошибка, в validate_passwords, где флаг required просто не учитывается.

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