2010-03-05 8 views
15

Esiste un modo per eseguire la convalida su un oggetto dopo (o come) le proprietà vengono impostate ma prima che la sessione venga confermata?Esiste un modo per eseguire in modo trasparente la convalida sugli oggetti SQLAlchemy?

Per esempio, ho un modello di dominio Device che ha una proprietà mac. Vorrei assicurarmi che la proprietà mac contenga un valore mac valido e disinfettato prima di essere aggiunto o aggiornato nel database.

Sembra che l'approccio Python sia quello di fare la maggior parte delle cose come proprietà (incluso SQLAlchemy). Se avessi codificato questo in PHP o Java, probabilmente avrei optato per creare metodi getter/setter per proteggere i dati e darmi la flessibilità di gestirli nel modello di dominio stesso.

public function mac() { return $this->mac; } 
public function setMac($mac) { 
    return $this->mac = $this->sanitizeAndValidateMac($mac); 
} 
public function sanitizeAndValidateMac($mac) { 
    if (! preg_match(self::$VALID_MAC_REGEX)) { 
     throw new InvalidMacException($mac); 
    } 
    return strtolower($mac); 
} 

Che è un modo Pythonic per gestire questo tipo di situazione usando SQLAlchemy?

(Sebbene sia a conoscenza della convalida e debba essere gestito altrove (ad esempio, il framework Web) vorrei capire come gestire alcune di queste regole di convalida specifiche del dominio poiché sono destinate a venire frequentemente.)

UPDATE

so che potrei usare property per fare questo in circostanze normali. La parte fondamentale è che sto usando SQLAlchemy con queste classi. Non capisco esattamente come SQLAlchemy stia eseguendo la sua magia, ma sospetto che la creazione e l'override di queste proprietà sul mio potrebbero portare a risultati instabili e/o imprevedibili.

risposta

2

"Sembra che l'approccio Pythonic è quello di fare la maggior parte delle cose come proprietà"

Esso varia, ma che è vicino.

"Se avessi codificato questo in PHP o Java, probabilmente avrei scelto di creare metodi getter/setter ..."

Good. È abbastanza pitone. Le tue funzioni getter e setter sono legate in una proprietà; è piuttosto buono.

Qual è la domanda?

Stai chiedendo come si scrive property?

Tuttavia, "convalida trasparente", se leggo correttamente il codice di esempio, potrebbe non essere proprio un'idea valida.

Probabilmente il tuo modello e la tua convalida dovrebbero essere tenuti separati. È comune avere più convalide per un singolo modello. Per alcuni utenti, i campi sono opzionali, fissi o non utilizzati; questo porta a più convalide.

Sarai più contento di seguire lo schema di progettazione Django dell'uso di uno Form per la convalida, a parte del modello.

+2

non sono sicuro che tipo di magia SQLAlchemy utilizza per legarsi alle proprietà della classe del modello. Non darei per scontato che sia sicuro definire la mia 'mac = property()' sulla mia classe. Penso che la mia domanda sia questa sicurezza da fare? Se è così, ci sono dei trucchi? In caso contrario, quali sono alcune altre alternative? –

+0

Non c'è magia. Non sono trucchi. L'alternativa è ciò che generalmente facciamo: facciamo la validazione * al di fuori * della classe del modello, rendendola ancora più semplice. Definisci la tua convalida * al di fuori * del modello, e quindi non devi preoccuparti di nulla di tutto ciò. –

7

Sì. Questo può essere fatto bene usando un MapperExtension.

# uses sqlalchemy hooks to data model class specific validators before update and insert 
class ValidationExtension(sqlalchemy.orm.interfaces.MapperExtension): 
    def before_update(self, mapper, connection, instance): 
     """not every instance here is actually updated to the db, see http://www.sqlalchemy.org/docs/reference/orm/interfaces.html?highlight=mapperextension#sqlalchemy.orm.interfaces.MapperExtension.before_update""" 
     instance.validate() 
     return sqlalchemy.orm.interfaces.MapperExtension.before_update(self, mapper, connection, instance) 
    def before_insert(self, mapper, connection, instance): 
     instance.validate() 
     return sqlalchemy.orm.interfaces.MapperExtension.before_insert(self, mapper, connection, instance) 


sqlalchemy.orm.mapper(model, table, extension = ValidationExtension(), **mapper_args) 

Si consiglia di verificare before_update di riferimento perché non ogni caso qui è in realtà aggiornato al db.

13

È possibile aggiungere la convalida dei dati all'interno delle classi SQLAlchemy utilizzando il decoratore @validates().

Dalla documentazione - Simple Validators:

un validatore attributo può sollevare un'eccezione, arrestare il processo di mutazione valore dell'attributo, oppure può modificare il valore dato in qualcosa di diverso.

from sqlalchemy.orm import validates 

class EmailAddress(Base): 
    __tablename__ = 'address' 

    id = Column(Integer, primary_key=True) 
    email = Column(String) 

    @validates('email') 
    def validate_email(self, key, address): 
     # you can use assertions, such as 
     # assert '@' in address 
     # or raise an exception: 
     if '@' not in address: 
      raise ValueError('Email address must contain an @ sign.') 
     return address 
+0

Solo una domanda veloce: se voglio una semplice convalida "presenza", dovrei semplicemente aggiungere "nullable = false" alla dichiarazione della colonna e quindi provare a catturare gli errori di 'session.commit()'? O dovrei provare un validatore simile? Grazie. –