CONTEXT:
- A model object's user_account_granted account indicates if a model object is linked to an non null user account.
- When user_account_granted changes from False to True, I detect this in the overridden save() function. Here, I successfully make a User, pulling arguments (email, username, name, etc) from the model object
- I create a password and send the new account login info to the object's email
- If the email failed, I delete the account
PROBLEM:
I want to alert the current user (who just submitted the form triggering the save() ) that the email was either successful (and the new account now exists) or unsuccessful (and no new account is created). I cannot use the Django Messaging Framework in the save() function, as it requires the request. What can I do?
def save(self, *args, **kwargs):
if self.id:
previous_fields = MyModel(pk=self.id)
if previous_fields.user_account_granted != self.user_account_granted:
title = "Here's your account!"
if previous_fields.user_account_granted == False and self.user_account_granted == True:
user = User(
username=self.first_name + "." + self.last_name,
email=self.email,
first_name=self.first_name,
last_name=self.last_name
)
random_password = User.objects.make_random_password() #this gets hashed on user create
user.set_password(random_password)
user.save()
self.user = user
message = "You have just been given an account! \n\n Here's your account info: \nemail: " + self.user.email + "\npassword: " + random_password
if previous_fields.user_account_granted == True and self.user_account_granted == False:
message = "You no longer have an account. Sorry :( "
try:
sent_success = send_mail(title, message, '[email protected]', [self.email], fail_silently=False)
if sent_success == 1:
##HERE I WANT TO INDICATE EMAIL SENT SUCCESS TO THE USER'S VIEW AFTER THE FORM IS SUBMITTED
else:
##HERE I WANT TO INDICATE EMAIL SENT FAILURE TO THE USER'S VIEW AFTER THE FORM IS SUBMITTED
user.delete()
self.user_account_granted = False
except:
##HERE I WANT TO INDICATE EMAIL SENT FAILURE TO THE USER'S VIEW AFTER THE FORM IS SUBMITTED
user.delete()
self.user_account_granted = False
super(MyModel, self).save(*args, **kwargs)
You don't send an email in a model's
save()
function. Ever. That's just not its purpose. Consider:save()
may be called from the shell.save()
may be called from wherever in your project.The purpose of the
save()
method is to save the object. End of story.Now. Let's get back to what you really want to achieve. You are in the process of handling a form submitted by the user. Which means you're dealing with at least to other things here: the form and the view.
Let's have a closer look at what their purpose is:
The Form's basic role is to encapsulate data input and validation. It can be extended so as to encompass the full role of a Command. After all, it only misses an
execute()
function.The View's basic role is to take action based on the browser's request (here, the POSTing of a form) and trigger the displaying of the result.
You may choose either. You could have an
execute()
method on your form, and just call that from you view. Or you could have the view take action after checking that the formis_valid()
. I would personnaly choose the form for the actual action, and the view for showing the result.Thus, in my view, I would customize the
form_valid()
method to callexecute()
and do whatever is needed depending on the result. That is:AnAppropriateError
may just beRuntimeError
if you want it quick and dirty, but you should define your own exception class.Also, you may want to decorate the
execute()̀ method with
@transaction.atomic()`, depending on its content.As a final note, remember you cannot be sure the email was actually sent. It's common to have mail systems that accept emails even if there is some error. You'll only know when it bounces, several days later.