从Django 1.4到Django 2.1.5:不再调用自定义字段to_python()

时间:2019-02-08 16:07:46

标签: python django

我正在将应用程序从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中则不会:知道为什么吗?

2 个答案:

答案 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