简体   繁体   English

在Django中使用观察者模式的问题

[英]problems using observer pattern in django

I'm working on a website where I sell products (one class Sale, one class Product). 我正在一个销售产品的网站上工作(一类销售,一类产品)。 Whenever I sell a product, I want to save that action in a History table and I have decided to use the observer pattern to do this. 每当我销售产品时,我想在History表中保存该操作,并且我决定使用观察者模式来执行此操作。

That is: my class Sales is the subject and the History class is the observer, whenever I call the save_sale() method of the Sales class I will notify the observers. 那就是:我的类Sales是主题而History类是观察者,每当我调用Sales类的save_sale()方法时,我都会通知观察者。 (I've decided to use this pattern because later I'll also send an email, notify the admin, etc.) (我决定使用这种模式,因为稍后我也会发送电子邮件,通知管理员等)

This is my subject class (the Sales class extends from this) 这是我的主题类(Sales类从此扩展)

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)

on the view I do something like this 在视图上我做这样的事情

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

This is the update method on History 这是History的更新方法

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

It works fine the first time, but when I try to make another sale, I get an error saying I can't insert into History because of a primary key constraint. 它第一次工作正常,但是当我尝试再次销售时,我收到一条错误消息,说由于主键约束我无法插入历史记录。

My guess is that when I call the view the second time, the first observer is still in the Subject class, and now I have two history observers listening to the Sales, but I'm not sure if that's the problem (gosh I miss the print_r from php). 我的猜测是,当我第二次调用视图时,第一个观察者仍然在Subject类中,现在我有两个历史观察员正在听销售,但我不确定这是不是问题(天哪,我想念了来自php的print_r)。

What am I doing wrong? 我究竟做错了什么? When do I have to "attach" the observer? 我何时必须“附加”观察者? Or is there a better way of doing this? 或者有更好的方法吗?

BTW: I'm using Django 1.1 and I don't have access to install any plugins. 顺便说一句:我正在使用Django 1.1,我没有权限安装任何插件。

This may not be an acceptable answer since it's more architecture related, but have you considered using signals to notify the system of the change? 这可能不是一个可接受的答案,因为它与架构有关,但您是否考虑使用信号通知系统更改? It seems that you are trying to do exactly what signals were designed to do. 您似乎正在尝试完成信号的设计目标。 Django signals have the same end-result functionality as Observer patterns. Django信号具有与Observer模式相同的最终结果功能。

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

I think this is because _observers = [] acts like static shared field. 我认为这是因为_observers = []就像静态共享字段一样。 So every instance of Subject changes the _observers instance and it has unwanted side effect. 因此, Subject每个实例都会更改_observers实例,并且它会产生不必要的副作用。

Initialize this variable in constructor: 在构造函数中初始化此变量:

class Subject:

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

@Andrew Sledge 's answer indicates a good way of tackling this problem. @Andrew Sledge回答表明解决这个问题的好方法。 I would like to suggest an alternate approach. 我想建议一种替代方法。

I had a similar problem and started out using signals. 我有类似的问题,并开始使用信号。 They worked well but I found that my unit tests had become slower as the signals were called each time I loaded an instance of the associated class using a fixture. 它们运行良好,但我发现我的单元测试变慢了,因为每次使用夹具加载相关类的实例时都会调用信号。 This added tens of seconds to the test run. 这为测试运行增加了几十秒。 There is a work around but I found it clumsy. 有一个工作,但我发现它很笨拙。 I defined a custom test runner and disconnected my functions from the signals before loading fixtures. 我定义了一个自定义测试运行器,并在加载灯具之前将我的功能与信号断开。 I reconnected them afterwards. 之后我重新连接了它们。

Finally I decided to ditch signals altogether and overrode the appropriate save() methods of models instead. 最后,我决定完全抛弃信号,而是覆盖模型的相应save()方法。 In my case whenever an Order is changed a row is automatically created in and OrderHistory table, among other things. 在我的情况下,每当更改Order时, Order在和OrderHistory表中自动创建一行,以及其他内容。 In order to do this I added a function to create an instance of OrderHistory and called it from within the Order.save() method. 为了做到这一点,我添加了一个函数来创建OrderHistory的实例,并在Order.save()方法中调用它。 this also made it possible to test the save() and the function separately. 这也使得可以分别测试save()和函数。

Take a look at this SO question . 看看这个问题 It has a discussion about when to override save() versus when to use signals. 它讨论了何时覆盖save()与何时使用信号。

Thank you all for your answers, reading about signals gave me another perspective but i dont want to use them because of learning purposes (i wanted to use the observer pattern in web development :P) In the end, i solved doing something like this: 谢谢大家的答案,阅读信号给了我另一个视角,但我不想因为学习目的而使用它们(我想在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

now every time i call the save(), the observers will be notified and if i need it, i could add or delete an observer 现在每次调用save()时,都会通知观察者,如果需要,我可以添加或删除观察者

what do you think? 你怎么看? is this a good way to solve it? 这是解决它的好方法吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM