#1584 closed defect (fixed)
[patch] auto_now_add fields set to NULL on update
Reported by: | Owned by: | Adrian Holovaty | |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | normal | Keywords: | |
Cc: | farcepest@…, fawad@…, upadhyay@… | Triage Stage: | Unreviewed |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
When adding an object with a models.DateTimeField(auto_now_add=True), the column is set to the current time as it should be. However, when doing an update, Django tries to set the column to NULL.
Example: These are SQL statements (and parameters) as they are executed. created is auto_now_add and updated is auto_now. First, adding the new objects in the admin interface:
INSERT INTO `dns_domain` (`name`) VALUES (%s) ['foo.com'] INSERT INTO `dns_nameserver` (`domain_id`,`name`,`ip`,`starts`,`expires`,`ttl`,`location_id`,`created`,`updated`) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s) [2L, 'a', '1.2.3.4', None, None, None, None, '2006-04-04 17:50:40', '2006-04-04 17:50:40'] INSERT INTO `dns_nameserver` (`domain_id`,`name`,`ip`,`starts`,`expires`,`ttl`,`location_id`,`created`,`updated`) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s) [2L, 'b', '1.2.3.5', None, None, None, None, '2006-04-04 17:50:40', '2006-04-04 17:50:40']
Then, updating the objects in the admin interface:
SELECT `dns_domain`.`id`,`dns_domain`.`name` FROM `dns_domain` WHERE (`dns_domain`.`name` = %s) ['foo.com'] SELECT 1 FROM `dns_domain` WHERE `id`=%s LIMIT 1 ['2'] UPDATE `dns_domain` SET `name`=%s WHERE `id`=%s ['foo.com', '2'] SELECT `dns_nameserver`.`id`,`dns_nameserver`.`domain_id`,`dns_nameserver`.`name`,`dns_nameserver`.`ip`,`dns_nameserver`.`starts`,`dns_nameserver`.`expires`,`dns_nameserver`.`ttl`,`dns_nameserver`.`location_id`,`dns_nameserver`.`created`,`dns_nameserver`.`updated` FROM `dns_nameserver` WHERE (`dns_nameserver`.`domain_id` = %s AND `dns_nameserver`.`id` = %s) [2L, '4'] SELECT 1 FROM `dns_nameserver` WHERE `id`=%s LIMIT 1 ['4'] UPDATE `dns_nameserver` SET `domain_id`=%s,`name`=%s,`ip`=%s,`starts`=%s,`expires`=%s,`ttl`=%s,`location_id`=%s,`created`=%s,`updated`=%s WHERE `id`=%s ['2', 'a', '1.2.3.4', None, None, None, None, None, '2006-04-04 17:55:02', '4'] SELECT `dns_domain`.`id`,`dns_domain`.`name` FROM `dns_domain` WHERE (`dns_domain`.`id` = %s) ['2'] SELECT `dns_nameserver`.`id`,`dns_nameserver`.`domain_id`,`dns_nameserver`.`name`,`dns_nameserver`.`ip`,`dns_nameserver`.`starts`,`dns_nameserver`.`expires`,`dns_nameserver`.`ttl`,`dns_nameserver`.`location_id`,`dns_nameserver`.`created`,`dns_nameserver`.`updated` FROM `dns_nameserver` WHERE (`dns_nameserver`.`domain_id` = %s AND `dns_nameserver`.`id` = %s) [2L, '5'] SELECT 1 FROM `dns_nameserver` WHERE `id`=%s LIMIT 1 ['5'] UPDATE `dns_nameserver` SET `domain_id`=%s,`name`=%s,`ip`=%s,`starts`=%s,`expires`=%s,`ttl`=%s,`location_id`=%s,`created`=%s,`updated`=%s WHERE `id`=%s ['2', 'b', '1.2.3.5', None, None, None, None, None, '2006-04-04 17:55:02', '5'] SELECT `dns_domain`.`id`,`dns_domain`.`name` FROM `dns_domain` WHERE (`dns_domain`.`id` = %s) ['2']
Note that created is being set to None/NULL.
Example done with mysql backend. The problem seems to lie in models itself and is not specific to the admin interface: I could update an object using the shell and it would do the same thing. I'm assuming "Database wrapper" encompasses django.db.models and not just the backends.
Attachments (3)
Change History (19)
comment:1 by , 19 years ago
Cc: | added |
---|
by , 19 years ago
Attachment: | auto_now_add_fix.diff added |
---|
by , 19 years ago
Attachment: | auto_now_add_fix.2.diff added |
---|
Better bug fix (evaluation order matters)
comment:2 by , 19 years ago
Summary: | auto_now_add fields set to NULL on update → [patch] auto_now_add fields set to NULL on update |
---|
Patch available
comment:3 by , 19 years ago
Cc: | added |
---|
comment:4 by , 19 years ago
Cc: | added |
---|
comment:5 by , 19 years ago
I'm seeing that this problem happens on the current Django trunk as well.
class NotificationSchedule(models.Model): class Admin: pass def __str__(self): return "%s (%s hours before the event)" % (self.name, self.offset) name = models.CharField("Description of the schedule", maxlength = 72, core = True) offset = models.FloatField("Number of hours before event the notifications should be sent", core = True, decimal_places = 2, max_digits = 10) created = models.DateTimeField(auto_now_add = True) modified = models.DateTimeField(auto_now = True)
[fawad@fawad5 meetup]$ python manage.py shell --plain Python 2.5a1 (trunk:45386, Apr 14 2006, 09:22:06) [GCC 3.4.4 20050721 (Red Hat 3.4.4-2)] on linux2 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from meetup.models import NotificationSchedule >>> x=NotificationSchedule(name="foo", offset=3) >>> x.save() >>> x.offset=4 >>> x.save() Traceback (most recent call last): File "<console>", line 1, in <module> File "/usr/local/stow/python-cvs/lib/python2.5/site-packages/django/db/models/base.py", line 169, in save db_values + [pk_val]) File "/usr/local/stow/python-cvs/lib/python2.5/site-packages/django/db/backends/util.py", line 12, in execute return self.cursor.execute(sql, params) File "/usr/local/stow/python-cvs/lib/python2.5/site-packages/django/db/backends/sqlite3/base.py", line 73, in execute return Database.Cursor.execute(self, query, params) IntegrityError: meetup_notificationschedule.created may not be NULL
comment:6 by , 19 years ago
Cc: | added |
---|---|
milestone: | → Version 0.92 |
Version: | magic-removal → SVN |
With the demise/merging of magic-removal, this is still an issue in the trunk. Presumably other people who are watching this bug would have spoken up if the patch didn't work, and it's simple enough that it's "obviously" correct (for sufficiently large values of obvious).
comment:7 by , 18 years ago
This is biting me as well on trunk. Using a a FileField in conjunction.
The patch does not seem to work for me though. Although someone on IRC did solve his problem with it. Leaves me to assume the issue runs a bit deeper.
I see that after a save in the Admin gui the failing SQL is an UPDATE with the date field set to None on the update, which of course fails the constraint and gives me an IntegrityError submit_date may not be NULL.
submit_date is: models.DateField(auto_now_add = True)
comment:8 by , 18 years ago
The person on IRC noted that his constraint was removed. It also fails for him.
comment:9 by , 18 years ago
i had to change the line in question (line 421 in 3001) to
if self.auto_now or (self.auto_now_add and (add or value is None)):
which works for me. i dont understand why pre_save() just returns the value without setting it at the instance...
comment:10 by , 18 years ago
The patch doesn't exactly fix it: if you save an object n times, the datetime value will be set to the datetime at the last time you saved, not the first. The problem is that the value is not being saved to the object. The pre_save method cannot currently do this, since it doesn't have access to the object (pre_save is a method on a Field object and Field objects are essentially definitions that don't contain data -- the data itself is a member of the model class instance).
The reason this is not normally a problem is that you normally discard' an object after saving it -- the next time it is used will be by another request, which will load it fresh from the database, and the datetime value will be set.
The easiest way to fix it, as I see it, is to change the arguments of Field.pre_save() so that it takes a model instance instead of a value. I'll come up with a patch and check it with Adrian.
by , 18 years ago
Attachment: | auto_now_add_fix.patch added |
---|
Patch involving reworked Field.pre_save()
comment:11 by , 18 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
comment:12 by , 18 years ago
As you can tell, I went ahead and committed the patch -- I was pretty sure about it. The only issue might be compatibility with people who have subclassed Field, but as I understand it that's an internal class -- subclassing Field isn't documented anywhere.
comment:13 by , 18 years ago
Resolution: | fixed |
---|---|
Status: | closed → reopened |
Even with the patch, when updating edit_inline fields, null is set (individual editing of the same model has been fixed.) Had to override save() for inline editing of models with auto_now_add.
comment:14 by , 18 years ago
Resolution: | → fixed |
---|---|
Status: | reopened → closed |
Reclosing -- edit_inline is broken in more ways than this, and is being rewritten.
Bug fix (against magic-removal)