I have to write a serializer that returns datetime in the following formats:
2012-01-01T13:00:00+00:00 (utc_with_timezone) 2020-01-01T09:00:00 (must be in localtime without timezone info)
class SomeResponse(serializers.Serializer):
modified = serializers.DateTimeField() # AutoLastModifiedField(_('modified'))
local_time = serializers.DateTimeField()
but the response for modified field contains miliseconds: 2022-01-01T18:14:05.378897+05:00the response for local_time field contains timezone info and I have to convert it to local time
How can I manipulate the output format without changing the settings for the whole project?
I solved the problem by overriding to_representation of serializers.DateTimeField:
class SomeResponse(serializers.Serializer):
modified = TimeZoneWithUTCField()
local_time = TimeZoneWithUTCField()
class TimeZoneWithUTCField(serializers.DateTimeField):
def to_representation(self, value):
if not value:
return None
value = value.strftime("%Y-%m-%dT%H:%M:%S%z")
if value.endswith('+0000'):
value = value[:-5] + '+00:00'
return value
Related
I have the following (simplified) model and factory:
models.py
class Event():
duration = FloatField()
start_time = TimeField()
finish_time = DateTimeField()
def save(self, *args, **kwargs):
self.finish_time = self.start_time + timedelta(hours=self.duration)
event_factory.py
from factory import Faker
class EventFactory:
date = Faker(
"date_time_this_month",
before_now=False,
after_now=True,
tzinfo=timezone.get_current_timezone(),
)
start_time = Faker("time_object")
duration = Faker("random_int")
However, my save method raises Warning: DateTimeField Event.finish_time received a naive datetime (2022-03-28 12:43:38) while time zone support is active.
date is aware due to tzinfo argument, but start_time is naive (I checked with django's timezone.is_aware()), because time providers in Faker do not seem to allow any timezone parameters.
Any suggestion in getting a fake aware time object in factory-boy/Faker?
Try to use FuzzyDateTime objects, they return a timezone aware object: https://factoryboy.readthedocs.io/en/stable/fuzzy.html?highlight=timezone
this code cannot work and give json serializable error
class Bank(peewee.Model): // create Bank table
bank_id = peewee.PrimaryKeyField()
bank_name = peewee.CharField()
account_no = peewee.CharField()
ifc_code = peewee.CharField()
swift_code = peewee.CharField(null = True)
modify_date = peewee.DateTimeField(default=datetime.datetime.now(),formats=['%Y-%m-%d'])/*date in yyyy-mm-dd formate*/
status = peewee.IntegerField(default = 0)
class Meta:
database = db
This answer is very incorrect - please see my answer below (#coleifer).
The default date that you are providing is not a datetime object. Rather it's a string!
modify_date = peewee.DateTimeField(default=datetime.datetime.now().strftime('%Y-%m-%d'))
type(datetime.datetime.now().strftime('%Y-%m-%d')) --> str
You can pass default current datetime object like this:
date = datetime.datetime.now().strftime('%Y-%m-%d')
need_date = datetime.strptime(date, '%Y-%m-%d')
modify_date = peewee.DateTimeField(default=need_date)
or
peewee.DateTimeField(default=datetime.datetime.now)
It looks like non-timezone aware datetimes work fine, so if you're using UTC then you can store datetime.utcnow() as that returns the current UTC date and time with tzinfo None i.e. as a "naive" datetime object.
I found this solution to store and retrieve the timezone aware field as text, however it's not ideal as the datetime object isn't being stored.
from datetime import datetime
from peewee import *
class TimestampTzField(Field):
"""
A timestamp field that supports a timezone by serializing the value
with isoformat.
"""
field_type = "TEXT"
def db_value(self, value: datetime) -> str:
if value:
return value.isoformat()
def python_value(self, value: str) -> str:
if value:
return datetime.fromisoformat(value)
https://compileandrun.com/python-peewee-timezone-aware-datetime/
If you want to store a date, use the DateField. Also, the default needs to be a callable -- in other words, leave OFF the parentheses!
class Bank(peewee.Model): // create Bank table
bank_id = peewee.PrimaryKeyField()
bank_name = peewee.CharField()
account_no = peewee.CharField()
ifc_code = peewee.CharField()
swift_code = peewee.CharField(null = True)
modify_date = peewee.DateField(default=datetime.date.today)
status = peewee.IntegerField(default = 0)
class Meta:
database = db
When it comes time to serialize this as Json, just use a custom json formatter that can handle python datetime.date objects. This is the proper way. You should always store your data using the appropriate format and worry about presentation (and serialization) in another layer.
It is very simple to extend Python's json serializer to handle unsupported types:
def convert_date(o):
if isinstance(o, datetime.date):
return o.__str__()
json.dumps(my_obj, default=convert_date)
I've defined a model as follows (Shortened it for the question)
from datetime import datetime, date, timedelta
class Case(models.Model):
received_email_sent = models.DateTimeField(null=True, blank=True, default=None)
def send_received_email(self):
message = settings.EMAIL_HEADER + self.case_received_email() + settings.EMAIL_FOOTER
send_mail('Subject here', message, settings.EMAIL_HOST_USER, ['xxx#xxx.com'], fail_silently=False)
self.received_email_sent = datetime.now()
and in the view I call send_received_email on an existing object. I know that the send_received_email block is being entered because I'm receiving the emails every time I test this out, but the self.received_email_sent = datetime.now() part is leaving that field as its default value (None) every time. Here's the relevant part of the view:
from logbook.models import Case
def job_email(request, case_id):
case = get_object_or_404(Case,pk=case_id)
case.send_received_email()
return HttpResponseRedirect('/jobs/'+str(case.case_id))
I have also tried an alternative method, where saving the field is done in the view instead of the model, like so:
models:
class Case(models.Model):
received_email_sent = models.DateTimeField(null=True, blank=True, default=None)
def send_received_email(self):
message = settings.EMAIL_HEADER + self.case_received_email() + settings.EMAIL_FOOTER
send_mail('Subject here', message, settings.EMAIL_HOST_USER, ['xxx#xxx.com'], fail_silently=False)
#self.received_email_sent = datetime.now()
views:
from datetime import datetime
from logbook.models import Case
def job_email(request, case_id):
case = get_object_or_404(Case,pk=case_id)
case.send_received_email()
case.received_email_sent = datetime.now()
return HttpResponseRedirect('/jobs/'+str(case.case_id))
I have also tried both of the above routes with various tweaks like removing the brackets on now() and changing from datetime import datetime to just import datetime. No joy. Thanks for having a look at this.
You need to call self.save() at the end of send_received_email().
In Flask, when I define a slug in a class (for mongoengine), and I want to return 2014-04-11 (YY-MM-dd), I always get YY-mm-dd hh:mm:ss as slug.
Why is it so?
When I remove the slug variable, my demo variable returns the right format...
class Post(db.Document):
demo = db.DateTimeField(default=datetime.date.today())
slug = db.DateTimeField(default=datetime.date.today())
And this is how an example URL looks like hence:
http://0.0.0.0:5000/2014-03-19%2000:00:00/
DateTimeField always returns a full date with time formated as hh:mm:ss.
To get only the date, you should change the database field to string.
This is how your class should look like hence:
class Post(db.Document):
demo = db.DateTimeField(default=datetime.date.today())
slug = db.StringFieldField(default=datetime.date.today())
I want to disable django fields for 6 months after the date of update. I have saved update_time to a table.
updated_time = a.update_time
disabled_time = a.update_time + timedelta(180)
I want to diable field that updated:
self.fields['first_name'].widget.attrs['readonly'] = True
How can I disable self.fields['first_name'].widget.attrs['readonly'] = True for disabled_time?
Thanks in advance
You can compare, and substract basic datetime objects and make some check at form initialization time:
from datetime import timedelta, datetime
...
class FooForm(ModelForm):
def __init__(self, *args, **kwargs):
super(FooForm, self).__init__(*args, **kwargs)
# check if we already have a saved object and it's not older than 180 days
if self.instance.pk and
(datetime.now() - self.instance.update_time) < timedelta(180):
self.fields['first_name'].widget.attrs['readonly'] = True
class Meta:
model = Foo
(Not really tested but should work as it is.)
Also note, that it is often convenient to keep update_time with auto_now set to True.