class WithMethod: ''' Provides the definition used to add method on other mappings. ''' methodId = declared_attr(lambda cls: Column('fk_method_id', ForeignKey(Method.id, ondelete='RESTRICT'), nullable=False)) method = declared_attr(lambda cls: relationship(Method, lazy='joined', uselist=False, viewonly=True)) @validate(Mandatory, lambda prop: MaxLen(prop, columnFor(Method.name).type.length)) @hybrid(expr=joinedExpr(Method, 'name')) def Method(self): if self.method: return self.method.name @Method.setter def MethodSet(self, name): assert isinstance(name, str), 'Invalid name %s' % name name = name.strip().upper() assert name, 'Empty string is not a valid name' session = openSession() try: methodId, = session.query(Method.id).filter(Method.name == name).one() except NoResultFound: method = Method() method.name = name session.add(method) session.flush((method,)) methodId = method.id self.methodId = methodId
class WithPath: ''' Provides the definition used to add path on other mappings. ''' pathId = declared_attr(lambda cls: Column('fk_path_id', ForeignKey(Path.id, ondelete='RESTRICT'), nullable=False)) path = declared_attr(lambda cls: relationship(Path, lazy='joined', uselist=False, viewonly=True)) @validate(Mandatory, lambda prop: MaxLen(prop, columnFor(Path.path).type.length)) @hybrid(expr=joinedExpr(Path, 'path')) def Path(self): if self.path: return self.path.path @Path.setter def PathSet(self, path): assert isinstance(path, str), 'Invalid path %s' % path path = path.strip().strip('/') session = openSession() try: pathId, = session.query(Path.id).filter(Path.path == path).one() except NoResultFound: aclPath = Path() aclPath.path = path for k, item in enumerate(path.split('/'), 1): if item == '*' and aclPath.priority is None: aclPath.priority = k break if aclPath.priority is None: aclPath.priority = 0 session.add(aclPath) session.flush((aclPath,)) pathId = aclPath.id self.pathId = pathId
def insert_in_bases_table_args(self, new_base, transformation_properties): """Add table __table_args__ in new_base :param new_base: the base to be put on front of all bases :param transformation_properties: the properties of the model """ if (transformation_properties['table_args'] and transformation_properties['table_kwargs']): def __table_args__(cls_): res = cls_.define_table_args() + (cls_.define_table_kwargs(), ) return res new_base.__table_args__ = declared_attr(__table_args__) elif transformation_properties['table_args']: def __table_args__(cls_): return cls_.define_table_args() new_base.__table_args__ = declared_attr(__table_args__) elif transformation_properties['table_kwargs']: def __table_args__(cls_): return cls_.define_table_kwargs() new_base.__table_args__ = declared_attr(__table_args__)
class WithSignature: ''' Provides the definition used to add signature on other mappings. ''' signatureId = declared_attr(lambda cls: Column('fk_signature_id', ForeignKey(Signature.id, ondelete='RESTRICT'), nullable=False)) signature = declared_attr(lambda cls: relationship(Signature, lazy='joined', uselist=False, viewonly=True)) @classmethod def createSignature(cls): ''' Create the signature name link, only one can be created. ''' def fget(self): if self.signature: return self.signature.name def fset(self, name): assert isinstance(name, str), 'Invalid signature name %s' % name name = name.strip() assert name, 'Empty string is not a valid signature name' session = openSession() try: signatureId, = session.query(Signature.id).filter(Signature.name == name).one() except NoResultFound: signature = Signature() signature.name = name session.add(signature) session.flush((signature,)) signatureId = signature.id self.signatureId = signatureId validation = validate(Mandatory, lambda prop: MaxLen(prop, columnFor(Signature.name).type.length)) return validation(hybrid_property(fget, fset, expr=joinedExpr(Signature, 'name')))
class AudioInfoDefinition: ''' Provides the mapping for AudioInfo. ''' __tablename__ = 'archive_audio_info' __table_args__ = dict(mysql_engine='InnoDB', mysql_charset='utf8') Id = declared_attr(lambda cls: Column('fk_metainfo_id', ForeignKey(MetaInfoMapped.Id), primary_key=True)) Caption = declared_attr(lambda cls: Column('caption', String(255), nullable=False, key='Caption'))
class ConfigurationDescription: ''' Provides abstract mapping for Configuration. ''' __table_args__ = dict(mysql_engine='InnoDB', mysql_charset='utf8') Name = declared_attr(lambda cls: Column('name', String(255), primary_key=True)) Value = declared_attr(lambda cls: Column('value', String(1024))) # None REST model attribute -------------------------------------- parent = declared_attr(lambda cls: abstractMapping())
class AdminDefinition: ''' Provides the mapping for a BlogAdmin definition. ''' __tablename__ = 'livedesk_admin' __table_args__ = dict(mysql_engine='InnoDB') Blog = declared_attr(lambda cls: Column( 'fk_blog_id', ForeignKey(BlogMapped.Id), primary_key=True)) # Non REST model attribute -------------------------------------- adminId = declared_attr(lambda cls: Column( 'fk_user_id', ForeignKey(UserMapped.userId), primary_key=True))
class BlogCollaboratorDefinition: ''' Provides the mapping for BlogCollaborator definition. ''' __tablename__ = 'livedesk_collaborator' __table_args__ = dict(mysql_engine='InnoDB') Blog = declared_attr(lambda cls: Column('fk_blog_id', ForeignKey(BlogMapped.Id), primary_key=True)) # Non REST model attribute -------------------------------------- blogCollaboratorId = declared_attr(lambda cls: Column('fk_collaborator_id', ForeignKey(CollaboratorMapped.Id), primary_key=True))
class BlogTypePostDefinition: ''' Provides the mapping for BlogCollaborator definition. ''' __tablename__ = 'livedesk_blog_type_post' __table_args__ = dict(mysql_engine='InnoDB', mysql_charset='utf8') BlogType = declared_attr(lambda cls: Column( 'fk_blog_type_id', ForeignKey(BlogMapped.Id), nullable=False)) Order = declared_attr(lambda cls: Column('ordering', REAL)) # Non REST model attribute -------------------------------------- blogTypePostId = declared_attr(lambda cls: Column( 'fk_post_id', ForeignKey(PostMapped.Id), primary_key=True))
def person_relation_factory(relation_name, fulltext_attr=None, api_attr=None): """Factory that will generate person """ def field_declaration(cls): # pylint: disable=no-self-argument return deferred(db.Column(db.Integer, db.ForeignKey('people.id'), nullable=True), cls.__name__) def attr_declaration(cls): return db.relationship( 'Person', primaryjoin='{0}.{1}_id == Person.id'.format(cls.__name__, relation_name), foreign_keys='{0}.{1}_id'.format(cls.__name__, relation_name), remote_side='Person.id', uselist=False, ) gen_fulltext_attr = ( fulltext_attr or attributes.FullTextAttr(relation_name, relation_name, ["email", "name"])) api_attr = api_attr or reflection.Attribute(relation_name) # pylint: disable=too-few-public-methods,missing-docstring class DecoratedClass(object): _api_attrs = reflection.ApiAttributes(api_attr) fulltext_attr = [gen_fulltext_attr] @classmethod def indexed_query(cls): return super(DecoratedClass, cls).indexed_query().options( orm.Load(cls).joinedload( relation_name ).load_only( "name", "email", "id" ), ) return type( "{}_mixin".format(relation_name), (DecoratedClass, ), { "{}_id".format(relation_name): declared_attr(field_declaration), relation_name: declared_attr(attr_declaration), })
def ForeignMixin(name, target): global defcnt attr = name + '_id' obj = type( 'ForeignMixin_' + name, (object, ), { attr: declared_attr(lambda cls: Column( Integer, ForeignKey(target.__tablename__ + '.id'))), name: declared_attr(lambda cls: relationship( target, primaryjoin=target.id == getattr(cls, attr))) }) setattr(target, str(defcnt), declared_attr(lambda cls: relationship(obj))) defcnt += 1 return obj
class UserBase(Base): created_by_id = declared_attr(lambda cls: db.Column( UUID, db.ForeignKey('user.id'), default=cls._get_current_user)) created_by = declared_attr(lambda cls: relationship( 'User', primaryjoin='%s.created_by_id == User.id' % cls.__name__)) updated_by_id = declared_attr( lambda cls: db.Column(UUID, db.ForeignKey('user.id'), default=cls._get_current_user, onupdate=cls._get_current_user)) updated_by = declared_attr(lambda cls: relationship( 'User', primaryjoin='%s.updated_by_id == User.id' % cls.__name__)) @classmethod def _get_current_user(cls): return None
class ProjectLink(UUIDPrimaryKeyMixin, db.Model): __tablename__ = "project_links" __table_args__ = declared_attr( table_args((db.Index("project_link_idx", "project_id", "link", unique=True), ))) project_id = db.Column(pg.UUID(as_uuid=True), db.ForeignKey("projects.id", ondelete="CASCADE"), nullable=False) link = db.Column(db.UnicodeText, nullable=False) @classmethod def extract(cls, project, html): parser = html5lib.HTMLParser( tree=html5lib.treebuilders.getTreeBuilder("etree", ElementTree), namespaceHTMLElements=False, ) parsed = parser.parse(html) for anchor in parsed.iter("a"): if "href" in anchor.attrib: href = anchor.attrib["href"] try: link = cls.query.filter_by( project=project, link=href, ).one() except NoResultFound: link = cls(project=project, link=href) db.session.add(link)
def named_relationship_mixin_factory(container_name, cls_name, names): """Build a Mixin of relationships for the container class. Example: health = relationship( "Health", primaryjoin="and_(Proficiencies.id==Proficiency.proficiencies_id, " "Proficiency.name=='Heath')", back_populates="proficiencies", uselist=False) OR scholar = relationship( "AuraAbility", primaryjoin="and_(Abilities.id==Ability.abilities_id, " "Ability.name=='scholar')", back_populates="abilities", uselist=False) """ dct = {} for false_name in names: attr_name = false_name.lower().replace(" ", "_") name = false_name.title().replace(" ", '') dct[attr_name] = lambda cls, name_=name: relationship( name_, primaryjoin="and_({}.id=={}.{}_id, {}.name=='{}')".format( container_name, cls_name, container_name.lower(), cls_name, name_), back_populates=container_name.lower(), uselist=False, cascade="all, delete-orphan") dct[attr_name] = declared_attr(dct[attr_name]) return type('NamedRelationshipMixin', (), dct)
class Entry(AbstractConcreteBase, db.Model): id = db.Column(db.Integer, primary_key=True, nullable=False) created = db.Column(db.DateTime, nullable=False) post_id = declared_attr(lambda c: db.Column(db.ForeignKey('post.id'))) post = declared_attr(lambda c: db.relationship('Post', lazy='joined')) @declared_attr def __tablename__(self): return self.__name__.lower() @declared_attr def __mapper_args__(self): return { 'polymorphic_identity': self.__name__, 'concrete': True } if self.__name__ != 'Entry' else {}
def _decorate_base_class(klass): class Mixed(klass, CrudMixin): def __init__(self, *args, **kwargs): """ Variant on SQLAlchemy model __init__ which sets default values on initialization instead of immediately before the model is saved. """ if '_model' in kwargs: assert kwargs.pop('_model') == self.__class__.__name__ declarative_base_constructor(self, *args, **kwargs) for attr, col in self.__table__.columns.items(): if kwargs.get(attr) is None and col.default: self.__dict__.setdefault(attr, col.default.execute()) orig_kwargs['cls'] = Mixed if 'name' not in orig_kwargs: orig_kwargs['name'] = klass.__name__ if 'constructor' not in orig_kwargs: orig_kwargs['constructor'] = klass.__init__ if '__init__' in klass.__dict__ else Mixed.__init__ Mixed = declarative.declarative_base(*orig_args, **orig_kwargs) Mixed.BaseClass = _SessionInitializer._base_classes[klass.__module__] = Mixed Mixed.__tablename__ = declarative.declared_attr(lambda cls: _camelcase_to_underscore(cls.__name__)) return Mixed
def _decorate_base_class(klass): class Mixed(klass, CrudMixin): def __init__(self, *args, **kwargs): """ Variant on SQLAlchemy model __init__ which sets default values on initialization instead of immediately before the model is saved. """ if '_model' in kwargs: assert kwargs.pop('_model') == self.__class__.__name__ declarative_base_constructor(self, *args, **kwargs) for attr, col in self.__table__.columns.items(): if kwargs.get(attr) is None and col.default: self.__dict__.setdefault(attr, col.default.execute()) orig_kwargs['cls'] = Mixed if 'name' not in orig_kwargs: orig_kwargs['name'] = klass.__name__ if 'constructor' not in orig_kwargs: orig_kwargs[ 'constructor'] = klass.__init__ if '__init__' in klass.__dict__ else Mixed.__init__ Mixed = declarative.declarative_base(*orig_args, **orig_kwargs) Mixed.BaseClass = _SessionInitializer._base_classes[ klass.__module__] = Mixed Mixed.__tablename__ = declarative.declared_attr( lambda cls: _camelcase_to_underscore(cls.__name__)) return Mixed
def declarative_base(klass): class Mixed(klass, CrudMixin): pass Mixed = declarative.declarative_base(cls=Mixed) Mixed.BaseClass = _SessionInitializer.BaseClass = Mixed Mixed.__tablename__ = declarative.declared_attr(lambda cls: _camelcase_to_underscore(cls.__name__)) return Mixed
def create_descriptor(name): def get_attr(cls): if cls not in attrs: # Call func only once per class attrs[cls] = return_locals(func)() return attrs[cls][name] get_attr.__name__ = name return declared_attr(get_attr)
def foreign_key_rel_factory(model, nullable, primary_key, extra): return declarative.declared_attr( lambda self, model=model, extra=extra: sqla_orm.relationship( model, **extra ) )
class WithRbac: ''' Provides the with Rbac definition. ''' __table_args__ = dict(mysql_engine='InnoDB') rbacId = declared_attr( lambda cls: Column('fk_rbac_id', ForeignKey(Rbac.id), nullable=True))
def declarative_base(klass): class Mixed(klass, CrudMixin): pass constructor = {'constructor': klass.__init__} if '__init__' in klass.__dict__ else {} Mixed = declarative.declarative_base(cls=Mixed, **constructor) Mixed.BaseClass = _SessionInitializer._base_classes[klass.__module__] = Mixed Mixed.__tablename__ = declarative.declared_attr(lambda cls: _camelcase_to_underscore(cls.__name__)) return Mixed
def foreign_key_column_factory(model, nullable, primary_key, extra): return declarative.declared_attr( lambda self, model=model, nullable=nullable, primary_key=primary_key: sqla.Column( sqla.Integer, sqla.ForeignKey(f'{get_model(self, model).__tablename__}.id'), nullable=nullable, primary_key=primary_key ) )
def apply_table_and_mapper_args(cls, base, registry, namespace, bases, transformation_properties, properties): """ Create overwrite to define table and mapper args to define some options for SQLAlchemy :param registry: the current registry :param namespace: the namespace of the model :param base: One of the base of the model :param transformation_properties: the properties of the model :param properties: assembled attributes of the namespace """ table_args = tuple(properties['add_in_table_args']) if table_args: def define_table_args(cls_): if cls_.__registry_name__ == namespace: res = super(base, cls_).define_table_args() fks = [x.name for x in res if isinstance(x, ForeignKeyConstraint)] t_args = [x for x in table_args if (not isinstance(x, ForeignKeyConstraint) or x.name not in fks)] return res + tuple(t_args) return () base.define_table_args = classmethod(define_table_args) transformation_properties['table_args'] = True if transformation_properties['table_args']: def __table_args__(cls_): return cls_.define_table_args() base.__table_args__ = declared_attr(__table_args__) if transformation_properties['mapper_args']: def __mapper_args__(cls_): return cls_.define_mapper_args() base.__mapper_args__ = declared_attr(__mapper_args__)
class PropertyFilterDefinition: ''' Provides the Property to Filter definition. ''' __tablename__ = declared_attr( lambda cls: '%s_property_filter' % cls._acl_tablename) __table_args__ = dict(mysql_engine='InnoDB') aclAccessId = declared_attr(lambda cls: Column( 'fk_acl_access_id', ForeignKey('%s.id' % cls._acl_tablename, ondelete='CASCADE'), primary_key=True)) propertyId = declared_attr( lambda cls: Column('fk_property_id', ForeignKey(PropertyMapped.id, ondelete='CASCADE'), primary_key=True)) filterId = declared_attr( lambda cls: Column('fk_filter_id', ForeignKey(FilterMapped.id, ondelete='CASCADE'), primary_key=True))
def insert_in_bases_mapper_args(self, new_base, transformation_properties): """Add table __mapper_args__ in new_base :param new_base: the base to be put on front of all bases :param transformation_properties: the properties of the model """ if transformation_properties['mapper_args']: def __mapper_args__(cls_): return cls_.define_mapper_args() new_base.__mapper_args__ = declared_attr(__mapper_args__)
def many_to_many_factory(instance, secondary, attr_name): """ This method is responsible for creating required M2M objects. """ return declarative.declared_attr( lambda self, instance=instance, secondary=secondary: relationship( instance.model, secondary=secondary, **instance.extra ) )
def declarative_base(klass): class Mixed(klass, CrudMixin): pass constructor = { 'constructor': klass.__init__ } if '__init__' in klass.__dict__ else {} Mixed = declarative.declarative_base(cls=Mixed, **constructor) Mixed.BaseClass = _SessionInitializer._base_classes[ klass.__module__] = Mixed Mixed.__tablename__ = declarative.declared_attr( lambda cls: _camelcase_to_underscore(cls.__name__)) return Mixed
def datetime_mixin_factory(relation_name, fulltext_attr=None, api_attr=None): """Factory responsible for datetime mixin generation.""" def field_declaration(cls): # pylint: disable=no-self-argument return deferred(db.Column(db.DateTime, nullable=True), cls.__name__) return type( "{}_mixin".format(relation_name), (object, ), { relation_name: declared_attr(field_declaration), "_api_attrs": reflection.ApiAttributes(api_attr or relation_name), "fulltext_attr": [fulltext_attr or relation_name], }, )
class WithCategoryAction: ''' Provides the action category relation definition. ''' __table_args__ = dict(mysql_engine='InnoDB') actionPath = declared_attr( lambda cls: Column('fk_action_path', ForeignKey(ActionMapped.Path, ondelete='CASCADE'), primary_key=True)) @declared_attr def categoryId(cls): raise Exception('A category id definition is required') # @NoSelf
class WithAclAccess: ''' Provides the ACL Access relation definition. ''' # Table relations ---------------------------------------------- EntryFilter = EntryFilterDefinition PropertyFilter = PropertyFilterDefinition # Fixed attributes ---------------------------------------------- id = declared_attr( lambda cls: Column('id', INTEGER(unsigned=True), primary_key=True)) accessId = declared_attr(lambda cls: Column( 'fk_access_id', ForeignKey(AccessMapped.Id, ondelete='CASCADE'))) # Declared attributes ------------------------------------------- @declared_attr def aclId(cls): raise Exception('An acl id definition is required') # @NoSelf @declared_attr def __table_args__(cls): # @NoSelf return (UniqueConstraint(cls.aclId, cls.accessId, name='uix_%s' % cls.__tablename__), dict(mysql_engine='InnoDB')) @declared_attr def entriesFilters(cls): # @NoSelf cls.EntryFilter = type('%sEntryFilter' % cls.__name__, (cls.__bases__[0], EntryFilterDefinition), dict(_acl_tablename=cls.__tablename__)) return relationship(cls.EntryFilter, cascade='delete') @declared_attr def propertiesFilters(cls): # @NoSelf cls.PropertyFilter = type('%sPropertyFilter' % cls.__name__, (cls.__bases__[0], PropertyFilterDefinition), dict(_acl_tablename=cls.__tablename__)) return relationship(cls.PropertyFilter, cascade='delete')
def declare_field(cls, registry, name, field, namespace, properties, transformation_properties): """ Declare the field/column/relationship to put in the properties of the model :param registry: the current registry :param name: name of the field / column or relationship :param field: the declaration field / column or relationship :param namespace: the namespace of the model :param properties: the properties of the model """ if name in properties['loaded_columns']: return if field.must_be_duplicate_before_added(): field = deepcopy(field) attr_name = name if field.use_hybrid_property: attr_name = anyblok_column_prefix + name if field.must_be_declared_as_attr(): # All the declaration are seen as mixin for sqlalchemy # some of them need de be defered for the initialisation # cause of the mixin as relation ship and column with foreign key def wrapper(cls): return field.get_sqlalchemy_mapping( registry, namespace, name, properties) properties[attr_name] = declared_attr(wrapper) properties[attr_name].anyblok_field = field else: properties[attr_name] = field.get_sqlalchemy_mapping( registry, namespace, name, properties) if field.use_hybrid_property: properties[name] = field.get_property( registry, namespace, name, properties) properties['hybrid_property_columns'].append(name) registry.call_plugins('declare_field', name, field, namespace, properties, transformation_properties) properties['loaded_columns'].append(name) field.update_properties(registry, namespace, name, properties)
def declare_field(cls, registry, name, field, namespace, properties, transformation_properties): """ Declare the field/column/relationship to put in the properties of the model :param registry: the current registry :param name: name of the field / column or relationship :param field: the declaration field / column or relationship :param namespace: the namespace of the model :param properties: the properties of the model """ if name in properties['loaded_columns']: return if field.must_be_duplicate_before_added(): field = deepcopy(field) attr_name = name if field.use_hybrid_property: attr_name = anyblok_column_prefix + name if field.must_be_declared_as_attr(): # All the declaration are seen as mixin for sqlalchemy # some of them need de be defered for the initialisation # cause of the mixin as relation ship and column with foreign key def wrapper(cls): return field.get_sqlalchemy_mapping(registry, namespace, name, properties) properties[attr_name] = declared_attr(wrapper) properties[attr_name].anyblok_field = field else: properties[attr_name] = field.get_sqlalchemy_mapping( registry, namespace, name, properties) if field.use_hybrid_property: properties[name] = field.get_property(registry, namespace, name, properties) properties['hybrid_property_columns'].append(name) registry.call_plugins('declare_field', name, field, namespace, properties, transformation_properties) properties['loaded_columns'].append(name) field.update_properties(registry, namespace, name, properties)
def create_column(self, cname, remote_type, foreign_key, properties): def wrapper(cls): return SA_Column( cname.attribute_name, remote_type, nullable=self.nullable, unique=self.unique, info=dict(label=self.label, foreign_key=foreign_key)) properties[(anyblok_column_prefix + cname.attribute_name)] = declared_attr(wrapper) properties['loaded_columns'].append(cname.attribute_name) properties['hybrid_property_columns'].append(cname.attribute_name) properties[cname.attribute_name] = hybrid_property( self.wrap_getter_column(cname.attribute_name), super(Many2One, self).wrap_setter_column(cname.attribute_name), expr=self.wrap_expr_column(cname.attribute_name))
class ImageDataDefinition: ''' Provides the mapping for ImageData definition. ''' __tablename__ = 'archive_image_data' __table_args__ = dict(mysql_engine='InnoDB', mysql_charset='utf8') Id = declared_attr(lambda cls: Column('fk_metadata_id', ForeignKey(MetaDataMapped.Id, ondelete='CASCADE'), primary_key=True)) Width = declared_attr(lambda cls: Column('width', Integer)) Height = declared_attr(lambda cls: Column('height', Integer)) CreationDate = declared_attr(lambda cls: Column('creation_date', DateTime)) CameraMake = declared_attr(lambda cls: Column('camera_make', String(255))) CameraModel = declared_attr(lambda cls: Column('camera_model', String(255)))
def __init__(self, session, model_class=Model, query_class=Query, persistent_cache=False): session.configure(query_cls=query_class) self.session = session self._registry = {} self.Model = declarative_base(cls=Model, class_registry=self._registry) self.Model.q = _QueryProperty(session) self.Model.t = _TableProperty(session) if persistent_cache: def __cache__(cls): return Column(JSONEncodedDict) self.Model.__cache__ = declared_attr(__cache__) self.backref = partial(_backref, query_class=query_class) self.relationship = partial(_relationship, query_class=query_class)
def create_column(self, cname, remote_type, foreign_key, properties): def wrapper(cls): return SA_Column(cname.attribute_name, remote_type, nullable=self.nullable, unique=self.unique, index=self.index, primary_key=self.primary_key, info=dict(label=self.label, foreign_key=foreign_key)) properties[(anyblok_column_prefix + cname.attribute_name)] = declared_attr(wrapper) properties['loaded_columns'].append(cname.attribute_name) properties['hybrid_property_columns'].append(cname.attribute_name) properties[cname.attribute_name] = hybrid_property( self.wrap_getter_column(cname.attribute_name), super(Many2One, self).wrap_setter_column(cname.attribute_name), expr=self.wrap_expr_column(cname.attribute_name))
def declared(name, c): def fn(self): return c fn.__name__ = name return declared_attr(fn)
setattr(self, k, v) return self def add(self): """save object to database""" db.session.add(self) return self def save(self): """save object to database""" db.session.add(self) db.session.commit() return self ForeignColumn = lambda *args, **kwargs: sad.declared_attr( lambda _: db.Column(*args, **kwargs)) ############# # CONTRACTS # ############# class Graph(Base): """abstract for a graph""" __tablename__ = 'graph' name = db.Column(db.Text) directed = db.Column(db.Boolean) vertices = db.relationship('Vertex', backref='graph')
def __init__(cls, name, bases, dct): if "modified" in dct: event.listen(cls,'before_update',update_modified) if "id" not in dct: # the inherited class already has an 'id' column, but that ends # up being ambiguous and thus won't work dct['id'] = cls.id = db.Column(db.Integer, primary_key=True) cls._refs = [] cls.__table_args__ = list(dct.get('__table_args__',[])) # collect indices for foreign-key tables here # Now walk through all our (base) classes. # This is necessary to collect ObjectRef entries in subclasses seen = set() _alias = cls._alias for ccls in cls.__mro__: for k,v in ccls.__dict__.items(): if k == "_alias": # merge _alias content for kk,vv in v.items(): _alias.setdefault(kk,vv) continue if k.startswith('_'): continue if k in seen: continue seen.add(k) if ObjectRef is not None and isinstance(v,ObjectRef): if v.typ is None: ## any table from .objtyp import ObjType ## Create a new composite. if v.declared_attr: col_typ = declared_attr((lambda k,v: lambda cls: Column(k+'_typ_id',db.Integer, db.ForeignKey(ObjType.id),nullable=v.nullable, doc=v.doc, unique=v.unique))(k,v)) col_id = declared_attr((lambda k,v: lambda cls: Column(k+'_id',db.Integer, nullable=v.nullable))(k,v)) else: col_typ = db.Column(k+'_typ_id',db.Integer, db.ForeignKey(ObjType.id),nullable=v.nullable, doc=v.doc, unique=v.unique) col_id = db.Column(k+'_id',db.Integer, nullable=v.nullable) setattr(cls,k+"_typ_id", col_typ) setattr(cls,k+"_id", col_id) if v.declared_attr: setattr(cls,k, declared_attr((lambda col_typ,col_id: lambda cls: composite(ObjectRef, col_typ,col_id, deferred=True))(col_typ,col_id))) else: setattr(cls,k, composite(ObjRefComposer, col_typ,col_id, deferred=True)) _refs.append((cls,k)) cls.__table_args__.append(Index("i_%s_%s"%(name,k),col_typ,col_id)) else: ## specific table rem = {'lazy': v.lazy} if v.backref: rem['back_populates'] = v.backref if isinstance(v.typ,(int,long)): v.typ = ObjType.get_mod(v.typ) v.typ_id = v.typ.id elif isinstance(v.typ,string_types): if v.typ == "self": col_typ = db.Column(k+'_id',db.Integer, db.ForeignKey(cls.__tablename__+'.id'),nullable=v.nullable, doc=v.doc, unique=v.unique) col_ref = db.relationship(cls, remote_side=[cls.id], foreign_keys=(col_typ,), **rem) setattr(cls,k+"_id", col_typ) setattr(cls,k, col_ref) cls._refs.append((cls,k)) cls.__table_args__.append(Index("i_%s_%s"%(name,k),col_typ)) if v.backref: setattr(cls,v.backref, db.relationship(cls, primaryjoin = col_typ==cls.id, back_populates=k, uselist=not v.unique)) continue else: v.typ = ObjType.q.get_by(path=v.typ).mod v.typ_id = v.typ.id else: v.typ_id = v.typ.id rem['remote_side'] = v.typ.__name__+'.id' if v.declared_attr: col_typ = declared_attr((lambda k,v: lambda cls: Column(k+'_id',db.Integer, db.ForeignKey(v.typ_id),nullable=v.nullable, doc=v.doc, unique=v.unique))(k,v)) col_ref = declared_attr((lambda k,v,r: lambda cls: db.relationship(v.typ, primaryjoin = col_typ==v.typ_id, **r))(k,v,rem)) else: col_typ = db.Column(k+'_id',db.Integer, db.ForeignKey(v.typ_id),nullable=v.nullable, doc=v.doc, unique=v.unique) col_ref = db.relationship(v.typ, primaryjoin = col_typ==v.typ_id, **rem) setattr(cls,k+"_id", col_typ) setattr(cls,k, col_ref) v.typ._refs.append((cls,k)) cls.__table_args__.append(Index("i_%s_%s"%(name,k),col_typ)) if v.backref: if isinstance(v.typ,string_types): setattr(v.typ,v.backref, db.relationship(cls, primaryjoin = "%s_id==%s" % (k,v.typ_id), back_populates=k)) else: setattr(v.typ,v.backref, db.relationship(cls, primaryjoin = col_typ==v.typ.id, back_populates=k, uselist=not v.unique)) elif k == 'modified': event.listen(cls,'before_update',update_modified) _tables.append(cls) cls_ = cls cls.__table_args__ = tuple(cls.__table_args__) class serializer(_serialize_object): cls = cls_ clsname = cls_.__module__+'.'+cls_.__name__ json_adapter(serializer) setup_events(cls) super(ObjectMeta, cls).__init__(name, bases, dct)
def __new__(meta, classname, bases, classDict): """ """ # schemafp = os.path.abspath('schema.yaml') # sch = get_lexical_tokens('schema.yaml') \ # if os.path.exists('schema.yaml') \ # else None schemafp = os.path.abspath('domain.plr') sch = get_lexical_tokens('domain.plr') \ if os.path.exists('domain.plr') \ else None if not sch: # using a shared state config # will only work if its been initialised # before this point in execution sch = dinj.ModelConfig() table_name_plural, table_name_standard = \ create_default_tablename(classname) classDict['__domain__'] = sch classDict['__tablename__'] = table_name_plural classDict['__pkname__'] = '%s_id'%table_name_standard classDict['__relationships__'] = {} # TODO: refactor a bit prettier classDict['__pyaella_args__'] = {} if sch and classname in sch: classDict['__schema__'] = sch[classname] sch_lex = classDict['__schema__'] members = {} if 'Fields' in sch_lex: if type(sch_lex['Fields']) == list: # use fields as slots classDict['__slots__'] = \ tuple(sch_lex['Fields']) else: # load fields from schema definition if(type(sch_lex['Fields']) == dict or isinstance(sch_lex, LexicalToken)): members = dict([ (k,eval(str(v)),) for k,v in sch_lex['Fields'].items() if k not in RSRVD_CLS_METH_NAMES and k not in RSRVD_OBJ_METH_NAMES and k not in RSRVD_PYAELLA_NAMES ]) # deprecated GeoAlchemy 0.6 support for k,v in sch_lex['Fields'].items(): if 'GeometryColumn' in v: classDict['__geocolumns__'] = True members[k] = declared_attr( make_f(k, v)) if 'EntityMixes' in sch_lex: em = sch_lex['EntityMixes'] if type(em) == list: for s in em: rindx, lindx = s.rindex('.'), s.index('.') if rindx != lindx: mn, clsn = s[:rindx], s[rindx+1:] mod = None try: mod = __import__(mn, fromlist=[mn]) except: print 'WARNING. Mix module threw exception on __import__', traceback.format_exc() mix_cls = mod.__dict__[clsn] classDict['__pyaella_args__'] \ .setdefault('entity_mixes', []).append(mix_cls) if 'Relations' in sch_lex: if(type(sch_lex['Relations']) == dict or isinstance(sch_lex, LexicalToken)): for attr, attrval in \ sch_lex['Relations'].items(): if attrval.startswith('relationship'): classDict['__relationships__'][attr] = attrval members[attr] = declared_attr(make_f(attr, attrval)) else: members[attr] = declared_attr( make_f(attr, attrval)) if 'Rules' in sch_lex: if 'Unique' in sch_lex.Rules: classDict['__unique_fields__'] = \ sch_lex.Rules.Unique # TODO: more inheritence support someday? proxy_base = (object,) classDict['__clz_proxy__'] = classobj( classname+'Proxy', proxy_base, members) return type.__new__( meta, classname, #(bases[-1],) if bases else (), # only use last base bases, classDict )