장고 모델 사용자 정의 save () 메소드에서 새 객체를 어떻게 식별해야합니까?


172

새 레코드를 저장할 때 (기존 레코드를 업데이트하지 않음) Django 모델 객체의 save () 메서드에서 특수 동작을 트리거하고 싶습니다.

자체 기록이 새롭고 업데이트되지 않도록하기 위해 (self.id! = None) 검사가 필요하고 충분합니까? 이것이 간과 될 수있는 특별한 경우?


정답으로 stackoverflow.com/a/35647389/8893667 을 선택하십시오 . 답변은 다음과 같은 많은 경우에 효과가 없습니다.UUIDField pk
Kotlinboy

답변:


204

업데이트 :self._state 개인 인스턴스 변수가 아니지만 충돌을 피하기 위해 이름이 지정된 설명을 사용하면 확인 self._state.adding하는 것이 선호되는 방법입니다.


self.pk is None:

객체가로 설정 UUIDField되어 있지 않으면 새 Model 객체 내에서 True를 반환 합니다 primary_key.

걱정해야 할 가장 중요한 경우는 ID 이외의 필드에 고유 제약 조건이 있는지 여부입니다 (예 : 다른 필드의 보조 고유 인덱스). 이 경우 여전히 새 레코드를 보유 할 수는 있지만 저장할 수는 없습니다.


20
객체 와의 신원을 확인할 is not때보다는 사용해야 합니다!=None
Ben James

3
모든 모델에 id 속성이있는 것은 아닙니다. 즉, 모델을 통해 다른 모델을 확장하는 모델 models.OneToOneField(OtherModel, primary_key=True)입니다. 나는 당신이 사용해야한다고 생각합니다self.pk
AJP

4
경우에 따라 작동하지 않을 수 있습니다. 이 답변을 확인하십시오 : stackoverflow.com/a/940928/145349
fjsj

5
이것은 정답이 아닙니다. 를 UUIDField기본 키로 사용하는 경우 self.pknever None입니다.
Daniel van Flymen

1
참고 :이 답변은 사전에 작성된 UUIDField입니다.
Dave W. Smith

190

모델을 self.pk확인할 수있는 대체 방법self._state

self._state.adding is True 창조

self._state.adding is False 업데이트

페이지 에서 얻었습니다


12
사용자 정의 기본 키 필드를 사용할 때 이것이 올바른 방법입니다.
webtweakers 2012.

9
self._state.adding작동 방식 에 대한 모든 세부 사항에 대해서는 확실하지 않지만 github.com/django/django/blob/stable/1.10.x/django/db/models/를 호출 한 후False 확인하면 항상 동일한 것 같습니다 . super(TheModel, self).save(*args, **kwargs)
agilgur5

1
이것은 올바른 방법이며 정답으로 공표 / 설정해야합니다.
flungo

7
@guival : _state비공개가 아닙니다. 처럼 _meta필드 이름과 혼동을 피하기 위해 밑줄이 붙습니다. (링크 된 문서에서 어떻게 사용되는지에 주목하십시오.)
Ry-

2
이것이 가장 좋은 방법입니다. 내가 사용 is_new = self._state.adding후, super(MyModel, self).save(*args, **kwargs)다음if is_new: my_custom_logic()
kotrfa

45

검사 self.id는 이것이 id모델의 기본 키 라고 가정합니다 . 보다 일반적인 방법은 pk 단축키 를 사용하는 것 입니다.

is_new = self.pk is None


15
프로 팁 :이 넣어 전에super(...).save() .
sbdchd 2016 년

39

에 대한 검사 self.pk == None입니다 하지 개체를 삽입하거나 데이터베이스에 업데이트 될 것입니다 있는지 확인하기에 충분.

Django O / RM에는 기본적으로 PK 위치에 무언가가 있는지 확인하고 UPDATE가있는 경우 INSERT를 수행하는 특히 불쾌한 해킹이 있습니다 (PK가 None 인 경우 INSERT에 최적화 됨).

이 작업을 수행해야하는 이유는 개체를 만들 때 PK를 설정할 수 있기 때문입니다. 기본 키의 시퀀스 열이있는 경우 일반적이지 않지만 다른 유형의 기본 키 필드에는 적용되지 않습니다.

정말로 알고 싶다면 O / RM의 기능을 수행하고 데이터베이스를 살펴 봐야합니다.

물론 코드에 특정 사례 self.pk == None가 있으며 알아야 할 모든 것을 알려주는 가능성이 높지만 일반적인 해결책 은 아닙니다 .


좋은 지적! 새 객체에 pk를 설정하지 않기 때문에 응용 프로그램에서 기본 키 없음을 확인 하여이 문제를 해결할 수 있습니다. 그러나 이것은 재사용 가능한 플러그인이나 프레임 워크의 일부를 확인하는 것이 아닙니다.
MikeN 2016 년

1
기본 키를 사용자 자신과 데이터베이스를 통해 할당 할 때 특히 그렇습니다. 이 경우 가장 확실한 것은 db로 이동하는 것입니다.
콘스탄틴 M

1
응용 프로그램 코드가 pks를 명시 적으로 지정하지 않더라도 테스트 케이스에 대한 조명기가있을 수 있습니다. 그러나 테스트 전에 일반적으로로드되므로 문제가되지 않을 수 있습니다.
Risadinha

1
이는 UUIDField기본 키로 사용하는 경우에 특히 그렇습니다 . 키는 DB 수준에서 채워지지 않으므로 self.pk항상 True입니다.
Daniel van Flymen

10

"만들어진"크워 그를 보내는 post_save 신호에 연결할 수 있습니다. 참이면 개체가 삽입 된 것입니다.

http://docs.djangoproject.com/en/stable/ref/signals/#post-save


8
로드가 많은 경우 경쟁 조건이 발생할 수 있습니다. post_save 신호는 저장시 전송되지만 트랜잭션이 커밋되기 전에 전송되기 때문입니다. 이것은 문제가 될 수 있으며 디버깅하기가 매우 어려울 수 있습니다.
Abel Mohler

이전 버전에서 변경 사항이 있는지 확실하지 않지만 신호 핸들러가 동일한 트랜잭션 내에서 호출되므로 실패가 전체 트랜잭션을 롤백합니다. 을 사용 ATOMIC_REQUESTS하고 있으므로 기본값에 대해 잘 모르겠습니다.
Tim Tisdall

7

self.idforce_insert플래그를 확인하십시오 .

if not self.pk or kwargs.get('force_insert', False):
    self.created = True

# call save method.
super(self.__class__, self).save(*args, **kwargs)

#Do all your post save actions in the if block.
if getattr(self, 'created', False):
    # So something
    # Do something else

새로 만든 객체 (자체)에 pk가치 가 있기 때문에 편리 합니다.


5

이 대화에 늦었지만 self.pk와 관련된 기본값이있을 때 채워지는 문제가 발생했습니다.

이 문제를 해결하는 방법은 모델에 date_created 필드를 추가하는 것입니다

date_created = models.DateTimeField(auto_now_add=True)

여기에서 당신은 갈 수 있습니다

created = self.date_created is None


4

UUIDField기본 키 를 가지고 있어도 작동하는 솔루션의 경우 (다른 사람이 무시한None 경우 가 아니라 save) 장고의 post_save 신호에 연결할 수 있습니다 . 당신이 추가 models.py :

from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=MyModel)
def mymodel_saved(sender, instance, created, **kwargs):
    if created:
        # do extra work on your instance, e.g.
        # instance.generate_avatar()
        # instance.send_email_notification()
        pass

이 콜백은 save메소드 를 차단 하므로 양식을 사용하든 AJAX 호출에 Django REST 프레임 워크를 사용하든 응답을 유선으로 다시 보내기 전에 트리거 알림과 같은 작업을 수행하거나 모델을 추가로 업데이트 할 수 있습니다. 물론 사용자를 기다리지 않고 책임감있게 사용하고 무거운 작업을 작업 대기열로 오프로드하십시오. :)




0

위의 모든 시나리오에서 작동합니까?

if self.pk is not None and <ModelName>.objects.filter(pk=self.pk).exists():
...

이로 인해 추가 데이터베이스 적중이 발생합니다.
David Schumann

0
> def save_model(self, request, obj, form, change):
>         if form.instance._state.adding:
>             form.instance.author = request.user
>             super().save_model(request, obj, form, change)
>         else:
>             obj.updated_by = request.user.username
> 
>             super().save_model(request, obj, form, change)

cleaning_data.get ()을 사용하여 인스턴스가 있는지 여부를 확인할 수 있었으며 CharField가 null이고 비어있는 곳이 true였습니다. 이것은 로그인 한 사용자에 따라 각 업데이트마다 업데이트됩니다
Swelan Auguste

-3

개체 (데이터)를 업데이트하거나 삽입하고 있는지 확인하려면 self.instance.fieldname양식에서 사용 하십시오. 양식에서 정리 함수를 정의하고 현재 값 항목이 이전 값과 동일한 지 확인하십시오.

self.instanceself.instance.fieldname새로운 가치 와 비교

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.