声明性模型生命周期挂钩,灵感来自rails回调。

django-lifecycle的Python项目详细描述


django生命周期挂钩

Package versionPython versions

概述

此项目提供了一个^ {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 ^{} and ^{} keyword arguments allow you to compare the model's state from when it was first instantiated to the current moment. You can also pass an ^{} to indicate any value - these are the defaults, meaning that by default the hooked method will fire. The ^{} keyword specifies which field to check against.

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 ^{} keyword meaning that the initial state of the ^{} field can be any value ("*").

Hook with Simple Change Condition

As we saw in the very first example, you can also pass the keyword argument ^{} to run the hooked method if a field has changed, regardless of previous or current value.

^{pr 10}$

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 ^{} and ^{} methods:

^{pr 12}$

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. ^{}.

^{}

^{tb 1}$

Condition Arguments

^{}

^{tb 2}$

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 ^{} when calling save:

^{pr 14}$

Limitations

Foreign key fields on a lifecycle model can only be checked with the ^{} argument. That is, this library only checks to see if the value of the foreign key has changed. If you need more advanced conditions, consider omiting the run conditions and accessing the related model's fields in the hooked method.

Changelog

0.4.1 (June 2019)

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

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java如何将jaxb插件扩展与gradlejaxbplugin一起使用   java Hibernate列表<Object[]>到特定对象   java使用多态性显示arraylist的输出   java水平堆叠卡,带有一定偏移量   java错误:找不到符号方法liesInt()   java客户机/服务器文件收发中的多线程流管理   在java中可以基于访问重载方法吗?   包含空元素的java排序数组   swing Java按钮/网格布局   java BottomNavigationView getmaxitemcount   java空指针异常字符串生成器   java Xamarin升级导致“类文件版本错误52.0,应为50.0”错误   java我正在尝试打印它,而不只是对每一行进行println   Tomcat7中的java是否需要复制上下文。将xml转换为conf/Catalina/locahost以使其生效   带有注入服务的java REST端点在何处引发自定义WebServiceException?   在Java中使用GPS数据   java如何将JFreeChart ChartPanel导出到包含添加的CrosshairOverlay的图像对象?   内置Eclipse期间的Java 8堆栈溢出   java在GWT编译的JavaScript中如何表示BigDecimal