#29173 closed Cleanup/optimization (invalid)
Document that Model.save() doesn't refresh fields from the database
Reported by: | Xtreak | Owned by: | nobody |
---|---|---|---|
Component: | Documentation | Version: | 2.0 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Accepted | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
When there is a custom field in the model and we do some transformation before storing it to the database. Hence while creating the object we give a value which is transformed and stored. But the returned object as a result of the create call has the given value instead of the transformed value.
In the below model I have a custom field FooField which always returns "foo" as the value to be stored but when I create an object with another value say 'a' it only has the value 'a' stored in the returned object. I need to do a refresh_from_db to clear the wrong value and to fetch the correct value. I couldn't find this documented at https://docs.djangoproject.com/en/2.0/howto/custom-model-fields/. If this an expected behavior adding a note in the documentation will be of help.
models.py
from __future__ import unicode_literals from django.db import models # Create your models here. class FooField(models.CharField): def to_python(self, value): value = super(self.__class__, self).to_python(value) return "foo" def get_prep_value(self, value): value = super(self.__class__, self).get_prep_value(value) return "foo" class FooBase(models.Model): base_field = models.CharField(max_length=120) class Foo(FooBase): custom_field = FooField(max_length=120)
Shell session
In [11]: bar = Foo.objects.create(base_field='1', custom_field='a') In [12]: bar.custom_field Out[12]: 'a' In [13]: bar.refresh_from_db() In [14]: bar.custom_field Out[14]: 'foo' In [15]: bar.id Out[15]: 10
sqlite> select * from TestApp_foo; 10|foo
Change History (3)
follow-up: 2 comment:1 by , 7 years ago
Component: | Database layer (models, ORM) → Documentation |
---|---|
Summary: | Models with custom fields returns the given value instead of stored value → Document that Model.save() doesn't refresh fields from the database |
Triage Stage: | Unreviewed → Accepted |
Type: | Bug → Cleanup/optimization |
comment:2 by , 7 years ago
Replying to Tim Graham:
I think that's expected behavior as it would add non-trivial overhead to refresh fields from the database after each
save()
. There might be a hook that would allow you to do that for your custom field.
I think we don't need to refresh the value from db. Since we know during get_prep_value
function call the value to be inserted in the database and we can update the object with the correct value which is inserted at https://github.com/django/django/blob/a2e97abd8149e78071806a52282a24c27fe8236b/django/db/models/sql/compiler.py#L1217. I might be wrong here due to my limited knowledge of ORM.
However I also think it's a breaking change and it will be good to make a note of this behavior in the documentation than to make this code change.
Thanks for all the work.
comment:3 by , 7 years ago
Resolution: | → invalid |
---|---|
Status: | new → closed |
The Custom Model Fields How-To discusses this usage explicitly in the Preprocessing values before saving section.
It suggests using pre_save(model_instance, add)
for this kind of behaviour.
It explicitly makes updating the model's attribute the user's responsibility:
You should also update the model’s attribute if you make any changes to the value so that code holding references to the model will always see the correct value.
Note pre_save
takes the model_instance
parameter precisely for this purpose.
The canonical example of this usage is from DateField
, for handling auto_now
and auto_now_add
:
def pre_save(self, model_instance, add): if self.auto_now or (self.auto_now_add and add): value = datetime.date.today() setattr(model_instance, self.attname, value) return value else: return super().pre_save(model_instance, add)
The quoted line from the docs was introduced in 2007 so this behaviour (and expected usage) is part of the original design of the Field
API. As such I'm going to close this ticket.
I think that's expected behavior as it would add non-trivial overhead to refresh fields from the database after each
save()
. There might be a hook that would allow you to do that for your custom field.