def registerValidation(mapped, exclude=None): ''' Register to mapped class all the validations that can be performed based on the SQL alchemy mapping. @param mapped: class The mapped model class. @param exclude: list[string]|tuple(string) A list of column names to be excluded from automatic validation. @return: Property The property id of the model. ''' assert isclass(mapped), 'Invalid class %s' % mapped mapper, typeModel = mappingFor(mapped), typeFor(mapped) assert isinstance(mapper, Mapper), 'Invalid mapped class %s' % mapped assert isinstance(typeModel, TypeModel), 'Invalid model class %s' % mapped assert not exclude or isinstance(exclude, (list, tuple)), 'Invalid exclude %s' % exclude model = typeModel.container assert isinstance(model, Model) properties = set(model.properties) for cp in mapper.iterate_properties: if not isinstance(cp, ColumnProperty): continue assert isinstance(cp, ColumnProperty) if cp.key: prop = cp.key try: properties.remove(prop) except KeyError: continue if not (exclude and prop in exclude): propRef = getattr(mapped, prop) column = getattr(mapper.c, cp.key, None) assert isinstance(column, Column), 'Invalid column %s' % column if __debug__: propType = typeFor(propRef) assert isinstance(propType, TypeModelProperty), 'Invalid property %s with type %s' % (prop, propType) if column.primary_key and column.autoincrement: if prop != model.propertyId: raise MappingError('The primary key is expected to be %s, but got SQL primary key for %s' % (model.propertyId, prop)) validateAutoId(propRef) elif not column.nullable: validateRequired(propRef) if isinstance(column.type, String) and column.type.length: validateMaxLength(propRef, column.type.length) if column.unique: validateProperty(propRef, partial(onPropertyUnique, mapped)) if column.foreign_keys: for fk in column.foreign_keys: assert isinstance(fk, ForeignKey) try: fkcol = fk.column except AttributeError: raise MappingError('Invalid foreign column for %s, maybe you are not using the meta class' % prop) validateProperty(propRef, partial(onPropertyForeignKey, mapped, fkcol), index=INDEX_PROP_FK) for prop in properties: if not (exclude and prop in exclude): validateManaged(getattr(mapped, prop))
def testValidation(self): Entity._ally_listeners = {} validateAutoId(Entity.Id) validateRequired(Entity.Required) validateMaxLength(Entity.WithLength, 5) validateManaged(Entity.Managed) dummyService = DummyServiceEntity() proxySrvNonValid = proxyWrapFor(dummyService) proxySrv = proxyWrapFor(dummyService) bindValidations(proxySrv) assert isinstance(proxySrv, IServiceEntity) e = Entity() self.assertRaisesRegex(InputError, "(Entity.Required='Expected a value')", proxySrv.insert, e) self.assertEqual(proxySrvNonValid.insert(e), 'inserted') self.assertRaisesRegex(InputError, "(Entity.Id='Expected a value')", proxySrv.update, e) self.assertEqual(proxySrvNonValid.update(e), 'updated') e.Id = 'custom id' self.assertRaisesRegex(InputError, "(Entity.Id='No value expected')", proxySrv.insert, e) self.assertTrue(proxySrv.update(e) == 'updated') e = Entity() e.Required = 'Provided a value' self.assertTrue(proxySrv.insert(e) == 'inserted') e.Id = 'id' self.assertTrue(proxySrv.update(e) == 'updated') e = Entity() e.Required = 'required' e.WithLength = 'This is a longer text then 5' self.assertRaisesRegex(InputError, "(Entity.WithLength='Maximum length allowed is 5)", proxySrv.insert, e) e.WithLength = 'hello' self.assertTrue(proxySrv.insert(e) == 'inserted') e.WithLength = 'This is a longer text then 5' e.Id = 'id' self.assertRaisesRegex(InputError, "(Entity.WithLength='Maximum length allowed is 5)", proxySrv.update, e) e.WithLength = 'hello' self.assertTrue(proxySrv.update(e) == 'updated') e = Entity() e.Required = 'required' e.Managed = 'should not have value' self.assertRaisesRegex(InputError, "(Entity.Managed='No value expected')", proxySrv.insert, e) e.Id = 'id' self.assertRaisesRegex(InputError, "(Entity.Managed='No value expected')", proxySrv.update, e) self.assertRaises(AttributeError, getattr, proxySrv, '_hidden')
return self.author.Name # Non REST model attributes -------------------------------------- typeId = Column('fk_type_id', ForeignKey(PostTypeMapped.id, ondelete='RESTRICT'), nullable=False) type = relationship(PostTypeMapped, uselist=False, lazy='joined') author = relationship(CollaboratorMapped, uselist=False, lazy='joined') creator = relationship(UserMapped, uselist=False, lazy='joined') # Expression for hybrid ------------------------------------ @classmethod @IsModified.expression def _IsModified(cls): return case([(cls.UpdatedOn != None, True)], else_=False) @classmethod @IsPublished.expression def _IsPublished(cls): return case([(cls.PublishedOn != None, True)], else_=False) @classmethod @AuthorName.expression def _AuthorName(cls): return case([(cls.Author == None, UserMapped.Name)], else_=CollaboratorMapped.Name) validateRequired(PostMapped.Type) validateManaged(PostMapped.Type, key=EVENT_PROP_UPDATE) validateManaged(PostMapped.Author, key=EVENT_PROP_UPDATE) validateManaged(PostMapped.CreatedOn) validateManaged(PostMapped.PublishedOn) validateManaged(PostMapped.UpdatedOn) validateManaged(PostMapped.DeletedOn)
''' from ..api.user import User from sqlalchemy.schema import Column, ForeignKey from sqlalchemy.types import String, DateTime from superdesk.person.meta.person import PersonMapped from ally.support.sqlalchemy.mapper import validate from ally.container.binder_op import validateManaged, validateRequired # -------------------------------------------------------------------- @validate(exclude=('Password', 'CreatedOn', 'DeletedOn')) class UserMapped(PersonMapped, User): ''' Provides the mapping for User entity. ''' __tablename__ = 'user' __table_args__ = dict(mysql_engine='InnoDB', mysql_charset='utf8') Name = Column('name', String(20), nullable=False) CreatedOn = Column('created_on', DateTime, nullable=False) DeletedOn = Column('deleted_on', DateTime) # Non REST model attribute -------------------------------------- userId = Column('fk_person_id', ForeignKey(PersonMapped.Id, ondelete='CASCADE'), primary_key=True) password = Column('password', String(255), nullable=False) # Never map over the inherited id validateRequired(UserMapped.Password) validateManaged(UserMapped.CreatedOn) validateManaged(UserMapped.DeletedOn)
from superdesk.person.meta.person import PersonMapped from sqlalchemy.dialects.mysql.base import INTEGER # -------------------------------------------------------------------- @validate(exclude=('Password', 'CreatedOn', 'Active', 'Type')) class UserMapped(PersonMapped, User): ''' Provides the mapping for User entity. ''' __tablename__ = 'user' __table_args__ = (UniqueConstraint('name', name='uix_user_name'), dict(mysql_engine='InnoDB', mysql_charset='utf8')) Name = Column('name', String(150), nullable=False, unique=True) Uuid = Column('uuid', String(32), unique=True, nullable=True) Cid = Column('cid', INTEGER(unsigned=True), nullable=True, default=0) CreatedOn = Column('created_on', DateTime, nullable=False) Active = Column('active', Boolean, nullable=False, default=True) Type = association_proxy('type', 'Key') # Non REST model attribute -------------------------------------- userId = Column('fk_person_id', ForeignKey(PersonMapped.Id, ondelete='CASCADE'), primary_key=True) password = Column('password', String(255), nullable=False) # Never map over the inherited id typeId = Column('fk_type_id', ForeignKey(UserTypeMapped.id, ondelete='RESTRICT'), nullable=False) type = relationship(UserTypeMapped, uselist=False, lazy='joined') validateRequired(UserMapped.Password) validateManaged(UserMapped.CreatedOn) validateManaged(UserMapped.Active)
# Non REST model attributes -------------------------------------- typeId = Column('fk_type_id', ForeignKey(PostTypeMapped.id, ondelete='RESTRICT'), nullable=False) type = relationship(PostTypeMapped, uselist=False, lazy='joined') author = relationship(CollaboratorMapped, uselist=False, lazy='joined') creator = relationship(UserMapped, uselist=False, lazy='joined') # Expression for hybrid ------------------------------------ @classmethod @IsModified.expression def _IsModified(cls): return case([(cls.UpdatedOn != None, True)], else_=False) @classmethod @AuthorName.expression def _AuthorName(cls): return case([(cls.Author == None, UserMapped.Name)], else_=CollaboratorMapped.Name) validateRequired(PostMapped.Type) validateManaged(PostMapped.Type, key=EVENT_PROP_UPDATE) validateManaged(PostMapped.Author, key=EVENT_PROP_UPDATE) validateManaged(PostMapped.CreatedOn) validateManaged(PostMapped.PublishedOn) validateManaged(PostMapped.UpdatedOn) validateManaged(PostMapped.DeletedOn)
def registerValidation(mapped, exclude=None): ''' Register to mapped class all the validations that can be performed based on the SQL alchemy mapping. @param mapped: class The mapped model class. @param exclude: list[string]|tuple(string) A list of column names to be excluded from automatic validation. @return: Property The property id of the model. ''' assert isclass(mapped), 'Invalid class %s' % mapped mapper, typeModel = mappingFor(mapped), typeFor(mapped) assert isinstance(mapper, Mapper), 'Invalid mapped class %s' % mapped assert isinstance(typeModel, TypeModel), 'Invalid model class %s' % mapped assert not exclude or isinstance( exclude, (list, tuple)), 'Invalid exclude %s' % exclude model = typeModel.container assert isinstance(model, Model) properties = set(model.properties) for cp in mapper.iterate_properties: if not isinstance(cp, ColumnProperty): continue assert isinstance(cp, ColumnProperty) if cp.key: prop = cp.key try: properties.remove(prop) except KeyError: continue if not (exclude and prop in exclude): propRef = getattr(mapped, prop) column = getattr(mapper.c, cp.key, None) assert isinstance(column, Column), 'Invalid column %s' % column if __debug__: propType = typeFor(propRef) assert isinstance( propType, TypeModelProperty ), 'Invalid property %s with type %s' % (prop, propType) if column.primary_key and column.autoincrement: if prop != model.propertyId: raise MappingError( 'The primary key is expected to be %s, but got SQL primary key for %s' % (model.propertyId, prop)) validateAutoId(propRef) elif not column.nullable: validateRequired(propRef) if isinstance(column.type, String) and column.type.length: validateMaxLength(propRef, column.type.length) if column.unique: validateProperty(propRef, partial(onPropertyUnique, mapped)) if column.foreign_keys: for fk in column.foreign_keys: assert isinstance(fk, ForeignKey) try: fkcol = fk.column except AttributeError: raise MappingError( 'Invalid foreign column for %s, maybe you are not using the meta class' % prop) validateProperty(propRef, partial(onPropertyForeignKey, mapped, fkcol), index=INDEX_PROP_FK) for prop in properties: if not (exclude and prop in exclude): validateManaged(getattr(mapped, prop))