2013-01-03 13 views
6

Quindi, per un progetto Django, mi piacerebbe molto essere in grado di generare e visualizzare tabelle (non basate sui querysets) in modo dinamico senza bisogno per conoscere in anticipo il contenuto o lo schema.Problemi nel tentativo di aggiungere dinamicamente metodi alla classe Python (es. Django-tables2 'Table')

Sembra che l'app django-tables2 fornisca una buona funzionalità per il rendering delle tabelle, ma richiede che si dichiarino esplicitamente i nomi delle colonne dichiarando gli attributi su una sottoclasse Tabella definita dall'utente oppure fornire un modello per inferirne le colonne.

Vale a dire, per usare una colonna denominata "nome", faresti:

class NameTable(tables.Table): 
    name = tables.Column() 

La classe tabelle non fornisce un metodo per aggiungere le colonne post-facto perché, dalla lettura della fonte, a quanto pare per utilizzare una metaclasse che spazza gli attributi di classe su __new__ e li blocchi.

Sembrava molto semplice metaprogramming sarebbe una soluzione elegante. Ho definito una fabbrica di base di classe che accetta i nomi di colonna sono argomenti:

def define_table(columns): 
    class klass(tables.Table): pass    
    for col in columns: 
     setattr(klass, col, tables.Column()) 
    return klass 

Purtroppo questo non funziona. Se corro `

x = define_table(["foo", "bar"])(data) 
x.foo 
x.bar 

torno:

<django_tables2.columns.base.Column object at 0x7f34755af5d0> 
<django_tables2.columns.base.Column object at 0x7f347577f750> 

Ma se vi elenco le colonne:

print x.base_columns 

mi rimetto niente cioè {}

mi rendo conto che ci sono probabilmente soluzioni più semplici (ad esempio, basta mordere il proiettile e definire ogni possibile configurazione di dati nel codice, o non usare django-tables2 e arrotolo il mio), ma ora sto trattando questo come un'opportunità per saperne di più sulla meta-programmazione, quindi mi piacerebbe davvero farlo funzionare in questo modo.

Qualche idea su cosa ho sbagliato a fare qualcosa di sbagliato? La mia teoria è che il metodo __new__ (che viene ridefinito nella tabella metaclasse utilizza) viene richiamato quando klass è definito piuttosto che quando viene istanziato, quindi nel momento in cui scrivo sugli attributi è troppo tardi. Ma questo viola la mia comprensione di quando __new__ dovrebbe accadere. Altrimenti, sto faticando a capire come il metaclass __new__ possa dire la differenza tra gli attributi definiti nel codice e quelli definiti dinamicamente.

Grazie!

risposta

4

Sei sulla strada giusta qui, ma invece di creare una classe barebone e aggiungere attributi ad essa, dovresti usare la funzione integrata type(). Il motivo per cui non sta funzionando nel modo in cui stai provando, è perché il metaclass ha già fatto il suo lavoro.

L'utilizzo di type() consente di creare una nuova classe con i propri attributi, mentre si imposta la classe base. Significato: puoi descrivere i campi che desideri come modello per la tua classe, consentendo alla metrica di Table di assumere il controllo dopo la tua definizione.

Here's an example di utilizzo di type() con django. L'ho usato io stesso per il mio progetto (con qualche leggera variazione) ma dovrebbe darti un bel posto da cui partire, considerando che sei già quasi lì.

def define_table(columns): 
    attrs = dict((c, tables.Column()) for c in columns) 
    klass = type('DynamicTable', (tables.Table,), attrs) 
    return klass 
+0

Impressionante. Funziona! In alternativa, sembra che il solo fatto di eseguire 'table.base_columns [col] = col' funzioni, ma potrebbe essere una violazione di qualcosa che non vedo poiché aggira il codice di creazione della colonna. Quindi la tua soluzione è molto meglio. I futuri lettori dovrebbero anche verificare la risposta di @ BrenBarn poiché spiega perché il mio metodo stava fallendo. – rogueleaderr

+2

@rogueleaderr contento che ha funzionato per voi. Mentre è nella tua mente, dovresti leggere i metaclassi e le varie funzioni che forniscono. Sono particolarmente utili per capire quando si gioca con Django, dato che i modelli e le forme li usano entrambi, ea volte dovrete entrare nel nocciolo della questione. –

1

Stai confondendo la __new__ di una classe "normale" con il __new__ di una metaclasse. Come si nota, lo Table si basa sul metodo __new__ sulla sua metrera . Il metaclasse viene effettivamente invocato quando la classe è definita. La classe è di per sé un'istanza del metaclasse, quindi definire la classe è creare un'istanza del metaclasse. (In questo caso, Table è un'istanza di DeclarativeColumnMetaClass.) Quindi, quando viene definita la classe, è troppo tardi.

Una possibile soluzione è scrivere una sottoclasse Table con metodo refreshColumns o simile. È possibile adattare il codice da DeclarativeColumnMetaclass.__new__ essenzialmente per fare di nuovo la stessa magia con lo refreshColumns. Quindi puoi chiamare refreshColumns() sulla tua nuova classe.

+0

Grazie per aver risposto! Penso che la risposta di Josh sia un po 'più elegante, ma la tua risposta aiuta a spiegare perché il mio metodo ha fallito. – rogueleaderr

Problemi correlati