如何从 Django 模型(覆盖)save() 函数向视图发送警报或消息?
Posted
技术标签:
【中文标题】如何从 Django 模型(覆盖)save() 函数向视图发送警报或消息?【英文标题】:How would I send an alert or message to a view from a Django model's (overridden) save( ) function? 【发布时间】:2015-09-01 21:45:06 【问题描述】:上下文:
-
模型对象的 user_account_granted 帐户指示模型对象是否链接到非空用户帐户。
当 user_account_granted 从 False 变为 True 时,我在覆盖的 save() 函数中检测到这一点。在这里,我成功创建了一个用户,从模型对象中提取参数(电子邮件、用户名、姓名等)
我创建一个密码并将新帐户登录信息发送到对象的电子邮件
如果邮件失败,我会删除帐户
问题:
我想提醒当前用户(刚刚提交了触发 save() 的表单)电子邮件成功(并且新帐户现在存在)或不成功(并且没有创建新帐户)。我不能在 save() 函数中使用 Django 消息传递框架,因为它需要请求。我能做什么?
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, 'example@email.com', [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)
【问题讨论】:
【参考方案1】:您不会在模型的 save()
函数中发送电子邮件。曾经。这不是它的目的。考虑:
save()
可以从 shell 调用。
save()
可以在项目中的任何位置调用。
save()
方法的目的是保存对象。故事结束。
现在。让我们回到你真正想要实现的目标。您正在处理用户提交的表单。这意味着您在这里至少要处理其他事情:表单和视图。
让我们仔细看看他们的目的是什么:
Form 的基本作用是封装数据输入和验证。它可以扩展以包含Command 的全部角色。毕竟,它只漏掉了一个execute()
函数。
View 的基本作用是根据浏览器的请求(这里是表单的 POST)采取行动并触发结果的显示。
您可以选择其中一个。您可以在表单上有一个execute()
方法,然后从您的视图中调用它。或者您可以在检查is_valid()
表单后让视图采取行动。我会亲自选择实际操作的形式,以及显示结果的视图。
因此,在我看来,我会自定义 form_valid()
方法来调用 execute()
并根据结果执行所需的任何操作。那就是:
class MyForm(Form):
# whatever you already have there
def clean(self):
# unrelated to your issue, but just reminding you
# this is the place you make sure everything is right
# This very method MUST raise an error if any input or any
# other condition already known at that point is not fulfilled.
if you_do_not_want_to_grand_an_account:
raise ValidationError('Account not granted, because.')
return self.cleaned_data
def execute(self):
do_whatever()
needs_to_be_done()
if it_failed:
raise AnAppropriateError(_('Descriptive message'))
class MyView(FormView):
form = MyForm
# your other view stuff here
def form_valid(self, form):
try:
form.execute()
except AnAppropriateError as err:
messages.add_message(self.request, messages.ERROR, err.message)
else:
messages.add_message(self.request, messages.INFO, _('It worked'))
AnAppropriateError
可能只是 RuntimeError
,如果您希望它快速而肮脏,但您应该定义自己的异常类。
此外,您可能想要装饰 execute()̀ method with
@transaction.atomic()`,具体取决于其内容。
最后一点,请记住,您无法确定电子邮件是否真的被发送。即使有一些错误,邮件系统也会接受电子邮件是很常见的。几天后,您只会知道它何时反弹。
【讨论】:
【参考方案2】:首先,从模型类发送电子邮件确实不是一个好主意。我对此表示强烈反对。最好在你的表单中这样做
但是你已经实现了。
有一种方法可以在模型中访问请求,但我不推荐它。有一个插件叫CRequest Middlewar,https://pypi.python.org/pypi/django-crequest
您可以使用此中间件在任何新的地方访问请求。
$ python pip install django-crequest
首先导入crequest的中间件:
from crequest.middleware import CrequestMiddleware
获取当前请求;):
current_request = CrequestMiddleware.get_request()
完成。现在您可以在该模型中随意使用消息框架。
【讨论】:
有替代方案很好,但我真的建议不要使用该包,因为实现有点幼稚(例如,如果视图引发异常并且另一个中间件处理它 - 它依赖于 CPython 实现细节 - 即 GIL - 用于线程安全)。此外,您必须处理它返回None
的可能性。
是的,我也不喜欢。以上是关于如何从 Django 模型(覆盖)save() 函数向视图发送警报或消息?的主要内容,如果未能解决你的问题,请参考以下文章
覆盖 Django InlineModelAdmin 上的 save_model
为啥在 Django 管理员的 save() 覆盖中将站点添加到对象似乎不起作用?
Django,如何在调用super()后更新save()方法中的模型?
如何从覆盖的雄辩的 save() 方法访问模型属性? (将空输入转换为空)
如何使用 Django 信号(Pre_save,Post_save)从“B”模型的 ImageField 设置“A”模型的 ImageField