Переопределение QuerySet.delete() в Django
У меня есть модель Django, которая содержит настройки ядра для функции приложения. Вы никогда не должны удалять эту модель. Я пытаюсь обеспечить применение этого приложения. Я отключил функцию удаления в администраторе, а также отключил метод удаления на модели, но у QuerySet есть собственный метод удаления. Пример:
MyModel.objects.all()[0].delete() # Overridden, does nothing
MyModel.objects.all().delete() # POOF!
По иронии судьбы, в документах Django сказано, что это говорит о том, почему delete() - это метод в QuerySet, а не Manager:
Это механизм безопасности, который предотвращает случайное обращение к Entry.objects.delete() и удаление всех записей.
Вопрос о том, как включить .all()
, - это "механизм безопасности", может быть, как минимум, сомнительным. Вместо этого это эффективно создает бэкдор, который не может быть закрыт обычными средствами (переопределение менеджера).
Кто-нибудь знает, как переопределить этот метод на чем-то в качестве ядра QuerySet без обезвреживания исправления источника?
Ответы
Ответ 1
Вы можете переопределить 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
Ответ 2
подход mixin
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()
и создайте mixin:
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
другой интересный материал