Как установить значение по умолчанию для поля модели 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))
ORIGINAL:
Я хочу создать значение по умолчанию для параметра функции, чтобы он был динамическим и вызывался и устанавливался каждый раз при вызове функции. Как я могу это сделать? например.
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))
Ответы
Ответ 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))
Эта последняя строка не определяет функцию; он вызывает функцию для создания поля в классе.
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))
Django 1.7 +
Обратите внимание, что поскольку Django 1.7, использование lambda в качестве значения по умолчанию не рекомендуется (c.f. @stvnw comment). Правильный способ сделать это - объявить функцию до и использовать ее как вызываемую в 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 ниже
Ответ 2
Выполнение этого 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)
Ответ 3
Существует важное различие между следующими двумя конструкторами DateTimeField:
my_date = models.DateTimeField(auto_now=True)
my_date = models.DateTimeField(auto_now_add=True)
Если вы используете auto_now_add=True
в конструкторе, время datetime, на которое ссылается my_date, является "неизменным" (устанавливается только один раз, когда строка вставлена в таблицу).
При auto_now=True
значение datetime будет обновляться каждый раз, когда объект сохраняется.
Это определенно было для меня в какой-то момент. Для справки, документы находятся здесь:
https://docs.djangoproject.com/en/dev/ref/models/fields/#datetimefield
Ответ 4
Вы не можете сделать это напрямую; значение по умолчанию оценивается при вычислении определения функции. Но есть два пути.
Во-первых, вы можете каждый раз создавать (а затем и вызывать) новую функцию.
Или, проще говоря, просто используйте специальное значение для отметки по умолчанию. Например:
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
Ответ 5
Передайте функцию в качестве параметра вместо передачи в результате вызова функции.
То есть вместо этого:
def myfunc(date=datetime.now()):
print date
Попробуйте следующее:
def myfunc(date=datetime.now):
print date()
Ответ 6
Иногда вам может потребоваться доступ к данным модели после создания новой модели пользователя.
Вот как я генерирую токен для каждого нового пользовательского профиля, используя первые 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)))