Джанго - Что является обратным эквивалентом каскада? - PullRequest
3 голосов
/ 03 марта 2011

Я пишу небольшую музыкальную базу данных.Изучение SQL лежит довольно давно в моем прошлом, и я всегда хотел попробовать django.Но есть одна вещь, которую я не мог обернуть вокруг себя.

Сейчас мои модели состоят только из двух классов, Album и Song.Song имеет внешний ключ, указывающий на альбом, которому он принадлежит.Теперь, если бы я удалил Album, все Song s, «принадлежащие» ему, были бы удалены из-за каскадного эффекта.

Альбомы вроде как виртуальны в моей базе данных, только песни фактически представлены нафайловая система и альбомы построены в соответствии с тегами песен, поэтому я могу только знать, что альбом больше не существует, если на него больше нет песен (так как их больше нет в файловой системе).

Или, короче говоря, как я могу добиться каскада в обратном порядке, то есть, если больше песен не указывает на альбом, альбом также должен быть удален?

Ответы [ 4 ]

4 голосов
/ 03 марта 2011

Вы можете использовать сигнал pre-delete для удаления альбома, когда песня удалена и песен больше нет.

from yourapp.models import Album, Song
from django.db.models.signals import pre_delete

def delete_parent(sender, **kwargs):
    # Here you check if there are remaining songs.
    ....

pre_delete.connect(delete_parent, sender=Song)
1 голос
/ 03 марта 2011

У меня была похожая проблема, и я добавил счетчик в эквивалент альбома. Если счетчик равен 0, а операция - delete (), тогда объект альбома - delete () d.

Другое решение для перегрузки метода delete () в модели песни или использования post-delete для удаления альбома.

0 голосов
/ 29 января 2014

Есть очень деликатный момент реализации, который я решил добавить к этому обсуждению.

Допустим, у нас есть две модели, одна из которых ссылается на другую по внешнему ключу, например:

class A(models.Model):
    x = models.IntegerField()

class B(models.Model):
    a = models.ForeignKey(A, null=True, blank=True)

Теперь, если мы удалим запись A, каскадное поведение также приведет к удалению ссылки в B.

Пока все хорошо. Теперь мы хотим изменить это поведение. Очевидный способ, как уже упоминалось, состоит в том, чтобы использовать сигналы, излучаемые во время удаления, поэтому мы идем:

def delete_reverse(sender, **kwargs):
    if kwargs['instance'].a:
        kwargs['instance'].a.delete()

post_delete.connect(delete_reverse, sender=B)

Это кажется идеальным. Это даже работает! Если мы удалим запись B, соответствующая A также будет удалена.

ПРОБЛЕМА состоит в том, что это имеет циклическое поведение, которое вызывает исключение: если мы удаляем элемент A из-за каскадного поведения по умолчанию (которое мы хотим сохранить), соответствующий элемент B также будет удален, что вызовет delete_reverse, который попытается удалить уже удаленный элемент!

Хитрость в том, что для правильной реализации обратного каскадирования требуется ИСКЛЮЧЕНИЕ:

def delete_reverse(sender, **kwargs):
    try:
        if kwargs['instance'].a:
            kwargs['instance'].a.delete()
    except:
        pass

Этот код будет работать в любом случае. Я надеюсь, что это помогает некоторым людям.

0 голосов
/ 03 марта 2011

Django 1.3 предоставит возможность настраивать каскадное поведение [1], но, насколько я знаю, по-прежнему не будет способа достичь того, что вы описываете автоматически.

Вероятно, наилучшим методом было бы написать обработчик сигнала post_delete [2] для вашего класса Song, который проверяет, есть ли у соответствующего Album оставшиеся Song и удаляет ли он, если это необходимо. Я считаю, что значение ForeignKey все еще должно присутствовать в аргументе instance, несмотря на то, что Song был удален из базы данных. (Если нет, попробуйте использовать pre_delete)

В качестве альтернативы вы можете переопределить метод delete [3], но это не всегда вызывается в зависимости от того, как вы удаляете свои объекты.

[1] http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.on_delete

[2] http://docs.djangoproject.com/en/dev/ref/signals/#django.db.models.signals.post_delete

[3] http://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.delete

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