在 Django >=1.10 中初始化(未从数据库加载)后立即访问字段时,如何进行自定义模型字段调用 to_python?

How do I make a custom model Field call to_python when the field is accessed immediately after initialization (not loaded from DB) in Django >=1.10?

从 Django 1.9 升级到 1.10 后,我发现 django-geolocation 包提供的字段的行为发生了变化。

这是为 1.10 兼容性所做的更改,破坏了行为:https://github.com/philippbosch/django-geoposition/commit/689ff1651a858d81b2d82ac02625aae8a125b9c9

以前,如果您使用 GeopositionField 初始化模型,然后立即访问该字段,您会得到一个 Geoposition 对象。现在您只需取回您在初始化时提供的字符串值。

如何使用 Django 1.10 实现相同的行为?是否需要重写 from_db_value 之类的其他方法才能调用 to_python

经过大量挖掘后发现,在 1.8 中,自定义字段的行为发生了变化,因此 to_python 不再在分配给字段时被调用。

https://docs.djangoproject.com/en/1.10/releases/1.8/#subfieldbase

The new approach doesn’t call the to_python() method on assignment as was the case with SubfieldBase. If you need that behavior, reimplement the Creator class from Django’s source code in your project.

这是一张 Django 票证,其中包含有关此更改的更多讨论:https://code.djangoproject.com/ticket/26807

所以为了保留旧的行为,你需要做这样的事情:

class CastOnAssignDescriptor(object):
    """
    A property descriptor which ensures that `field.to_python()` is called on _every_ assignment to the field.
    This used to be provided by the `django.db.models.subclassing.Creator` class, which in turn
    was used by the deprecated-in-Django-1.10 `SubfieldBase` class, hence the reimplementation here.
    """

    def __init__(self, field):
        self.field = field

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        return obj.__dict__[self.field.name]

    def __set__(self, obj, value):
        obj.__dict__[self.field.name] = self.field.to_python(value)

然后将此添加到自定义字段:

def contribute_to_class(self, cls, name):
    super(MyField, self).contribute_to_class(cls, name)
    setattr(cls, name, CastOnAssignDescriptor(self))

解决方案取自此拉取请求:https://github.com/hzdg/django-enumfields/pull/61