声明性模型生命周期挂钩,灵感来自rails回调。
django-lifecycle的Python项目详细描述
django生命周期挂钩
概述
此项目提供了一个^ {CD1>}装饰器,以及一个基础模型或MIXIN,为您的Django模型添加生命周期挂钩。django提供生命周期挂钩的内置方法是Signals。然而,在我所从事的项目中,我的团队经常发现,信号引入了不必要的间接性,并且与Django的“胖模型”方法(在模型类本身包含相关逻辑)相冲突。
简而言之,您可以编写如下所示的模型代码:
fromdjango_lifecycleimportLifecycleModel,hookclassUserAccount(LifecycleModel):username=models.CharField(max_length=100)password=models.CharField(max_length=200)password_updated_at=models.DateTimeField(null=True)@hook('before_update',when='password',has_changed=True)deftimestamp_password_change(self):self.password_updated_at=timezone.now()
而不是以损害可读性的笨拙方式重写save
和__init___
:
# same class and field declarations as above ...def__init__(self,*args,**kwargs):super().__init__(*args,**kwargs)self.__original_password=self.passworddefsave(self,*args,**kwargs):ifself.pkisnotNoneandself.password!=self.__original_password:self.password_updated_at=timezone.now()super().save(*args,**kwargs)
*这并不是说信号永远都没有用处;我的团队更喜欢将它们用于与业务领域无关的附带问题,比如缓存失效。
目录:
安装
pip install django-lifecycle
要求
- Python(3.3、3.4、3.5、3.6)
- Django(1.8、1.9、1.10、1.11、2.0)
用法
扩展提供的抽象基本模型类:
fromdjango_lifecycleimportLifecycleModel,hookclassYourModel(LifecycleModel):name=models.CharField(max_length=50)
或将MIXIN添加到Django模型定义:
fromdjango.dbimportmodelsfromdjango_lifecycleimportLifecycleModelMixin,hookclassYourModel(LifecycleModelMixin,models.Model):name=models.CharField(max_length=50)
:感叹号:如果您使用的是django 1.8或更低版本,并且要扩展基本模型,则还必须将django_lifecycle
添加到INSTALLED_APPS
。
很好,现在我们可以开始添加生命周期挂钩了!让我们做几个例子来说明不仅能够钩住某些事件,而且能够添加基本条件来代替样板条件代码的需要。
示例
简单挂钩-无条件
Say you want to process a thumbnail image in the background and send the user an email when they first sign up:
^{pr 6}$Or say you want to email a user when their account is deleted. You could add the decorated method below:
^{pr 7}$Hook with Transition Conditions: Part I
Maybe you only want the hooked method to run only under certain circumstances related to the state of your model. Say if updating a model instance changes a "status" field's value from "active" to "banned", you want to send them an email:
^{pr 8}$The ^{
Hook with Transition Conditions: Part II
You can also enforce certain dissallowed transitions. For example, maybe you don't want your staff to be able to delete an active trial because they should always expire:
^{pr 9}$We've ommitted the ^{
Hook with Simple Change Condition
As we saw in the very first example, you can also pass the keyword argument ^{
Hook with "Is Not" Condition
You can also have a hooked method fire when a field's value IS NOT equal to a certain value. See a common example below involving lowercasing a user's email.
^{pr 11}$Custom Condition
If you need to hook into events with more complex conditions, you can take advantage of ^{
Multiple decorators, same method
You can decorate the same method multiple times if you want.
^{pr 13}$Documentation
Lifecycle Hooks
The hook name is passed as the first positional argument to the @hook decorator, e.g. ^{
^{
Condition Arguments
^{
Other Utility Methods
These are available on your model when you use the mixin or extend the base model.
^{tb 3}$Suppressing Hooked Methods
To prevent the hooked methods from being called, pass ^{
Limitations
Foreign key fields on a lifecycle model can only be checked with the ^{
Changelog
0.4.1 (June 2019)
- Fixes urlman-兼容性。
0.4.0(2019年5月)
- 修正
initial_value(field_name)
行为-即使没有改变也应该返回值。谢谢@adamjlev!
0.3.2(2019年2月)
- 修复了防止钩子为自定义pks触发的错误。谢谢@atugushev!
0.3.1(2018年8月)
- 修复了M2M字段错误,其中访问{{CD4}}中自动生成的反向字段导致异常B/C PK尚未存在。谢谢@garyd203!
0.3.0(2018年4月)
- 重置调用
save
后挂钩条件的模型比较状态。
0.2.4(2018年4月)
- 修复了向同一方法添加多个
@hook
修饰符的支持。
0.2.3(2018年4月)
- 从早期的实现中删除剩余的MIXIN方法。
0.2.2(2018年4月)
- save方法现在接受
skip_hooks
,这是一个可选的布尔关键字参数,用于控制是否调用挂接的方法。
0.2.1(2018年4月)
- 修正了
_potentially_hooked_methods
中的错误,该错误通过访问用@cache_property
或@property
修饰的模型实例方法而导致不必要的副作用。
0.2.0(2018年4月)
- 添加了django 1.8支持。谢谢@jtiai!
- 为python 3.4、3.5、3.6和django 1.8、1.11和2.0添加了tox测试。谢谢@jtiai!
测试
测试可以在/tests
文件夹中的简化django项目中找到。安装项目需求并执行./manage.py test
以运行它们。
许可证
见License。