gpt4 book ai didi

django - 为什么 get_FOO_display() 在记录信息(django)时返回整数值?

转载 作者:行者123 更新时间:2023-12-02 09:24:12 25 4
gpt4 key购买 nike

为什么 get_FOO_display() 在记录信息 (django) 时返回整数值?

我有一个模型字段,它使用一个选项来限制其值。这很好用我让它在应用程序中的任何地方都可以工作,除了记录信息时,当 get_FOO_display() 方法返回底层整数值时人类可读版本。

这是模型定义(删节):

THING_ROLE_MONSTER = 0
THING_ROLE_MUMMY = 1

ROLE_CHOICES = (
(THING_ROLE_MONSTER, u'Monster'),
(THING_ROLE_MUMMY, u'Mummy'),
)

# definition of property within model
class Thing(models.Model):
...
role = models.IntegerField(
'Role',
default=0,
choices=ROLE_CHOICES
)

如果我在(django)交互式 shell 中运行它,它的行为完全符合您的预期:

>>> from frankenstein.core.models import Thing
>>> thing = Thing()
>>> thing.role = 0
>>> thing.get_role_display()
u'Monster'

但是,当我在字符串格式化/日志记录中使用完全相同的构造时我遇到问题的场景:

logger.info('New thing: <b>%s</b>', thing.get_role_display())

返回:

New thing: <b>0</b> 

救命啊!

[更新1]

当我在交互式 shell 中运行日志记录时,我得到了正确的输出:

>>> from frankenstein.core.models import Thing
>>> import logging
>>> thing = Thing()
>>> thing.role = 0
>>> logging.info('hello %s', b.get_role_display())
INFO hello Monster

[更新2] Django 内部结构

根据下面 @joao-oliveira 的回答,我深入研究了内部结构并发现了以下内容。

django.db.models 中的底层 _get_FIELD_display 方法如下所示:

def _get_FIELD_display(self, field):
value = getattr(self, field.attname)
return force_unicode(dict(field.flatchoices).get(value, value), strings_only=True)

如果我在代码中放置一个断点,然后运行 ​​ipdb,我可以看到存在问题:

ipdb> thing.get_role_display()
u'1'
ipdb> thing._get_FIELD_display(thing._meta.get_field('role'))
u'1'

所以,修复并没有改变任何东西。如果我随后尝试手动运行 _get_FIELD_display 方法代码,我会得到以下结果:

ipdb> fld = thing._meta.get_field('role')
ipdb> fld.flatchoices
[(0, 'Monster'), (1, 'Mummy')]
ipdb> getattr(thing, fld.attname)
u'1'
ipdb> value = getattr(thing, fld.attname)
ipdb> dict(fld.flatchoices).get(value, value)
u'1'

这相当于说:

ipdb> {0: 'Monster', 1: 'Mummy'}.get(u'1', u'1')
u'1'

所以。我们遇到的问题是,该方法使用字符串值 u'1' 在选择字典中查找相应的描述,但字典键是整数,而不是字符串。因此,我们永远不会得到匹配项,而是得到默认值,该值设置为现有值(字符串)。

如果我手动强制转换为 int,代码将按预期工作:

ipdb> dict(fld.flatchoices).get(int(value), value)
'Mummy'
ipdb> print 'w00t'

这一切都很棒,但并没有回答我最初的问题,即为什么 get_foo_display 方法在大多数情况下确实返回正确的值。在某些时候,字符串 (u'1') 必须转换为正确的数据类型 (1)。

[更新3]答案

虽然必须对 Joao 的洞察力给予荣誉奖,但 Josh 指出了我一开始就传递了错误值这一直言不讳的事实。我把这归结为来自“强类型世界”的移民,这些事情不可能发生!

我在这里没有包含的代码是,该对象是使用 ChoiceField 中的 cleaned_data 从 django 表单初始化的。 。问题在于 ChoiceField 的输出是字符串,而不是整数。我错过的一点是,在松散类型语言中,可以使用字符串设置整数属性,并且不会发生任何不好的事情。

现在研究了这一点,我发现我应该使用 TypedChoiceField ,以确保 cleaned_data 的输出始终是整数。

谢谢大家。

最佳答案

如果这听起来有点居高临下,我真的很抱歉,但是您是否 100% 确定将值设置为整数 1 而不是字符串“1”?

我已经深入研究了内部结构并运行了一些测试,您遇到的问题唯一有意义的方法是您是否将值设置为字符串。在这里查看我的简单测试:

>>> from flogger.models import TestUser
>>> t = TestUser()
>>> t.status = 1
>>> t.get_status_display()
u'Admin'
>>> t.status = '1'
>>> t.get_status_display()
u'1'

检查您的 View 代码或实际设置值的任何代码,并直接检查字段的输出。

当您从内部模型代码粘贴时:

def _get_FIELD_display(self, field):
value = getattr(self, field.attname)
return force_unicode(dict(field.flatchoices).get(value, value), strings_only=True)

它只是获取字段的当前值,并索引到字典中,如果未找到查找,则返回属性的值。

我猜之前没有错误,因为该值在插入数据库之前被强制转换为整数。

编辑:

关于您提到 python 类型系统的更新。首先,您应该使用 TypedChoiceField以确保表单验证您期望的类型。其次,Python一种强类型语言,但是IntegerField在准备数据库时会使用int()进行自己的强制转换。

变量不是类型化的,而是变量中的值。事实上,我很惊讶 IntegerField 也将字符串强制转换为 int。在这里学习很好的类(class) - 首先检查基础知识!

关于django - 为什么 get_FOO_display() 在记录信息(django)时返回整数值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13347128/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com