Должен ли Django сам реализовывать правила DB on_delete? - PullRequest
7 голосов
/ 11 июля 2011

У меня есть приложение Django 1.3, для которого я использую South 0.7.3 для миграции БД.У меня проблема, когда правило on_delete=models.SET_NULL не срабатывает при удалении родительской сущности, что дает мне нарушение ограничения из базовой БД (которая является Postgres 8.4).

Соответствующие частииз определения сущностей:

class AccessPeriod:
    ....

class Payment:
    period = models.ForeignKey( 
        AccessPeriod, related_name = "payments", db_index = True,
        null = True, on_delete = models.SET_NULL )

После некоторого копания я обнаружил, что Юг фактически не вставляет предложение ON DELETE в SQL, который он генерирует для миграций, поэтому БД определенно не собирается делатьаннулирование самих нарушенных отношений:

http://south.aeracode.org/ticket/763

Затем я читаю документы Django для правил on_delete, в которых говорится (мой акцент):

Когдаобъект, на который ссылается ForeignKey, удаляется, по умолчанию Django эмулирует поведение ограничения SQL ON DELETE CASCADE, а также удаляет объект, содержащий ForeignKey.Это поведение можно изменить, указав аргумент on_delete.

Часть «эмулирует», предложенная мне, что Django пытается реализовать поведение on_delete, и не полагается на базовую БД для автоматического выполнения этого какчасть транзакции, что делает Южный баг неуместным.

Я пробил db/models/deletion.py в источнике Django и на основе реализации SET() / SET_NULL() и collect() это, безусловно, кажетсяподобно тому, как Django должен делать это сам, однако, если я пытаюсь удалить AccessPeriod от администратора Django, я получаю нарушения ограничений в таблице платежей для идентификатора, на который он все еще ссылается, который теперь удален, т.е. он не похож на Djangoвызывает SET_NULL() отношения Payment.period как часть вызова accessPeriod.delete().

Я что-то здесь не так делаю или неправильно понимаю, что должен делать Джанго?Просто стараюсь не взламывать БД вручную, чтобы вставить правило ON DELETE самостоятельно, которое кажется чрезвычайно хрупким и ужасным.

1 Ответ

4 голосов
/ 31 января 2012

В моем конкретном случае я проследил эту ошибку до объявлений встроенного ModelAdmin в моем коде models.py, в результате чего классы моей модели были созданы некорректно, с нарушенным поведением on_delete наиболее заметным побочным эффектом этого.Из моего сообщения о коммите:

Исправлена ​​проблема с неправильным каскадным удалением в Django DB.

Оказывается, очень важно не объявлять ваши ModelAdmins в глобальной области видимости в моделях.py, в противном случае все отношения между различными моделями вычисляются до того, как все модели загружены, и многие из них не учитываются.На самом деле не так уж и много об этом говорится в документации по Django.

Так что в моем исходном (неработающем) коде я хотел бы, чтобы ModelAdmin для каждой Модели был объявлен сразу после нее, например: 1010 *

class AccessPeriod( models.Model ):
    ....

class AccessPeriodAdmin( models.ModelAdmin ):
    ....

# This causes the metaclass setup for AccessPeriod to happen right now,
# and since related models for AccessPeriod are not all declared yet,
# relationship handling behaviour becomes broken for AccessPeriod
admin.site.register( AccessPeriod, AccessPeriodAdmin )

class Payment( models.Model ):
    ....

class PaymentAdmin( models.ModelAdmin ):
    ....

# Same effect on the Payment model here    
admin.site.register( Payment, PaymentAdmin )

Решением было переместить все объявления ModelAdmin из myapp/models.py в myapp/admin.py, чтобы все настройки метакласса для каждого класса происходили после обработки всех объявлений модели, а затем начинались все отношенияснова ведет себя правильно.

...