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.