ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Django 에서 일시적으로 auto_now, auto_now_add 를 disable 시키는 법
    기술/Django 2021. 9. 24. 19:39

    종종 엑셀에서 가끔 데이터를 마이그레이션 할 때가 있습니다.

    그 때 특정 필드를 마이그레이션 데이터 값으로 채워넣어야하지만 auto_now, auto_now_add 가 설정되어 있다면

    저장 시 데이터가 마이그레이션 데이터가 아니라 최신 값의 Datetime 으로 설정됩니다.

    그래서 그 때마다 사용할 수 있는 문법이 필요했습니다.

    TemporarilyDisableAutoNowAndAutoNowAdd

    주어진 모델의 save, bulk_ 메서드를 사용할 때 auto_now, auto_now_add 필드를 비활성화하는 Context Manager 를 생성하기로 했습니다.

    매개변수

    • Model: auto_now, auto_now_add 를 비활성하고싶은 Django 의 모델을 제공합니다.
    • field_names: 여러 개의 auto_now, auto_now_add 필드가 있을 때 그 중 특정 필드만 비활성화 시킵니다. 만약 field_names 가 제공되지 않으면 모든 auto_now, auto_now_add 를 비활성화합니다.

    구현

    1. field_names 가 주어짐에 따라 auto_now, auto_now_add 를 비활성화합니다.
    2. Context Manager 가 종료되면 비활성화한 auto_now, auto_now_add 를 다시 활성화합니다.
    class TemporarilyDisableAutoNowAndAutoNowAdd:
      def __init__(self, model, field_names: List[str] = None):
        self.model = model
        self.field_names = field_names
    
        # 나중에 활성화하기위해 비활성화한 필드들을 담아둡니다.
        self.auto_now_fields = []
        self.auto_now_add_fields = []
    
      def _disable_field(self, field: models.Field, field_name: str):
        """
        주어진 필드의 속성을 비활성화하기 위해 사용하는 메서드
        """
        # 활성화되어있는 필드만 비활성화
        if getattr(field, field_name):
          setattr(field, field_name, False)
          getattr(self, f'{field_name}_fields').append(field)
    
      def __enter__(self):
        fields = []
        if self.field_names:
          # field_names 가 주어졌다면 주어진 필드만 비활성화
          for field_name in self.field_names:
            fields.append(self.model._meta.get_field(field_name))
        else:
          # field_names 가 주어지지 않았다면 모든 필드를 비활성화
          for field in self.model._meta.local_fields:
            if field.__class__ in [models.DateField, models.DateTimeField]:
              fields.append(field)
    
        for field in fields:
          self._disable_field(field, 'auto_now')
          self._disable_field(field, 'auto_now_add')
    
      def __exit__(self, type, value, traceback):
        for field in self.auto_now_fields:
          field.auto_now = True
        for field in  self.auto_now_add_fields:
          field.auto_now_add = True

    사용해보기

    모든 필드의 auto_now, auto_now_add 비활성화

    post = Post.objects.first()
    post.modified = random_datetime()  # auto_now_add 가 True 로 설정된 필드
    
    with TemporarilyDisableAutoNowAndAutoNowAdd(Post):
      # save 할 때만 TemporarilyDisableAutoNowAndAutoNowAdd 로 감싸주면 됩니다.
      post.save()

    특정 필드의 auto_now, auto_now_add 비활성화

    post = Post.objects.first()
    post.modified = random_datetime()  # auto_now_add 가 True 로 설정된 필드
    post.timestamp = random_datetime()  # auto_now_add 가 True 로 설정된 필드
    
    with TemporarilyDisableAutoNowAndAutoNowAdd(Post, field_names=['modified']):
      # save 할 때만 TemporarilyDisableAutoNowAndAutoNowAdd 로 감싸주면 됩니다.
      # 이 때 결과는 modified 는 random_datetime() 값으로 변경되지만
      # post.timestamp 는 auto_now_add 가 설정되어있으므로 random_datetime() 값이 아니라 현재 시간이 설정되어있습니다.
      post.save()

     

    댓글

Designed by Tistory.