Skip to main content

Active Record like validation on SQLAlchemy declarative model objects

Project description

https://ci.appveyor.com/api/projects/status/92uq13xnk8woa59d?svg=true https://circleci.com/gh/blazelibs/sqlalchemy-validation.svg?&style=shield https://codecov.io/github/blazelibs/sqlalchemy-validation/coverage.svg?branch=master

Introduction

SAValidation facilitates Active Record like validation on SQLAlchemy declarative model objects.

You can install the in-development version of savalidation with easy_install savalidation==dev.

The home page is currently the GitHub repository.

Usage Example

The following is a snippet from the examples.py file:

from datetime import datetime
import formencode
import sqlalchemy as sa
import sqlalchemy.ext.declarative as sadec
import sqlalchemy.sql as sasql
import sqlalchemy.orm as saorm

from savalidation import ValidationMixin, watch_session
import savalidation.validators as val

engine = sa.create_engine('sqlite://')
#engine.echo = True
meta = sa.MetaData()
Base = sadec.declarative_base(metadata=meta)

Session = saorm.scoped_session(
    saorm.sessionmaker(
        bind=engine,
        autoflush=False
    )
)

sess = Session

class Family(Base, ValidationMixin):
    __tablename__ = 'families'

    # SA COLUMNS
    id = sa.Column(sa.Integer, primary_key=True)
    createdts = sa.Column(sa.DateTime, nullable=False, default=datetime.now, server_default=sasql.text('CURRENT_TIMESTAMP'))
    updatedts = sa.Column(sa.DateTime, onupdate=datetime.now)
    name =  sa.Column(sa.Unicode(75), nullable=False, unique=True)
    reg_num = sa.Column(sa.Integer, nullable=False, unique=True)
    status =  sa.Column(sa.Unicode(15), nullable=False, default=u'active', server_default=u'active')

    # VALIDATION
    STATUS_CHOICES = (
        ('active', 'Active'),
        ('inactive', 'Inactive'),
        ('moved', 'Moved'),
    )
    # will validate nullability and string types
    val.validates_constraints()
    val.validates_one_of('status', [k for k, v in STATUS_CHOICES])

    #OTHER
    def __str__(self):
        return '<Family id=%s, name=%s>' % (self.id, self.name)

class Person(Base, ValidationMixin):
    __tablename__ = 'people'

    id = sa.Column(sa.Integer, primary_key=True)
    createdts = sa.Column(sa.DateTime, nullable=False, server_default=sasql.text('CURRENT_TIMESTAMP'))
    updatedts = sa.Column(sa.DateTime, onupdate=datetime.now)
    name_first = sa.Column(sa.Unicode(75), nullable=False)
    name_last = sa.Column(sa.Unicode(75), nullable=False)
    family_role = sa.Column(sa.Unicode(20), nullable=False)
    nullable_but_required = sa.Column(sa.Unicode(5))

    ROLE_CHOICES = (
        ('father', 'Father'),
        ('mother', 'Mother'),
        ('child', 'Child'),
    )
    val.validates_constraints(exclude='createdts')
    val.validates_presence_of('nullable_but_required')
    val.validates_choices('family_role', ROLE_CHOICES)

class ReverseConverter(formencode.api.FancyValidator):
    def _to_python(self, value, state):
        if not isinstance(value, basestring):
            raise formencode.Invalid('Must be a string type', value, state)
        # this reverse a string or list...yah, I know, it looks funny
        return value[::-1]

validates_reverse = val.formencode_factory(ReverseConverter)
converts_reverse = val.formencode_factory(ReverseConverter, sv_convert=True)

class ConversionTester(Base, ValidationMixin):
    __tablename__ = 'conversion_testers'

    id = sa.Column(sa.Integer, primary_key=True)
    val1 = sa.Column(sa.String(25))
    val2 = sa.Column(sa.String(25))
    val3 = sa.Column(sa.String(25))
    val4 = sa.Column(sa.String(25))

    validates_reverse('val1')
    validates_reverse('val2', sv_convert=True)
    converts_reverse('val3')
    converts_reverse('val4', sv_convert=False)

See more examples in the tests directory of the distribution.

Installing & Testing Source

(this is one way, there are others)

# create a virtualenv
# activate the virtualenv

$ pip install -e git+git://github.com/blazelibs/sqlalchemy-validation.git@master#egg=savalidation
$ pip install nose
$ cd src/savalidation/savalidation
$ nosetests

Questions & Comments

Please visit: http://groups.google.com/group/blazelibs

Known Issues

Final values that get set on an ORM mapped object attributes through relationships, the default or onupdate column parameters, and possibly others are not availble at the time validation is done.

In some cases, this can be caught after the flush (before commit) when those values become available on the ORM object.

Unfortunately, that is of limited value in the case where the the value that slipped through violates a DB constraint. In that case, a true DB exception will be raised.

Dependencies

  • SQLAlchemy > 0.7.6

  • FormEncode

  • python-dateutil (for date/time converters)

  • Nose (if you want to run the tests)

Credits

This project borrows code and ideas from:

Current Status

The code itself seems stable, but the API may change in the future.

Change Log

0.4.1 released 2016-11-23

  • fixed Python versions listed for package

  • fixed a broken link in the readme to the Elixir project

  • updated CircleCI setup and added AppVeyor

0.4.0 released 2016-06-10

  • added support for Python 3.4+ (thanks to Jiří Bireš)

  • set up continuous integration on Circle CI

  • coverage reports through CodeCov

0.3.2 released 2016-04-20

  • validate precision and scale of numeric column values

0.3.1 released 2016-02-23

  • fix formencode compatibility, supports formencode 1.2 and 1.3

0.3.0 released 2014-09-30

  • fix bug with .validates_constraints() and Text column types

  • watch_session() is no longer needed, SQLAlchemy >= 0.7.6 required

  • now beta quality: been used in production a long time, but not used widely

0.2.1 released 2013-05-15

  • fixed issue #6 - adjustment to the version of python-dateutil required.

0.2.0 released 2012-10-24

This release contains some BC BREAKS.

  • internal API cleaned up

  • refactored to use SQLAlchemy (SA) events, we are now compatable with & require SA >= 0.7

  • CHANGE: if using SA < 0.7.6, savalidation.watch_session() must be called with each instance of your session IF the savalidation module is being instantiated before your session is created.

  • CHANGE: the validator API has changed. If you have created custom validators you will need to look at the changes in validators.py.

  • the Formencode state object sent to a validator’s method has changed the “instance” attribute to be “entity.”

  • add before_flush() helper to decorate entity instance methods

  • got rid of after_flush validation. Instead, before_insert/before_update events

    can now be used to validate non-nullable foreign keys.

  • got rid of validation support for a column’s default and server_default

    values (b/c it required after_flush validation)

  • formencode schemas are now only created once per class, not per instance,

    boosting performance.

0.1.5 released 2011-06-11

  • fix 0.1.4 release which didn’t include version file

0.1.4 released 2011-06-11

  • change python-dateutil dependence to < 2.0, 2.x is for python 3

0.1.3 released 2011-05-19

  • change SQLAlchemy requirement so the latest package < 0.7 is installed

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

SAValidation-0.4.1.tar.gz (19.6 kB view details)

Uploaded Source

File details

Details for the file SAValidation-0.4.1.tar.gz.

File metadata

File hashes

Hashes for SAValidation-0.4.1.tar.gz
Algorithm Hash digest
SHA256 2a91bb2977f69856f2c91fbc1b856bb3d1b399f36a7a5f0b7b12570e4d606bce
MD5 d9d5b63ec74b63d13dca8d9c9c532d10
BLAKE2b-256 77b9b88e840e46266c99b3a35cfe77272302b6023ee2fa3d2d408ca873268b72

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page