2010-07-15 59 views
1

爲了跟蹤我的一些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

我在python中實現了一個有限狀態機...機器模塊本身的代碼沒有Django ...但是,這臺機器用於管理Django模型上的state屬性。

我認爲你唯一需要的領域是state字段。其餘的應該只是python聲明(除非你有特殊的理由保存你的數據庫中的所有內容)。

這裏是這個有限狀態機的東西,你可以從中獲取想法,甚至可以獲取整個代碼並重構它。有非常好的文檔,我認爲它非常乾淨和簡單:http://dl.dropbox.com/u/1869644/state_automaton.zip(你可以生成點圖格式的圖表!)

編輯:

我想能夠在一個特定的狀態鏈接到用戶

在這種情況下(如果你想保留它通用)把用戶領域在子類中你的狀態自動機。例如:

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),您幾乎可以做任何事情,而且非常簡單:檢查我發送給您的代碼!

+0

我想在我的情況下,在數據庫中存儲所有內容是必要的。瞭解國家有幫助,但並不能解決我所有的問題。例如,我想能夠將特定狀態鏈接到用戶,以便用戶在狀態更改時接收更新。 – 2010-07-15 15:06:14

+0

我認爲它不會改變任何事情:檢查我的編輯... – sebpiq 2010-07-15 15:38:15

+0

謝謝!我會檢查代碼:) – 2010-07-15 20:14:23

相關問題