Переопределение QuerySet.delete () в Django - PullRequest
19 голосов
/ 23 июня 2011

У меня есть модель Django, которая содержит ядро ​​настроек для функции приложения.Вы никогда не должны удалять эту модель.Я пытаюсь обеспечить соблюдение этого приложения.Я отключил функцию удаления в админке, а также отключил метод удаления в модели, но QuerySet имеет свой собственный метод удаления.Пример:

MyModel.objects.all()[0].delete() # Overridden, does nothing

MyModel.objects.all().delete() # POOF!

По иронии судьбы, в документации Django говорится о том, почему delete () является методом в QuerySet, а не Manager:

Это механизм безопасности дляпредотвратить случайный запрос Entry.objects.delete () и удаление всех записей.

То, что включение .all() является «механизмом безопасности», по меньшей мере сомнительно.Вместо этого это эффективно создает бэкдор, который нельзя закрыть обычными средствами (переопределяя менеджер).

У кого-нибудь есть подсказка, как переопределить этот метод на таком ядре, как QuerySet, без необходимости чинить патчи исходному тексту?

Ответы [ 2 ]

44 голосов
/ 23 июня 2011

Вы можете переопределить Manager's значение по умолчанию QuerySet, переопределив метод Manager.get_query_set().

Пример:

class MyQuerySet(models.query.QuerySet):

    def delete(self):
        pass  # you can throw an exception


class NoDeleteManager(models.Manager):
    def get_query_set(self):
        return MyQuerySet(self.model, using=self._db)

class MyModel(models.Model)
    field1 = ..
    field2 = ..


    objects = NoDeleteManager()

Теперь MyModel.objects.all().delete() ничего не сделает.

Для получения дополнительной информации: Изменение исходного менеджера QuerySets

6 голосов
/ 26 февраля 2015

смешанный подход

https://gist.github.com/dnozay/373571d8a276e6b2af1a

используйте тот же рецепт, что и @manji,

class DeactivateQuerySet(models.query.QuerySet):
    '''
    QuerySet whose delete() does not delete items, but instead marks the
    rows as not active, and updates the timestamps
    '''
    def delete(self):
        self.deactivate()

    def deactivate(self):
        deleted = now()
        self.update(active=False, deleted=deleted)

    def active(self):
        return self.filter(active=True)


class DeactivateManager(models.Manager):
    '''
    Manager that returns a DeactivateQuerySet,
    to prevent object deletion.
    '''
    def get_query_set(self):
        return DeactivateQuerySet(self.model, using=self._db)

    def active(self):
        return self.get_query_set().active()

и создайте миксин:

class DeactivateMixin(models.Model):
    '''
    abstract class for models whose rows should not be deleted but
    items should be 'deactivated' instead.

    note: needs to be the first abstract class for the default objects
    manager to be replaced on the subclass.
    '''
    active = models.BooleanField(default=True, editable=False, db_index=True)
    deleted = models.DateTimeField(default=None, editable=False, null=True)
    objects = DeactivateManager()

    class Meta:
        abstract = True

другие интересные вещи

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