Есть ли способ прозрачно выполнить проверку объектов SQLAlchemy? - PullRequest
17 голосов
/ 06 марта 2010

Есть ли способ выполнить проверку объекта после того, как (или как) свойства заданы, но до того, как сеанс будет зафиксирован?

Например, у меня есть модель домена Device, которая имеет свойство mac. Я хотел бы убедиться, что свойство mac содержит допустимое и очищенное значение mac перед его добавлением или обновлением в базе данных.

Похоже, что Pythonic-подход заключается в том, чтобы делать большинство вещей как свойства (включая SQLAlchemy). Если бы я кодировал это в PHP или Java, я бы, вероятно, решил создать методы получения / установки для защиты данных и дать мне гибкость для обработки этого в самой модели предметной области.

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

Что представляет собой Pythonic способ справиться с ситуацией такого типа с помощью SQLAlchemy?

(Хотя я знаю, что проверка и должна обрабатываться в другом месте (т. Е. В веб-среде), я хотел бы выяснить, как обращаться с некоторыми из этих правил проверки, относящихся к конкретному домену, так как они обязательно будут появляться часто.) *

UPDATE

Я знаю, что мог бы использовать свойство , чтобы сделать это при обычных обстоятельствах. Ключевой частью является то, что я использую SQLAlchemy с этими классами. Я не совсем понимаю, как SQLAlchemy выполняет свою магию, но я подозреваю, что создание и переопределение этих свойств самостоятельно может привести к нестабильным и / или непредсказуемым результатам.

Ответы [ 3 ]

14 голосов
/ 14 августа 2013

Вы можете добавить проверку данных внутри ваших классов SQLAlchemy с помощью декоратора @validates().

Из документов - Простые валидаторы :

Атрибут валидатораможет вызвать исключение, останавливая процесс изменения значения атрибута, или может изменить данное значение на что-то другое.

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
8 голосов
/ 06 декабря 2010

Да. Это можно сделать с помощью 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 )

Возможно, вы захотите проверить ссылку before_update , потому что здесь не каждый экземпляр фактически обновляется до базы данных.

2 голосов
/ 06 марта 2010

«Похоже, Pythonic-подход заключается в том, чтобы делать большинство вещей как свойства»

Это меняется, но это близко.

"Если бы я закодировал это в PHP или Java, я бы, вероятно, решил создать методы получения / установки ..."

Хорошо. Это достаточно Pythonic. Ваши функции получения и установки связаны в свойстве; это очень хорошо.

В чем вопрос?

Вы спрашиваете, как пишется свойство ?

Однако, «прозрачная проверка» - если я правильно прочитал ваш пример кода - может не быть такой уж хорошей идеей.

Ваша модель и ваша валидация, вероятно, должны храниться отдельно. Распространено иметь несколько проверок для одной модели. Для некоторых пользователей поля являются необязательными, фиксированными или не используются; это приводит к нескольким проверкам.

Вы будете более счастливы, следуя шаблону проектирования Django, используя для проверки форму , отдельную форму модели.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...