views:

1103

answers:

4

Django lets you create a model foreign-keyed to User and define it in settings as the official "profile" model holding additional data for user accounts. django-profiles lets you easily display/create/edit that profile data. But the user's primary email address is part of their main account, not part of their extended profile. Therefore when you put

{{ form }}

in the profile/edit_profile template, the primary email address does not show up. You can retrieve it manually with

{{ user.email }}

but changes to it aren't saved back to the account upon submit of course. I'm assuming a custom ModelForm has been created, such as:

class ProfileForm(ModelForm):

  class Meta:
      model = Parent
      exclude = ('family','user','board_pos','comm_job',)

and that ProfileForm is being passed to django-profiles' view code with a urlconf like:

('^profiles/edit', 'profiles.views.edit_profile', {'form_class': ProfileForm,}),

The same problem would come up if you wanted to let users change their first or last names. What's the best way to let users change their own email addresses or names when using django-profiles?

+1  A: 

I think the easiest way would definitely be to use a form. Use the form to display their current email address (which they could change), and then use your view to extract the request, retrieve the appropriate profile belonging to that user by matching some other parameter you could pass to the template, and then storing the new data and saving the model.

AlbertoPL
+2  A: 

A sample solution from the Filmaster project:

  • The form: https://musielak.eu/public/film20/film20/userprofile/forms.py (class ProfileForm(forms.ModelForm))
  • The view: https://musielak.eu/public/film20/film20/userprofile/views.py (def personal(request) method)

To access the code you have to login using the credentials: justlookingaround, pass: film@ster.

michuk
Very useful sample code michuk. This solution involves storing the first, last, and email in TWO places (in the User model and in the Profile model). But when the Profile is updated, the User is updated as well, so the non-DRYness of it isn't really a problem. Would be nice to paste the relevant code inline here.
shacker
@shacker the fields are stored only in User model, we just add them to the form for editing profile and update the associated user table on submit.
michuk
Ah, now I understand what you're doing here more clearly - this looks really clean then. So now it's a choice between this method and handling these fields in a separate form. Will think on that. Thanks.
shacker
+2  A: 

I think that implementing a Separate page just for change of email is best, since it would need to be verified etc...

If you would like to enable users to modify all their profile info together with their main email address, then you need to create your own Form (ModelForm will not work here). I suggest you start doing this and post a question when you get stuck.

Start by copying all the fields out of django-profile model into your custom form, and add the users primary email field.

You will have to "override" the django-profile edit url and basically copy the html template if there is one.

Another option (bad) would be to hack django-profiles app and change it there. But that will, likely, introduce a lot of bugs, and will render your app unapgradable.

drozzy
Solutions suggested so far are fairly similar, and michuk's sample code is very useful, but drozzy makes an excellent point that validation and separate approval may be necessary when changing these things. The more I think about it, the more I realize that a separate page makes sense when changing these fields.
shacker
A: 

Here's the solution we ended up using:

# urls.py
    # First match /profiles/edit before django-profiles gets it so we can pass in our custom form object.
    ('^profiles/edit', 'profiles.views.edit_profile', {'form_class': ProfileForm,}),
    (r'^profiles/', include('profiles.urls')),

Now we override the save method in the form itself, so that when the form is saved, the email address is pushed into the saving user's User object at the same time. Graceful.

# forms.py , trimmed for brevity

class ProfileForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(ProfileForm, self).__init__(*args, **kwargs)
        try:
            self.fields['email'].initial = self.instance.user.email
        except User.DoesNotExist:
            pass

    email = forms.EmailField(label="Primary email")

    class Meta:
      model = Parent

    def save(self, *args, **kwargs):
      """
      Update the primary email address on the related User object as well. 
      """
      u = self.instance.user
      u.email = self.cleaned_data['email']
      u.save()
      profile = super(ProfileForm, self).save(*args,**kwargs)
      return profile

Works perfectly. Thanks mandric.

shacker