Skip to main content
 首页 » 编程设计

python之你能编写一个针对任意状态的 django-fsm 转换吗

2025年05月04日39cmt

我正在使用 django-fsm在 state 字段(类型 FSMField)上通过过程跟踪 sample 管。我需要允许某些高级用户将对象从一种状态“跳转”到另一种状态,并在发生这种情况时记录并发送通知。我的问题是:如何在避免代码重复(即 DRY)的同时编写此转换?

更多详情:

我在我的 FSMField 上设置了 protected=True:我真的很喜欢它提供的保护 - 没有其他代码路径能够更改 state。

这是基础知识(注意:不是完整的代码,预计不会工作,只是为了说明)

class SampleTube(model.Model): 
    state = FSMField(default='new', choices=(...), protected=True) 
 
    @transition(field=state, source='*', target='*', permission='my_app.superpowers')  # <-- problem 1 
    def set_state(self, user, new_state): 
        assert is_valid_state(new_state) 
        log_event(self, user, new_state) 
        send_notification(self, self.owner) 
        self.state = new_state  # <-- problem 2 

问题 1:据我所知,我只能为 target ( docs link ) 使用单个字符串值。很公平。我喜欢调用方法模型自动设置 state 这一事实。所以我不认为我可以编写到任意状态的转换。

问题2:如果我想保留protected=True(由于上述原因),我不能直接修改state字段(引发AttributeError ,如文档所示)

我必须求助于在我的模型类中编写这个吗?是否有一些元编程方法可以让我保持干爽?

@transition(field=state, source='*', target='used', permission='myapp.superpowers') 
def set_used(self, user): 
    # ... 
 
@transition(field=state, source='*', target='received', permission='myapp.superpowers') 
def set_received(self, user): 
    # ... 
 
#... loads more set_xyz methods that all have the same signature... 

我想解决这个问题的原因(除了欣赏简洁的代码)是潜在状态的数量非常大(10+)。

[edit] 我突然想到,暂时明确禁用 set_state 方法内的 state 字段的保护可能是解决此问题的另一种方法,如果我能解决的话怎么……

请您参考如下方法:

虽然作为 django-fsm 的作者我强烈不推荐使用你的方法,但我认为最终它可能会导致你在 set_state 函数中使用 if-elif 垃圾,我可以建议,不要将其作为模型方法来实现。

相反,只需创建一个常规函数,并直接更新数据库状态。

def set_state(tube, new_state): SampleTube.objects.filter(pk=tube.pk).update(state=new_state);