David542
David542

Reputation: 110382

Processing multiple forms on same page

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.

Upvotes: 0

Views: 1484

Answers (2)

Timmy O&#39;Mahony
Timmy O&#39;Mahony

Reputation: 53999

I think martin means:

@login_required
def account(request):
    name_message = password_message = email_message = ''
    if request.method == "POST":
       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)
       ...
   else:
       change_name_form = ChangeNameForm(instance=request.user)
       change_password_form = PasswordChangeForm(instance = request.user)
       change_email_form = ChangeEmailForm(instance=request.user)
   ...
   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))

If the request to this view is a POST (i.e. the user is trying to edit a their account) you instantiate the form with the request POST attributes. If they are just arriving at the edit page, but haven't performed an edit (i.e. they haven't POST'ed any forms yet) then just instantiate the forms with the data from the database

Upvotes: 2

Martin
Martin

Reputation: 86

To answer your second question, you're instantiating all 3 forms with POSTd data, even when that particular form wasn't the one triggered. This will trigger validation on all 3 forms. I would move the form instantiation down into the if statements you have below where you check for presence of the appropriate submit button.

Upvotes: 1

Related Questions