проблемы с использованием шаблона наблюдателя в Django - PullRequest
4 голосов
/ 09 сентября 2010

Я работаю над сайтом, где продаю товары (один класс Продажа, один класс Продукт). Всякий раз, когда я продаю продукт, я хочу сохранить это действие в таблице истории, и я решил использовать для этого шаблон наблюдателя.

То есть: мой класс Sales является субъектом, а класс History - наблюдателем, всякий раз, когда я вызываю метод save_sale () класса 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)

на вид я делаю что-то вроде этого

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, и теперь у меня есть два наблюдателя истории, слушающих Продажи, но я не уверен, что это проблема (черт возьми) Я скучаю по print_r из php).

Что я делаю не так? Когда мне нужно «прикрепить» наблюдателя? Или есть лучший способ сделать это?

Кстати: я использую Django 1.1, и у меня нет доступа для установки каких-либо плагинов.

Ответы [ 4 ]

9 голосов
/ 09 сентября 2010

Это может быть неприемлемым ответом, поскольку он в большей степени связан с архитектурой, но рассматривали ли вы возможность использования сигналов для уведомления системы об изменении?Кажется, что вы пытаетесь сделать именно то, для чего предназначены сигналы.Сигналы Django имеют те же функции конечного результата, что и шаблоны Observer.

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

3 голосов
/ 13 сентября 2010

Я думаю, это потому, что _observers = [] действует как статическое общее поле. Таким образом, каждый экземпляр Subject изменяет экземпляр _observers и имеет нежелательный побочный эффект.

Инициализировать эту переменную в конструкторе:

class Subject:

    def __init__(self):
        self._observers = []
1 голос
/ 09 сентября 2010

Спасибо всем за ваши ответы, чтение о сигналах дало мне другую перспективу, но я не хочу использовать их из-за целей обучения (я хотел использовать шаблон наблюдателя в веб-разработке: 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 (), наблюдатели будут уведомлены, и, если мне это понадобится, я могу добавить или удалить наблюдателя

что ты думаешь? это хороший способ решить это?

1 голос
/ 09 сентября 2010

@ Andrew Sledge 's answer указывает на хороший способ решения этой проблемы.Я хотел бы предложить альтернативный подход.

У меня была похожая проблема, и я начал использовать сигналы.Они работали хорошо, но я обнаружил, что мои модульные тесты стали медленнее, так как сигналы вызывались каждый раз, когда я загружал экземпляр связанного класса с помощью прибора.Это добавило десятки секунд к тесту.Есть работа, но я нашел ее неуклюжей.Я определил пользовательский тестовый прогон и отключил свои функции от сигналов перед загрузкой приборов.Я переподключил их потом.

Наконец, я решил полностью отказаться от сигналов и вместо этого переопределил соответствующие save() методы моделей.В моем случае, когда меняется Order, автоматически создается строка в таблице OrderHistory.Для этого я добавил функцию для создания экземпляра OrderHistory и вызвал его из метода Order.save().это также позволило протестировать save() и функцию отдельно.

Взгляните на этот ТАК вопрос .В нем обсуждается, когда следует переопределить save() по сравнению с тем, когда использовать сигналы.

...