SW개발/Django

[Django]DateField의 auto_now_add 옵션, 정확히 알아보기

이번 포스팅에서는 DateFieldauto_now_add 옵션을 사용하다가 이슈를 겪었던 경험과, 잘 모르고 있던 내용에 대해서 정리해보겠습니다.

 

auto_now_add 옵션을 True로 설정하면 얻게되는 효과는 너무 간단합니다. 현재 시간을 기준으로 값이 설정되도록 하는 것입니다. 따라서 보통은 created_at 처럼 객체가 생성된 시각을 기록하는 필드에 사용합니다. 

너무 단순하게 생각했던 나머지 세부적인 사항에 대해서는 잘 알지 못했습니다. 코드를 통해 자세히 알아보겠습니다.

 

문제가 생겼던 코드

Event 모델이 존재하고, 속성으로 이벤트가 시작하는 시간과 종료되는 시간이 있다고 가정하겠습니다.

class Event(models.Model):
    name = models.CharField(max_length=256)
    started_at = models.DateTimeField(auto_now_add=True)
    ended_at = models.DateTimeField()
    ...
    

from django.utils import timezone
from datetime import timedelta

now = timezone.now()
create_params = {
    'name': 'leffe event',
    'started_at': now + timedelta(days=7), # 이 값은 무시됩니다.
    'ended_at': now + timedelta(days=14),
}

event = Event.objects.get_or_create(**create_params)

# 값이 무시되어 다시 저장을 시도함.
event.started_at = create_params['started_at']
event.save()

제가 원했던 코드는 Event 생성하는 시점을 지정하고 싶었습니다. 따라서 객체를 생성할 때 값을 지정 해주었습니다.

그렇게 create_params와 함께 객체 생성을 시도하였는데 이 곳에서 문제가 발생했습니다. 

 

바로, 제가 지정한 시간은 무시되고 현재 시각의 정보로 create_at의 값이 저장이 되는 것이었습니다. 처음에는 단지 "값을 할당했는데 왜 자꾸 현재 시간이 들어가지..?" 라는 점을 보고 이상하게 생각했습니다. 그래서 우선은 객체를 생성한 후 create_at 의 값을 다시 변경 -> save() 하고나니 정상적으로 저장되었습니다.

 

긴급하게 위 처럼 코드를 작성했지만, 무언가 이상함을 느끼고 Document를 찾아보게 되었습니다.

https://docs.djangoproject.com/en/4.0/ref/models/fields/#django.db.models.DateField.auto_now_add

 

Model field reference | Django documentation | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com

 

auto_now_add 문서 내용

DateField.auto_now_add
Automatically set the field to now when the object is first created. Useful for creation of timestamps. Note that the current date is always used; it’s not just a default value that you can override. So even if you set a value for this field when creating the object, it will be ignored. 

If you want to be able to modify this field, set the following instead of auto_now_add=True
For DateField: default=date.today - from datetime.date.today()
For DateTimeField: default=timezone.now - from django.utils.timezone.now()

아뿔싸, 문서에서도 명확히 설명하고 있습니다. auto_now_add 옵션을 활성화 하면 객체 생성시에 지정한 값은 무시가 된다. 

첨언으로 필드의 수정을 원하면 default 옵션을 통해 함수를 할당하면 된다고 제안하고 있습니다.

 

동작하는 원리

조금 더 궁금해서 내부적으로 어떻게 동작 하는지에 대해서도 찾아보게 되었습니다.

# in DateTime class, django.db.models.fields.__init__.py
def pre_save(self, model_instance, add):
    if self.auto_now or (self.auto_now_add and add):
        value = datetime.date.today()
        setattr(model_instance, self.attname, value)
        return value
    else:
        return super().pre_save(model_instance, add)

코드를 보면 pre_save 라는 시그널 함수를 통해 옵션이 True일 경우 필드에 값을 지정해주는 것을 확인할 수 있었습니다.

이렇기 때문에 값을 할당하여 생성한다 하더라도 무시가 되고, 최종적으로 저장되기 전의 시간으로 덮어 씌워지게 되는 것입니다.

 

잘 알고 사용하기

auto_now_add는 사실 너무 간단한 옵션이었기에 따로 문서를 읽어본 적이 없었습니다. 하지만 이번 트러블 슈팅을 통해서 내부적으로는 pre_save 시그널을 통해 값을 설정 해준다는 것과, 지정한 값은 무시가 되는 점 또한 알게 되었습니다. 

 

간단하게 객체 생성 후 -> 값을 재 지정해주는 것으로 해결을 하고 있었지만 찜찜한 마음이었기에 조금 더 파고들게 되었습니다. 동작하는 원리를 알고난 후에 auto_now_add 옵션 대신 defaults 옵션을 통해 now 함수를 전달해 줌으로써 깔끔하게 해결할 수 있었습니다.

 

가끔은 너무 쉽다고 생각한 것들로부터 문제가 발생하기도 합니다. 그럴 때에는 본인이 해당 기능의 원리를 정확하게 이해하고 있는지, 잘못 알고 있는 것은 아닌지를 체크해볼 필요가 있다고 생각합니다.

 

읽어주셔서 감사합니다 :)

 

728x90