2012-11-12 14 views
6

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.

+0

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 –

+0

Questa è la registrazione a un servizio, su HTTP, utilizzando le richieste. –

+0

Il codice è qui - https://gist.github.com/3176710 –

risposta

11

mi dispiace davvero se questo suona c Ovviamente, sei sicuro al 100% che stai impostando il valore sul numero intero 1 e non sulla stringa '1'?

Ho passato le immersioni attraverso i meccanismi interni e l'esecuzione di alcuni test e l'unico modo che il problema riscontrato ha senso è che se si sta impostando il valore in una stringa. Vedi la mia semplice test qui:

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

esaminare il vostro codice di vista, o qualunque sia il codice è in realtà l'impostazione del valore, ed esaminare l'output del campo direttamente.

Come si è incollato dal codice modello interno:

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

Si ottiene semplicemente il valore corrente del campo, e gli indici nel dizionario, e restituisce il valore dell'attributo, se una ricerca non viene trovato .

Sto indovinando non ci sono errori in precedenza, in quanto il valore è costretto in un intero prima di essere inserito nel database.

Edit:

Per quanto riguarda l'aggiornamento menzionare il sistema di tipi di pitone. In primo luogo, dovresti usare TypedChoiceField per assicurarti che il modulo verifichi il tipo che ti aspetti. In secondo luogo, python è un linguaggio fortemente tipizzato, ma il IntegerField esegue il proprio coercing con int() durante la preparazione per il database.

Le variabili non sono state digitate, ma i valori al loro interno sono. Sono stato davvero sorpreso dal fatto che il IntegerField stava forzando la stringa su un int anche. Buono da imparare qui - controlla prima le basi!

+0

Non ero affatto d'accordo - stavo proprio per ripubblicare su questo argomento esatto - come aver tracciato le cose attraverso questo è esattamente ciò che sta accadendo. Sto usando form.cleaned_data per impostare il valore, e viene usata la stringa, non il numero intero. –

+0

@ HugoRodger-Brown felice che tu sia arrivato lì, potrebbe valere la pena di leggere la mia modifica riguardante il sistema di tipi di Python. –

+0

Josh, mi hai salvato il culo oggi! molte grazie da parte mia – doniyor

0

provare questo:

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) 
+0

Questo non ha fatto nulla di diverso per me. Ho postato un aggiornamento alla domanda dopo un'immersione più profonda negli interni di django. –

2

non hanno provato il codice, né il @ like-it rispondere dispiace, ma _get_FIELD_display da models.Model è curried nei campi per impostare la funzione get_Field_display, di modo che è probabilmente perché you'r ottenere che la produzione

provare a chiamare il _get_FIELD_display:

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

Ho cambiato leggermente per usare _meta.get_field ('role') come un semplice .get() non funziona. Tuttavia, ottiene sempre lo stesso risultato: il numero intero sottostante, anziché il nome visualizzato. –

+0

Ho pubblicato un aggiornamento a questo seguito dalla tua risposta. Ho anche votato la tua risposta, poiché è stata davvero utile, tuttavia non l'ho contrassegnata come 'la' risposta in quanto il problema esiste ancora. –

+0

mi dispiace, non riesco a trovare nient'altro, e non avevo letto l'intero codice, ma se funziona sul repl deve essere il codice del file, perché evochi la funzione e dai il suo output alla registrazione, quindi il comportamento deve essere lo stesso credo indeciso – jxs

Problemi correlati