Django-Model.create () 메서드 재정의?


87

장고 문서는 단지 오버라이드 (override)에 대한 예제 목록 save()delete(). 그러나 모델 이 생성 될 때만 모델에 대한 추가 처리를 정의하고 싶습니다 . Rails에 익숙한 사람이라면 :before_create필터 를 만드는 것과 같습니다 . 이것이 가능한가?

답변:


160

재정의 __init__()하면 객체의 파이썬 표현이 인스턴스화 될 때마다 코드가 실행됩니다. 레일을 모르지만 :before_created데이터베이스에서 개체가 생성 될 때 실행되는 코드처럼 필터가 들립니다. 데이터베이스에 새 객체가 생성 될 때 코드를 실행 save()하려면 객체에 pk속성 이 있는지 여부를 확인 하여을 재정의해야합니다 . 코드는 다음과 같습니다.

def save(self, *args, **kwargs):
    if not self.pk:
        # This code only happens if the objects is
        # not in the database yet. Otherwise it would
        # have pk
    super(MyModel, self).save(*args, **kwargs)

7
실제로 신호를 사용하는 솔루션을 찾았습니다 : docs.djangoproject.com/en/dev/topics/signals (특히 pre_save 신호). 그러나 이것은 훨씬 더 실용적인 해결책 인 것 같습니다. 무리 감사.
ground5hark 2010

4
나는 당신이 관리자 방법을 재정의하는 것을 의미한다고 가정 create합니까? 이것은 흥미로운 솔루션이지만 객체가 생성되는 경우 Object(**kwargs).save()또는 다른 변형을 사용하는 경우에는 작동하지 않습니다 .
Zach

3
나는 그것이 해킹이라고 생각하지 않습니다. 공식 솔루션 중 하나입니다.
les

6
super(MyModel, self).save(*args, **kwargs)그래?
Mark Chackerian

1
어쩌면 확인하는 것은 self.pk객체가 새로 생성되는하거나 업데이트되어 있는지 확인하는 가장 좋은 옵션이 아닙니다. 때로는 생성 시간에 개체 ID (예 : 사용자 지정 비 데이터베이스 생성 값 KSUID)를 제공하면이 절이 실행되지 않습니다 ... self._state.adding처음으로 저장하는지 아니면 업데이트 만하는지 확인하는 값이 있습니다. 이러한 경우에 도움이됩니다.
Shahinism

22

post_save 신호를 생성하는 방법의 예 ( http://djangosnippets.org/snippets/500/에서 )

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

@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
    """Create a matching profile whenever a user object is created."""
    if created: 
        profile, new = UserProfile.objects.get_or_create(user=instance)

다음은 신호 또는 사용자 지정 저장 방법을 사용하는 것이 가장 좋은지에 대한 신중한 토론입니다. https://web.archive.org/web/20120815022107/http://www.martin-geber.com/thought/2007/10/29/ django-signals-vs-custom-save-method /

제 생각에는이 작업에 신호를 사용하는 것이 더 강력하고 읽기 쉽지만 더 길다고 생각합니다.


이것은 객체 내부를 엉망으로 만드는 대신 선호되는 방법이지만 위의 예에서 다른 모델을 만드는 것이 아니라 문제의 모델을 수정하는 경우을 호출하는 것을 잊지 마십시오instance.save() . 따라서이 경우 데이터베이스에 대한 INSERT 및 UPDATE 쿼리가 하나씩 있기 때문에 성능이 저하됩니다.
Mike Shultz 2015

신호 대 사용자 지정 저장 방법에 대한 링크가 끊어졌습니다.
Sander Vanden Hautte

21

이것은 오래되었고 작동하는 대답 (Zach 's)과 좀 더 관용적 인 대답 (Michael Bylstra 's)도 가지고 있지만 여전히 대부분의 사람들이 Google에서 보는 첫 번째 결과이기 때문에 더 많은 모범 사례 modern-django가 필요하다고 생각합니다. 여기 스타일 대답 :

from django.db.models.signals import post_save

class MyModel(models.Model):
    # ...
    @classmethod
    def post_create(cls, sender, instance, created, *args, **kwargs):
        if not created:
            return
        # ...what needs to happen on create

post_save.connect(MyModel.post_create, sender=MyModel)

요점은 다음과 같습니다.

  1. 신호 사용 ( 공식 문서에서 자세한 내용 참조 )
  2. 좋은 네임 스페이스를위한 방법을 사용하십시오 (이치가 있다면) ... @classmethod대신 @staticmethod코드에서 정적 클래스 멤버를 참조해야 할 가능성이 높기 때문에 대신 표시했습니다.

핵심 Django가 실제 post_create신호를 가지고 있다면 더 깨끗할 것 입니다. (메소드의 동작을 변경하기 위해 부울 인수를 전달해야하는 경우 Imho는 2 개의 메서드 여야합니다.)


15

문자 그대로 질문에 답하기 위해 create모델 관리자 의 메서드는 Django에서 새 개체를 만드는 표준 방법입니다. 재정의하려면 다음과 같이하십시오.

from django.db import models

class MyModelManager(models.Manager):
    def create(self, **obj_data):
        # Do some extra stuff here on the submitted data before saving...
        # For example...
        obj_data['my_field'] = my_computed_value(obj_data['my_other_field'])

        # Now call the super method which does the actual creation
        return super().create(**obj_data) # Python 3 syntax!!

class MyModel(models.model):
    # An example model
    my_field = models.CharField(max_length=250)
    my_other_field = models.CharField(max_length=250)

    objects = MyModelManager()

이 예에서는 create인스턴스가 실제로 생성되기 전에 몇 가지 추가 처리를 수행하기 위해 Manager의 메서드 메서드를 재정의합니다 .

참고 : 다음 과 같은 코드

my_new_instance = MyModel.objects.create(my_field='my_field value')

이 수정 된 create메서드 를 실행 하지만 다음과 같은 코드

my_new_unsaved_instance = MyModel(my_field='my_field value')

하지 않을 것이다.


3

재정의 __init__()하면 모델이 인스턴스화 될 때 코드를 실행할 수 있습니다. 부모에게 전화하는 것을 잊지 마십시오 __init__().


아 네 이것이 답이었습니다. 내가 이것을 어떻게 간과했는지 모르겠다. 감사합니다 Ignacio.
ground5hark


1

선호하는 대답은 맞지만 모델이 UUIDModel에서 파생 된 경우 개체가 생성되고 있는지 여부를 알려주는 테스트가 작동하지 않습니다. pk 필드에는 이미 값이 있습니다.

이 경우 다음을 수행 할 수 있습니다.

already_created = MyModel.objects.filter(pk=self.pk).exists()

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