Django使用猴子补丁为django-newsletter添加上下文时无法访问内部variable_dict的问题
报错根因
- 从Python作用域规则来看,原方法里的
variable_dict是函数内部的局部临时变量,它的存在范围仅在send_message方法的执行栈帧里,方法执行结束后就会被内存回收,既不会挂载到类实例self上,也不会作为send_message方法自身的属性存在。外部补丁代码尝试直接引用variable_dict、或者通过方法/实例属性访问它,必然会触发NameError、AttributeError。 - 原补丁的执行时序完全错误:补丁逻辑是等
old_send_message全流程跑完(包括模板渲染、邮件发送的所有节点都执行完)之后,才尝试修改上下文。就算能拿到变量引用,也已经错过了模板渲染的时机,修改完全不会生效。
可落地的实现方案
方案1:统一拦截上下文生成入口(推荐,维护成本最低)
不需要硬抠原方法内部的局部变量,直接拦截原包中负责生成模板上下文的get_context函数,在它处理原有的variable_dict时,自动把自定义变量合并进去。这种方式完全适配原方法的执行逻辑,不会因为包版本升级出现功能错位,还能自动适配原方法对纯文本、HTML内容的转义规则。
对应的补丁代码调整如下:
# monkey_patches.py from unittest.mock import patch def monkey_patch(): from newsletter.models import Submission from newsletter.models import get_context as original_get_context # 优先存储原始的send_message引用,避免后续替换后出现无限递归 original_send_message = Submission.send_message def new_send_message(self, *args, **kwargs): # 提取当前发送动作对应的订阅者对象,支持动态生成个性化变量 current_subscription = args[0] if args else kwargs["subscription"] # 在这里组装需要追加的自定义上下文 custom_context = { "custom_salutation": f"尊敬的{current_subscription.name}", # 其他需要的自定义变量,支持接入你自己的业务查询逻辑 } # 重写get_context的逻辑,合并自定义变量 def patched_get_context(var_dict, *ctx_args, **ctx_kwargs): merged_dict = {**var_dict, **custom_context} return original_get_context(merged_dict, *ctx_args, **ctx_kwargs) # 在拦截get_context的生命周期内,执行原发送逻辑 with patch("newsletter.models.get_context", side_effect=patched_get_context): original_send_message(self, *args, **kwargs) Submission.send_message = new_send_message
注意:必须在打补丁前就把原始的
send_message、get_context引用存储到闭包里,不要在替换方法后再反向引用类上的同名方法,否则会触发无限递归栈溢出。
方案2:全量重写send_message逻辑(不推荐)
直接把原包中send_message的完整代码拷贝到自定义的新方法里,在原本组装variable_dict的位置手动追加自定义变量,再把替换后的方法挂载回Submission模型。
这种方式的缺陷很明显:后续django-newsletter版本迭代如果修改了send_message的内部逻辑(比如新增上下文字段、调整邮件发送参数),需要手动同步更新补丁代码,否则会出现功能和版本不兼容的问题。
开发注意事项
- 自定义上下文的取数逻辑要尽量轻量化。因为
send_message是针对每个订阅者单独调用的,如果在自定义逻辑里加入无缓存的数据库查询,批量发送邮件时会给数据库造成极大的查询压力。 - 不要随意修改原方法的转义逻辑。拦截
get_context的方式会自动跟随原方法的配置:渲染纯文本、邮件主题时使用无转义上下文,渲染HTML模板时使用开启转义的上下文,不会出现内容被错误转义、或者HTML内容存在XSS风险的问题。
内容的提问来源于stack exchange,提问作者Saleh Rabbaniy
相关产品推荐
相关产品推荐

