编辑:
如何将 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))
这个问题被误导了。在 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)
将被评估一次,并存储为默认值。
预Django 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))
姜戈 1.7+
请注意,从 Django 1.7 开始,不建议使用 lambda 作为默认值(参见 @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 答案 [1]:https://docs.djangoproject.com/en/dev/ref/models/fields/#default
这样做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)
get_deadline(my_parameter)
那样将该字段作为参数传递?
get_deadline
函数的同时再次删除 deadline
字段?我已经删除了一个带有默认值函数的字段,但是现在 Django 在删除该函数后在启动时崩溃。我可以手动编辑迁移,在这种情况下可以,但是如果您只是更改了默认功能,并想删除旧功能怎么办?
default=datetime.today
不适用于 DJango 版本 3.2,我的数据库是 MySQL,并且运行您的代码仍将默认值设置为 None。
以下两个 DateTimeField 构造函数之间有一个重要区别:
my_date = models.DateTimeField(auto_now=True)
my_date = models.DateTimeField(auto_now_add=True)
如果在构造函数中使用 auto_now_add=True
,则 my_date 引用的日期时间是“不可变的”(仅在将行插入表时设置一次)。
但是,使用 auto_now=True
,日期时间值将在每次保存对象时更新。
这对我来说绝对是一个问题。作为参考,文档在这里:
https://docs.djangoproject.com/en/dev/ref/models/fields/#datetimefield
default=datetime.today
不适用于 DJango 版本 3.2,我的数据库是 MySQL,并且运行您的代码仍将默认值设置为 None。
有时您可能需要在创建新用户模型后访问模型数据。
以下是我使用用户名的前 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)))
你不能直接这样做;评估函数定义时评估默认值。但是有两种方法可以解决它。
首先,您可以每次都创建(然后调用)一个新函数。
或者,更简单地说,只是使用一个特殊的值来标记默认值。例如:
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
myfunc
函数用于获取(并打印)datetime
;您已对其进行了更改,因此不能以这种方式使用它。
将函数作为参数传入,而不是传入函数调用的结果。
也就是说,而不是这样:
def myfunc(date=datetime.now()):
print date
尝试这个:
def myfunc(date=datetime.now):
print date()
print datetime.now()
。
datetime.now
函数。但是任何传递非默认值的用户都将传递日期时间,而不是函数。 foo = datetime.now(); myfunc(foo)
将引发 TypeError: 'datetime.datetime' object is not callable
。如果我真的想使用你的功能,我必须做类似 myfunc(lambda: foo)
的事情,这不是一个合理的界面。
不定期副业成功案例分享
get_default_my_date
传递一些参数怎么办?