Opened 6 years ago

Closed 4 years ago

#12401 closed Bug (invalid)

DateField/DateTimeField is not instantiated with Python datetime.date/datetime object until after retrieving from the database

Reported by: heidar_rafn Owned by: nobody
Component: Core (Other) Version: 1.1
Severity: Normal Keywords: DateField DateTimeField
Cc: jroesslein@… Triage Stage: Design decision needed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX:

Description

The Django documentation states:
" DateField
class DateField([auto_now=False, auto_now_add=False, options])
A date, represented in Python by a datetime.date instance..."

I have experienced a problem with this - the field is not represented by the datetime.date class until after retrived from the database.

Here is a small example that shows this:

class chgLog(models.Model):
    chgReason = models.CharField(max_length=255)
    validFrom = models.DateField()

Following is from a Python/Django shell session :

>>> chglog = chgLog(chgReason='testing datefield',validFrom='2009-13-43')
>>> chglog.id
>>> chglog.validFrom
'2009-13-43'
>>> chglog.validFrom.__class__
<type 'str'>
>>> chglog.save()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/lib/pymodules/python2.6/django/db/models/base.py", line 410, in save
    self.save_base(force_insert=force_insert, force_update=force_update)
  File "/usr/lib/pymodules/python2.6/django/db/models/base.py", line 483, in save_base
    values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True))) for f in meta.local_fields if not isinstance(f, AutoField)]
  File "/usr/lib/pymodules/python2.6/django/db/models/fields/__init__.py", line 192, in get_db_prep_save
    return self.get_db_prep_value(value)
  File "/usr/lib/pymodules/python2.6/django/db/models/fields/__init__.py", line 511, in get_db_prep_value
    return connection.ops.value_to_db_date(self.to_python(value))
  File "/usr/lib/pymodules/python2.6/django/db/models/fields/__init__.py", line 484, in to_python
    raise exceptions.ValidationError(msg)
ValidationError: Invalid date: month must be in 1..12

It shows that validation is done properly.
Now let us have all values valid:

>>> chglog = chgLog(chgReason='testing datefield',validFrom='2009-12-18')
>>> chglog.id
>>> chglog.validFrom
'2009-12-18'
>>> chglog.validFrom.__class__
<type 'str'>
>>> chglog.save()
>>> chglog.id
130L
>>> chglog.validFrom
'2009-12-18'
>>> chglog.validFrom.__class__
<type 'str'>

And here it is obvious that the object stores the DateField as string, even after saving.
Now let us investigate it after retrieving it from the database:

>>> chglog_from_db = chgLog.objects.get(id=130)
>>> chglog_from_db.id
130L
>>> chglog_from_db.validFrom
datetime.date(2009, 12, 18)
>>> chglog_from_db.validFrom.__class__
<type 'datetime.date'>

And finally this code segments that compares DateField from two different Django objects, the first one after saving, the second one after retrieving from the database. Same object (2 copies), but reported to have different values:

>>> chglog.validFrom == chglog_from_db.validFrom
False
>>> 

Change History (10)

comment:1 Changed 6 years ago by josh

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Triage Stage changed from Unreviewed to Design decision needed

The DateTimeField is also affected.

One way to get around this issue is to initialize the value with a datetime.date object
instead of using a string. Example:

t = TestModel()
t.somedate = date.today()
t.save()

Changing save() to convert the strings to datetime types might break apps that expect
the type to not change. If we choose not to fix this this behaviour should at least be documented.

comment:2 Changed 6 years ago by josh

  • Cc jroesslein@… added
  • Summary changed from DateField is not instantiated with Python datetime.date object until after retrieving from the database to DateField/DateTimeField is not instantiated with Python datetime.date/datetime object until after retrieving from the database

comment:3 Changed 6 years ago by josh

  • Keywords DateTimeField added

comment:4 follow-up: Changed 6 years ago by JohnDoe

See ticket #12223, there's no validation on Models.

comment:5 in reply to: ↑ 4 ; follow-up: Changed 6 years ago by josh

Replying to JohnDoe:

See ticket #12223, there's no validation on Models.

What does this issue have to do with validation?

comment:6 in reply to: ↑ 5 ; follow-up: Changed 6 years ago by JohnDoe

Replying to josh:

Replying to JohnDoe:

See ticket #12223, there's no validation on Models.

What does this issue have to do with validation?

Well, try using a ModelForm instead of working directly on models like that, you'll see that a form converts it into a datetime object.

Which brings me to my validation point, Models does nothing but bare minimum to get it saved, Forms does validation and conversion.

comment:7 in reply to: ↑ 6 Changed 6 years ago by heidar_rafn

Replying to JohnDoe:

Replying to josh:

Replying to JohnDoe:

See ticket #12223, there's no validation on Models.

What does this issue have to do with validation?

Well, try using a ModelForm instead of working directly on models like that, you'll see that a form converts it into a datetime object.

Which brings me to my validation point, Models does nothing but bare minimum to get it saved, Forms does validation and conversion.

As I pointed out in this ticket, a validation on the Date field is performed on saving (it does not save invalid date).
Therefore the statement that models do not validate is not absolutely true.
I pointed out in my last point in the ticket, that after saving the model, it is in different form when retrived from the database - and that was my main point with this ticket and it made me wonder whether Django is as perfect as I have found it so far.

comment:8 Changed 6 years ago by JohnDoe

Replying to heidar_rafn:

Replying to JohnDoe:

Replying to josh:

Replying to JohnDoe:

See ticket #12223, there's no validation on Models.

What does this issue have to do with validation?

Well, try using a ModelForm instead of working directly on models like that, you'll see that a form converts it into a datetime object.

Which brings me to my validation point, Models does nothing but bare minimum to get it saved, Forms does validation and conversion.

As I pointed out in this ticket, a validation on the Date field is performed on saving (it does not save invalid date).
Therefore the statement that models do not validate is not absolutely true.
I pointed out in my last point in the ticket, that after saving the model, it is in different form when retrived from the database - and that was my main point with this ticket and it made me wonder whether Django is as perfect as I have found it so far.

You just need to understand how things really work (and if you look at the other ticket, you'll see i argued like you're doing, mostly because it seems undocumented. Your ticket supports my original point).

This is the best example i can come up with on the spot.

Use SQLite which does not enforce datatypes ect.

The Setup:

class Test(models.Model):
    text = models.CharField(max_length=10)

The Test:

>>> t = Test()
>>> t.text = '0123456789horse'
>>> t.save()
>>> y = Test.objects.all()
>>> y[0].text
u'0123456789horse'

This discussion should probably be moved to the mailinglist if it should be discussed further, as it is not a bug, but merely a design discussion.

comment:9 Changed 4 years ago by mattmcc

  • Severity set to Normal
  • Type set to Bug

comment:10 Changed 4 years ago by lukeplant

  • Easy pickings unset
  • Resolution set to invalid
  • Status changed from new to closed

Model attributes are not converted to the correct Python types when you set the attribute. That's the design decision we made, and one that isn't going to change without a lot of backwards incompatibilities. It is consistent with normal Python behaviour - it would be rare for type checking to occur when attributes are set, and even rarer for type conversion to occur. In general, I expect to the following to pass, no matter what 'foo' is:

>>> x = '2011-06-06'
>>> foo.bar = x
>>> assert foo.bar == x

The proposed behaviour, though potentially useful or even better for Django model objects, would certainly violate that expectation, so the design decision we've made is certainly sensible.

Note: See TracTickets for help on using tickets.
Back to Top