How to get timezone.today as default value? - django

I would like to set my DateField's default value to today's date in UTC timezone.
If I put datetime.now(timezone.utc).date(), it will be a fixed value that is called when the server restarts.
If I put timezone.now, it will return datetime object, not day object.
How can I get the today's date in UTC timezone? A function that is callable (without parentheses at the end)
Because if I put timezone.now().date, won't it be called when the server is restarted only, not every time the object is created/updated?

You seem to have mostly solved your own problem. Simple create "a function that is callable".
from datetime import datetime
def today_utc():
return datetime.utcnow().date()
class MyModel(models.Model):
my_field = models.DateField(default=today_utc)

In your settings.py file you must set this var:
USE_TZ = True
Now in your code if you want to get the correct utc now date you do so:
from django.utils import timezone
today = timezone.now().date()
If you have field in your model is DateField, example: my_field do so:
my_field = models.DateFiel(default=date.today)
If you have field called my_field set DateTimeField do so:
my_field = models.DateTimeField(default=timezone.now)
And read the doc

Related

Django ORM update field to NOW() returning 0 error

I am trying to loop through a list of results, process them, then update their "updated_details" field.
users_to_update = TiktokUser.objects.filter(Q(updated_details__lt=datetime.utcnow() - timedelta(weeks=1)) | Q(updated_details__isnull=True))[0:1]
for user_to_update in users_to_update:
print(datetime.now())
user_to_update.updated_details = datetime.now()
user_to_update.save()
The idea being that on each loop, i set the updated_details to the current timestamp. My print() statement here correctly prints out the current date and time, however when I update the record itself and save, I get the following error:
['“0000-00-00 00:00:00.000000” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) but it is an invalid date/time.']
Which implies to me it is attempting to set it to 0.
My model definition is:
class User(models.Model):
...
updated_details = models.DateTimeField(blank=True, null=True)
...
How do I get Django to set the field to the current DateTime?
You are using python datetime, please use django datetime it's based upon settings file of your django project
from django.utils.timezone import now
users_to_update = TiktokUser.objects.filter(Q(updated_details__lt=datetime.utcnow() - timedelta(weeks=1)) | Q(updated_details__isnull=True))[0:1]
for user_to_update in users_to_update:
user_to_update.updated_details = now()
user_to_update.save()

How to write the right time to DB according my timezone?

When I save dates in my database Django shows message about succesfull adding with the right time but in fact in the databese time is different
models.py:
from datetime import datetime
from django.db import models
class Teg1(models.Model):
created_at = models.DateTimeField(default=datetime.now, null=True, blank=True, editable=False)
num = models.FloatField(default=0.0, null=True, blank=True)
def __str__(self):
return str(self.num) + " || " + str(self.created_at)
settings.py
TIME_ZONE = 'Asia/Novosibirsk'
USE_TZ = True
The first sentence of Django's time zone documentation explains what you're seeing:
When support for time zones is enabled, Django stores datetime information in UTC in the database, uses time-zone-aware datetime objects internally, and translates them to the end user’s time zone in templates and forms.
So the database value is in UTC. The str() value is also in UTC, since you've manually converted the UTC datetime to a string without changing the timezone. The value interpreted by the form and displayed by the template is in your local time, since templates convert DateTimeFields to the current timezone.
If you want the str() value to use the local timezone you can use Django's localtime() function:
from django.utils.timezone import localtime
class Teg1(models.Model):
...
def __str__(self):
return str(self.num) + " || " + str(localtime(self.created_at))
If i'm not mistaken, you must be in Russia which is 7 hours ahead of UTC. So, the server that you use must be using the UTC time which in my opinion is a good thing.
I personally prefer to save times in UTC time in the data base and then convert them to the local time in the front end.
from django.utils import timezone
from datetime import datetime
teg1 = Teg1(created_at=datetime.now(tz=timezone.utc)
teg1.save()
However, if you want to save the datetime in your local time, you can use:
from datetime import datetime
import pytz
novosibirsk = pytz.timezone("Asia/Novosibirsk")
now = datetime.now(novosibirsk)
teg1 = Teg1(created_at=now)
teg1.save()
Have in mind that in your admin interface, you might see the time and date based on the timezone you select in your settings.py. However, the data saved in the database is still in UTC time.
instead of using
from datetime import datetime
class Teg1(models.Model):
created_at = models.DateTimeField(default=datetime.now)
use (this will use timezone that you have set in settings.py)
from django.utils import timezone
class Teg1(models.Model):
created_at = models.DateTimeField(default=timezone.localtime())

Filtering DateTime field with Date Field using Filter Set

How do I go about filtering a date time field with just a Date field.
With the model and filter below
http://localhost:8020/applications/?created=19-07-2017 returns an empty queryset even with records which have date created=19-07-2017 (The created date of the record in datetime
IN MY MODELS.PY
Class Application(models.Model):
created = models.DateTimeField(auto_now=False, auto_now_add=True)
IN MY FILTERS.PY
import django_filters
class ApplicationFilter(django_filters.FilterSet)
created = django_filters.DateTimeFilter( label='Created')
class Meta:
model = Application
fields = ['created']
Using contains works.
Use DateFilter instead and specify the date format
created=django_filters.DateFilter(input_formats=['%Y-%m-%d','%d-%m-%Y'],lookup_expr='icontains'
use contains
created = django_filters.DateTimeFilter(name='created',lookup_expr='contains')
maybe also need to change the dateformat , yyyy-mm-dd
in your filters.py write this:
import django_filters
class ApplicationFilter(django_filters.FilterSet)
class Meta:
model = Application
fields = {'created': '__all__'}
in your url :
http://localhost:8020/applications/?created__date=19-07-2017
Some of the answers below mention the use of lookup_expr='icontains'. Whilst this does work, it will yield incorrect results when the apps timezone is not UTC. This is because the date field returns the datetime stamp in UTC.
The result is that if an object has a date in UTC+2, of 2022-07-31 00:30, this will be returned as 2022-07-30 22:30, yielding incorrect results. It will be included in a filtered queryset of date 2022-07-30 and not 2022-07-31 as we expect.
Instead, we can the field name to return the date, where Django already does the conversion of the date using the specified timezone in the settings.py file. This can be done by using field_name='created__date' rather than lookup_expr='icontains' so that it is timezone aware.
created=django_filters.DateFilter(field_name='created__date')

How to set a Django model field's default value to a function call / callable (e.g., a date relative to the time of model object creation)

EDITED:
How can I set a Django field's default to a function that gets evaluated each time a new model object gets created?
I want to do something like the following, except that in this code, the code gets evaluated once and sets the default to the same date for each model object created, rather than evaluating the code each time a model object gets created:
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:
I want to create a default value for a function parameter such that it is dynamic and gets called and set each time the function is called. How can I do that? e.g.,
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
Specifically, I want to do it in Django, e.g.,
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))
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_date = 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_date = 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_date():
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
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)
There's an important distinction between the following two DateTimeField constructors:
my_date = models.DateTimeField(auto_now=True)
my_date = 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
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)))
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 mydate(date=None):
if date is None:
date = datetime.now()
print date
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 _MyDateDummyDefault(object):
pass
def mydate(date=_MyDateDummyDefault):
if date is _MyDateDummyDefault:
date = datetime.now()
print date
del _MyDateDummyDefault
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 mydate(*args, **kw):
if 'date' in kw:
date = kw['date']
elif len(args):
date = args[0]
else:
date = datetime.now()
print date
Pass the function in as a parameter instead of passing in the result of the function call.
That is, instead of this:
def myfunc(date=datetime.now()):
print date
Try this:
def myfunc(date=datetime.now):
print date()

Subtract django.db.models.DateField from python's datetime.date to get age

In django, I want to get the age (in days) of an instance of a class. I tried doing that by subtracting its creation date field from today, but it does not seem to work properly. date.today() works fine, but DateField is giving me trouble. I looked at its source code and the django docs online for my version but I'm not sure how to manipulate it to perform the subtraction.
import datetime.date
from django.db import models
class MyItem(models.Model):
item_name = models.CharField(max_length = 30)
creation_date = models.DateField()
def age(self):
return date.today() - creation_date
my_first_item = MyItem(item_name = 'First', creation_date = '2005-11-01')
print my_first_item.age.days
Any input would be greatly appreciated!
Your problem is that you are trying to use a field instance outside of a model to represent a value.
models.DateField is a class which represents a database field with a type of "date". I suspect that you are looking to do one of the following:
Just do straight date math
Work with a value returned by a model
In the case of 1, you don't want to use Django's models at all. All you need and want is python's date and time handling classes. For your specific example all you need to use is a pair of date objects and you will end up with a timedelta object.
To do what you were trying to do in your example with the python standard classes, see the example below:
from datetime import date
birthday = date(year=2005, month=11, day=1)
today = date.today()
age = today - birthday
print age.days()
Here we instantiate a date with the birthdate values, we get a date with today's values, subtract them to get a timedelta, and finally print the number of days between the two dates.
In the case of 2, let's look at an example model:
class Person(models.Model):
name = models.CharField(max_length=100)
birthday = models.DateField()
Here we have a model where we've used models.CharField and models.DateField to describe a table in the database which contains a "varchar" column and a "date" column. When we fetch instances of this model using the ORM, Django handles converting whatever value the database returns to a native datatype. Now let's look at some code that figures out the age of an instance of a person:
from datetime import date
from myapp.models import Person
person = Person.objects.get(id=1)
age = date.today() - person.birthday
print age.days
Here you can see that we fetch an instance of the person model from the database and then we subtract their birthday from today. We're able to do this here, because when we access "person.birthday" Django is transforming whatever value the database returned into a python date object. This is the same type as the date object returned by "date.today()" so the "-" operator makes sense. The result of the subtraction operation is a timedelta object.