为什么在记录信息(django)时get\u FOO\u display()返回整数值?

为什么在记录信息(django)时get\u FOO\u display()返回整数值?,django,Django,为什么在记录信息(django)时get\u FOO\u display()返回整数值 我有一个模型字段,它使用一个选项来限制其值。这个很好用 我在应用程序中的任何地方都可以使用它,除了在记录信息时, 当get\u FOO\u display()方法返回基础整数值时 人类可读的版本 这是模型定义(节略): 如果我在(django)交互式shell中运行此命令,它的行为将完全符合您的预期: >>> from frankenstein.core.models import Thin

为什么在记录信息(django)时get\u FOO\u display()返回整数值

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

这是模型定义(节略):

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

>>> from frankenstein.core.models import Thing
>>> thing = Thing()
>>> thing.role = 0
>>> thing.get_role_display()
u'Monster'
但是,当我在字符串格式化/记录中使用完全相同的构造时 场景I遇到问题:

logger.info('New thing: <b>%s</b>', thing.get_role_display())
[更新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'
因此,修复没有改变任何事情。如果我尝试手动运行
\u get\u FIELD\u 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指出了一个直截了当的事实,即我在开始时传递了错误的值,这将是对他的赏金。我把这归因于我是一个来自“强类型世界”的流亡者,在那里这些事情不会发生

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

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

谢谢大家。

试试这个:

class Thing(models.Model):

    THING_ROLE_MONSTER = 0
    THING_ROLE_MUMMY = 1

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

    role = models.IntegerField('Role', default=0,choices=ROLE_CHOICES)

还没有尝试过你的代码,@like-it-response抱歉,但是来自models.Model在字段中设置函数,所以这可能就是你获得该输出的原因

尝试调用_get_FIELD_display:

logging.info('hello %s', b._get_FIELD_display(b._meta.get('role')))

如果这听起来有点屈尊,我真的很抱歉,但是您是否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'
检查视图代码或任何实际设置值的代码,并直接检查字段的输出

从内部模型代码粘贴时:

def _get_FIELD_display(self, field):
    value = getattr(self, field.attname)
    return force_unicode(dict(field.flatchoices).get(value, value), strings_only=True)
它只获取字段的当前值,并将其索引到字典中,如果找不到查找,则返回属性的值

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

编辑:

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


变量不是类型化的,但其中的值是类型化的。实际上,我很惊讶
IntegerField
也将字符串强制为int。在这里学习很好-首先检查基本知识

需要明确的是,这是记录到文件还是记录到服务(例如HipChat)?Irc,日志记录器做了懒惰的评估,可能不考虑一个服务调用“足够”来实际评估GETXFoWyDISPLAY,这是通过使用HTTP登录到一个服务,使用请求。代码在这里——我已经稍微改变了,使用了一个简单的。但是,仍然会得到相同的结果-基础整数,而不是显示名称。我也对你的答案投了赞成票,因为它确实很有帮助,但是我没有将它标记为“正确”答案,因为问题仍然存在。很抱歉,我找不到其他任何东西,我也没有阅读你的全部代码,但是如果它在repl上工作,它一定是你的文件代码,因为你调用函数并将其输出用于日志记录,所以行为一定是一样的,我猜这对我来说没什么不同。在深入研究了django的内部结构之后,我发布了一个关于这个问题的最新消息。我一点也不居高临下——我只是想转述一下这个确切的主题——因为通过这一点追踪到了事情的发展。我使用form.u数据来设置值,它是字符串,而不是整数。@HugoRodger Brown很高兴你能做到这一点,也许值得阅读我关于python类型系统的编辑。Josh,你今天救了我一命!我在查尔菲尔德省下了一个整数。该死谢谢你救了我一天。。
logging.info('hello %s', b._get_FIELD_display(b._meta.get('role')))
>>> 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'
def _get_FIELD_display(self, field):
    value = getattr(self, field.attname)
    return force_unicode(dict(field.flatchoices).get(value, value), strings_only=True)