78👍
Doing this default=datetime.now()+timedelta(days=1)
is absolutely wrong!
It gets evaluated when you start your instance of django. If you are under apache it will probably work, because on some configurations apache revokes your django application on every request, but still you can find you self some day looking through out your code and trying to figure out why this get calculated not as you expect.
The right way of doing this is to pass a callable object to default argument. It can be a datetime.today function or your custom function. Then it gets evaluated every time you request a new default value.
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)
176👍
The question is misguided. When creating a model field in Django, you are not defining a function, so function default values are irrelevant:
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))
This last line is not defining a function; it is invoking a function to create a field in the class.
In this case datetime.now() + timedelta(days=1)
will be evaluated once, and stored as the default value.
PRE Django 1.7
Django [lets you pass a callable as the default][1], and it will invoke it each time, just as you want:
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+
Please note that since Django 1.7, usage of lambda as default value is not recommended (c.f. @stvnw comment). The proper way to do this is to declare a function before the field and use it as a callable in default_value named 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)
More information in the @simanas answer below
[1]: https://docs.djangoproject.com/en/dev/ref/models/fields/#default
- [Django]-How to make the foreign key field optional in Django model?
- [Django]-How to duplicate virtualenv
- [Django]-Django Generic Views using decorator login_required
8👍
There’s an important distinction between the following two DateTimeField constructors:
my_datetime = models.DateTimeField(auto_now=True)
my_datetime = models.DateTimeField(auto_now_add=True)
If you use auto_now_add=True
in the constructor, the datetime referenced by my_date is "immutable" (only set once when the row is inserted to the table).
With auto_now=True
, however, the datetime value will be updated every time the object is saved.
This was definitely a gotcha for me at one point. For reference, the docs are here:
https://docs.djangoproject.com/en/dev/ref/models/fields/#datetimefield
- [Django]-Unittest Django: Mock external API, what is proper way?
- [Django]-Why won't Django use IPython?
- [Django]-Django: Get list of model fields?
6👍
Sometimes you may need to access model data after creating a new user model.
Here is how I generate a token for each new user profile using the first 4 characters of their username:
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)))
- [Django]-Django: Calculate the Sum of the column values through query
- [Django]-">", "<", ">=" and "<=" don't work with "filter()" in Django
- [Django]-Retrieving a Foreign Key value with django-rest-framework serializers
3👍
You can’t do that directly; the default value is evaluated when the function definition is evaluated. But there are two ways around it.
First, you can create (and then call) a new function each time.
Or, more simply, just use a special value to mark the default. For example:
from datetime import datetime
def mydatetime(date=None):
if datetime is None:
datetime = datetime.now()
print datetime
If None
is a perfectly reasonable parameter value, and there’s no other reasonable value you could use in its place, you can just create a new value that’s definitely outside the domain of your function:
from datetime import datetime
class _MyDateTimeDummyDefault(object):
pass
def mydatetime(date=_MyDateDummyTimeDefault):
if datetime is _MyDateDummyTimeDefault:
datetime = datetime.now()
print datetime
del _MyDateDummyTimeDefault
In some rare cases, you’re writing meta-code that really does need to be able to take absolutely anything, even, say, mydate.func_defaults[0]
. In that case, you have to do something like this:
def mydatetime(*args, **kw):
if 'datetime' in kw:
datetime = kw['datetime']
elif len(args):
datetime = args[0]
else:
datetime = datetime.now()
print datetime
- [Django]-Using Django time/date widgets in custom form
- [Django]-Django Admin – Disable the 'Add' action for a specific model
- [Django]-Django: accessing session variables from within a template?
1👍
Pass the function in as a parameter instead of passing in the result of the function call.
That is, instead of this:
def myfunc(datetime=datetime.now()):
print datetime
Try this:
def myfunc(datetime=datetime.now):
print datetime()
- [Django]-You need to install postgresql-server-dev-X.Y for building a server-side extension or libpq-dev for building a client-side application
- [Django]-Django admin default filter
- [Django]-No module named pkg_resources
0👍
You should set get_my_datetime
without ()
which returns the current date and time (+1 day) to DateTimeField() as a default value as shown below. *Don’t set get_my_datetime()
with ()
because the default date and time become when the Django server starts (Unchanged) and you should use Django’s timezone.now() because Python’s datetime.now() cannot save UTC correctly in database when TIME_ZONE = ‘UTC’ which is default in 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()" ↑
And as your code, don’t set datetime.now() + timedelta(days=1)
because the default date and time become when the Django server starts (Unchanged):
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))
- [Django]-How to get the name of current app within a template?
- [Django]-How to solve "Page not found (404)" error in Django?
- [Django]-How to put comments in Django templates?