我有这种形式,几乎是从此official Django doc page上第二个绿色框复制的:

class UserForm(forms.ModelForm):
    password1 = forms.CharField(label="Password", widget=forms.PasswordInput())
    password2 = forms.CharField(label="Password confirmation", widget=forms.PasswordInput())

    class Meta:
        model = User
        fields = ('username', 'email', 'password1', 'password2')

    def clean_password2(self):
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super(UserForm, self).save(commit=False)
        user.set_password(self.cleaned_data["password2"])
        if commit:
            user.save()
        return user


表单显示正确,并按预期捕获错误(例如“密码不匹配”),但是当尝试使用刚刚创建的用户/密码登录时,它将失败。我可以通过电子邮件重设密码,然后成功登录。

为什么表单无法正确保存密码?

(用户的“活动性”在这里重要吗?)

(此外,我知道已经有一个身份验证表单已经在执行此创建带有密码的用户帐户确认操作,但是我在https://docs.djangoproject.com/en/1.7/topics/auth/default/#built-in-auth-views上看不到它)



views.py

def create_user_account_success(request):
    return  render_to_response("registration/create_account_success.html", RequestContext(request))

MIDDLE_YEAR_STR = str(DISCOVERED_MIN_YEAR + ((DISCOVERED_MAX_YEAR - DISCOVERED_MIN_YEAR) // 2))


def create_user_account(request):
    context = RequestContext(request)

    if(request.method == "POST"):
        #Form was filled. Process it.

        user_form = UserForm(data=request.POST)
        profile_form = UserProfileForm(data=request.POST)

        if(user_form.is_valid() and profile_form.is_valid()):

            #To get a form element, use either
            #print(request.POST["password1"])

            #or, *after* calling UserForm(data=request.POST) and then
            #user_form.is_valid():
            #print(user_form.cleaned_data["password1"])

            #commit
            user = user_form.save()


            user.set_password(user.password)
            user.save()

            profile = profile_form.save(commit=False)
            profile.user = user

            if("picture" in request.FILES):
                profile.picture = request.FILES["picture"]

            profile.save()

            return  redirect("create_account_success")

    else:
        #Not a POST. Form hasn't been filled. Get a blank form
        global  MIDDLE_YEAR_STR

        user_form = UserForm()
        profile_form = UserProfileForm(initial={
            "year_discovered": MIDDLE_YEAR_STR})

    context["user_form"] = user_form
    context["profile_form"] = profile_form

    #Render and display the form
    return  render_to_response("registration/create_account.html", context)


表格

from  django import forms
from  django.contrib.auth.models import User
from  .models import UserProfile, DISCOVERED_MIN_YEAR, DISCOVERED_MAX_YEAR

class UserForm(forms.ModelForm):
    password1 = forms.CharField(label="Password", widget=forms.PasswordInput())
    password2 = forms.CharField(label="Password confirmation", widget=forms.PasswordInput())

    class Meta:
        model = User
        fields = ('username', 'email', 'password1', 'password2')

    def clean_password2(self):
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super(UserForm, self).save(commit=False)
        user.set_password(self.cleaned_data["password2"])
        if commit:
            user.save()
        return user

class UserProfileForm(forms.ModelForm):
    year_choices = ((x,str(x)) for x in range(DISCOVERED_MIN_YEAR, DISCOVERED_MAX_YEAR+1))
    #year_discovered = forms.ChoiceField(choices=year_choices)

    year_discovered = forms.IntegerField(required=False,
        min_value=DISCOVERED_MIN_YEAR, max_value=DISCOVERED_MAX_YEAR,
        widget=forms.Select(choices=year_choices))

    #year_discovered = forms.IntegerField(min_value=DISCOVERED_MIN_YEAR, max_value=DISCOVERED_MAX_YEAR)

    class Meta:
        model = UserProfile
        fields = ('year_discovered', 'profile_picture')


create_account.html

{% extends "base.html" %}

{% block title %}Create account{% endblock %}

{% block content %}
  <h1>Billy Joel Album Browser: Create account</h1>

        {% if registration_was_successful %}
          <P>Success!</P>
        {% else %}


        <form id="user_form" method="post" action="{% url 'create_account' %}"
                enctype="multipart/form-data">

            {% csrf_token %}

            <!-- Display each form. The as_p method wraps each element in a paragraph
                 (<p>) element. This ensures each element appears on a new line,
                 making everything look neater. -->
            {{ user_form.as_p }}
            {{ profile_form.as_p }}

            <!-- Provide a button to click to submit the form. -->
            <input type="submit" name="submit" value="Register" />
        </form>
        {% endif %}
{% endblock %}


create_account_succes.html

{% extends "base.html" %}

{% block content %}

<H1>Welcome!</H1>

<P><a href="{% url 'login' %}">Login</A> to proceed.</P>

{% endblock %}

最佳答案

表单和视图之间的密码设置代码重复,这导致了您的问题。

在窗体的save方法中,您正确地调用user.set_password()将散列的密码设置到新创建的用户上。但是,然后,您的视图再次调用set_password:因此,其作用是获取已经散列的密码,再次对其进行散列,然后将双重散列的密码设置给用户。当然,当您登录时,密码不再匹配,因为它已经被重复了两次。

只需从视图中删除两行(user.set_passworduser.save)。

10-08 02:57