首页 > 解决方案 > Django - cascade on other condition

问题描述

I'm using Django and a Postgres database and I have two tables (Authors, Books).

I don't want to enable to deletion of any records, instead to change the boolean of an unseen field called 'active' - a field both tables have. What i'd like to happen is when a user changes an Author's 'active' field to False, all of the books with a FK to this author in the Books table have their 'active' field's also set to False.

In Django, my only options it seems related to deletion, how would I set this up with the models.py file?

标签: djangopostgresql

解决方案


你可以用信号解决这个问题。在下面的示例中,我假设您的Book模型具有如下所示的外键字段。

class Book(models.Model):
    ...
    author = models.ForeignKey("Author", related_name="books")
    ...

选项 1. 使用信号

Author post_save连接到信号的示例函数。

from django.db import transaction
from django.db.models.signals import post_save

def update_books_status(sender, instance, created, **kwargs):
    with transaction.atomic():
        books = Books.objects.select_for_update().filter(author=instance)
        for book in books:
            book.active = False
            book.save()


post_save.connect(update_books_status, sender=Author)

在此示例中,原子事务确保所有更新都在单个数据库查询中运行。n+1如果您只是在for循环中运行查询集,这可以避免您遇到的问题。

选项 2. 覆盖save()

另一种可能更好的处理方法是覆盖Author模型save()方法。

class Author(models.Model):
    ...
    def save(self, *args, **kwargs):
        # Make sure we are not saving a new Author
        # And we are saving the Author as inactive
        # And we are are saving an Author that was active.
        if not self._state.adding and not self.active and self._loaded_values['active']:
            self.inactivate_books()
        super().save(*args, **kwargs)

    def inactivate_books(self):
        with transaction.atomic():
            books = Books.objects.select_for_update().filter(author=self)
            for book in books:
                book.active = False
                book.save()

推荐阅读