I created a customized User module that adds a few extra fields:
class User(AbstractUser):
"""User modle for staff, clients and customers"""
username = None # remove username from the model
email = models.EmailField("email address", unique=True)
# """Email of user this will be used for login"""
address = models.OneToOneField(Address, on_delete = models.PROTECT, null=True, blank=True)
""":class:`Postal Address` associated with the User"""
birth_date = models.DateField()
"""Date of birth of User"""
phone = models.CharField(max_length=15)
"""Phone number of User"""
org = models.ForeignKey(Organization, on_delete=models.PROTECT, null=True, blank=True)
""":class:`Organization` User is associated with"""
# changes for using email instead of username
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name', 'birth_date', 'phone']
objects = UserManager()
def __str__(self):
return F"{self.email} {self.first_name} {self.last_name}"
When I ran the createsuperuser command it asked me for birth_date (and knew how to parse it). I entered 2020-01-01 for the value.
Curious, I then temporarily put these statements in the createsuperuser function:
print("*****")
print(extra_fields)
print("*****")```
and got:
{'first_name': 'super', 'last_name': 'user', 'birth_date': datetime.date(2020, 1, 1), 'phone': '12345', 'is_staff': True, 'is_superuser': True, 'is_active': True}
How did it know to use datetime.date and how to parse it correctly?
More importantly, how can I make similar behavior for a custom related object?
How did it know to use datetime.date and how to parse it correctly?
It knows that it is a date because the birth_date is a DateField() object.
As for the format, it uses the formats in order that are defined in the DATE_INPUT_FORMAT setting [Django-doc]. By default these are:
[
'%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
'%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006'
'%d %b %Y', '%d %b, %Y', # '25 Oct 2006', '25 Oct, 2006'
'%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006'
'%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006'
]
So here the first format is a match. If that produces an error when parsing, it moves to the next one, etc. until either one of the formats accepts the input, or the list is exhausted, in which case it will not be able to parse the date string.
You can alter the setting to use your own formats, or change the order to make a format more preferred over the other one. This can be important since 2020-05-06 can be parsed as May 6, or June 5. So if you add a format %Y-%d-%m in front of the list, it will be parsed differently.
Related
Django version 2.1
I have an app where I show events. I want to show how long ago or how far in the future the event takes place. To do this I am using the naturaltime templatetag from the humanize package.
{{ event.date|naturaltime }}
# my model in models.py
class Event(models.model):
# ... other fields
date = models.DateTimeField(...)
I want the result to be in Dutch, so I changed the language in settings.py: LANGUAGE_CODE = 'nl-nl'
Here is the problem:
When the time difference between the current time and the datetime set in the model is larger than 24 hours, the translation is only partial.
Examples with time in the past:
# english
one hour ago
# dutch, correct
een uur geleden
# enlish
6 days, 2 hours ago
# dutch translation, only partial
6 dagen, 2 uur ago
Examples with a time in the future
# english
2 hours from now
# dutch translation, correct
over 2 uur
# enlish
1 month from now
# dutch translation, only partial
1 maand from now
As you can see, the 'ago' and 'from now' parts are not translated when the time difference is larger than 24 hours.
I dived into the source code, and found the following relevant information, but still couldn't find the culprit.
Naturaltime calls the default templatetag timesince/timeuntil when the difference is more than 1 day. The timesince templatetag translates correctly, but when the result is passed back to naturaltime to add the 'ago' and 'from now' part, this result is not translated at all.
Humanize
# lines 211-292
#register.filter
def naturaltime(value):
"""
For date and time values show how many seconds, minutes, or hours ago
compared to current timestamp return representing string.
"""
if not isinstance(value, date): # datetime is a subclass of date
return value
now = datetime.now(utc if is_aware(value) else None)
if value < now:
delta = now - value
if delta.days != 0:
# Translators: delta will contain a string like '2 months' or '1 month, 2 weeks'
return _('%(delta)s ago') % {'delta': defaultfilters.timesince(value, now, time_strings={
# Translators: 'naturaltime-past' strings will be included in
# '%(delta)s ago'
'year': npgettext_lazy('naturaltime-past', '%d year', '%d years'),
'month': npgettext_lazy('naturaltime-past', '%d month', '%d months'),
'week': npgettext_lazy('naturaltime-past', '%d week', '%d weeks'),
'day': npgettext_lazy('naturaltime-past', '%d day', '%d days'),
'hour': npgettext_lazy('naturaltime-past', '%d hour', '%d hours'),
'minute': npgettext_lazy('naturaltime-past', '%d minute', '%d minutes')
})}
# some more elif and else
...
else:
delta = value - now
if delta.days != 0:
# Translators: delta will contain a string like '2 months' or '1 month, 2 weeks'
return _('%(delta)s from now') % {'delta': defaultfilters.timeuntil(value, now, time_strings={
# Translators: 'naturaltime-future' strings will be included in
# '%(delta)s from now'
'year': npgettext_lazy('naturaltime-future', '%d year', '%d years'),
'month': npgettext_lazy('naturaltime-future', '%d month', '%d months'),
'week': npgettext_lazy('naturaltime-future', '%d week', '%d weeks'),
'day': npgettext_lazy('naturaltime-future', '%d day', '%d days'),
'hour': npgettext_lazy('naturaltime-future', '%d hour', '%d hours'),
'minute': npgettext_lazy('naturaltime-future', '%d minute', '%d minutes')
})}
# some more elif and else
...
NL locale .po file
# line 259-262 and 302-305, seems working
msgid "an hour ago"
msgid_plural "%(count)s hours ago"
msgstr[0] "een uur geleden"
msgstr[1] "%(count)s uur geleden"
...
msgid "an hour from now"
msgid_plural "%(count)s hours from now"
msgstr[0] "over een uur"
msgstr[1] "over %(count)s uur"
# line 253-254 and 310-311, not working
msgid "%(delta)s ago"
msgstr "%(delta)s geleden"
...
msgid "%(delta)s from now"
msgstr "over %(delta)s"
Am I doing something wrong or is this a bug in the humanize package or dutch translation files?
PS. I am not using any custom translation files
I don't know what the issue was, but upgrading to Django 2.2 solved the issue.
I have a model that shows a short string in __str__() method of the model
def __str__(self):
return "Scheduled at %s" % (self.date_time.strftime("%B %d %Y %I:%M %p"))
#Output: <Task: Scheduled at September 30 2018 12:30 AM>
# it should have been: <Task: Scheduled at September 29 2018 08:30 PM>
When I go to the Django admin, I see in the title Task: Scheduled at September 30 2018 12:30 AM and in the input it is filled with the correct TIME_ZONE: 20:30:00
settings.py
TIME_ZONE = 'Etc/GMT+4'
USE_I18N = False
USE_L10N = False
USE_TZ = True
He keeps showing time with UTC time zone, however I set TIME_ZONE='Etc/GMT+4 in my settings.
I want time data to be saved in database as UTC and show them with TIME_ZONE, in my case Etc/GTM+4
Django converts datetimes when displaying values with templates. If you want to do the conversion in arbitrary blocks of code, use the localtime() helper:
from django.utils.timezone import localtime
def __str__(self):
return "Scheduled at %s" % localtime(self.date_time).strftime("%B %d %Y %I:%M %p")
I've a booking form in my template that sends an email when it's submitted. In my database the datetime field is shown like: Oct. 6, 2015, 3:58 p.m. But when I get the email the datetime field is shown like: 2015-10-06 15:58:50.954102 How do i format it such that in the email it's shown exactly like how it's shown in the database?
models.py
class Booking(models.Model):
patient_name = models.CharField(max_length=1300)
phone = models.IntegerField(null=True, blank = True)
preference = models.CharField(max_length=150,null = True, blank = True) #morning,noon,night
doctor = models.ForeignKey(Doctor)
clinic = models.ForeignKey(Clinic,null=True, blank = True)
datetime = models.DateTimeField(auto_now=True, auto_now_add=True, blank = True, null = True)
def __unicode__(self):
return u"%s %s" % (self.patient_name, self.doctor)
views.py
lead = Booking(doctor_id=doctor.id, clinic_id=doctor.clinic.id, preference=preference, patient_name=patient_name, phone=phone)
lead.save()
body = "Request Made: " + str(lead.datetime) +" "
email = EmailMessage('Blah', body, to=[clinic.email])
email.send()
You can format datestrings using strftime
>>> from datetime import date
>>> dt = date(2015, 10, 6, 15, 58, 50)
>>> dt.strftime("%b. %-d %Y %-I:%M %p")
'Oct. 6 2015 2:12 PM'
There's a list of the codes for strftime at at http://strftime.org/
So in your view you would do something like
body = "Request Made: %s " % lead.datetime.strftime("%b. %-d %Y %-I:%M %p")
Thats not exactly how it's in the database, it's just what the tool you use to view inside the database, displays datetime.
However if you want your datetime to look exactly like that, use:
lead.datetime.strftime("%b. %-d %Y %-I:%M %p")
Here are some relevant sources:
https://docs.python.org/2/library/datetime.html#datetime.datetime
https://docs.python.org/2/library/datetime.html#strftime-strptime-behavior
Help please me)
I have error "[u"'data_date' value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."]" and don't know how resolve it.
my models.py
class Data(models.Model):
main = models.ForeignKey(Main)
user = models.CharField(max_length=200)
description = models.TextField()
data_date = models.DateTimeField(['%Y-%m-%d %H:%M'])
priority = models.CharField(max_length=1)
end_date = models.DateTimeField(['%Y-%m-%d %H:%M'])
def __unicode__(self):
return self.description
def was_published_recently(self):
return self.data_date >= timezone.now() - datetime.timedelta(days=1)
my views.py
def index(request, onsuccess='/', onfail='/login/'):
today = date.today()
data_data=timezone.now()
formatted_datetime = formats.date_format(data_data, "SHORT_DATETIME_FORMAT")
problems_filter = Data.objects.filter(main_id=1).filter(data_date__range=['data_date', 'end_date']).order_by('-data_date').order_by('priority')[:101]
# problems_filter = Data.objects.filter(main_id=1).order_by('-data_date').order_by('priority')[:10]
When i'm use the commend string, it's work fine.
Example format:
28 | 1 | admin | hi | 2014-05-01 00:41:00 | 2 | 2014-06-01 00:00:00
It seems you have ignored seconds portion from the input string to convert to datetime.
data_date = models.DateTimeField( ['%Y-%m-%d %H:%M'] )
end_date = models.DateTimeField( ['%Y-%m-%d %H:%M'] )
+---------------------+-------------------+----------------+
| input_date_string | its_format | what_you_tried |
+---------------------+-------------------+----------------+
| 2014-05-01 00:41:00 | %Y-%m-%d %H:%M:%S | %Y-%m-%d %H:%M |
| 2014-06-01 00:00:00 | %Y-%m-%d %H:%M:%S | %Y-%m-%d %H:%M |
+---------------------+-------------------+----------------+
Change accordingly for both data_date and end_date fields.
data_date = models.DateTimeField( ['%Y-%m-%d %H:%M:%S'] )
end_date = models.DateTimeField( ['%Y-%m-%d %H:%M:%S'] )
Refer to: DATETIME_INPUT_FORMATS
Default:
(
'%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
'%Y-%m-%d %H:%M:%S.%f', # '2006-10-25 14:30:59.000200'
'%Y-%m-%d %H:%M', # '2006-10-25 14:30'
'%Y-%m-%d', # '2006-10-25'
'%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59'
'%m/%d/%Y %H:%M:%S.%f', # '10/25/2006 14:30:59.000200'
'%m/%d/%Y %H:%M', # '10/25/2006 14:30'
'%m/%d/%Y', # '10/25/2006'
'%m/%d/%y %H:%M:%S', # '10/25/06 14:30:59'
'%m/%d/%y %H:%M:%S.%f', # '10/25/06 14:30:59.000200'
'%m/%d/%y %H:%M', # '10/25/06 14:30'
'%m/%d/%y', # '10/25/06'
)
I have a daterangepicker from which the users set date. The choice of format is "DD MMM YYYY"
now to go with that i'm using
datetime.datetime.strptime(time, "%d %b %Y");
but still i am getting an error
Exception Value:[u"'01 Aug 2013' value has an invalid date format. It must be in YYYY-MM-DD format."]
any idea what might be going wrong? stuck on this for a while.
def form_valid(self, form):
new_obj = form.save(commit=False)
new_obj.date_pickup_from, new_obj.date_pickup_to = form.cleaned_data['pickup_daterange'].split(' to ')
new_obj.date_delivery_from, new_obj.date_delivery_to = form.cleaned_data['delivery_daterange'].split(' to ')
here are the forms
pickup_daterange = forms.CharField(
label=_('Pickup Within'),
widget=forms.TextInput(attrs={'class': 'daterange'}),
validators=[
RegexValidator(
regex=r'\d{2}\ \w{3}\ \d{4}\ to\ \d{2}\ \w{3}\ \d{4}',
message=_(u'Range must be of format "mm/dd/yyyy to mm/dd/yyyy"'),
code='invalid_range'
)
],
help_text=_('Within what dates do you want the pickup?')
)
delivery_daterange = forms.CharField(
label=_('Delivery Within'),
widget=forms.TextInput(attrs={'class': 'daterange'}),
validators=[
RegexValidator(
regex=r'\d{2}\ \w{3}\ \d{4}\ to\ \d{2}\ \w{3}\ \d{4}',
message=_(u'Range must be of format "mm/dd/yyyy to mm/dd/yyyy"'),
code='invalid_range'
)
],
help_text=_('Within what dates do you want the delivery?')
)
models.py
date_delivery_from = models.DateField(_('Date of Delivery From'), blank=True, null=True)
date_delivery_to = models.DateField(_('Date of Delivery To'), blank=True, null=True)
function in my form class from where i am calling strptime
def clean_delivery_daterange(self):
daterange_pattern = re.compile(r'(\d{2}\ \w{3}\ \d{4})\ to\ (\d{2}\ \w{3}\ \d{4})')
delivery_daterange = self.cleaned_data['delivery_daterange']
pickup_daterange = self.cleaned_data['pickup_daterange']
str_pickup_from, str_pickup_to = daterange_pattern.search(pickup_daterange).groups()
str_delivery_from, str_delivery_to = daterange_pattern.search(delivery_daterange).groups()
delivery_from = datetime.datetime.strptime(str_delivery_from, "%d %b %Y")
pickup_from = datetime.datetime.strptime(str_pickup_from, "%d %b %Y")
if delivery_from < pickup_from:
raise forms.ValidationError('Delivery dates cannot be before pickup dates')
return delivery_daterange
Your problem is that you're not actually returning the converted values.
delivery_daterange = self.cleaned_data['delivery_daterange']
[...]
return delivery_daterange
That just returns the same string, unparsed. You could do something like this instead:
def clean_delivery_daterange(self):
[...]
delivery_from = datetime.datetime.strptime(str_delivery_from, "%d %b %Y")
delivery_to = datetime.datetime.strptime(str_delivery_to, "%d %b %Y")
return (delivery_from, delivery_to)
def clean_pickup_daterange(self):
[...]
pickup_from = datetime.datetime.strptime(str_pickup_from, "%d %b %Y")
pickp_to = datetime.datetime.strptime(str_pickup_to, "%d %b %Y")
return (pickup_from, pickup_to)
And then in the view:
new_obj.date_pickup_from, new_obj.date_pickup_to = form.cleaned_data['pickup_daterange']
An alternate approach would be to simply return the text values, then convert to Date objects in your view:
def form_valid(self, form):
new_obj = form.save(commit=False)
pickup_start_string, pickup_end_string = form.cleaned_data['pickup_daterange'].split(' to ')
new_obj.date_pickup_from = strptime(pickup_start_string, "%d %b %Y")
#...etc
A more advanced approach would be to define a custom field to cover the concept of a date range, and define its to_python method to return, say, a tuple of Date objects.
Personally, I would simplify the whole thing and just let date_pickup_from and the rest be DateField instances in the form, maybe with a custom widget if I wanted to use something like a jQuery DatePicker to help the user pick the dates, and take care of special rendering in the template.