Обновить поле в модели для всех изменений "подмоделей" - PullRequest
0 голосов
/ 08 января 2020

Я знаю, как обновить какое-то поле ForeignKey. Например, когда я хочу менять поле last_modified каждый раз, когда изменяется Configuration или SomeOtherImportantClass:

class Configuration(models.Model):
    title = models.TextField()
    last_modified = models.DateTimeField(auto_now=True)

class SomeOtherImportantClass(models.Model):
    conf = models.ForeignKey(Configuration)
    important_number = models.IntegerField()

    def save(self, *args, **kwargs):
        conf.last_modified = timezone.now() # I'm not sure if it is necessary
        conf.save()
        return super().save(*args, **kwargs)

, но в моей реальной ситуации модель Cofiguration является ForeignKey для более чем 30 других моделей. В каждом из них я хочу обновить поле configuration.last_modified для каждого изменения, выполненного для них, или когда другая модель (которая имеет ForeignKey для некоторой модели, которая имеет ForeignKey do Configuration) изменяется. Таким образом, это выглядит так:

class Configuration(models.Model):
    title = models.TextField()
    last_modified = models.DateTimeField(auto_now=True)

class A(models.Model):
    conf = models.ForeignKey(Configuration)  # conf.last_modified must be updated on every change on A model object.

class B(models.Model):
    conf = models.ForeignKey(Configuration)  # same

...

class Z(models.Model):
    conf = models.ForeignKey(Configuration)  # same

class AA(models.Model):
    some_field = models.TextField()
    a = models.ForeignKey(A)
...

class ZZ(models.Model)
    some_field = models.TextField()
    z = models.ForeignKey(Z)

, поэтому даже если поле объекта AA "some_field" изменено, я хочу обновить поле конфигурации last_modified. Есть ли рекурсивный способ объявить это один раз в Configuration или где-то еще?

ОБНОВЛЕНИЕ: Могут существовать и правнуки, такие как классы AAA и AAAA.

1 Ответ

1 голос
/ 08 января 2020

Используйте абстрактные базовые классы , как описано в документации. Для AZ это довольно просто:

class ConfigurationChild(Model):

    conf = ForeignKey(Configuration)

    class Meta:
        abstract = True

    def save(self):
        self.conf.last_modified = ...
        self.conf.save()
        super().save()

class A(ConfigurationChild):
    # other fields, without conf

Для внуков это немного сложнее, потому что тогда нет прямой ссылки на conf. Установите атрибут в базовом классе, который вы заполняете в каждом дочернем классе:

class ConfigurationDescendant(Model):

    conf_handle = None

    class Meta:
        abstract = True

    def get_conf(self):
        if not self.conf_handle:
            return None  # or raise an error
        parent = getattr(self, self.conf_handle)
        if isinstance(parent, ConfigurationDescendant):
            return parent.get_conf()  # recursion
        else:
            # reached `ConfigurationChild` class, might want to check this
            return parent.conf if parent else None  

    def save(self):
        conf = self.get_conf()  
        # you might want to handle the case that the attribute is None
        if conf:
            conf.last_modified = ...
            conf.save()
        super().save()

class AA(ConfigurationDescendant):
    conf_handle = 'a'

    a = ForeignKey(A)

class AAA(ConfigurationDescendant):
    conf_handle = 'aa'
    aa = ForeignKey(AA)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...