Saving DateTimeField with mongoengine - django

Using the Django framework (1.3.1), together with Mongoengine.
When trying to save a posted field (the due date), it bails out with a
ValidationError (cannot parse date "2013-12-31": ['DueDate'])
However when saving the date via datetime.datetime.now() it works fine. After searching for examples, I'm out of options.
The related parts of my code (with a normal HTML form using the text input tag):
views.py
goal.DueDate = request.POST['duedate']
goal.save()
models.py
class Goal(Document):
DueDate = DateTimeField()
last_update = DateTimeField(required=True)
Any idea?
Update (can't answer myself yet):
Ok.. found the solution. Typing it, apparently gave new insights.
goal.DueDate = datetime.datetime.strptime(request.POST['duedate'],
'%Y-%m-%d')

DateTimeField expects a datetime, not a string.
If the format is well known, you can use strptime like in your update, or dateutil parse method which is able to guess format.
You should also think about adopting a safer ISO formatted string send in the form from the web side.

Related

Change date format in Django admin page [duplicate]

I recently added a new model to my site, and I'm using an admin.py file to specify exactly how I want it to appear in the admin site. It works great, but I can't figure out how to get one of my date fields to include seconds in it's display format. I'm only seeing values like "Aug. 27, 2011, 12:12 p.m." when what I want to be seeing is "Aug. 27, 2011, 12:12*:37* p.m."
Try this in the ModelAdmin:
def time_seconds(self, obj):
return obj.timefield.strftime("%d %b %Y %H:%M:%S")
time_seconds.admin_order_field = 'timefield'
time_seconds.short_description = 'Precise Time'
list_display = ('id', 'time_seconds', )
Replacing "timefield" with the appropriate field in your model, of course, and adding any other needed fields in "list_display".
digging around I ended here but applied a different approach to my app.
Changing django admin default formats could be done changing the django locale formats for every type you want.
Put the following on your admin.py file (or settings.py) to change datetime default format at your django admin.
from django.conf.locale.es import formats as es_formats
es_formats.DATETIME_FORMAT = "d M Y H:i:s"
It will change the ModelAdmin's datetime formats on that file (or whole site if in settings).
It does not breaks admin datetime filters and order features as #Alan Illing has point out in comments .
hope this help in future
Extra info:
You can change it for every available locale in django, which are a lot.
You can change the following formats using this approach
from django.conf.locale.es import formats as es_formats
es_formats.DATETIME_FORMAT
es_formats.NUMBER_GROUPING
es_formats.DATETIME_INPUT_FORMATS
es_formats.SHORT_DATETIME_FORMAT
es_formats.DATE_FORMAT
es_formats.SHORT_DATE_FORMAT
es_formats.DATE_INPUT_FORMATS
es_formats.THOUSAND_SEPARATOR
es_formats.DECIMAL_SEPARATOR
es_formats.TIME_FORMAT
es_formats.FIRST_DAY_OF_WEEK
es_formats.YEAR_MONTH_FORMAT
es_formats.MONTH_DAY_FORMAT
If you've tried gabriel's answer but it did not work, try to set USE_L10N = False in settings.py, it works for me.
Note that if USE_L10N is set to True, then the locale-dictated format has higher precedence and will be applied instead
See: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-DATETIME_FORMAT
The accepted answer is correct, however I found it a bit confusing to understand how/why it works. Below is a small example that I hope illustrates how to do this more clearly.
Django provides a few ways to display "custom" fields in your admin view. The way I prefer to achieve this behavior is to define a custom field in the ModelAdmin class and display that instead of your intended field:
from django.contrib import admin
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
class PersonAdmin(admin.ModelAdmin):
#admin.display(description='Birthday')
def admin_birthday(self, obj):
return obj.birthday.strftime('%Y-%m-%d')
list_display = ('name', 'admin_birthday')
Notice that instead of displaying the actual birthday field from the Person model, we define a custom field (admin_birthday) as a method in the PersonAdmin and display that instead by adding it to the list_display attribute. Furthermore, the admin.display() decorator modifies how Django will display this custom field in the admin view. Using this approach, the admin panel will show the NAME and BIRTHDAY fields but using your preferred date formatting for the date.
The reason I prefer this approach is you keep the Model field definitions separate from how you display them in the admin panel. You can read more about alternative approaches in the Django admin documentation.

Django - Date and Time Field Explanation

I have few questions regarding the Date and time fields both in the model and in the form class.
1.)In the docs for the date field what does the line "Normalizes to: A Python datetime.date object." mean? Does it mean that the field data stored in the request.POST dictionary on the submission of the form is a datetime.date object?If yes, then when does it do it when the form is submitted or do we have to call one of its functions?
2.)If i have a models.DateField() then while assigning data to it manually in a view should i assign a datetime.date object or a unicode object with the 'yyyy-mm-dd' format?
3.)If i take a date in my forms.DateField() in the '%d/%m/%y' format how do i assign it to my models.DateField() because that seems to accept only the 'YYYY-mm-dd' format?
If somebody could also suggest some links which explain these fields in detail with examples apart from the docs it would be helpful.
For first question, datefield saves date object and if you are saving any data( for example a string: "01-01-2015") to datefield, then you have to convert that data into date object. You will not get a date object in request.POST, if you use a form, then you can get it by using cleaned_data.(example below) From request.POST ,you will get an unicode object.( then you will need to convert it to date object, example below)
For second question, obviously you have to save dateobject, not unicode object.
For third question, in forms, To display initial field value properly formatted, use DateInput widget. To customize validation, use input_formats keyword argument of DateField. Here, from forms, you will get a date object in views, so it can be saved directly, like:
In model.py:
class DateModelClass(models.Model):
date= models.DateField()
In forms.py:
date= forms.DateField(widget=forms.DateInput(format = '%d/%m/%Y'), input_formats=('%d/%m/%Y',))
In views.py:
date_data= DateModelClass(date= form.cleaned_data['date'])
date_data.save()
Also you can convert a string of your desired format to Date Object like this:
>>import datetime
>>datetime.datetime.strptime(u"09/07/2014", '%d/%m/%Y').date()

Does Django DateTimeField supporting time with time zone information?

When I post a time with timezone information eg:2013-02-27T14:00:00-05:00 to a Django datetime field throws a form error "Enter a valid date/time.".
My form field is defined as below
time = forms.DateTimeField()
I also tried passing in date formats to the form filed. eg:
DATE_FORMATS = [
'%Y-%m-%dT%H:%M:%S-%z',
]
time = forms.DateTimeField(input_formats=DATE_FORMATS)
Does Django DateTimeField not support time with timezone information?
Yes! Django DateTimeField supports timezones, but if you take a look at the documentation, you have to specify it separately from the actual DateTime in your form. Specifically, the documentation recommends that you activate a timezone on a per user basis, otherwise you will always end up entering the DateTime in the default timezone.
https://docs.djangoproject.com/en/dev/topics/i18n/timezones/#time-zone-aware-input-in-forms
This is the documentation I used for my project, and it worked out well.
In addition, if you need to specify a specific timezone in a form field, you can always create a drop down field with all of the timezones in it, and then save the DateTime in your view with the correct timezone.

Search DateFields by Format A in Django Admin though they are stored in Format B

Am building a Django app. And on the admin is is required to be able to search for items based on queries that might also include names of the month, year, etc.
The problem (I can't seem to find a solution to this on SO or elsewhere), is that the default format for dates in Django (ok, the way the dates have been persisted to the datastore) is %Y-%m-%d, but I wish to be able to search on say query like "June" from the Django Admin search.
How do I make it possible to search date fields using custom date formats, without converting the formats in which the dates are persisted?
Date and Time are stored in the database as a distinct type for them, because you should be able to do special date time operations like greater than, lesser than and not just string search.
Django ORM accepts the date-time as strings formatted in one of the standard formats including strings, integers and datetime objects .
Example:
Entry.objects.filter(pub_date__year=2006)
or
Entry.objects.filter(
... headline__startswith='What'
... ).exclude(
... pub_date__gte=datetime.now()
... ).filter(
... pub_date__gte=datetime(2005, 1, 1)
... )
You could use any one of the several libraries to parse the time in the string to datetime
objects. The easiest would be to use the awesome dateutil library:
import dateutil.parser
dateutil.parser.parse('2008-08-09T18:39:22Z')
#Following is returned
datetime.datetime(2008, 8, 9, 18, 39, 22, tzinfo=tzutc())
You've probably already found the answer but it could be useful to someone (like myself!) still looking into this.
Django 1.6+ :
You can customise the search results:
https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_search_results
Previous Django versions:
You just need to use your own ChangeList to temporary change the date format in the query string. Note that the format is only changed temporarily so that the user won't see any change in the query string.
The example here below accepts UK date format, e.g. '31 Dec 1999' but it can easily changed to accept any date format (or just a month name like 'June' as requested by #nemesisfixx)
In your admin model customise the changelist class:
class MyModelAdmin(admin.ModelAdmin):
...
def get_changelist(self, request, **kwargs):
"""
Returns the ChangeList class for use on the changelist page.
"""
return MyChangeList
Then in MyChangeList class:
class MyChangeList(ChangeList):
def get_query_set(self, request):
# Allow date search with UK format
try:
date_search = datetime.datetime.strptime(self.query, '%d %b %Y').date().strftime('%Y-%m-%d')
except ValueError:
date_search = self.query
with temporary_value(self, 'query', date_search):
qs = super(MyChangeList, self).get_query_set(request)
return qs

Django - Custom Model Method - How to specify datatype so Admin formats it properly?

Example:
class MyModel(models.Model):
field1=models.CharField(..)
field2=models.DateTimeField()
def today(self):
return self.field2
When I look at this in the admin site, field2 is formatted differently than the today field.
How can I tell the admin site to treat today like it's treating field2? I.e., tell Django admin that 'today' is a models.DateTimeField?
Here is what it's showing:
Field2 today
April 5, 2011, 9:10 a.m. 2011-04-11 08:47:27
To obtain DateTime object call datetime.datetime.now() instead of datetime.datetime.today()
EDIT:
Or use models.DateField() for field2 instead of models.DateTimeField() :-)
EDIT2:
Here is the solution:
def today(self):
from django.utils import formats
return formats.localize(self.field2)
That's some really really weird behaviour. At a total guess, it may have something to do with django settings; specifically the DATETIME_FORMAT (and related) settings. The framework probably does introspection on fields, and if they are of DateTime type, are rendered according to the aforementioned settings.
Introspection on methods wouldn't make sense in the majority of cases, so I could understand this behaviour if it is the case.
Try modifying the settings accordingly (provide different datetime formats), and see if the fields change and the method remains the same.
Edit:
Looking at django.contrib.databrowse.datastructures, there is a section of code that does something like:
if isinstance(self.field, models.DateTimeField):
objs = capfirst(formats.date_format(self.raw_value, 'DATETIME_FORMAT'))
I'd imagine a similar thing happening within the admin app, though I can't find an exact reference at the moment.
To achieve what you want, you'll need to format your datetime appropriately:
def today(self):
from django.conf import settings
return self.field2.strftime(settings.DATETIME_FORMAT)
Or, using #cata's comment:
def today(self):
from django.utils.formats import localize
return localize(self.field2)
If you choose to supply a "list_display" item through your own function, and you're not happy with the default output, you'll need to format it yourself. In this case, if you want to have identical formatting to what the DateTime database field ends up with:
from django.utils import formats
def today(self):
return formats.localize(self.field2)
Background:
templates/admin/change_list.html
uses the template tag
django.contrib.admin.templatetags.admin_list.result_list
which in turn will call
django.contrib.admin.templatetags.admin_list.items_for_result()
to render the individual column values for each row.
You'll see that both your values start off being a DateTime instance, either through database lookup or calling your function, see
django.contrib.admin.util.lookup_field()
but the return value "f" will only be a field if there was a database field. You provided a function, so lookup_field() will only provide the value, and "f" will be None.
So in items_for_result(), your value will run through the "if f is None" block and miss out on
result_repr = display_for_field(value, f)
In other words,
django.contrib.admin.util.display_for_field()
will only be called on the database value to format according to the field type, so this is the treatment your function value is missing out on:
elif isinstance(field, models.DateField) or isinstance(field, models.TimeField):
return formats.localize(value)
and you'll need to do that last line yourself, as shown above.
EDIT: Regarding your question
How can I tell the admin site to treat
today like it's treating field2? I.e.,
tell Django admin that 'today' is a
models.DateTimeField?
It's not a models.DateTimeField, it's a function value. If it were a models.DateTimeField, it would be describing your model. Look at all the stuff that entails: http://docs.djangoproject.com/en/dev/ref/models/fields/
In your example, you really could just use field2. Apparently you want to do things to its value, calculate it etc. - so what's today.db_column then?
That said, it would be nice if function values that are DateTime instances were run through format.localize() by default, as that's what the documentation on localization seems to be promising.
By the way, I would rather define a formatted value in the ModelAdmin than in the model itself. I usually call it something like "formatted_today" (to keep the datetime value of the original today()), it's just that if the Admin is the only place that needs the formatted value, imho that's where it should be defined.
All previous answers provide solutions, that will handle timezone info incorrectly in new Django versions.
field2 and today will generally show different time if settings.USE_TZ==True.
I found this question today and have spent some time to figure out the correct way:
from django.db import models
from django.contrib import admin
from django.utils.timezone import template_localtime
from django.utils.formats import localize
class MyModel(models.Model):
# ...
field2=models.DateTimeField()
class MyModelAdmin(admin.ModelAdmin):
# ...
def today(self, obj):
return localize(template_localtime(obj.field2))
admin.site.register(MyModel, MyModelAdmin)