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

flseospp  于 2023-06-07  发布在  Go
关注(0)|答案(7)|浏览(438)

已编辑:

如何将Django字段的默认值设置为每次创建新模型对象时都要求值的函数?
我想做一些类似于下面的事情,除了在这段代码中,代码只计算一次,并为每个创建的模型对象设置默认值,而不是每次创建模型对象时都计算代码:

from datetime import datetime, timedelta

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

ORIGINAL:

我想为一个函数参数创建一个默认值,这样它是动态的,每次调用函数时都会被调用和设置。我该怎么做?例如,在一个实施例中,

from datetime import datetime

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

mydatetime() 
mydatetime() # 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_datetime = models.DateTimeField(default=datetime.now() + timedelta(days=1))
tpgth1q7

tpgth1q71#

这个问题被误导了。在Django中创建模型字段时,你没有定义函数,所以函数的默认值是无关紧要的:

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

最后一行不是定义函数;它调用一个函数来在类中创建一个字段。
在这种情况下,datetime.now() + timedelta(days=1)将被计算一次,并存储为默认值。

PRE Django 1.7

Django [允许你传递一个可调用函数作为默认值][1],每次它都会调用它,就像你想要的那样:

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

Django 1.7+

请注意,自Django 1.7以来,不建议使用lambda作为默认值(参见:@stvnw comment)。正确的方法是在字段之前声明一个函数,并将其用作default_value中的一个名为arg的可调用函数:

from datetime import datetime, timedelta

# default to 1 day from now
def get_default_my_datetime():
    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

xpszyzbs

xpszyzbs2#

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

fnvucqvd3#

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

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

如果在构造函数中使用auto_now_add=True,则my_date引用的日期时间是“不可变的”(仅在将行插入表时设置一次)。
但是,对于auto_now=True,每次保存对象时都会更新datetime值。
这对我来说绝对是一个陷阱。作为参考,文档在这里:
https://docs.djangoproject.com/en/dev/ref/models/fields/#datetimefield

ujv3wf0j

ujv3wf0j4#

有时,您可能需要在创建新用户模型后访问模型数据。
下面是我如何使用用户名的前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)))
f4t66c6m

f4t66c6m5#

你不能直接这么做当函数定义被求值时,缺省值被求值。但有两种方法可以绕过它。
首先,每次都可以创建(然后调用)一个新函数。
或者,更简单地说,只是使用一个特殊的值来标记默认值。例如:

from datetime import datetime

def mydatetime(date=None):
    if datetime is None:
        datetime = datetime.now()
    print datetime

如果None是一个完全合理的参数值,并且没有其他合理的值可以使用它,则可以创建一个新值,该值肯定在函数的域之外:

from datetime import datetime

class _MyDateTimeDummyDefault(object):
    pass

def mydatetime(date=_MyDateDummyTimeDefault):
    if datetime is _MyDateDummyTimeDefault:
        datetime = datetime.now()
    print datetime

del _MyDateDummyTimeDefault

在一些罕见的情况下,您编写的元代码确实需要能够接受任何东西,甚至是mydate.func_defaults[0]。在这种情况下,你必须这样做:

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

nfzehxib6#

将函数作为参数传入,而不是传入函数调用的结果。
也就是说,而不是这样:

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

试试这个:

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

kcwpcxri7#

您应该设置get_my_datetime而不设置(),它将返回当前日期和时间(+1天)到DateTimeField()作为默认值,如下所示。* 不要用()设置get_my_datetime(),因为默认的日期和时间是Django服务器启动时(未更改),你应该使用Django的timezone.now(),因为Python的datetime.now()在TIME_ZONE = 'UTC'时不能正确地将UTC保存在数据库中,这是settings.py的默认值:

from datetime import timedelta
from django.utils import timezone

def get_my_datetime(): # ↓ Don't use "datetime.now()"
    return timezone.now() + timedelta(days=1)

class MyModel(models.Model):
    # default to 1 day from now
    my_datetime = models.DateTimeField(default=get_my_date)             
                        # Don't set "get_my_datetime()" ↑

在你的代码中,不要设置datetime.now() + timedelta(days=1),因为默认的日期和时间是Django服务器启动的时候(未更改):

from datetime import datetime, timedelta

class MyModel(models.Model):
    # default to 1 day from now              # ↓ ↓ ↓ ↓ ↓ ↓ Don't set ↓ ↓ ↓ ↓ ↓ ↓
    my_datetime = models.DateTimeField(default=datetime.now() + timedelta(days=1))

相关问题