10

情報(django)をログに記録するときにget_FOO_display()が整数値を返すのはなぜですか?

値を制限するための選択肢を使用しているモデルフィールドがあります。これは正常に機能し、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)インタラクティブシェル内で実行すると、期待どおりに動作します。

>>> 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]

インタラクティブシェル内でロギングを実行すると、正しい出力が得られます。

>>> 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からの回答に続いて、私は内部を掘り下げ、次のことを明らかにしました。

の基になる_get_FIELD_displayメソッドはdjango.db.models次のようになります。

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]答え

名誉ある言及は彼の洞察のためにジョアオに行かなければなりませんが、賞金は私が最初に間違った価値を渡しているという率直な事実を指摘するためにジョシュに行きます。私はこれを「強い型の世界」からの移民であると言いました。そこでは、これらのことは起こり得ません!

ここに含めなかったコードは、 ChoiceFieldcleaned_dataからを使用して、オブジェクトがdjangoフォームから初期化されることです。これに伴う問題は、ChoiceFieldからの出力が整数ではなく文字列であるということです。私が見逃したのは、緩く型付けされた言語では、文字列を使用して整数プロパティを設定することが可能であり、悪いことは何も起こらないということです。

これを調べたところ、からの出力が常に整数であることを確認するために、 TypedChoiceFieldを使用する必要があることがわかりました。cleaned_data

皆さん、ありがとうございました。

4

3 に答える 3

20

これが見下すように聞こえる場合は本当に申し訳ありませんが、値を文字列「1」ではなく整数1に設定していることを100%確信していますか?

内部を調べていくつかのテストを実行しましたが、発生している問題が理にかなっている唯一の方法は、値を文字列に設定している場合です。ここで私の簡単なテストを参照してください:

>>> 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の型システムに言及しているアップデートについて。まず、TypedChoiceFieldを使用して、フォームが期待するタイプを確認するようにする必要があります。第二に、Python強く型付けされた言語ですが、データベースの準備時にIntegerField独自の強制を行います。int()

変数は入力されませんが、変数内の値は入力されます。IntegerField文字列をintに強制変換していることに実際に驚いた。ここで学ぶのは良いことです-最初に基本を確認してください!

于 2012-12-13T12:00:36.023 に答える
2

コードを試していません。@like-申し訳ありませんが、models.Modelからの_get_FIELD_displayは、 get_Field_display関数を設定するためにフィールドでカレーされているため、おそらくその出力が得られます。

_get_FIELD_displayを呼び出してみてください。

logging.info('hello %s', b._get_FIELD_display(b._meta.get('role')))
于 2012-12-12T15:49:18.203 に答える
0

これを試して:

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)
于 2012-12-12T12:42:47.097 に答える