From 5d1b5f90ac8b3c1fc4b42dbee1f1d98c77cdacd6 Mon Sep 17 00:00:00 2001 From: Marcus Lindvall Date: Tue, 19 Nov 2019 16:24:10 +0100 Subject: [PATCH] Verify string lengths when creating model objects --- pyjeeves/models/abc.py | 2 ++ pyjeeves/models/ext.py | 55 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 pyjeeves/models/ext.py diff --git a/pyjeeves/models/abc.py b/pyjeeves/models/abc.py index 061d681..9cc76d9 100644 --- a/pyjeeves/models/abc.py +++ b/pyjeeves/models/abc.py @@ -17,6 +17,7 @@ from sqlservice import ModelBase, as_declarative from pyjeeves import logging from . import db +from .ext import InstallValidatorListeners logger = logging.getLogger("PyJeeves." + __name__) @@ -37,6 +38,7 @@ except OperationalError as e: class RawBaseModel(ModelBase): """ Generalize __init__, __repr__ and to_json Based on the models columns , ForetagKod=1""" + __sa_instrumentation_manager__ = InstallValidatorListeners __to_dict_filter__ = [] __to_dict_only__ = () diff --git a/pyjeeves/models/ext.py b/pyjeeves/models/ext.py new file mode 100644 index 0000000..f2f0ae0 --- /dev/null +++ b/pyjeeves/models/ext.py @@ -0,0 +1,55 @@ +from sqlalchemy.ext.instrumentation import InstrumentationManager +# from sqlalchemy.orm.interfaces import AttributeExtension +from sqlalchemy.orm import ColumnProperty +from sqlalchemy.types import String +from sqlalchemy import event + + +class Error(Exception): + """Base class for exceptions in this module.""" + pass + + +class ValidationError(Error): + pass + + +class JeevesDBError(Error): + """Exception raised for errors in the input. + + Attributes: + expression -- input expression in which the error occurred + message -- explanation of the error + """ + + def __init__(self, field, message): + super().__init__(message) + self.field = field + self.message = message + + +class InstallValidatorListeners(InstrumentationManager): + def post_configure_attribute(self, class_, key, inst): + """Add validators for any attributes that can be validated.""" + prop = inst.prop + # Only interested in simple columns, not relations + if isinstance(prop, ColumnProperty) and len(prop.columns) == 1: + col = prop.columns[0] + # if we have string column with a length, create a length validator listner + if isinstance(col.type, String) and col.type.length: + event.listen( + getattr(class_, key), 'set', LengthValidator( + col.name, col.type.length), retval=True) + + +class LengthValidator(): + def __init__(self, col_name, max_length): + self.col_name = col_name + self.max_length = max_length + + def __call__(self, state, value, oldvalue, initiator): + if len(value) > self.max_length: + raise ValidationError( + "%s.%s: Length %d exceeds allowed %d" % ( + state.__class__.__name__, self.col_name, len(value), self.max_length)) + return value