Perché get_FOO_display() restituisce il valore intero quando si registrano le informazioni (django)?Perché get_FOO_display() restituisce il valore intero quando si registrano le informazioni (django)?
Ho un campo modello che sta usando una scelta per limitare il suo valore. Funziona bene e ho funzionato ovunque all'interno della app, tranne quando si registrano le informazioni, quando il metodo get_FOO_display() restituisce il valore intero sottostante invece della versione leggibile.
Questa è la definizione del modello (abbreviata):
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
)
Se corro questo all'interno della (Django) shell interattiva si comporta esattamente come ci si aspetterebbe:
>>> from frankenstein.core.models import Thing
>>> thing = Thing()
>>> thing.role = 0
>>> thing.get_role_display()
u'Monster'
Tuttavia, quando uso esattamente lo stesso costrutto all'interno di uno scenario di formattazione/registrazione di stringhe , ho il problema:
logger.info('New thing: <b>%s</b>', thing.get_role_display())
rendimenti:
New thing: <b>0</b>
Aiuto!
[UPDATE 1]
Quando si esegue la registrazione all'interno della shell interattiva ho l'uscita corretta:
>>> from frankenstein.core.models import Thing
>>> import logging
>>> thing = Thing()
>>> thing.role = 0
>>> logging.info('hello %s', b.get_role_display())
INFO hello Monster
[UPDATE 2] Django interni
seguito sulla risposta da @ joao-oliveira di seguito, ho scavato nella parte interna e ho scoperto quanto segue.
Il metodo _get_FIELD_display sottostante django.db.models
assomiglia a questo:
def _get_FIELD_display(self, field):
value = getattr(self, field.attname)
return force_unicode(dict(field.flatchoices).get(value, value), strings_only=True)
Se metto un punto di interruzione nel codice, e quindi eseguire IPDB posso vedere che ho il problema:
ipdb> thing.get_role_display()
u'1'
ipdb> thing._get_FIELD_display(thing._meta.get_field('role'))
u'1'
Quindi, la correzione non ha cambiato nulla. Se dunque io provo che attraversa il codice _get_FIELD_display
metodo di mano, ottengo questo:
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'
che equivale a dire:
ipdb> {0: 'Monster', 1: 'Mummy'}.get(u'1', u'1')
u'1'
So. Il problema è che il metodo sta utilizzando il valore stringa u'1'
per cercare la descrizione corrispondente nel dizionario delle scelte, ma le chiavi del dizionario sono numeri interi e non stringhe. Quindi non otteniamo mai una corrispondenza, ma il valore predefinito, che è impostato sul valore esistente (la stringa).
Se forzo manualmente il cast a int, il codice funziona come previsto:
ipdb> dict(fld.flatchoices).get(int(value), value)
'Mummy'
ipdb> print 'w00t'
Questo è tutto grande, ma non risponde alla mia domanda iniziale sul perché il metodo get_foo_display fa restituire il giusto valore la maggior parte del tempo. Ad un certo punto la stringa (u'1 ') deve essere convertita nel tipo di dati corretto (1).
[UPDATE 3] La risposta
Mentre una menzione d'onore deve andare a Joao per la sua intuizione, la grazia sta per Josh per sottolineare il fatto smussato che sto passando nel valore sbagliato per iniziare con. Lo considero un emigrato di "mondo fortemente tipizzato", in cui queste cose non possono accadere!
Il codice che non ho incluso qui è che l'oggetto è inizializzato da un modulo django, utilizzando il cleaned_data
da un ChoiceField. Il problema con questo è che l'output di un ChoiceField è una stringa, non un intero. Il bit che mi è mancato è che in un linguaggio con caratteri generici è possibile impostare una proprietà intera con una stringa, e che non accade nulla di male.
Avendo ora esaminato ciò, vedo che avrei dovuto usare lo TypedChoiceField, per garantire che l'output da cleaned_data
fosse sempre un numero intero.
Grazie a tutti.
Per essere chiari, è questa registrazione su un file, o la registrazione a un servizio (ad esempio, HipChat)? IIRC, il logger esegue una valutazione lenta e potrebbe non considerare una chiamata di servizio "sufficiente" per valutare effettivamente get_FOO_display –
Questa è la registrazione a un servizio, su HTTP, utilizzando le richieste. –
Il codice è qui - https://gist.github.com/3176710 –