我有一个模型字段,使用一个选项来限制其值.这很好用
我在应用程序的任何地方都可以使用它,除非记录信息,
当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中的cleaning_data从django表单初始化对象.这个问题是ChoiceField的输出是字符串,而不是整数.我错过的一点是,在松散类型的语言中,可以使用字符串设置整数属性,并且没有什么不好的事情发生.
现在看看这个,我看到我应该使用TypedChoiceField,以确保来自cleaning_data的输出始终是一个整数.
谢谢你们.
解决方法
我已经潜入内部并运行一些测试,而你遇到的问题唯一的方法就是将值设置为字符串.在这里看我的简单测试:
>>> 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,strings_only=True)
它只是获取字段的当前值,并索引到字典中,并在未找到查找时返回属性的值.
我猜测之前没有错误,因为在插入数据库之前,该值被强制转换为整数.
编辑:
关于你的更新提到python的类型系统.首先,您应该使用TypedChoiceField来确保表单验证您期望的类型.其次,python是一种强类型语言,但在准备数据库时,IntegerField会自行强制使用int().
变量不是键入的,但是它们中的值是.我真的很惊讶IntegerField也将字符串强制转换为int.在这里学习很好 – 首先检查基础知识!