如何让Django每个模型只运行一次命令

时间:2016-01-11 16:11:55

标签: python django

我们正在为树项目使用django-MPTT,并且我必须在修改树项目时创建自动电子邮件通知的功能。绝对直截了当地使用保存信号,但是 - 如果修改了父项,那么n(n是儿童项目+父项目的数量)电子邮件将被发送而不是仅仅一个(因为父项的更改会自动对子项进行)。是否有一个很好的方法来防止这种情况,并基本上告诉Django“请为每个型号只发送一封电子邮件”?

我提出的最好的方法是一种hacky方式(和一个非常糟糕的黑客):在修改父项时更新的数据库中添加一个模型,并且子项在发送之前始终检查该模型电子邮件。即如果存在HiddenModel,请勿发送电子邮件,否则请发送电子邮件。在五秒钟之后(可能在另一个线程中)删除/撤消对该HiddenModel对象的修改。它可能会起作用,但在性能方面它只是糟糕的,所有这些数据库查询。

编辑:型号:( SO的缩写版本,可能在此格式中有一些错误)

来自mptt.models导入MPTTModel,TreeForeignKey

class TreeItem(MPTTModel):
    parent = TreeForeignKey('self', null=True, blank=True, related_name='children')

    users = models.ManyToManyField('accounts.User', blank=True)

    name = models.CharField(max_length=255)

    @property
    def tree_path(self):
        if self.is_root_node():
            return []

        tree = self.get_ancestors(include_self=False, ascending=False)
        return list(map(lambda item: item.name, tree))

    def save(self, *args, **kwargs):
        is_new = self.pk is None
        if not is_new:
            prev_allowed_users = TreeItem.objects.get(pk=self.pk).users.all()

        super().save(*args, **kwargs)

        new_allowed_users = self.users.all()
        if is_new:
            self.send_access_email(list(new_allowed_users)) #TODO
        else:
            if prev_allowed_users != new_allowed_users:
                send_to = self.get_new_access_users(prev_allowed_users, new_allowed_users) # TODO
                self.send_access_email(send_to)

    def get_new_access_users(self, prev_users, new_users):
        send_to = []
        for user in new_users:
            if user not in prev_users:
                send_to.append(user)

        return send_to

    def send_access_email(self, recipient_list):

        email_content = 'example'

        email = EmailMessage(
            'subject',
            email_content,
            'ex@ample.com',
            recipient_list)

        print("Sending email to %s users" % str(len(recipient_list)))

1 个答案:

答案 0 :(得分:0)

您需要某种注册表来确定您的对象是在外部保存还是作为遍历级联的树的一部分。似乎使用类变量可能是一种更优雅的方式。这里有一些伪代码来说明我的意思。

class TreeItem(MPTTModel):
    _save_registry = set()

    ... your model fields go here ...

    def is_save_locked(self):
        return any(p.id in TreeItem._save_registry for p in self.get_parents())

    def save_lock(self):
        TreeItem._save_registry.add(self.id)

    def save_release(self):
        TreeItem._save_registry.remove(self.id)

    def save(self, *args, **kwargs):
        if not self.is_save_locked:
             self.save_lock()
             ... do things only original save should do ...
             self.save_release()
        ... do things every save should do ...
        return super(TreeItem, self).save(*args, **kwargs)