2010-09-09 57 views
2

我正在一个网站上销售产品(一类销售,一类产品)。无论何时我销售产品,我都希望将该操作保存在历史记录表中,并决定使用观察者模式来执行此操作。django中使用观察者模式的问题

即:当我调用Sales类的save_sale()方法时,我的类Sales和Subject类都是观察者,我将通知观察者。 (我已经决定使用这种模式,因为以后我还会发送一封电子邮件,通知管理员等)

这是我的主题类(销售类从这个扩展)

class Subject: 
    _observers = [] 

    def attach(self, observer): 
     if not observer in self._observers: 
      self._observers.append(observer) 

    def detach(self, observer): 
     try: 
      self._observers.remove(observer) 
     except ValueError: 
      pass 

    def notify(self,**kargs): 
     for observer in self._observers: 
      observer.update(self,**kargs) 

在视图上我做这样的事

sale = Sale() 
sale.user = request.user 
sale.product = product 
h = History() #here I create the observer 
sale.attach(h) #here I add the observer to the subject class 
sale.save_sale() #inside this class I will call the notify() method 

这是历史

def update(self,subject,**kargs): 
    self.action = "sale" 
    self.username = subject.user.username 
    self.total = subject.product.total 
    self.save(force_insert=True) 

的更新方法它第一次正常工作,但是当我尝试进行另一次销售时,出现一个错误,指出由于主键约束而无法插入历史记录。

我的猜测是,当我第二次调用视图时,第一个观察者仍在Subject类中,现在我有两个历史观察者正在倾听Sales,但我不确定这是否是问题天哪我想念从PHP的print_r)。

我在做什么错?我什么时候需要“附加”观察者?或者有更好的方法来做到这一点?

顺便说一句:我正在使用Django 1.1,我无法安装任何插件。

回答

2

我想这是因为_observers = []行为像静态共享领域。因此,Subject的每个实例都会更改_observers实例,并且具有不必要的副作用。

在构造函数初始化这个变量:

class Subject: 

    def __init__(self): 
     self._observers = [] 
+0

我试过那天。但似乎主体的构造函数从来没有被调用过,我甚至试图用super(Sales,self)从子类的构造函数调用它.__ init __()但我没有运气 – pleasedontbelong 2010-09-13 07:19:23

+0

如果一个基类有一个__init __( )方法,派生类的__init __()方法(如果有的话)必须显式调用它以确保实例的基类部分正确初始化;例如:BaseClass的.__的init __(自我,[参数...]) – dmitko 2010-09-13 07:44:15

+0

http://docs.python.org/reference/datamodel.html#object.__init__ – dmitko 2010-09-13 07:44:47

9

这可能不是一个可以接受的答案,因为它与架构更相关,但是您是否考虑过使用信号来通知系统该更改?看起来你正在努力做什么信号被设计做。 Django信号与Observer模式具有相同的最终结果功能。

http://docs.djangoproject.com/en/1.1/topics/signals/

+0

我读的页面,我会试试看,如果我找不到任何其他的解决办法。我不想依赖于专门为Django的开发东西,因为明天我需要用PHP来做这件事,而且我想要知道使用信号的技巧 – pleasedontbelong 2010-09-09 12:55:54

1

@Andrew Sledgeanswer表示解决这个问题的好办法。我想建议一种替代方法。

我有一个类似的问题,并开始使用信号。他们工作的很好,但我发现我的单元测试变慢了,因为每次使用灯具加载相关类的实例时都会调用信号。这增加了数十秒的测试运行。有一个工作,但我发现它笨拙。我定义了一个定制的测试跑步者,并在加载灯具之前将我的功能与信号断开。之后我重新连接了它们。

最后,我决定完全消除信号,并取而代之,取代适当的save()模型方法。在我的情况下,每当Order被更改时,表格中会自动创建一行,并在其中包含OrderHistory表格。为了做到这一点,我添加了一个函数来创建OrderHistory的实例,并从Order.save()方法中调用它。这也使得可以分别测试save()和该功能。

看一下这个SO question。它讨论了何时覆盖save()与何时使用信号。

+0

对我来说似乎太“django”了,我宁愿用一些可以用OOP解释的东西。所以有一天我可以在另一种语言中使用相同的逻辑..我最后想要的是重写我的Sale类的save(),我会调用notify()方法 – pleasedontbelong 2010-09-09 17:41:44

+0

恕我直言的信号(这只是回调)可以用OO方式或其他方式处理。 Python支持OO和非OO风格,因此Django也是如此。这就是说你要接电话了。 – 2010-09-09 17:45:30

1

谢谢大家的回答,阅读有关信号给了我另一种观点,但我不想使用它们,因为学习的目的(我想在Web开发中使用观察者模式:P)最后,我解决了是这样的:

class Sales(models.Model,Subject): 
    ... 
    def __init__(self): 
     self._observers = [] #reset observers 
     self.attach(History()) #attach a History Observer 
    ... 
    def save(self): 
     super(Sales,self).save() 
     self.notify() # notify all observers 

现在,每当我调用save(),观察员将被告知,如果我需要它,我可以添加或删除观察者

你觉得什么时候?这是解决这个问题的好方法吗?

+0

是的,但是你可以在Subject构造函数中使用self._observers = []。 – dmitko 2010-09-13 07:15:24