我正在将应用程序从python 2 / Django 1.4迁移到python 3 / Django 2.1.5。我有一个自定义JSON字段的奇怪行为:
class JSONField(models.TextField):
"""JSONField is a generic textfield that neatly serializes/unserializes
JSON objects seamlessly. Main thingy must be a dict object."""
def __init__(self, *args, **kwargs):
if 'default' not in kwargs:
kwargs['default'] = '{}'
super().__init__(*args, **kwargs)
def to_python(self, value):
"""Convert our string value to JSON after we load it from the DB"""
if not value:
return {}
elif isinstance(value, str):
res = loads(value)
assert isinstance(res, dict)
return res
else:
return value
def get_db_prep_save(self, value, connection):
"""Convert our JSON object to a string before we save"""
if not value:
return super(JSONField, self).get_db_prep_save("", connection=connection)
else:
return super(JSONField, self).get_db_prep_save(dumps(value), connection=connection)
在Django 1.4中,当我从数据库中读取对象时会调用JSONField.to_python(),而在Django 2.1.5中则不会:知道为什么吗?
答案 0 :(得分:1)
据我所知,每当将对象实例保存到DB时,就会调用to_python
。并且此函数用于将值从给定类型转换为所需类型。从方法的文档中:
将输入值转换为预期的Python数据类型,提高 django.core.exceptions.ValidationError如果无法转换数据。 返回转换后的值。子类应对此进行覆盖。
例如,在TextField
中,to_python
函数如下所示:
def to_python(self, value):
if isinstance(value, str) or value is None:
return value
return str(value)
此处,它将值转换为字符串,而不管其先前的类型如何。意思是,您可以通过TextField
传递一个整数值,但是当将其保存到数据库时,它将转换为String。
最后,如果要使用JSONField,则对于Postgresql,请考虑使用django提供的JSONField。如果您使用的是MySQL,则可以查看django-mysql's JSONField。
答案 1 :(得分:0)
在Django 1.4中,当查询数据库时,当将值设置为模型对象属性时,每次都会调用to_python():
class Creator(object):
"""
A placeholder class that provides a way to set the attribute on the model.
"""
def __init__(self, field):
self.field = field
def __get__(self, obj, type=None):
if obj is None:
raise AttributeError('Can only be accessed via an instance.')
return obj.__dict__[self.field.name]
def __set__(self, obj, value):
obj.__dict__[self.field.name] = self.field.to_python(value)
但是对于Django 2.1.5不再是这种情况:to_python()
主要是在表单验证(https://docs.djangoproject.com/en/2.1/howto/custom-model-fields/#converting-values-to-python-objects)中调用的。要在数据库查询中修改值,现在应该覆盖from_db_value()
将此方法添加到转换器列表中,该列表适用于每个查询:
在field.py中:
def get_db_converters(self, connection):
if hasattr(self, 'from_db_value'):
return [self.from_db_value]
return []
在models / query.py中:
if converters:
query = compiler.apply_converters(query, converters)
在sql / compiler.py中:
def apply_converters(self, rows, converters):
connection = self.connection
converters = list(converters.items())
for row in map(list, rows):
for pos, (convs, expression) in converters:
value = row[pos]
for converter in convs:
value = converter(value, expression, connection)
row[pos] = value
yield row