web-dev-qa-db-ja.com

Djangoモデルフィールドのデフォルト値を関数呼び出し/呼び出し可能オブジェクトに設定する方法(たとえば、モデルオブジェクトの作成時を基準とした日付)

編集済み:

新しいモデルオブジェクトが作成されるたびに評価される関数にDjangoフィールドのデフォルトを設定するにはどうすればよいですか?

このコードでは、コードが1回評価され、モデルオブジェクトが作成されるたびにコードを評価するのではなく、作成された各モデルオブジェクトのデフォルトを同じ日付に設定することを除いて、次のようなことを行います:

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))
85
Rob Bednark

質問は見当違いです。 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))

この最後の行は関数を定義していません。クラス内にフィールドを作成する関数を呼び出しています。

PRE Django 1.7

Django デフォルトとしてcallableを渡すことができます 、そしてあなたが望むように、毎回それを呼び出します:

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コメントを参照)。これを行う適切な方法は、関数を宣言することですbeforeフィールドを使用し、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回答の詳細

125
Ned Batchelder

これを行うdefault=datetime.now()+timedelta(days=1)は絶対に間違っています!

Djangoのインスタンスを起動すると評価されます。 Apacheを使用している場合、おそらく動作します。一部の構成では、Apacheはすべてのリクエストで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)
51
Simanas

次の2つのDateTimeFieldコンストラクターには重要な違いがあります。

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

コンストラクタでauto_now_add=Trueを使用する場合、my_dateによって参照される日時は「不変」です(行がテーブルに挿入されるときに1回だけ設定されます)。

ただし、auto_now=Trueを使用すると、オブジェクトが保存されるたびに日時値が更新されます。

ある時点でこれは間違いなく私にとって落とし穴でした。参考のため、ドキュメントは次のとおりです。

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

6
damzam

関数呼び出しの結果を渡すのではなく、パラメーターとして関数を渡します。

つまり、これの代わりに:

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

これを試して:

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

それを直接行うことはできません。関数定義が評価されるときにデフォルト値が評価されます。しかし、それを回避する方法は2つあります。

まず、毎回新しい関数を作成(および呼び出し)できます。

または、より簡単に、特別な値を使用してデフォルトをマークします。例えば:

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
2
abarnert

新しいユーザーモデルを作成した後、モデルデータにアクセスする必要がある場合があります。

ユーザー名の最初の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)))
2
CoreCreatives