2012-12-22 11 views
23

Recentemente ho capito che di default Colonna di SQLAlchemy non funziona come mi aspetto che:Perché il valore della colonna predefinito di SQLAlchemy non è disponibile prima che venga eseguito il commit dell'oggetto?

>>> Base = declarative_base() 
>>> class TestModel(Base): 
    ...     __tablename__ = 'tmodel' 
...     id = sa.Column(sa.Integer, primary_key=True) 
...     foo = sa.Column(sa.Integer, default=0) 
...     
>>> tmodel_instance = TestModel() 
>>> print tmodel_instance.foo 
None 
>>> session.add(tmodel_instance) 
>>> print tmodel_instance.foo 
None 
>>> session.commit() 
>>> print tmodel_instance.foo 
0 

voglio tmodel_instance.foo di eguagliare 0 subito dopo di istanze di oggetti, ma sembra che il valore di default viene utilizzato solo quando si esegue INSERT comando, e questo mi confonde davvero. Perché uno preferirebbe lo default su server_default? E come ottengo ciò che voglio? Devo specificare tutti gli argomenti predefiniti in __init__? Questa sembra essere la duplicazione del codice: per cambiare il valore predefinito, devo cambiarlo due volte e mantenere l'uguaglianza dei valori - c'è un modo per evitarlo?

+2

+1 Accetto. Non ha senso che l'atto di persistenza di un oggetto cambierà i suoi attributi. –

risposta

14

si preferirebbe di default nel corso di default del server per uno dei quattro motivi:

  1. si desidera eseguire una funzione Python, non una funzione SQL, per il valore di default (o un'espressione SQL che ha bisogno di un po ' stato Python per-INSERT anche).

  2. il valore predefinito fa parte di una colonna chiave primaria. l'ORM non può caricare una riga indietro senza la chiave primaria, quindi in genere server_default non è utile per una colonna PK quando si utilizza l'ORM.

  3. l'espressione SQL che si desidera eseguire non è supportata dal database come "predefinito del server".

  4. Hai a che fare con uno schema che non puoi/non vuoi cambiare.

In questo caso, quando vuoi "foo" essere "0" nella vostra applicazione indipendente delle operazioni di database, le scelte sono:

  1. uso __init__(). È un pitone!

  2. utilizzare eventi.

Ecco __init__():

class TestModel(Base): 
    # ... 

    def __init__(self): 
     self.foo = 0 

Ecco gli eventi (in particolare il init event):

from sqlalchemy import event 

@event.listens_for(Foo, "init") 
def init(target, args, kwargs): 
    target.foo = 0 
+3

Ma perché i modelli sqlAlchemy sono stati progettati in questo modo: Nessuno invece del valore predefinito subito dopo la creazione? Questo confonde sicuramente le persone che sono abituate ad altre librerie ORM – d9k

+3

il "default" è più spesso che non un'espressione SQL o un valore lato server completo.Non può essere conosciuto fino a quando l'INSERT non ha luogo, o molto meno idealmente se fosse eseguito in modo impacciato quando l'oggetto è creato (il che a sua volta degrada il valore come un vero "INSERT defaut"). pertanto, fare in modo che un solo sottoinsieme di azioni predefinite di inserimento a livello di core agisca in modo completamente diverso sarebbe incoerente. – zzzeek

4

È possibile utilizzare l'evento init per riempire le impostazioni predefinite. Questo listener di eventi lo farà:

from sqlalchemy import event 
from sqlalchemy.orm import mapper 
from sqlalchemy.inspection import inspect 


def instant_defaults_listener(target, args, kwargs): 
    for key, column in inspect(target.__class__).columns.items(): 
     if column.default is not None: 
      if callable(column.default.arg): 
       setattr(target, key, column.default.arg(target)) 
      else: 
       setattr(target, key, column.default.arg) 


event.listen(mapper, 'init', instant_defaults_listener) 
2

È possibile utilizzare force_instant_defaults ascoltatore dalla sqlalchemy_utils a modificare questo comportamento:

from sqlalchemy_utils import force_instant_defaults 

force_instant_defaults() 

class TestModel(Base): 
    __tablename__ = 'tmodel' 
    id = sa.Column(sa.Integer, primary_key=True) 
    foo = sa.Column(sa.Integer, default=0) 

model = TestModel() 
assert model.foo == 0 
Problemi correlati