Question

This question is in relation to Django: Only update fields that have been changed in UpdateView

I'm trying to provide a way for a user to change his email. Before I save the new email as the default, I want to send an email to the newly provided address to make sure it's valid. The newly provided email will be saved in a temporary table in the DB but the previous(old) email will remain unchanged until it has been verified.

The problem I'm running into is, the old email gets over-written by the new one before it has been verified. How do I go about preventing this from happening using an UpdateView? Below is my code:

class AccountUpdate(UpdateView):
    """Updates an account"""

    context_object_name = 'account'
    form_class = UpdateForm
    template_name = 'accounts/update_account.html'
    success_url = '/accounts/home'

    def get_object(self, queryset=None):
        return self.request.user

    @sensitive_variables('new_password')
    @sensitive_post_parameters('new_password')
    def form_valid(self, form):
        account = Account.objects.get(pk=self.request.user.id)
        clean = form.cleaned_data
        new_password = clean.get('new_password')
        old_email = account.email
        new_email = clean.get('email')

        # check for new password and encrypt it before saving
        if new_password:
            #encrypt plain password
            form.instance.password = hash_password(clean['new_password'])

        # check for new email address and save it in temporary field until verified
        # ensure that new email is different and is currently not taken
        if old_email != new_email:
            try:
                # make sure someone else has not used this email as their new email
                temp_taken = Account.objects.get(new_email=new_email)
                if temp_taken:
                    raise ValidationError('An account with this email exist.')

            # save new email and send verification message
            # make sure we set 'DoesNotExist' on the 'Account' object itself
            # it prevents 'str' object has no attribute 'DoesNotExist' error
            except Account.DoesNotExist:
                verifystring = get_random_string()
                self.object.new_email = new_email
                self.object.new_email_verifystring = verifystring
                message = "Hey %s! " \
                      "Please click the link below to verify your new email:" \
                      "<a href='link_here'>Verify Email!</a>" %\
                      (clean['first_name'])
                self.object.email_user(subject='email verification.',
                                   message=message,
                                   from_email='no-reply@localhost')
        else:
            context = {}
            self.object = context.update(first_name=clean.get('first_name'),
                                     last_name=clean.get('last_name'),
                                     username=clean.get('username'),
                                     force_update=False)
        return super(AccountUpdate, self).form_valid(form)

If an answer already exist elsewhere, please point me to it.

Était-ce utile?

La solution 2

The problem I'm running into is, the old email gets over-written by the new one before it has been verified.

This doesn't answer the question of how to prevent fields from being saved using an UpdateView but it solves the stated problem above.

# views.py snippet
# because we are using the same 'email' variable to add new email addresses
# 'self.object.email' will be saved with the new provided value
# to prevent this from happening, manually set the 'email' variable to the value
# retrieved from the DB
except Account.DoesNotExist:
            verifystring = get_random_string()
            self.object.email = old_email
            self.object.new_email = new_email

Autres conseils

This doesn't have much to do with the class-based fields, but with the problem of not losing an address that is known to be working.

I suggest you to add a field to your Account model, called new_email or unverified_email or something; when the account is changed, the actual email field is left untouched, and the new address is stored into that new field, and the verification email is sent to it.

Only when the verification process is complete, the new address is stored into the email field, overriding the old address. It would also be prudent to warn the user in the update form that their old address will be used until they verify the new address.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top