2015-01-26 16 views
7

nel mio database, ho alcune colonne in cui dati sono memorizzati in un formato strano. Poiché il database è utilizzato anche da un altro codice, non posso modificare il formato dei dati.SQLAlchemy: Convertire valore colonna avanti e indietro tra formato interno e il database

Per esempio, uno dei formati strana è che un valore di tempo è rappresentato come una stringa come 23:42:30. Mi piacerebbe avere qualche magia che mi permetta di usare sempre gli oggetti datetime.time sul lato python.

Una soluzione molto semplice sarebbe qualcosa di simile:

col_raw = Column('col', String(7)) 

@property 
def col(self): 
    return datetime.strptime(self.col_raw, '%H:%M:%S').time() 
@col.setter 
def colself, t): 
    self.col_raw = t.strftime('%H:%M:%S') 

Tuttavia, questo risolve solo il problema per i dati di lettura e scrittura. Cose come questa non sarebbe possibile:

Table.query.filter(Table.col == time(23,42,30)) 

Un altro modo sarebbe quello di utilizzare il hybrid extension. In questo modo, l'interrogazione sarebbe possibile, ma se la vedo correttamente, la scrittura non lo farebbe. Inoltre, richiede la scrittura di codice tutto doppio, una volta nel codice Python e una volta nel codice SQL. (Ulteriori problema. Non sono sicuro se la conversione può essere scritta utilizzando solo il codice SQL)

C'è davvero alcun modo da garantire sia? Come definisco due funzioni, diciamo python2sql e sql2python, che SQLAlchemy utilizza per convertire in modo trasparente i valori dall'oggetto python alla stringa e al retro? So che questo renderebbe alcune query impossibili, come ad esempio between o like o somme e simili, ma va bene.

prega di tenere presente che la cosa il tempo è unico esempio uno dei casi in cui ho bisogno per convertire i dati; quindi per favore prova a mantenere le risposte generiche.

risposta

12

Utilizzare un type decorator che gestisce la conversione da e verso il formato personalizzato. Utilizzare questo tipo anziché String quando si definisce la colonna.

class MyTime(TypeDecorator): 
    impl = String 

    def __init__(self, length=None, format='%H:%M:%S', **kwargs) 
     super().__init__(length, **kwargs) 
     self.format = format 

    def process_literal_param(self, value, dialect): 
     # allow passing string or time to column 
     if isinstance(value, basestring): # use str instead on py3 
      value = datetime.strptime(value, self.format).time() 

     # convert python time to sql string 
     return value.strftime(self.format) if value is not None else None 

    process_bind_param = process_literal_param 

    def process_result_value(self, value, dialect): 
     # convert sql string to python time 
     return datetime.strptime(value, self.format).time() if value is not None else None 

# in your model 
class MyModel(Base): 
    time = Column(MyTime(length=7))