Принудительное каскадное удаление для модели Django - PullRequest
0 голосов
/ 09 января 2020

У меня есть модель Django с отношениями внешних ключей, помеченными как deletion.PROTECT, и я согласен с таким поведением, поскольку именно так должна вести себя модель в большинстве сценариев ios.

Тем не менее, есть один вариант использования для тех моделей, где я вроде как должен выполнить «полное удаление» (ie пользователь хочет удалить свою учетную запись). В этом случае мне бы очень хотелось, чтобы все работало как CASCADE, вместо того, чтобы вручную удалять каждое отношение внешнего ключа. Есть ли способ сделать это чисто? В идеальном мире вызов model.delete () будет принимать параметр, похожий на force_cascade=True.

Ответы [ 2 ]

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

Поскольку django также создает базу данных с ЗАЩИЩЕННЫМИ отношениями, вам необходимо вручную выполнить каскадное удаление. В противном случае сама база данных запретит удаление. ORM

Django может помочь вам в этом, единственное, что вам нужно сделать, это найти рекурсивно все ссылки на пользователя и удалить их в обратном порядке. Это также является преимуществом, если вы делаете это вручную, так как вы можете заменить некоторые вхождения пользователя заменой (т. Е. Виртуальным «удаленным пользователем»). Я мог бы подумать о комментариях на доске объявлений, которые должны быть сохранены, даже если пользователь удаляет свою учетную запись.

Чтобы найти отношения, указывающие на текущего пользователя, и заменить их на пользователя-призрака, вы можете использовать следующее фрагмент.


from django.db.models import Model
from django.db.models.fields.reverse_related import ManyToOneRel, ManyToManyRel, ForeignObjectRel


def get_all_relations(model: Model) -> List[ForeignObjectRel]:
    result: List[ForeignObjectRel] = []
    for field in model._meta.get_fields(include_hidden=True):
        if isinstance(field, ManyToOneRel):
            result.append(field)
    return result

def print_updated(name, number):
    """
    Simple Debug function
    """
    if number>0:
        print(f"   Update {number} {name}")

def delete_user_and_replace_with_substitute(user_to_delete)
   # Replace all relations to user with fake replacement user 
   replacement_user = users.object.get(pk=0)   # define your replacement user
   for field in get_all_relations(user_to_delete):
        field: ManyToOneRel
        target_model: Model = field.related_model
        target_field: str = field.remote_field.name
        print_updated(target_model._meta.verbose_name,
                      target_model.objects.filter(**{target_field: station_to_delete}).update(**{target_field: replacement_user}))
    user_to_delete.delete()    

Для реального удаления просто замените функцию .update(...) на вызов .delete() (не забывайте рекурсивно искать защищенные отношения, если необходимо)

Также может быть решение, связанное с postgresql, о котором я не знаю. Данное решение не зависит от базы данных.

Как правило, рекомендуется сохранять все отношения ЗАЩИЩЕННЫМИ, чтобы предотвратить случайное удаление важных записей базы данных, и осторожно удалять вручную.

0 голосов
/ 09 января 2020

models.PROTECT - настройка таблицы в базе данных. Вам нужно будет выполнить необработанные SQL инструкции, чтобы переопределить его, и это будет спецификация базы данных c (и я понятия не имею, как это сделать).

Альтернативой является перемещение по «дереву» объектов, которые вы хотите удалить, а затем удаление объектов, работающих с защищенных «листьев» внутрь, в «ствол». Так что если бы у вас было

class Bar( models.Model):
   user = models.ForeignKey( User, models.PROTECT, ...) 
   ...

class Foo( models.Model):
    bar = models.ForeignKey( Bar, models.PROTECT, ... )
    ...

Затем, чтобы удалить пользовательский объект user, вам понадобится

def delete_user( user):
    for bar in user.bar_set.all():
        bar.foo_set.all().delete()
        bar.delete()
    user.delete()

Я бы заключил его в транзакцию, чтобы он либо удалил все, либо ничего.

Он ударит по БД несколько раз. Я предполагаю, что количество связанных (bar, baz) объектов довольно мало и что вы не будете удалять пользователей очень часто.

Мне всегда было интересно, что делать, если экземпляр a имеет отношение защищенного внешнего ключа к экземпляру b и наоборот (возможно, через промежуточные соединения). По номинальной стоимости это означает, что вы можете создавать объекты, которые нельзя удалить.

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