需要一些关于为Django编写可重用应用程序的建议

时间:2010-07-15 07:09:55

标签: python django

我需要实现一个有限状态机来跟踪我的一些Django项目模型。我已经有一个类似的应用程序,但它与其他应用程序模型严重耦合,不能以任何方式重复使用。所以我决定重新考虑它。

几个小时后,这就是我提出的:

class StateMachine(models.Model):
    name = models.CharField(max_length=50, help_text="Must be an unique name")
    template = models.BooleanField(default=True)

    current_state = models.ForeignKey('State', blank=True, null=True, related_name='current_state')
    initial_state = models.ForeignKey('State', blank=True, null=True, related_name='initial_state')

    def get_valid_actions(self):
        return Action.objects.filter(machine=self, from_state=self.current_state)

    def copy(self):
        ...

class State(models.Model):
    name = models.CharField(max_length=50)

    machine = models.ForeignKey(StateMachine)

    def enter_hook(self, machine=None, action=None, state=None):
        pass

    def exit_hook(self, machine=None, action=None, state=None):
        pass

    def _copy(self, machine):
        ...

class Action(models.Model):
    name = models.CharField(max_length=50)

    machine = models.ForeignKey(StateMachine)

    from_state = models.ForeignKey(State, related_name='from_state')
    to_state = models.ForeignKey(State, blank=True, null=True, related_name='to_state')

    def is_valid(self):
        if self.machine.current_state == self.from_state:
            return True
        else:
            return False

    def act(self):
        if self.is_valid():
            self.from_state.exit_hook(machine=self.machine, action=self, state=self.from_state)
            self.machine.current_state = self.to_state
            self.machine.save()
            self.to_state.enter_hook(machine=self.machine, action=self, state=self.to_state)
        else:
            raise ActionNotApplicable()

        return self.machine

    def _copy(self, machine):
        ...

我对结果很满意。它完成了状态机预期要做的事情而不是其他事情。在我的模型中,我使用它是这样的:

class SampleModel(models.Model):
    machine = models.ForeignKey(StateMachine, null=True)

    def setup_machine(self):
        self.machine = StateMachine.objects.get(template=True, name='bla bla bla').copy()
        self.save()

我基本上使用管理界面创建一个“模板”机器,然后运行复制方法,复制状态机模型并将模板设置为False。

现在,我的问题出现了:)

  • 在SampleModel中使用ForeignKey是最佳附加方式 StateMachine到了吗?我读了 关于Django中的通用关系但是 我以前从未使用过它。在我的场景中使用它会有益吗?

  • 我想跟随“做一个 事情并且做得很好“哲学, 但我还有其他事情 要求实施。对于 例如,我需要发送每次状态更改时向特定组发送电子邮件,并且该组因州而异。我提出的第一个解决方案是在状态模型中添加“email”字段。但这违反了这个应用程序建议做的事情,这只是为了跟踪状态机。什么是解决这个问题的最佳方法?

  • 我还包括一些钩子函数 该模型,以便有人可以 稍后附加自定义行为,是这样的 最好的方法吗? (我认为 Django已经是一个信号系统了)

  • 还有其他想法/评论吗?我是新来的 这个可重复使用的应用程序:)

谢谢!

1 个答案:

答案 0 :(得分:1)

我在python中实现了一个有限状态机...机器模块本身的代码没有Django ......但是,这台机器用于管理Django模型上的state属性。 / p>

我认为您真正需要的唯一字段是state字段。其余的应该只是python声明(除非你有特殊的理由保存数据库中的所有内容)。

这是有限状态机的东西,你可以从中获取想法,甚至可以获取整个代码并重构它。有非常好的文档,我认为它非常简洁:http://dl.dropbox.com/u/1869644/state_automaton.zip(你可以生成点格式的图表!)

编辑:

  

我希望能够将特定状态链接到用户

在这种情况下(如果要保持通用),将users字段放在state automaton的子类中。例如:

class UserAlertStateAutomaton(FiniteStateAutomaton):
    """
    An automaton that alerts users when the state changes
    """
    users_to_alert = models.ManyToManyField(User)

    def change_state(self, new_state):
        """
        overrides the parent method to alert users that state has changed
        """
        super(UserAlertStateAutomaton, self).change_state(new_state)
        for user in self.users_to_alert:
            #do your thing
    def subscribe#... etc

这仍然意味着除了基本状态自动机类中的状态之外,您不需要保存任何其他内容。通过实现一个钩子系统(当从状态A转换到状态B时执行一个方法X),你几乎可以做任何事情,而且非常简单:检查我发给你的代码!