3
\$\begingroup\$

I have an account page where I have three forms. A user can change his name, his email address, and his password.

There are two difficulties I am having from trying to do this:

1) the request.user information is not updating accordingly (e.g., it will lag behind by one change or it will update if the form is not validated)

2) I have three separate forms, however, when I submit one form, I will get validation messages from another form. For example, if I submit the "Change Name" form, I will get 'This field is required from the password fields in the password form. Here is what I currently have --

in forms:

# in forms.py
from django.contrib.auth.models import User

class ChangeNameForm(ModelForm):
    first_name = forms.CharField(required=True)
    last_name = forms.CharField(required=True)
    class Meta:
        model = User
        fields = ('first_name', 'last_name' )

class ChangeEmailForm(ModelForm):
    email = forms.EmailField(required=True)
    class Meta:
        model = User
        fields = ('email',)

in views:

@login_required
def account(request):
    name_message = password_message = email_message = ''
    change_name_form = ChangeNameForm(data=request.POST or None, instance=request.user)
    change_password_form = PasswordChangeForm(data=request.POST or None, user = request.user)
    change_email_form = ChangeEmailForm(data=request.POST or None, instance=request.user)
    if request.method == "POST":
        if "change_name" in request.POST and change_name_form.is_valid():
            change_name_form.save()
            name_message = 'Your name has been changed.'
        if "change_password" in request.POST and change_password_form.is_valid():
            change_password_form.save()        
            password_message = 'Your password has been changed.'
        if "change_email" in request.POST and change_email_form.is_valid():
            ...
            email_message = 'Please click the link in your email to confirm changes.'
    return render_to_response('userprofile/account.html', 
                       {'change_name_form': change_name_form,
                        'change_email_form': change_email_form, 
                        'change_password_form': change_password_form,
                        'password_message': password_message,
                        'name_message': name_message,
                        'email_message': email_message,}, 
                        context_instance=RequestContext(request))

in template:

<h3>Change Name</h3>
    <form method="post" action="/account/change/" name='name'> {% csrf_token %}
        <h4>Change name: {{user.first_name}} {{user.last_name}}</h4>
        <table>{{change_name_form.as_table}}</table>
        <p>{{name_message}}</p>
        <p><input type="submit" value="Save Changes" name="change_name"/></p>
    </form>

<h3>Change Email</h3>
    <form method="post" action="/account/change/" name='email'> {% csrf_token %}
        <h4>Change email: {{user.email}}</h4>
        <table>{{change_email_form.as_table}}</table>
        <p>{{email_message}}</p>
        <p><input type="submit" value="Save Changes" name="change_email" /></p>
    </form>

<h3>Change Password</h3>
    <form method="post" action="/account/change/" name='password'> {% csrf_token %}
        <h4>Change password</h4>
        <table>{{change_password_form.as_table}}</table>
        <p>{{password_message}}</p>
        <p><input type="submit" value="Save Changes" name="change_password"/></p>
    </form>

What do I need to do to solve the two issues of making sure the request.user information is current and making sure validation only runs for the current form? Also, would it be possible to run a for loop to reduce redundancy in the template code? If so, how would I do this given the fact that the two name fields?. Thank you.

\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

Somewhere on "Stack Overflow" was question like your.

Main idea was remove None from form initialization.

if "change_name" in request.POST:
    change_name_form = ChangeNameForm(data=request.POST, instance=request.user)
    if change_name_form.is_valid():
        change_name_form.save()
        name_message = 'Your name has been changed.'
else:
    change_name_form = ChangeNameForm(instance=request.user)

if "change_password" in request.POST:
    change_password_form = PasswordChangeForm(user=request.user)
    if change_password_form.is_valid():
        change_password_form.save()        
        password_message = 'Your password has been changed.'
else:
    change_password_form = PasswordChangeForm(user=request.user)

if "change_email" in request.POST:
    change_email_form = ChangeEmailForm(request.POST, instance=request.user)
    if change_email_form.is_valid():
        ...
        email_message = 'Please click the link in your email to confirm changes.'
else:
    change_email_form = ChangeEmailForm(instance=request.user)

This is my solution. I hope it's help you.

\$\endgroup\$
3
  • \$\begingroup\$ Thank you for the answer. Could you please elaborate on why you'd remove None. What is the advantage of doing so? \$\endgroup\$
    – David542
    Jun 8, 2011 at 22:18
  • \$\begingroup\$ For your case, when you commit any form request.POST isn't empty and you put it into all your forms. Then form got any POST data it become bound form, and fields already have error attribute after this form become bound. docs.djangoproject.com/en/1.3/ref/forms/api/… \$\endgroup\$
    – srusskih
    Jun 9, 2011 at 5:21
  • 1
    \$\begingroup\$ I think there is one more solution, form prefixes. docs.djangoproject.com/en/1.3/ref/forms/api/#prefixes-for-forms \$\endgroup\$
    – srusskih
    Jun 9, 2011 at 10:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.