如何将 Django 模型字段的默认值设置为函数调用/可调用(例如,相对于模型对象创建时间的日期)

编辑:

如何将 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))
107643 次浏览

不能直接这样做; 在计算函数定义时会计算默认值。但是有两条路可以绕过去。

首先,每次都可以创建(然后调用)一个新函数。

或者,更简单地说,使用一个特殊的值来标记默认值。例如:

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

将函数作为参数传入,而不是传入函数调用的结果。

换句话说:

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

试试这个:

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

下面两个 DateTimeField 构造函数之间有一个重要的区别:

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

如果在构造函数中使用 auto_now_add=True,my _ date 引用的 datetime 是“ immutable”(只有在将行插入到表中时才设置一次)。

但是,对于 auto_now=True,每次保存对象时都会更新 datetime 值。

这对我来说一度是个难题,参考文件如下:

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

这个问题被误导了。在 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))

最后一行不是定义函数,而是调用函数在类中创建字段。

在这种情况下,datetime.now() + timedelta(days=1)将被计算一次,并作为默认值存储。

PRE 姜戈1.7

Django [允许您将一个可调用文件作为默认文件传递][1] ,并且它每次都会调用它,就像您希望的那样:

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以来,不推荐使用 lambda 作为默认值(c.f@stvnw 注释)。正确的方法是将函数 之前声明为字段,并将其用作 default _ value 中名为 arg 的可调用函数:

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 答案 返回文章页面【一分钟科普】【一分钟科普】【一分钟科普】【一分钟科普】【一分钟科普】【一分钟科普】【一分钟科普】【一分钟科普】【一分钟科普】【一分钟科普】【一分钟科普】【一分钟科普】【一分钟科普】【一分钟科普】【一分钟科普

这样做 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)

有时您可能需要在创建新的用户模型之后访问模型数据。

下面是我如何使用用户名的前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)))