Reputation: 658
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.
Upvotes: 0
Views: 1263
Reputation: 17243
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.
Upvotes: 1
Reputation: 658
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
Upvotes: 0