Django의 계단식 삭제 동작을 재정의하는 옵션은 무엇입니까?
Django 모델은 일반적으로 ON DELETE CASCADE 동작을 상당히 적절하게 처리합니다 (기본적으로 지원하지 않는 데이터베이스에서 작동하는 방식으로).
그러나 예를 들어 다음 시나리오에서 적절하지 않은 경우이 동작을 재정의하는 가장 좋은 방법을 찾기 위해 고군분투하고 있습니다.
ON DELETE RESTRICT (즉, 하위 레코드가있는 경우 개체 삭제 방지)
ON DELETE SET NULL (즉, 자식 레코드를 삭제하지 않고 관계를 끊기 위해 부모 키를 NULL로 설정)
기록 삭제시 기타 관련 데이터 업데이트 (예 : 업로드 된 이미지 파일 삭제)
다음은 내가 알고있는이를 달성 할 수있는 잠재적 인 방법입니다.
모델의
delete()
메서드를 재정의합니다 . 이러한 종류의 작업이 수행되지만QuerySet
. 또한 모든 모델delete()
은 Django의 코드가 호출super()
되지 않고QuerySet
하위 개체를 삭제 하는 데 사용할 수 있으므로 호출 할 수 없도록 재정의해야 합니다.신호를 사용하십시오. 모델을 직접 삭제하거나 QuerySet을 통해 삭제할 때 호출되므로 이상적입니다. 그러나 자식 개체가 삭제되는 것을 막을 수는 없으므로 ON CASCADE RESTRICT 또는 SET NULL을 구현하는 데 사용할 수 없습니다.
이를 적절히 처리하는 데이터베이스 엔진을 사용하십시오 (이 경우 Django는 무엇을합니까?).
Django가 지원할 때까지 기다립니다 (그리고 그때까지 버그와 함께 살고 있습니다 ...)
첫 번째 옵션이 유일하게 실행 가능한 것처럼 보이지만 추악하고 아기를 목욕물로 내 던지고 새 모델 / 관계가 추가되면 무언가를 놓칠 위험이 있습니다.
내가 뭔가를 놓치고 있습니까? 권장 사항이 있습니까?
이 문제를 겪는 사람들을위한 참고 사항으로, 이제 Django 1.3에 기본 제공 솔루션이 있습니다.
django.db.models.ForeignKey.on_delete 문서의 세부 사항을 참조하십시오. Fragments of Code 사이트의 편집자에게 지적 해 주셔서 감사합니다.
가장 간단한 시나리오는 모델 FK 필드 정의를 추가하는 것입니다.
on_delete=models.SET_NULL
Django는 CASCADE 동작 만 에뮬레이트합니다.
Django Users Group의 토론 에 따르면 가장 적절한 솔루션은 다음과 같습니다.
- ON DELETE SET NULL 시나리오를 반복하려면-obj.delete () 전에 obj.rel_set.clear () (모든 관련 모델에 대해)를 수동으로 수행하십시오.
- ON DELETE RESTRICT 시나리오를 반복하려면-수동으로 obj.delete () 전에 obj.rel_set이 비어 있는지 확인합니다.
좋아, 다음은 만족스럽지 않지만 내가 정한 해결책입니다.
모든 모델에 대한 추상 기본 클래스를 추가했습니다.
class MyModel(models.Model):
class Meta:
abstract = True
def pre_delete_handler(self):
pass
신호 처리기 pre_delete
는이 모델의 하위 클래스에 대한 모든 이벤트를 포착합니다 .
def pre_delete_handler(sender, instance, **kwargs):
if isinstance(instance, MyModel):
instance.pre_delete_handler()
models.signals.pre_delete.connect(pre_delete_handler)
각 모델에서 하위 레코드가있는 경우 메서드 ON DELETE RESTRICT
에서 예외를 throw하여 " "관계를 시뮬레이션 pre_delete_handler
합니다.
class RelatedRecordsExist(Exception): pass
class SomeModel(MyModel):
...
def pre_delete_handler(self):
if children.count():
raise RelatedRecordsExist("SomeModel has child records!")
데이터가 수정되기 전에 삭제가 중단됩니다.
불행히도, ON DELETE SET NULL
삭제할 객체 목록이 이미 Django에서 신호를 보내기 전에 생성 했기 때문에 pre_delete 신호 (예 : 에뮬레이션 )의 데이터를 업데이트 할 수 없습니다 . Django는 순환 참조에 갇히지 않고 객체에 불필요하게 여러 번 신호를 보내는 것을 방지하기 위해 이렇게합니다.
삭제를 수행 할 수 있는지 확인하는 것은 이제 호출 코드의 책임입니다. 이를 지원하기 위해 각 모델에는 via 또는 이와 유사한 prepare_delete()
키 설정을 처리 하는 방법이 있습니다 .NULL
self.related_set.clear()
class MyModel(models.Model):
...
def prepare_delete(self):
pass
내 너무 많은 코드를 변경하는 것을 방지하기 위해 views.py
와 models.py
의 delete()
방법에 오버라이드 (override) MyModel
호출 prepare_delete()
:
class MyModel(models.Model):
...
def delete(self):
self.prepare_delete()
super(MyModel, self).delete()
This means that any deletes explicitly called via obj.delete()
will work as expected, but if a delete has cascaded from a related object or is done via a queryset.delete()
and the calling code hasn't ensured that all links are broken where necessary, then the pre_delete_handler
will throw an exception.
And lastly, I've added a similar post_delete_handler
method to the models that gets called on the post_delete
signal and lets the model clear up any other data (for example deleting files for ImageField
s.)
class MyModel(models.Model):
...
def post_delete_handler(self):
pass
def post_delete_handler(sender, instance, **kwargs):
if isinstance(instance, MyModel):
instance.post_delete_handler()
models.signals.post_delete.connect(post_delete_handler)
I hope that helps someone and that the code can be re-threaded back into something more useable without too much trouble.
Any suggestions on how to improve this are more than welcome.
ReferenceURL : https://stackoverflow.com/questions/2475249/what-are-the-options-for-overriding-djangos-cascading-delete-behaviour
'developer tip' 카테고리의 다른 글
'위치 : 절대'가 flexbox와 충돌합니까? (0) | 2020.12.25 |
---|---|
Django의 ModelForm unique_together 유효성 검사 (0) | 2020.12.25 |
캐치 블록으로 돌아 오시겠습니까? (0) | 2020.12.25 |
Java에서 RESTful API를 만드는 방법을 배우는 데 가장 좋은 소스는 무엇입니까? (0) | 2020.12.25 |
다른 개체에 전달 될 때 IDisposable 개체에 대해 Dispose를 호출해야하는 사람은 누구입니까? (0) | 2020.12.25 |