Django: отключить двоичные журналы для метода delete () - PullRequest
0 голосов
/ 06 июля 2018

У меня есть конфигурация MySQL master-slave, и я хочу удалить некоторые старые записи только на master и сохранить их на slave:

class MonitorQuerySet(models.QuerySet):
    def delete(self, *args, **kwargs):
        with connection.cursor() as cur:
            cur.execute('SET @@session.sql_log_bin = 0')
            ret = super().delete(*args, **kwargs)
            cur.execute('SET @@session.sql_log_bin = 1')
        return ret

class Monitor(models.Model):
...
    objects = models.Manager()
    nobinlog = MonitorQuerySet.as_manager()

Проблема с этим подходом состоит в том, что теперь можно сделать Monitor.nobinlog.delete(), и это уничтожит всю таблицу.

Я хотя бы около pre_delete и post_delete сигналов, но они будут работать при каждом вызове на delete().

Есть ли лучшее решение для этого?

1 Ответ

0 голосов
/ 09 июля 2018

Я думаю, что вы имеете в виду тот факт, что вам обычно приходится явно указывать Monitor.objects.all().delete(). Вы можете получить такое поведение, реализовав свой менеджер и набор запросов отдельно вместо создания менеджера из набора запросов с помощью as_manager, например:

class NoBinLogQuerySet(models.QuerySet):
    def delete(self, *args, **kwargs):
        with connection.cursor() as cur:
            cur.execute('SET @@session.sql_log_bin = 0')
            ret = super().delete(*args, **kwargs)
            cur.execute('SET @@session.sql_log_bin = 1')
        return ret

class NoBinLogManager(models.Manager):
    def get_queryset(self):
        return NoBinLogQuerySet(self.model, using=self._db)

class Monitor(models.Model):
    ...
    objects = models.Manager()
    nobinlog = NoBinLogManager()

Это не защитит вас от неправильных аргументов .filter() и случайного удаления ваших данных.

Если у вас есть фиксированный набор условий, таких как «записи старше 30 дней», я бы написал вспомогательный метод вместо создания дополнительного менеджера. В этом методе вы можете жестко запрограммировать условие. В качестве дополнительной меры безопасности вы можете добавить параметр dry_run, который будет возвращать записи, которые будут удалены. Примерно так:

def delete_old_records(dry_run=True):
    kwargs = {'updated_at__lt': now() - timedelta(days=30)}
    if dry_run:
        ret = Monitor.objects.filter(**kwargs)
    else:
        with connection.cursor() as cur:
            cur.execute('SET @@session.sql_log_bin = 0')
            ret = Monitor.objects.delete(**kwargs)
            cur.execute('SET @@session.sql_log_bin = 1')
    return ret

В зависимости от того, как вы выполняете этот метод, рассмотрите возможность создания настраиваемой команды управления.

...