python - Django how to call a method from a custom field given a model instance?
问题描述
I have the following model:
class CustomField(models.CharField):
def foo(self):
return 'foo'
class Test(models.Model):
col1 = models.CharField(max_length=45)
col2 = CustomField(max_length=45)
How can I call the foo
method from CustomField
, if I'm given an instance of Test
?
For example:
>>> t = Test.objects.create(col1='bar', col2='blah')
>>> t.col2
'blah'
>>> t.col2.foo() # 'str' object has not attribute 'foo'
'foo'
This, of course, throws:
'str' object has not attribute 'foo'
because calling model_instance.column
returns the value of that column, not an instance of column
.
But why exactly? It seems Django's ORM magically transforms an instance of a field class into a value. I've spent hours digging through source code and can't seem to find where the transformation takes place.
TLDR;
Is it possible to return an instance of a field class given a model instance?
Any idea where this happens in Django's source code? I assume this takes place in django/db/models/base.py
, but that file is over 1800 lines of code, so it's really hard to tell.
Update
Here is a practical example of why this would be useful:
class TempField(models.DecimalField):
def __init__(self, initial_unit='C', **kwargs):
self.initial_unit = initial_unit
self.units = ['F', 'C', 'K']
def convert(self, unit):
if self.initial_unit == unit:
return self.value
if unit not in self.units:
raise
attr = getattr(self, f'_{initial_unit}_to_{unit}', None)
if attr is None:
raise
return attr(unit)
def _C_to_F(self, unit):
...
Now you can conveniently convert this field to the desired unit:
class Test(models.Model):
temperature = TempField(...)
>>>t = Test.objects.create(temperature=100)
>>>t.temperature
100
>>>t.temperature.convert('F')
212
This is all just untested pseudo code. Also, I can think of several ways of having this functionality without the headache of using custom fields in this manner; so this question is really about understanding how Django's ORM works, and not necessarily how to solve any real world problems.
解决方案
David Wheeler在计算机科学中有一句名言,“计算机科学中的所有问题都可以通过另一个层次的间接来解决(除了太多的间接层) ”。
因此,我们可以定义一个类Temperature
来存储温度:
from enum import Enum
from decimal import Decimal
NINE_FIFTHS = Decimal(9)/Decimal(5)
class TemperatureUnit(Enum):
KELVIN = (1,0, 'K')
FAHRENHEIT = (NINE_FIFTHS, Decimal('-459.67'), '°F')
CELSIUS = (1, Decimal('-273.15'), '°C')
RANKINE = (NINE_FIFTHS, 0, '°R')
class Temperature:
def __init__(self, kelvin, unit=TemperatureUnit.CELSIUS):
self.kelvin = Decimal(kelvin)
self.unit = unit
@staticmethod
def from_unit(value, unit=TemperatureUnit.CELSIUS):
a, b, *__ = unit.value
return Temperature((value-b)/a, unit)
@property
def value(self):
a, b, *__ = self.unit.value
return a * self.kelvin + b
def convert(self, unit):
return Temperature(self.kelvin, unit)
def __str__(self):
return '{} {}'.format(self.value, self.unit.value[2])
例如,我们可以在这里创建温度:
>>> str(Temperature(15, unit=TemperatureUnit.FAHRENHEIT))
'-432.67 °F'
>>> str(Temperature(0, unit=TemperatureUnit.FAHRENHEIT))
'-459.67 °F'
>>> str(Temperature(1, unit=TemperatureUnit.FAHRENHEIT))
'-457.87 °F'
>>> str(Temperature(0, unit=TemperatureUnit.FAHRENHEIT))
'-459.67 °F'
>>> str(Temperature(0, unit=TemperatureUnit.CELSIUS))
'-273.15 °C'
现在我们可以创建一个存储和检索Temperature
s 的 Django 模型字段,例如通过将这些保存在数据库端的小数中,在开尔文中:
class TemperatureField(models.DecimalField):
def from_db_value(self, value):
kelvin = super().from_db_value(value)
if kelvin is not None:
return Temperature(kelvin)
return None
def to_python(self, value):
if isinstance(value, Temperature):
return value
if value is None:
return value
kelvin = super().to_python(value)
return Temperature(kelvin)
def get_prep_value(self, value):
if isinstance(value, Temperature):
value = value.kelvin
return super().get_prep_value(value)
以上当然是原始草图。有关详细信息,请参阅有关编写自定义模型字段的文档。您可以添加表单字段、小部件、查询数据库等。因此您可以为您的TemperatureField
.
推荐阅读
- javascript - 我想要一个粘性标题中的按钮在向下滚动时变为红色
- ionic-framework - Ionic 4:离子标签栏未显示在 Chrome 中
- java - 工具栏上的叠加元素
- html - 问题:用 HTML/CSS 在计算器上创建一个按钮(仅限设计)
- python - 尝试使用 Python 正确缩进和缩进 asp 代码
- android - 启用/禁用全屏模式
- python - 如何获得 2 个字符串列表之间的差异(忽略部分匹配)
- c++ - 从 C++ 更新 QML 文本
- c - 数组的地址及其第一个元素的地址是一个回填问题
- docker - 从 docker ce 迁移到 docker-EE