[Django] #29173: Models with custom fields returns the given value instead of stored value

10 views
Skip to first unread message

Django

unread,
Mar 1, 2018, 5:01:04 AM3/1/18
to django-...@googlegroups.com
#29173: Models with custom fields returns the given value instead of stored value
-------------------------------------+-------------------------------------
Reporter: Xtreak | Owner: nobody
Type: Bug | Status: new
Component: Database | Version: 2.0
layer (models, ORM) |
Severity: Normal | Keywords:
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
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
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/29173>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Mar 1, 2018, 1:38:21 PM3/1/18
to django-...@googlegroups.com
#29173: Document that Model.save() doesn't refresh fields from the database
--------------------------------------+------------------------------------
Reporter: Xtreak | Owner: nobody
Type: Cleanup/optimization | Status: new
Component: Documentation | Version: 2.0
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------------+------------------------------------
Changes (by Tim Graham):

* type: Bug => Cleanup/optimization
* stage: Unreviewed => Accepted
* component: Database layer (models, ORM) => Documentation


Comment:

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.

--
Ticket URL: <https://code.djangoproject.com/ticket/29173#comment:1>

Django

unread,
Mar 2, 2018, 3:53:45 AM3/2/18
to django-...@googlegroups.com
#29173: Document that Model.save() doesn't refresh fields from the database
--------------------------------------+------------------------------------
Reporter: Xtreak | Owner: nobody

Type: Cleanup/optimization | Status: new
Component: Documentation | Version: 2.0
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------------+------------------------------------

Comment (by Xtreak):

Replying to [comment:1 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.

--
Ticket URL: <https://code.djangoproject.com/ticket/29173#comment:2>

Django

unread,
Mar 19, 2018, 5:29:28 AM3/19/18
to django-...@googlegroups.com
#29173: Document that Model.save() doesn't refresh fields from the database
--------------------------------------+------------------------------------
Reporter: Xtreak | Owner: nobody
Type: Cleanup/optimization | Status: closed
Component: Documentation | Version: 2.0
Severity: Normal | Resolution: invalid
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------------+------------------------------------
Changes (by Carlton Gibson):

* status: new => closed
* resolution: => invalid


Comment:

The ''Custom Model Fields'' How-To discusses this usage explicitly in the
[https://docs.djangoproject.com/en/2.0/howto/custom-model-fields
/#preprocessing-values-before-saving 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 `DateTimeField`, 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
[https://github.com/django/django/commit/8216abe74889cc867a4cf89e6708f37cec6b2e72
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.

--
Ticket URL: <https://code.djangoproject.com/ticket/29173#comment:3>

Reply all
Reply to author
Forward
0 new messages