Django 모델 필드의 기본값을 함수 호출 / 호출 가능으로 설정하는 방법 (예 : 모델 객체 생성 시간과 관련된 날짜)


101

편집 :

Django 필드의 기본값을 새 모델 객체가 생성 될 때마다 평가되는 함수로 설정하려면 어떻게해야합니까?

이 코드에서는 모델 개체가 생성 될 때마다 코드를 평가하는 대신 코드가 한 번 평가되고 생성 된 각 모델 개체에 대해 동일한 날짜로 기본값이 설정된다는 점을 제외하면 다음과 같은 작업을 수행하고 싶습니다.

from datetime import datetime, timedelta
class MyModel(models.Model):
  # default to 1 day from now
  my_date = models.DateTimeField(default=datetime.now() + timedelta(days=1))



실물:

함수 매개 변수의 기본값을 동적으로 만들고 함수가 호출 될 때마다 호출되고 설정되도록하고 싶습니다. 어떻게 할 수 있습니까? 예 :

from datetime import datetime
def mydate(date=datetime.now()):
  print date

mydate() 
mydate() # prints the same thing as the previous call; but I want it to be a newer value

특히 Django에서하고 싶습니다.

from datetime import datetime, timedelta
class MyModel(models.Model):
  # default to 1 day from now
  my_date = models.DateTimeField(default=datetime.now() + timedelta(days=1))

질문은 잘못된 것입니다. 함수 매개 변수 기본값과 관련이 없습니다. 내 대답을 참조하십시오.
Ned Batchelder 2012 년

@NedBatchelder의 의견에 따라 내 질문을 수정하고 ( "EDITED :"로 표시) 원본을 남겼습니다 ( "ORIGINAL :"로 표시)
Rob Bednark 2014

답변:


140

질문은 잘못된 것입니다. Django에서 모델 필드를 만들 때 함수를 정의하지 않으므로 함수 기본값은 무관합니다.

from datetime import datetime, timedelta
class MyModel(models.Model):
  # default to 1 day from now
  my_date = models.DateTimeField(default=datetime.now() + timedelta(days=1))

이 마지막 줄은 함수를 정의하지 않습니다. 클래스에 필드를 만드는 함수를 호출합니다.

장고 1.7 이전

Django를 사용하면 callable을 default로 전달할 수 있으며 원하는대로 매번 호출 할 수 있습니다.

from datetime import datetime, timedelta
class MyModel(models.Model):
  # default to 1 day from now
  my_date = models.DateTimeField(default=lambda: datetime.now() + timedelta(days=1))

Django 1.7 이상

Django 1.7 이후로 람다를 기본값으로 사용하지 않는 것이 좋습니다 (@stvnw 주석 참조). 이를 수행하는 적절한 방법 은 필드 앞에 함수를 선언하고 arg라는 default_value의 콜러 블로 사용하는 것입니다.

from datetime import datetime, timedelta

# default to 1 day from now
def get_default_my_date():
  return datetime.now() + timedelta(days=1)

class MyModel(models.Model):
  my_date = models.DateTimeField(default=get_default_my_date)

아래 @simanas 답변에 대한 자세한 정보


2
@NedBatchelder 감사합니다. 이것이 바로 제가 찾던 것입니다. 나는 '기본'이 콜 러블을 취할 수 있다는 것을 몰랐습니다. 그리고 이제 함수 호출 대 함수 정의와 관련하여 내 질문이 실제로 어떻게 잘못되었는지 알 수 있습니다.
Rob Bednark 2015 년

25
Django> = 1.7의 경우 마이그레이션과 호환되지 않으므로 필드 옵션에 람다를 사용하지 않는 것이 좋습니다. 참고 : 문서 , 티켓
StvnW 2014

4
Djange> = 1.7에 대해 잘못되었으므로이 답변을 선택 취소하십시오. @Simanas의 대답은 절대적으로 정확하므로 받아 들여 져야합니다.
AplusKminus 2015 년

마이그레이션에서 어떻게 작동합니까? 모든 이전 개체가 마이그레이션 시간을 기준으로 날짜를 얻습니까? 마이그레이션에 사용할 다른 기본값을 설정할 수 있습니까?
timthelion 18:57에

3
매개 변수를 전달하려면 get_default_my_date어떻게 해야 합니까?
Sadan A.

59

이것은 default=datetime.now()+timedelta(days=1)절대적으로 잘못되었습니다!

django 인스턴스를 시작할 때 평가됩니다. 일부 구성에서 아파치가 모든 요청에 ​​대해 장고 응용 프로그램을 취소하기 때문에 아파치 아래에 있다면 아마도 작동 할 것입니다. .

이를 수행하는 올바른 방법은 호출 가능한 객체를 기본 인수에 전달하는 것입니다. datetime.today 함수 또는 사용자 지정 함수일 수 있습니다. 그런 다음 새 기본값을 요청할 때마다 평가됩니다.

def get_deadline():
    return datetime.today() + timedelta(days=20)

class Bill(models.Model):
    name = models.CharField(max_length=50)
    customer = models.ForeignKey(User, related_name='bills')
    date = models.DateField(default=datetime.today)
    deadline = models.DateField(default=get_deadline)

21
내 기본값이 모델의 다른 필드 값을 기반으로하는 경우 해당 필드를 매개 변수로 전달할 수 get_deadline(my_parameter)있습니까?
YPCrumble

@YPCrumble 이것은 새로운 질문이어야합니다!
jsmedmar

2
아니. 그건 불가능하다. 이를 수행하려면 몇 가지 다른 접근 방식을 사용해야합니다.
Simanas

기능을 deadline삭제하면서 필드를 다시 제거하는 방법은 get_deadline무엇입니까? 기본값에 대한 함수가있는 필드를 제거했지만 이제는 함수를 제거한 후 시작할 때 Django가 충돌합니다. 마이그레이션을 수동으로 편집 할 수 있습니다.이 경우에는 괜찮습니다.하지만 단순히 기본 기능을 변경하고 이전 기능을 제거하려면 어떻게해야합니까?
beruic

8

다음 두 DateTimeField 생성자 간에는 중요한 차이점이 있습니다.

my_date = models.DateTimeField(auto_now=True)
my_date = models.DateTimeField(auto_now_add=True)

auto_now_add=True생성자에서 사용하는 경우 my_date가 참조하는 datetime은 "불변"입니다 (행이 테이블에 삽입 될 때 한 번만 설정 됨).

으로는 auto_now=True, 그러나, 날짜 시간 값은 객체가 저장 될 때마다 업데이트됩니다.

이것은 한 번에 나에게 확실히 맞았습니다. 참고로 문서는 다음과 같습니다.

https://docs.djangoproject.com/en/dev/ref/models/fields/#datetimefield


3
auto_now와 auto_now_add의 정의가 섞여 있습니다. 그 반대입니다.
마이클 베이츠

@MichaelBates 더 나은 지금?
Hendy Irawan 2011

4

새 사용자 모델을 만든 후 모델 데이터에 액세스해야하는 경우가 있습니다.

사용자 이름의 처음 4자를 사용하여 각 새 사용자 프로필에 대한 토큰을 생성하는 방법은 다음과 같습니다.

from django.dispatch import receiver
class Profile(models.Model):
    auth_token = models.CharField(max_length=13, default=None, null=True, blank=True)


@receiver(post_save, sender=User) # this is called after a User model is saved.
def create_user_profile(sender, instance, created, **kwargs):
    if created: # only run the following if the profile is new
        new_profile = Profile.objects.create(user=instance)
        new_profile.create_auth_token()
        new_profile.save()

def create_auth_token(self):
    import random, string
    auth = self.user.username[:4] # get first 4 characters in user name
    self.auth_token =  auth + ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits + string.ascii_lowercase) for _ in range(random.randint(3, 5)))

3

직접 할 수는 없습니다. 기본값은 함수 정의가 평가 될 때 평가됩니다. 그러나 주위에는 두 가지 방법이 있습니다.

첫째, 매번 새 함수를 생성 한 다음 호출 할 수 있습니다.

또는 더 간단하게 특수 값을 사용하여 기본값을 표시하십시오. 예를 들면 :

from datetime import datetime
def mydate(date=None):
  if date is None:
    date = datetime.now()
  print date

경우 None완벽하게 합리적인 매개 변수 값이, 그리고 당신이 그 자리에 사용할 수있는 다른 합리적인 가치가 없습니다, 당신은 당신의 함수의 도메인 외부 확실히의 새 값을 생성 할 수 있습니다 :

from datetime import datetime
class _MyDateDummyDefault(object):
  pass
def mydate(date=_MyDateDummyDefault):
  if date is _MyDateDummyDefault:
    date = datetime.now()
  print date
del _MyDateDummyDefault

드문 경우에, 당신은 정말로 무엇이든 취할 수 있어야하는 메타 코드를 작성하고 있습니다 mydate.func_defaults[0]. 이 경우 다음과 같이해야합니다.

def mydate(*args, **kw):
  if 'date' in kw:
    date = kw['date']
  elif len(args):
    date = args[0]
  else:
    date = datetime.now()
  print date

1
실제로 더미 값 클래스를 인스턴스화 할 이유는 없습니다. 클래스 자체를 더미 값으로 사용하기 만하면됩니다.
황색

이 작업을 직접 수행 할 수 있습니다. 함수 호출 결과 대신 함수를 전달하면됩니다. 내 게시 된 답변을 참조하십시오.

당신은 할 수 없습니다. 함수의 결과는 일반 매개 변수와 동일한 유형이 아니므로 이러한 일반 매개 변수에 대한 기본값으로 합리적으로 사용할 수 없습니다.
abarnert

1
예, 함수 대신 객체를 전달하면 예외가 발생합니다. 문자열이 필요한 매개 변수에 함수를 전달하는 경우에도 마찬가지입니다. 그게 어떤 관련이 있는지 모르겠습니다.

그의 myfunc기능이 a datetime; 그런 식으로 사용할 수 없도록 변경했습니다.
abarnert

2

함수 호출의 결과를 전달하는 대신 매개 변수로 함수를 전달하십시오.

즉,이 대신 :

def myfunc(date=datetime.now()):
    print date

이 시도:

def myfunc(date=datetime.now):
    print date()

이것은 작동하지 않습니다. 사용자가 실제로 매개 변수를 전달할 수 없기 때문에 함수로 호출하고 예외를 발생 시키려고합니다. 어떤 경우에는 print datetime.now()무조건 매개 변수를 사용하지 않는 것이 좋습니다.
abarnert

시도 해봐. 날짜를 전달하지 않고 datetime.now 함수를 전달합니다.

예, 기본값은 datetime.now함수를 전달하는 것입니다. 그러나 기본값이 아닌 값을 전달하는 모든 사용자는 함수가 아닌 datetime을 전달합니다. foo = datetime.now(); myfunc(foo)올릴 것 TypeError: 'datetime.datetime' object is not callable입니다. 실제로 함수를 사용하고 싶다면 myfunc(lambda: foo)합리적인 인터페이스가 아닌 대신에 같은 작업을 수행해야합니다 .
abarnert

1
기능을 받아들이는 인터페이스는 드물지 않습니다. 원하는 경우 파이썬 stdlib에서 예제 이름 지정을 시작할 수 있습니다.

2
Django는 이미이 질문이 제안하는대로 정확히 콜 러블을 받아들입니다.
Ned Batchelder
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.