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

Rob*_*ark 88 django parameters lambda default django-models

编辑:

如何将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))
Run Code Online (Sandbox Code Playgroud)



原版的:

我想为函数参数创建一个默认值,使其成为动态的,并在每次调用函数时调用和设置.我怎样才能做到这一点?例如,

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
Run Code Online (Sandbox Code Playgroud)

具体来说,我想在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))
Run Code Online (Sandbox Code Playgroud)

Ned*_*der 127

这个问题是错误的.在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))
Run Code Online (Sandbox Code Playgroud)

最后一行没有定义函数; 它正在调用一个函数来在类中创建一个字段.

PRE Django 1.7

Django 允许你传递一个可调用的默认值,并且每次都会调用它,就像你想要的那样:

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))
Run Code Online (Sandbox Code Playgroud)

Django 1.7+

请注意,自Django 1.7以来,不推荐使用lambda作为默认值(cf @stvnw comment).执行此操作的正确方法是在字段之前声明一个函数,并将其用作名为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)
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅下面的@simanas答案

  • 请注意,对于Django> = 1.7,不建议对字段选项使用lambdas,因为它们与迁移不兼容.参考:[文件](https://docs.djangoproject.com/en/1.7/ref/models/fields/#default),[门票](https://code.djangoproject.com/ticket/22351) (25认同)
  • 请取消选中此答案,因为Djange> = 1.7是错误的.@Simanas的答案是绝对正确的,因此应该被接受. (4认同)
  • 谢谢@NedBatchelder.这正是我所寻找的.我没有意识到'默认'可能会被赎回.现在我看到我的问题在函数调用和函数定义方面确实是错误的. (2认同)
  • 如果我想将一些参数传递给`get_default_my_date`,该怎么办? (2认同)

Sim*_*nas 54

这样做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)
Run Code Online (Sandbox Code Playgroud)

  • 如果我的默认值基于模型中另一个字段的值,是否可以将该字段作为参数传递,如`get_deadline(my_parameter)`? (18认同)
  • 不。那是不可能的。你将不得不使用一些不同的方法来做到这一点。 (2认同)

dam*_*zam 6

以下两个DateTimeField构造函数之间存在重要区别:

my_date = models.DateTimeField(auto_now=True)
my_date = models.DateTimeField(auto_now_add=True)
Run Code Online (Sandbox Code Playgroud)

如果auto_now_add=True在构造函数中使用,则my_date引用的日期时间为"不可变"(仅在将行插入表时设置一次).

随着auto_now=True,但是,时间值将每次更新的对象保存.

这对我来说绝对是一个难题.作为参考,文档在这里:

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

  • 你已经将auto_now和auto_now_add的定义混淆了,反之亦然. (3认同)

Gal*_*aph 5

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

以下是我使用用户名的前 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)))
Run Code Online (Sandbox Code Playgroud)