2010-03-05 3 views
15

Gibt es eine Möglichkeit, die Überprüfung eines Objekts nach (oder als) dem Festlegen der Eigenschaften, aber vor dem Festschreiben der Sitzung durchzuführen?Gibt es eine Möglichkeit, Validierung für SQLAlchemy-Objekte transparent durchzuführen?

Zum Beispiel habe ich ein Domänenmodell Device, das eine mac Eigenschaft hat. Ich möchte sicherstellen, dass die mac Eigenschaft einen gültigen und bereinigten Mac-Wert enthält, bevor es in der Datenbank hinzugefügt oder aktualisiert wird.

Es sieht so aus, als ob der Pythonic-Ansatz die meisten Dinge als Eigenschaften (einschließlich SQLAlchemy) zu tun hat. Wenn ich dies in PHP oder Java programmiert hätte, hätte ich wahrscheinlich Getter/Setter-Methoden zum Schutz der Daten erstellt und mir die Flexibilität gegeben, dies im Domänenmodell selbst zu handhaben.

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); 
} 

Was ist ein Pythonic Weg, um diese Art von Situation mit SQLAlchemy zu behandeln?

(Während ich weiß, dass die Validierung und anderswo (dh Web-Framework) behandelt werden sollte, würde ich gerne herausfinden, wie einige dieser domänenspezifischen Validierungsregeln zu behandeln, da sie häufig auftreten werden.)

UPDATE

ich weiß, dass ich property tun dies unter normalen Umständen nutzen konnte. Der Schlüssel ist, dass ich SQLAlchemy mit diesen Klassen verwende. Ich verstehe nicht genau, wie SQLAlchemy seine Magie ausübt, aber ich vermute, dass das Erstellen und Überschreiben dieser Eigenschaften allein zu instabilen und/oder unvorhersehbaren Ergebnissen führen kann.

Antwort

2

„Es sieht aus wie der Pythonic Ansatz ist die meisten Dinge als Eigenschaften zu tun“

Es variiert, aber das ist in der Nähe.

"Wenn ich dies in PHP oder Java codiert wäre, hätte ich wahrscheinlich entschieden Getter/Setter-Methoden zu erstellen ..."

Gut. Das ist Pythonic genug. Ihre Getter- und Setter-Funktionen sind in einer Eigenschaft gebunden; Das ist sehr gut.

Was ist die Frage?

Fragen Sie, wie man property buchstabiert?

Allerdings, "transparente Validierung" - wenn ich Ihren Beispielcode richtig gelesen habe - kann nicht wirklich so gut eine Idee sein.

Ihr Modell und Ihre Validierung sollten wahrscheinlich getrennt gehalten werden. Es ist üblich, mehrere Validierungen für ein einzelnes Modell zu haben. Für einige Benutzer sind Felder optional, fest oder nicht verwendet; Dies führt zu mehreren Validierungen.

Sie werden glücklicher sein, wenn Sie dem Django-Entwurfsmuster der Verwendung eines Form zur Validierung folgen, getrennt vom Modell.

+2

Ich bin nicht sicher, welche Art von Magie SQLAlchemy auf die Modellklasse Eigenschaften zu binden, verwendet. Ich würde nicht annehmen, dass es sicher ist, meine eigene 'mac = property()' in meiner Klasse zu definieren. Ich denke meine Frage ist, ist das sicher zu tun? Wenn ja, gibt es irgendwelche Probleme? Wenn nicht, was sind andere Alternativen? –

+0

Es gibt keine Magie. Das sind keine Probleme. Die Alternative ist, was wir generell tun: Wir machen Validierung * außerhalb * der Modellklasse, was es noch einfacher macht. Definieren Sie Ihre Validierung * außerhalb des Modells und Sie müssen sich dann keine Gedanken darüber machen. –

7

Ja. Dies kann mit einer MapperExtension gut gemacht werden.

# 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) 

Sie möchten before_update Bezug überprüfen, weil hier nicht jede Instanz tatsächlich an die DB aktualisiert wird.

13

Sie können Datenvalidierung innerhalb Ihrer SQLAlchemy-Klassen mit dem @validates() Decorator hinzufügen.

Aus der Dokumentation - Simple Validators:

Ein Attribut Validator eine Ausnahme auslösen kann, um den Prozess des Anhaltens der Wert des Attributs mutiert, oder kann den angegebenen Wert in etwas anderes ändern.

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

Nur eine kurze Frage: Wenn ich eine einfache "Präsenz" -Validierung haben möchte, sollte ich einfach 'nullable = false' zur Spaltendeklaration hinzufügen und dann versuchen, die 'session.commit()' -Fehler zu erfassen? Oder sollte ich einen ähnlichen Validator versuchen? Vielen Dank. –