Repository: incubator-ariatosca Updated Branches: refs/heads/ARIA-105-integrate-modeling d3b487366 -> b4b1127ec
Move relationship utilities to be global functions Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/b4b1127e Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/b4b1127e Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/b4b1127e Branch: refs/heads/ARIA-105-integrate-modeling Commit: b4b1127ec1672263c210d97a7c29f52c2fbbbe0f Parents: d3b4873 Author: Tal Liron <[email protected]> Authored: Thu Mar 16 12:53:05 2017 -0500 Committer: Tal Liron <[email protected]> Committed: Thu Mar 16 12:53:05 2017 -0500 ---------------------------------------------------------------------- aria/modeling/mixins.py | 376 ------------------------------ aria/modeling/orchestration.py | 29 +-- aria/modeling/relationships.py | 402 +++++++++++++++++++++++++++++++++ aria/modeling/service_changes.py | 23 +- aria/modeling/service_common.py | 13 +- aria/modeling/service_instance.py | 194 ++++++++-------- aria/modeling/service_template.py | 186 +++++++-------- 7 files changed, 623 insertions(+), 600 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b4b1127e/aria/modeling/mixins.py ---------------------------------------------------------------------- diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py index 06f2497..8eb08e8 100644 --- a/aria/modeling/mixins.py +++ b/aria/modeling/mixins.py @@ -13,32 +13,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -# pylint: disable=invalid-name - """ -ARIA's storage.structures module -Path: aria.storage.structures - -models module holds ARIA's models. - classes: * ModelMixin - abstract model implementation. * ModelIDMixin - abstract model implementation with IDs. """ -from sqlalchemy.orm import relationship, backref -from sqlalchemy.orm.collections import attribute_mapped_collection from sqlalchemy.ext import associationproxy from sqlalchemy import ( Column, - ForeignKey, Integer, Text, - Table, ) from .utils import classproperty -from ..utils import formatting class ModelMixin(object): @@ -107,370 +95,6 @@ class ModelMixin(object): cls=self.__class__.__name__, id=getattr(self, self.name_column_name())) - # Model property declaration helpers - - @classmethod - def _declare_fk(cls, - other_table, - nullable=False): - """ - Declare a foreign key property, which will also create a foreign key column in the table - with the name of the property. By convention the property name should end in "_fk". - - You are required to explicitly create foreign keys in order to allow for one-to-one, - one-to-many, and many-to-one relationships (but not for many-to-many relationships). If you - do not do so, SqlAlchemy will fail to create the relationship property and raise an - exception with a clear error message. - - You should normally not have to access this property directly, but instead use the - associated relationship properties. - - *This utility method should only be used during class creation.* - - :param other_table: Other table name - :type other_table: basestring - :param nullable: True to allow null values (meaning that there is no relationship) - :type nullable: bool - """ - - return Column(Integer, - ForeignKey('{table}.id'.format(table=other_table), ondelete='CASCADE'), - nullable=nullable) - - @classmethod - def _declare_one_to_one_self(cls, - fk, - relationship_kwargs=None): - """ - Declare a one-to-one relationship property. The property value would be an instance of - the same model. - - You will need an associated foreign key to our own table. - - *This utility method should only be used during class creation.* - - :param fk: Foreign key name - :type fk: basestring - :param relationship_kwargs: Extra kwargs for SqlAlchemy `relationship` - :type relationship_kwargs: {} - """ - - relationship_kwargs = relationship_kwargs or {} - - remote_side = '{cls}.{remote_column}'.format( - cls=cls.__name__, - remote_column=cls.id_column_name() - ) - - primaryjoin = '{remote_side} == {cls}.{column}'.format( - remote_side=remote_side, - cls=cls.__name__, - column=fk - ) - - return relationship( - cls._get_class_for_table(cls.__tablename__).__name__, - primaryjoin=primaryjoin, - remote_side=remote_side, - post_update=True, - **relationship_kwargs - ) - - @classmethod - def _declare_one_to_many_self(cls, - fk, - dict_key=None, - relationship_kwargs=None): - """ - Declare a one-to-many relationship property. The property value would be a list or dict - of instances of the same model. - - You will need an associated foreign key to our own table. - - *This utility method should only be used during class creation.* - - :param fk: Foreign key name - :type fk: basestring - :param dict_key: If set the value will be a dict with this key as the dict key; otherwise - will be a list - :type dict_key: basestring - :param relationship_kwargs: Extra kwargs for SqlAlchemy `relationship` - :type relationship_kwargs: {} - """ - - relationship_kwargs = relationship_kwargs or {} - - relationship_kwargs.setdefault('remote_side', '{cls}.{remote_column}'.format( - cls=cls.__name__, - remote_column=fk - )) - - return cls._declare_relationship(cls.__tablename__, None, relationship_kwargs, - other_property=False, dict_key=dict_key) - - @classmethod - def _declare_one_to_one(cls, - other_table, - fk=None, - other_fk=None, - other_property=None, - relationship_kwargs=None, - backref_kwargs=None): - """ - Declare a one-to-one relationship property. The property value would be an instance of - the other table's model. - - You have two options for the foreign key. Either this table can have an associated key to - the other table (use the `fk` argument) or the other table can have an associated foreign - key to this our table (use the `other_fk` argument). - - *This utility method should only be used during class creation.* - - :param other_table: Other table name - :type other_table: basestring - :param fk: Foreign key name at our table (no need specify if there's no ambiguity) - :type fk: basestring - :param other_fk: Foreign key name at the other table (no need specify if there's no - ambiguity) - :type other_fk: basestring - :param relationship_kwargs: Extra kwargs for SqlAlchemy `relationship` - :type relationship_kwargs: {} - :param backref_kwargs: Extra kwargs for SqlAlchemy `backref` - :type backref_kwargs: {} - """ - - backref_kwargs = backref_kwargs or {} - backref_kwargs.setdefault('uselist', False) - - return cls._declare_relationship(other_table, backref_kwargs, relationship_kwargs, - other_property, fk=fk, other_fk=other_fk) - - @classmethod - def _declare_one_to_many(cls, - child_table, - child_fk=None, - dict_key=None, - child_property=None, - relationship_kwargs=None, - backref_kwargs=None): - """ - Declare a one-to-many relationship property. The property value would be a list or dict - of instances of the child table's model. - - The child table will need an associated foreign key to our table. - - The declaration will automatically create a matching many-to-one property at the child - model, named after our table name. Use the `child_property` argument to override this name. - - *This utility method should only be used during class creation.* - - :param child_table: Child table name - :type child_table: basestring - :param child_fk: Foreign key name at the child table (no need specify if there's no - ambiguity) - :type child_fk: basestring - :param dict_key: If set the value will be a dict with this key as the dict key; otherwise - will be a list - :type dict_key: basestring - :param child_property: Override name of matching many-to-one property at child table; set to - false to disable - :type child_property: basestring|bool - :param relationship_kwargs: Extra kwargs for SqlAlchemy `relationship` - :type relationship_kwargs: {} - :param backref_kwargs: Extra kwargs for SqlAlchemy `backref` - :type backref_kwargs: {} - """ - - backref_kwargs = backref_kwargs or {} - backref_kwargs.setdefault('uselist', False) - - return cls._declare_relationship(child_table, backref_kwargs, relationship_kwargs, - child_property, other_fk=child_fk, dict_key=dict_key) - - @classmethod - def _declare_many_to_one(cls, - parent_table, - fk=None, - parent_fk=None, - parent_property=None, - relationship_kwargs=None, - backref_kwargs=None): - """ - Declare a many-to-one relationship property. The property value would be an instance of - the parent table's model. - - You will need an associated foreign key to the parent table. - - The declaration will automatically create a matching one-to-many property at the child - model, named after the plural form of our table name. Use the `parent_property` argument to - override this name. Note: the automatic property will always be a SqlAlchemy query object; - if you need a Python collection then use :meth:`_declare_one_to_many` at that model. - - *This utility method should only be used during class creation.* - - :param parent_table: Parent table name - :type parent_table: basestring - :param fk: Foreign key name at our table (no need specify if there's no ambiguity) - :type fk: basestring - :param parent_property: Override name of matching one-to-many property at parent table; set - to false to disable - :type parent_property: basestring|bool - :param relationship_kwargs: Extra kwargs for SqlAlchemy `relationship` - :type relationship_kwargs: {} - :param backref_kwargs: Extra kwargs for SqlAlchemy `backref` - :type backref_kwargs: {} - """ - - if parent_property is None: - parent_property = formatting.pluralize(cls.__tablename__) - - backref_kwargs = backref_kwargs or {} - backref_kwargs.setdefault('uselist', True) - backref_kwargs.setdefault('lazy', 'dynamic') - backref_kwargs.setdefault('cascade', 'all') # delete children when parent is deleted - - return cls._declare_relationship(parent_table, backref_kwargs, relationship_kwargs, - parent_property, fk=fk, other_fk=parent_fk) - - @classmethod - def _declare_many_to_many(cls, - other_table, - prefix=None, - dict_key=None, - other_property=None, - relationship_kwargs=None, - backref_kwargs=None): - """ - Declare a many-to-many relationship property. The property value would be a list or dict - of instances of the other table's model. - - You do not need associated foreign keys for this relationship. Instead, an extra table will - be created for you. - - The declaration will automatically create a matching many-to-many property at the other - model, named after the plural form of our table name. Use the `other_property` argument to - override this name. Note: the automatic property will always be a SqlAlchemy query object; - if you need a Python collection then use :meth:`_declare_many_to_many` again at that model. - - *This utility method should only be used during class creation.* - - :param parent_table: Parent table name - :type parent_table: basestring - :param prefix: Optional prefix for extra table name as well as for `other_property` - :type prefix: basestring - :param dict_key: If set the value will be a dict with this key as the dict key; otherwise - will be a list - :type dict_key: basestring - :param other_property: Override name of matching many-to-many property at other table; set - to false to disable - :type other_property: basestring|bool - :param relationship_kwargs: Extra kwargs for SqlAlchemy `relationship` - :type relationship_kwargs: {} - :param backref_kwargs: Extra kwargs for SqlAlchemy `backref` - :type backref_kwargs: {} - """ - - this_table = cls.__tablename__ - this_column_name = '{0}_id'.format(this_table) - this_foreign_key = '{0}.id'.format(this_table) - - other_column_name = '{0}_id'.format(other_table) - other_foreign_key = '{0}.id'.format(other_table) - - secondary_table = '{0}_{1}'.format(this_table, other_table) - - if other_property is None: - other_property = formatting.pluralize(this_table) - if prefix is not None: - secondary_table = '{0}_{1}'.format(prefix, secondary_table) - other_property = '{0}_{1}'.format(prefix, other_property) - - backref_kwargs = backref_kwargs or {} - backref_kwargs.setdefault('uselist', True) - - relationship_kwargs = relationship_kwargs or {} - relationship_kwargs.setdefault('secondary', ModelMixin._get_secondary_table( - cls.metadata, - secondary_table, - this_column_name, - other_column_name, - this_foreign_key, - other_foreign_key - )) - - return cls._declare_relationship(other_table, backref_kwargs, relationship_kwargs, - other_property, dict_key=dict_key) - - @classmethod - def _declare_relationship(cls, other_table, backref_kwargs, relationship_kwargs, - other_property, fk=None, other_fk=None, dict_key=None): - relationship_kwargs = relationship_kwargs or {} - - if fk: - relationship_kwargs.setdefault('foreign_keys', - lambda: getattr( - cls._get_class_for_table(cls.__tablename__), - fk)) - - elif other_fk: - relationship_kwargs.setdefault('foreign_keys', - lambda: getattr( - cls._get_class_for_table(other_table), - other_fk)) - - if dict_key: - relationship_kwargs.setdefault('collection_class', - attribute_mapped_collection(dict_key)) - - if other_property is False: - # No backref - return relationship( - lambda: cls._get_class_for_table(other_table), - **relationship_kwargs - ) - else: - if other_property is None: - other_property = cls.__tablename__ - backref_kwargs = backref_kwargs or {} - return relationship( - lambda: cls._get_class_for_table(other_table), - backref=backref(other_property, **backref_kwargs), - **relationship_kwargs - ) - - @classmethod - def _get_class_for_table(cls, tablename): - if tablename in (cls.__name__, cls.__tablename__): - return cls - - for table_cls in cls._decl_class_registry.values(): - if tablename == getattr(table_cls, '__tablename__', None): - return table_cls - - raise ValueError('unknown table: {0}'.format(tablename)) - - @staticmethod - def _get_secondary_table(metadata, - name, - first_column, - second_column, - first_foreign_key, - second_foreign_key): - return Table( - name, - metadata, - Column( - first_column, - Integer, - ForeignKey(first_foreign_key) - ), - Column( - second_column, - Integer, - ForeignKey(second_foreign_key) - ) - ) - class ModelIDMixin(object): id = Column(Integer, primary_key=True, autoincrement=True) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b4b1127e/aria/modeling/orchestration.py ---------------------------------------------------------------------- diff --git a/aria/modeling/orchestration.py b/aria/modeling/orchestration.py index 683c526..c5e550a 100644 --- a/aria/modeling/orchestration.py +++ b/aria/modeling/orchestration.py @@ -41,12 +41,7 @@ from sqlalchemy.ext.declarative import declared_attr from ..orchestrator.exceptions import (TaskAbortException, TaskRetryException) from .types import (List, Dict) from .mixins import ModelMixin - -__all__ = ( - 'ExecutionBase', - 'PluginBase', - 'TaskBase' -) +from . import relationships class ExecutionBase(ModelMixin): @@ -101,13 +96,13 @@ class ExecutionBase(ModelMixin): @declared_attr def service(cls): - return cls._declare_many_to_one('service') + return relationships.many_to_one(cls, 'service') # region foreign keys @declared_attr def service_fk(cls): - return cls._declare_fk('service') + return relationships.fk('service') # endregion @@ -197,23 +192,23 @@ class TaskBase(ModelMixin): @declared_attr def node(cls): - return cls._declare_many_to_one('node') + return relationships.many_to_one(cls, 'node') @declared_attr def relationship(cls): - return cls._declare_many_to_one('relationship') + return relationships.many_to_one(cls, 'relationship') @declared_attr def plugin(cls): - return cls._declare_many_to_one('plugin') + return relationships.many_to_one(cls, 'plugin') @declared_attr def execution(cls): - return cls._declare_many_to_one('execution') + return relationships.many_to_one(cls, 'execution') @declared_attr def inputs(cls): - return cls._declare_many_to_many('parameter', prefix='inputs', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') status = Column(Enum(*STATES, name='status'), default=PENDING) @@ -259,19 +254,19 @@ class TaskBase(ModelMixin): @declared_attr def node_fk(cls): - return cls._declare_fk('node', nullable=True) + return relationships.fk('node', nullable=True) @declared_attr def relationship_fk(cls): - return cls._declare_fk('relationship', nullable=True) + return relationships.fk('relationship', nullable=True) @declared_attr def plugin_fk(cls): - return cls._declare_fk('plugin', nullable=True) + return relationships.fk('plugin', nullable=True) @declared_attr def execution_fk(cls): - return cls._declare_fk('execution', nullable=True) + return relationships.fk('execution', nullable=True) # endregion http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b4b1127e/aria/modeling/relationships.py ---------------------------------------------------------------------- diff --git a/aria/modeling/relationships.py b/aria/modeling/relationships.py new file mode 100644 index 0000000..140b9f1 --- /dev/null +++ b/aria/modeling/relationships.py @@ -0,0 +1,402 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# pylint: disable=invalid-name, redefined-outer-name + +from sqlalchemy.orm import relationship, backref +from sqlalchemy.orm.collections import attribute_mapped_collection +from sqlalchemy import ( + Column, + ForeignKey, + Integer, + Table +) + +from ..utils import formatting + + +def fk(other_table, + nullable=False): + """ + Declare a foreign key property, which will also create a foreign key column in the table with + the name of the property. By convention the property name should end in "_fk". + + You are required to explicitly create foreign keys in order to allow for one-to-one, + one-to-many, and many-to-one relationships (but not for many-to-many relationships). If you do + not do so, SqlAlchemy will fail to create the relationship property and raise an exception with + a clear error message. + + You should normally not have to access this property directly, but instead use the associated + relationship properties. + + *This utility method should only be used during class creation.* + + :param other_table: Other table name + :type other_table: basestring + :param nullable: True to allow null values (meaning that there is no relationship) + :type nullable: bool + """ + + return Column(Integer, + ForeignKey('{table}.id'.format(table=other_table), ondelete='CASCADE'), + nullable=nullable) + + +def one_to_one_self(model_class, + fk, + relationship_kwargs=None): + """ + Declare a one-to-one relationship property. The property value would be an instance of the same + model. + + You will need an associated foreign key to our own table. + + *This utility method should only be used during class creation.* + + :param model_class: The class in which this relationship will be declared + :type model_class: type + :param fk: Foreign key name + :type fk: basestring + :param relationship_kwargs: Extra kwargs for SqlAlchemy `relationship` + :type relationship_kwargs: {} + """ + + relationship_kwargs = relationship_kwargs or {} + + remote_side = '{model_class}.{remote_column}'.format( + model_class=model_class.__name__, + remote_column=model_class.id_column_name() + ) + + primaryjoin = '{remote_side} == {model_class}.{column}'.format( + remote_side=remote_side, + model_class=model_class.__name__, + column=fk + ) + + return relationship( + _get_class_for_table(model_class, model_class.__tablename__).__name__, + primaryjoin=primaryjoin, + remote_side=remote_side, + post_update=True, + **relationship_kwargs + ) + + +def one_to_many_self(model_class, + fk, + dict_key=None, + relationship_kwargs=None): + """ + Declare a one-to-many relationship property. The property value would be a list or dict of + instances of the same model. + + You will need an associated foreign key to our own table. + + *This utility method should only be used during class creation.* + + :param model_class: The class in which this relationship will be declared + :type model_class: type + :param fk: Foreign key name + :type fk: basestring + :param dict_key: If set the value will be a dict with this key as the dict key; otherwise will + be a list + :type dict_key: basestring + :param relationship_kwargs: Extra kwargs for SqlAlchemy `relationship` + :type relationship_kwargs: {} + """ + + relationship_kwargs = relationship_kwargs or {} + + relationship_kwargs.setdefault('remote_side', '{model_class}.{remote_column}'.format( + model_class=model_class.__name__, + remote_column=fk + )) + + return _relationship(model_class, model_class.__tablename__, None, relationship_kwargs, + other_property=False, dict_key=dict_key) + + +def one_to_one(model_class, + other_table, + fk=None, + other_fk=None, + other_property=None, + relationship_kwargs=None, + backref_kwargs=None): + """ + Declare a one-to-one relationship property. The property value would be an instance of the other + table's model. + + You have two options for the foreign key. Either this table can have an associated key to the + other table (use the `fk` argument) or the other table can have an associated foreign key to + this our table (use the `other_fk` argument). + + *This utility method should only be used during class creation.* + + :param model_class: The class in which this relationship will be declared + :type model_class: type + :param other_table: Other table name + :type other_table: basestring + :param fk: Foreign key name at our table (no need specify if there's no ambiguity) + :type fk: basestring + :param other_fk: Foreign key name at the other table (no need specify if there's no ambiguity) + :type other_fk: basestring + :param relationship_kwargs: Extra kwargs for SqlAlchemy `relationship` + :type relationship_kwargs: {} + :param backref_kwargs: Extra kwargs for SqlAlchemy `backref` + :type backref_kwargs: {} + """ + + backref_kwargs = backref_kwargs or {} + backref_kwargs.setdefault('uselist', False) + + return _relationship(model_class, other_table, backref_kwargs, relationship_kwargs, + other_property, fk=fk, other_fk=other_fk) + + +def one_to_many(model_class, + child_table, + child_fk=None, + dict_key=None, + child_property=None, + relationship_kwargs=None, + backref_kwargs=None): + """ + Declare a one-to-many relationship property. The property value would be a list or dict of + instances of the child table's model. + + The child table will need an associated foreign key to our table. + + The declaration will automatically create a matching many-to-one property at the child model, + named after our table name. Use the `child_property` argument to override this name. + + *This utility method should only be used during class creation.* + + :param model_class: The class in which this relationship will be declared + :type model_class: type + :param child_table: Child table name + :type child_table: basestring + :param child_fk: Foreign key name at the child table (no need specify if there's no ambiguity) + :type child_fk: basestring + :param dict_key: If set the value will be a dict with this key as the dict key; otherwise will + be a list + :type dict_key: basestring + :param child_property: Override name of matching many-to-one property at child table; set to + false to disable + :type child_property: basestring|bool + :param relationship_kwargs: Extra kwargs for SqlAlchemy `relationship` + :type relationship_kwargs: {} + :param backref_kwargs: Extra kwargs for SqlAlchemy `backref` + :type backref_kwargs: {} + """ + + backref_kwargs = backref_kwargs or {} + backref_kwargs.setdefault('uselist', False) + + return _relationship(model_class, child_table, backref_kwargs, relationship_kwargs, + child_property, other_fk=child_fk, dict_key=dict_key) + + +def many_to_one(model_class, + parent_table, + fk=None, + parent_fk=None, + parent_property=None, + relationship_kwargs=None, + backref_kwargs=None): + """ + Declare a many-to-one relationship property. The property value would be an instance of the + parent table's model. + + You will need an associated foreign key to the parent table. + + The declaration will automatically create a matching one-to-many property at the child model, + named after the plural form of our table name. Use the `parent_property` argument to override + this name. Note: the automatic property will always be a SqlAlchemy query object; if you need a + Python collection then use :meth:`one_to_many` at that model. + + *This utility method should only be used during class creation.* + + :param model_class: The class in which this relationship will be declared + :type model_class: type + :param parent_table: Parent table name + :type parent_table: basestring + :param fk: Foreign key name at our table (no need specify if there's no ambiguity) + :type fk: basestring + :param parent_property: Override name of matching one-to-many property at parent table; set to + false to disable + :type parent_property: basestring|bool + :param relationship_kwargs: Extra kwargs for SqlAlchemy `relationship` + :type relationship_kwargs: {} + :param backref_kwargs: Extra kwargs for SqlAlchemy `backref` + :type backref_kwargs: {} + """ + + if parent_property is None: + parent_property = formatting.pluralize(model_class.__tablename__) + + backref_kwargs = backref_kwargs or {} + backref_kwargs.setdefault('uselist', True) + backref_kwargs.setdefault('lazy', 'dynamic') + backref_kwargs.setdefault('cascade', 'all') # delete children when parent is deleted + + return _relationship(model_class, parent_table, backref_kwargs, relationship_kwargs, + parent_property, fk=fk, other_fk=parent_fk) + + +def many_to_many(model_class, + other_table, + prefix=None, + dict_key=None, + other_property=None, + relationship_kwargs=None, + backref_kwargs=None): + """ + Declare a many-to-many relationship property. The property value would be a list or dict of + instances of the other table's model. + + You do not need associated foreign keys for this relationship. Instead, an extra table will be + created for you. + + The declaration will automatically create a matching many-to-many property at the other model, + named after the plural form of our table name. Use the `other_property` argument to override + this name. Note: the automatic property will always be a SqlAlchemy query object; if you need a + Python collection then use :meth:`many_to_many` again at that model. + + *This utility method should only be used during class creation.* + + :param model_class: The class in which this relationship will be declared + :type model_class: type + :param parent_table: Parent table name + :type parent_table: basestring + :param prefix: Optional prefix for extra table name as well as for `other_property` + :type prefix: basestring + :param dict_key: If set the value will be a dict with this key as the dict key; otherwise will + be a list + :type dict_key: basestring + :param other_property: Override name of matching many-to-many property at other table; set to + false to disable + :type other_property: basestring|bool + :param relationship_kwargs: Extra kwargs for SqlAlchemy `relationship` + :type relationship_kwargs: {} + :param backref_kwargs: Extra kwargs for SqlAlchemy `backref` + :type backref_kwargs: {} + """ + + this_table = model_class.__tablename__ + this_column_name = '{0}_id'.format(this_table) + this_foreign_key = '{0}.id'.format(this_table) + + other_column_name = '{0}_id'.format(other_table) + other_foreign_key = '{0}.id'.format(other_table) + + secondary_table = '{0}_{1}'.format(this_table, other_table) + + if other_property is None: + other_property = formatting.pluralize(this_table) + if prefix is not None: + secondary_table = '{0}_{1}'.format(prefix, secondary_table) + other_property = '{0}_{1}'.format(prefix, other_property) + + backref_kwargs = backref_kwargs or {} + backref_kwargs.setdefault('uselist', True) + + relationship_kwargs = relationship_kwargs or {} + relationship_kwargs.setdefault('secondary', _get_secondary_table( + model_class.metadata, + secondary_table, + this_column_name, + other_column_name, + this_foreign_key, + other_foreign_key + )) + + return _relationship(model_class, other_table, backref_kwargs, relationship_kwargs, + other_property, dict_key=dict_key) + + +def _relationship(model_class, other_table, backref_kwargs, relationship_kwargs, other_property, + fk=None, other_fk=None, dict_key=None): + relationship_kwargs = relationship_kwargs or {} + + if fk: + relationship_kwargs.setdefault('foreign_keys', + lambda: getattr( + _get_class_for_table( + model_class, + model_class.__tablename__), + fk)) + + elif other_fk: + relationship_kwargs.setdefault('foreign_keys', + lambda: getattr( + _get_class_for_table( + model_class, + other_table), + other_fk)) + + if dict_key: + relationship_kwargs.setdefault('collection_class', + attribute_mapped_collection(dict_key)) + + if other_property is False: + # No backref + return relationship( + lambda: _get_class_for_table(model_class, other_table), + **relationship_kwargs + ) + else: + if other_property is None: + other_property = model_class.__tablename__ + backref_kwargs = backref_kwargs or {} + return relationship( + lambda: _get_class_for_table(model_class, other_table), + backref=backref(other_property, **backref_kwargs), + **relationship_kwargs + ) + + +def _get_class_for_table(model_class, tablename): + if tablename in (model_class.__name__, model_class.__tablename__): + return model_class + + for table_cls in model_class._decl_class_registry.values(): + if tablename == getattr(table_cls, '__tablename__', None): + return table_cls + + raise ValueError('unknown table: {0}'.format(tablename)) + + +def _get_secondary_table(metadata, + name, + first_column, + second_column, + first_foreign_key, + second_foreign_key): + return Table( + name, + metadata, + Column( + first_column, + Integer, + ForeignKey(first_foreign_key) + ), + Column( + second_column, + Integer, + ForeignKey(second_foreign_key) + ) + ) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b4b1127e/aria/modeling/service_changes.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_changes.py b/aria/modeling/service_changes.py index eb7bb27..a5c6658 100644 --- a/aria/modeling/service_changes.py +++ b/aria/modeling/service_changes.py @@ -35,12 +35,7 @@ from sqlalchemy.ext.declarative import declared_attr from .types import (List, Dict) from .mixins import ModelMixin - -__all__ = ( - 'ServiceUpdateBase', - 'ServiceUpdateStepBase', - 'ServiceModificationBase' -) +from . import relationships class ServiceUpdateBase(ModelMixin): @@ -65,21 +60,21 @@ class ServiceUpdateBase(ModelMixin): @declared_attr def execution(cls): - return cls._declare_many_to_one('execution') + return relationships.many_to_one(cls, 'execution') @declared_attr def service(cls): - return cls._declare_many_to_one('service', parent_property='updates') + return relationships.many_to_one(cls, 'service', parent_property='updates') # region foreign keys @declared_attr def execution_fk(cls): - return cls._declare_fk('execution', nullable=True) + return relationships.fk('execution', nullable=True) @declared_attr def service_fk(cls): - return cls._declare_fk('service') + return relationships.fk('service') # endregion @@ -140,13 +135,13 @@ class ServiceUpdateStepBase(ModelMixin): @declared_attr def service_update(cls): - return cls._declare_many_to_one('service_update', parent_property='steps') + return relationships.many_to_one(cls, 'service_update', parent_property='steps') # region foreign keys @declared_attr def service_update_fk(cls): - return cls._declare_fk('service_update') + return relationships.fk('service_update') # endregion @@ -213,13 +208,13 @@ class ServiceModificationBase(ModelMixin): @declared_attr def service(cls): - return cls._declare_many_to_one('service', parent_property='modifications') + return relationships.many_to_one(cls, 'service', parent_property='modifications') # region foreign keys @declared_attr def service_fk(cls): - return cls._declare_fk('service') + return relationships.fk('service') # endregion http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b4b1127e/aria/modeling/service_common.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py index 746f403..3ff0555 100644 --- a/aria/modeling/service_common.py +++ b/aria/modeling/service_common.py @@ -26,7 +26,10 @@ from ..parser.consumption import ConsumptionContext from ..utils import collections, formatting, console from .mixins import InstanceModelMixin, TemplateModelMixin from .types import List -from . import utils +from . import ( + relationships, + utils +) class ParameterBase(TemplateModelMixin): @@ -114,18 +117,18 @@ class TypeBase(InstanceModelMixin): @declared_attr def parent(cls): - return cls._declare_one_to_one_self('parent_type_fk') + return relationships.one_to_one_self(cls, 'parent_type_fk') @declared_attr def children(cls): - return cls._declare_one_to_many_self('parent_type_fk') + return relationships.one_to_many_self(cls, 'parent_type_fk') # region foreign keys @declared_attr def parent_type_fk(cls): """For Type one-to-many to Type""" - return cls._declare_fk('type', nullable=True) + return relationships.fk('type', nullable=True) # endregion @@ -251,7 +254,7 @@ class PluginSpecificationBase(InstanceModelMixin): @declared_attr def service_template_fk(cls): """For ServiceTemplate one-to-many to PluginSpecification""" - return cls._declare_fk('service_template', nullable=True) + return relationships.fk('service_template', nullable=True) # endregion http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b4b1127e/aria/modeling/service_instance.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_instance.py b/aria/modeling/service_instance.py index 13f355d..63fd9cc 100644 --- a/aria/modeling/service_instance.py +++ b/aria/modeling/service_instance.py @@ -28,8 +28,8 @@ from .mixins import InstanceModelMixin from ..parser import validation from ..parser.consumption import ConsumptionContext from ..utils import collections, formatting, console - from . import ( + relationships, utils, types as modeling_types ) @@ -88,46 +88,46 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods @declared_attr def service_template(cls): - return cls._declare_many_to_one('service_template') + return relationships.many_to_one(cls, 'service_template') description = Column(Text) @declared_attr def meta_data(cls): # Warning! We cannot use the attr name "metadata" because it's used by SqlAlchemy! - return cls._declare_many_to_many('metadata', dict_key='name') + return relationships.many_to_many(cls, 'metadata', dict_key='name') @declared_attr def nodes(cls): - return cls._declare_one_to_many('node', dict_key='name') + return relationships.one_to_many(cls, 'node', dict_key='name') @declared_attr def groups(cls): - return cls._declare_one_to_many('group', dict_key='name') + return relationships.one_to_many(cls, 'group', dict_key='name') @declared_attr def policies(cls): - return cls._declare_one_to_many('policy', dict_key='name') + return relationships.one_to_many(cls, 'policy', dict_key='name') @declared_attr def substitution(cls): - return cls._declare_one_to_one('substitution') + return relationships.one_to_one(cls, 'substitution') @declared_attr def inputs(cls): - return cls._declare_many_to_many('parameter', prefix='inputs', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') @declared_attr def outputs(cls): - return cls._declare_many_to_many('parameter', prefix='outputs', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='outputs', dict_key='name') @declared_attr def workflows(cls): - return cls._declare_one_to_many('operation', dict_key='name') + return relationships.one_to_many(cls, 'operation', dict_key='name') @declared_attr def plugin_specifications(cls): - return cls._declare_many_to_many('plugin_specification') + return relationships.many_to_many(cls, 'plugin_specification') created_at = Column(DateTime, nullable=False, index=True) updated_at = Column(DateTime) @@ -144,12 +144,12 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods @declared_attr def substitution_fk(cls): """Service one-to-one to Substitution""" - return cls._declare_fk('substitution', nullable=True) + return relationships.fk('substitution', nullable=True) @declared_attr def service_template_fk(cls): """For Service many-to-one to ServiceTemplate""" - return cls._declare_fk('service_template', nullable=True) + return relationships.fk('service_template', nullable=True) # endregion @@ -337,47 +337,47 @@ class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods @declared_attr def node_template(cls): - return cls._declare_many_to_one('node_template') + return relationships.many_to_one(cls, 'node_template') @declared_attr def type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') description = Column(Text) @declared_attr def properties(cls): - return cls._declare_many_to_many('parameter', prefix='properties', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') @declared_attr def interfaces(cls): - return cls._declare_one_to_many('interface', dict_key='name') + return relationships.one_to_many(cls, 'interface', dict_key='name') @declared_attr def artifacts(cls): - return cls._declare_one_to_many('artifact', dict_key='name') + return relationships.one_to_many(cls, 'artifact', dict_key='name') @declared_attr def capabilities(cls): - return cls._declare_one_to_many('capability', dict_key='name') + return relationships.one_to_many(cls, 'capability', dict_key='name') @declared_attr def outbound_relationships(cls): - return cls._declare_one_to_many('relationship', child_fk='source_node_fk', - child_property='source_node') + return relationships.one_to_many(cls, 'relationship', child_fk='source_node_fk', + child_property='source_node') @declared_attr def inbound_relationships(cls): - return cls._declare_one_to_many('relationship', child_fk='target_node_fk', - child_property='target_node') + return relationships.one_to_many(cls, 'relationship', child_fk='target_node_fk', + child_property='target_node') @declared_attr def plugin_specifications(cls): - return cls._declare_many_to_many('plugin_specification', dict_key='name') + return relationships.many_to_many(cls, 'plugin_specification', dict_key='name') @declared_attr def host(cls): - return cls._declare_one_to_one_self('host_fk') + return relationships.one_to_one_self(cls, 'host_fk') # region orchestration @@ -407,22 +407,22 @@ class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods @declared_attr def type_fk(cls): """For Node many-to-one to Type""" - return cls._declare_fk('type') + return relationships.fk('type') @declared_attr def host_fk(cls): """For Node one-to-one to Node""" - return cls._declare_fk('node', nullable=True) + return relationships.fk('node', nullable=True) @declared_attr def service_fk(cls): """For Service one-to-many to Node""" - return cls._declare_fk('service') + return relationships.fk('service') @declared_attr def node_template_fk(cls): """For Node many-to-one to NodeTemplate""" - return cls._declare_fk('node_template', nullable=True) + return relationships.fk('node_template', nullable=True) # endregion @@ -603,42 +603,42 @@ class GroupBase(InstanceModelMixin): @declared_attr def group_template(cls): - return cls._declare_many_to_one('group_template') + return relationships.many_to_one(cls, 'group_template') @declared_attr def type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') description = Column(Text) @declared_attr def nodes(cls): - return cls._declare_many_to_many('node') + return relationships.many_to_many(cls, 'node') @declared_attr def properties(cls): - return cls._declare_many_to_many('parameter', prefix='properties', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') @declared_attr def interfaces(cls): - return cls._declare_one_to_many('interface', dict_key='name') + return relationships.one_to_many(cls, 'interface', dict_key='name') # region foreign_keys @declared_attr def type_fk(cls): """For Group many-to-one to Type""" - return cls._declare_fk('type') + return relationships.fk('type') @declared_attr def service_fk(cls): """For Service one-to-many to Group""" - return cls._declare_fk('service') + return relationships.fk('service') @declared_attr def group_template_fk(cls): """For Group many-to-one to GroupTemplate""" - return cls._declare_fk('group_template', nullable=True) + return relationships.fk('group_template', nullable=True) # endregion @@ -702,42 +702,42 @@ class PolicyBase(InstanceModelMixin): @declared_attr def policy_template(cls): - return cls._declare_many_to_one('policy_template') + return relationships.many_to_one(cls, 'policy_template') @declared_attr def type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') description = Column(Text) @declared_attr def properties(cls): - return cls._declare_many_to_many('parameter', prefix='properties', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') @declared_attr def nodes(cls): - return cls._declare_many_to_many('node') + return relationships.many_to_many(cls, 'node') @declared_attr def groups(cls): - return cls._declare_many_to_many('group') + return relationships.many_to_many(cls, 'group') # region foreign_keys @declared_attr def type_fk(cls): """For Policy many-to-one to Type""" - return cls._declare_fk('type') + return relationships.fk('type') @declared_attr def service_fk(cls): """For Service one-to-many to Policy""" - return cls._declare_fk('service') + return relationships.fk('service') @declared_attr def policy_template_fk(cls): """For Policy many-to-one to PolicyTemplate""" - return cls._declare_fk('policy_template', nullable=True) + return relationships.fk('policy_template', nullable=True) # endregion @@ -797,27 +797,27 @@ class SubstitutionBase(InstanceModelMixin): @declared_attr def substitution_template(cls): - return cls._declare_many_to_one('substitution_template') + return relationships.many_to_one(cls, 'substitution_template') @declared_attr def node_type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') @declared_attr def mappings(cls): - return cls._declare_one_to_many('substitution_mapping', dict_key='name') + return relationships.one_to_many(cls, 'substitution_mapping', dict_key='name') # region foreign_keys @declared_attr def node_type_fk(cls): """For Substitution many-to-one to Type""" - return cls._declare_fk('type') + return relationships.fk('type') @declared_attr def substitution_template_fk(cls): """For Substitution many-to-one to SubstitutionTemplate""" - return cls._declare_fk('substitution_template', nullable=True) + return relationships.fk('substitution_template', nullable=True) # endregion @@ -869,37 +869,37 @@ class SubstitutionMappingBase(InstanceModelMixin): @declared_attr def node(cls): - return cls._declare_one_to_one('node') + return relationships.one_to_one(cls, 'node') @declared_attr def capability(cls): - return cls._declare_one_to_one('capability') + return relationships.one_to_one(cls, 'capability') @declared_attr def requirement_template(cls): - return cls._declare_one_to_one('requirement_template') + return relationships.one_to_one(cls, 'requirement_template') # region foreign keys @declared_attr def substitution_fk(cls): """For Substitution one-to-many to SubstitutionMapping""" - return cls._declare_fk('substitution') + return relationships.fk('substitution') @declared_attr def node_fk(cls): """For Substitution one-to-one to NodeTemplate""" - return cls._declare_fk('node') + return relationships.fk('node') @declared_attr def capability_fk(cls): """For Substitution one-to-one to Capability""" - return cls._declare_fk('capability', nullable=True) + return relationships.fk('capability', nullable=True) @declared_attr def requirement_template_fk(cls): """For Substitution one-to-one to RequirementTemplate""" - return cls._declare_fk('requirement_template', nullable=True) + return relationships.fk('requirement_template', nullable=True) # endregion @@ -970,27 +970,27 @@ class RelationshipBase(InstanceModelMixin): @declared_attr def relationship_template(cls): - return cls._declare_many_to_one('relationship_template') + return relationships.many_to_one(cls, 'relationship_template') @declared_attr def requirement_template(cls): - return cls._declare_many_to_one('requirement_template') + return relationships.many_to_one(cls, 'requirement_template') @declared_attr def type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') @declared_attr def target_capability(cls): - return cls._declare_one_to_one('capability') + return relationships.one_to_one(cls, 'capability') @declared_attr def properties(cls): - return cls._declare_many_to_many('parameter', prefix='properties', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') @declared_attr def interfaces(cls): - return cls._declare_one_to_many('interface', dict_key='name') + return relationships.one_to_many(cls, 'interface', dict_key='name') # region orchestration @@ -1004,32 +1004,32 @@ class RelationshipBase(InstanceModelMixin): @declared_attr def type_fk(cls): """For Relationship many-to-one to Type""" - return cls._declare_fk('type', nullable=True) + return relationships.fk('type', nullable=True) @declared_attr def source_node_fk(cls): """For Node one-to-many to Relationship""" - return cls._declare_fk('node') + return relationships.fk('node') @declared_attr def target_node_fk(cls): """For Node one-to-many to Relationship""" - return cls._declare_fk('node') + return relationships.fk('node') @declared_attr def target_capability_fk(cls): """For Relationship one-to-one to Capability""" - return cls._declare_fk('capability', nullable=True) + return relationships.fk('capability', nullable=True) @declared_attr def requirement_template_fk(cls): """For Relationship many-to-one to RequirementTemplate""" - return cls._declare_fk('requirement_template', nullable=True) + return relationships.fk('requirement_template', nullable=True) @declared_attr def relationship_template_fk(cls): """For Relationship many-to-one to RelationshipTemplate""" - return cls._declare_fk('relationship_template', nullable=True) + return relationships.fk('relationship_template', nullable=True) # endregion @@ -1129,11 +1129,11 @@ class CapabilityBase(InstanceModelMixin): @declared_attr def capability_template(cls): - return cls._declare_many_to_one('capability_template') + return relationships.many_to_one(cls, 'capability_template') @declared_attr def type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') min_occurrences = Column(Integer, default=None) max_occurrences = Column(Integer, default=None) @@ -1141,24 +1141,24 @@ class CapabilityBase(InstanceModelMixin): @declared_attr def properties(cls): - return cls._declare_many_to_many('parameter', prefix='properties', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') # region foreign_keys @declared_attr def type_fk(cls): """For Capability many-to-one to Type""" - return cls._declare_fk('type') + return relationships.fk('type') @declared_attr def node_fk(cls): """For Node one-to-many to Capability""" - return cls._declare_fk('node') + return relationships.fk('node') @declared_attr def capability_template_fk(cls): """For Capability many-to-one to CapabilityTemplate""" - return cls._declare_fk('capability_template', nullable=True) + return relationships.fk('capability_template', nullable=True) # endregion @@ -1237,48 +1237,48 @@ class InterfaceBase(InstanceModelMixin): @declared_attr def interface_template(cls): - return cls._declare_many_to_one('interface_template') + return relationships.many_to_one(cls, 'interface_template') @declared_attr def type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') description = Column(Text) @declared_attr def inputs(cls): - return cls._declare_many_to_many('parameter', prefix='inputs', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') @declared_attr def operations(cls): - return cls._declare_one_to_many('operation', dict_key='name') + return relationships.one_to_many(cls, 'operation', dict_key='name') # region foreign_keys @declared_attr def type_fk(cls): """For Interface many-to-one to Type""" - return cls._declare_fk('type') + return relationships.fk('type') @declared_attr def node_fk(cls): """For Node one-to-many to Interface""" - return cls._declare_fk('node', nullable=True) + return relationships.fk('node', nullable=True) @declared_attr def group_fk(cls): """For Group one-to-many to Interface""" - return cls._declare_fk('group', nullable=True) + return relationships.fk('group', nullable=True) @declared_attr def relationship_fk(cls): """For Relationship one-to-many to Interface""" - return cls._declare_fk('relationship', nullable=True) + return relationships.fk('relationship', nullable=True) @declared_attr def interface_template_fk(cls): """For Interface many-to-one to InterfaceTemplate""" - return cls._declare_fk('interface_template', nullable=True) + return relationships.fk('interface_template', nullable=True) # endregion @@ -1353,20 +1353,20 @@ class OperationBase(InstanceModelMixin): @declared_attr def operation_template(cls): - return cls._declare_many_to_one('operation_template') + return relationships.many_to_one(cls, 'operation_template') description = Column(Text) @declared_attr def plugin_specification(cls): - return cls._declare_one_to_one('plugin_specification') + return relationships.one_to_one(cls, 'plugin_specification') implementation = Column(Text) dependencies = Column(modeling_types.StrictList(item_cls=basestring)) @declared_attr def inputs(cls): - return cls._declare_many_to_many('parameter', prefix='inputs', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') executor = Column(Text) max_retries = Column(Integer) @@ -1377,22 +1377,22 @@ class OperationBase(InstanceModelMixin): @declared_attr def service_fk(cls): """For Service one-to-many to Operation""" - return cls._declare_fk('service', nullable=True) + return relationships.fk('service', nullable=True) @declared_attr def interface_fk(cls): """For Interface one-to-many to Operation""" - return cls._declare_fk('interface', nullable=True) + return relationships.fk('interface', nullable=True) @declared_attr def plugin_specification_fk(cls): """For Operation one-to-one to PluginSpecification""" - return cls._declare_fk('plugin_specification', nullable=True) + return relationships.fk('plugin_specification', nullable=True) @declared_attr def operation_template_fk(cls): """For Operation many-to-one to OperationTemplate""" - return cls._declare_fk('operation_template', nullable=True) + return relationships.fk('operation_template', nullable=True) # endregion @@ -1476,11 +1476,11 @@ class ArtifactBase(InstanceModelMixin): @declared_attr def artifact_template(cls): - return cls._declare_many_to_one('artifact_template') + return relationships.many_to_one(cls, 'artifact_template') @declared_attr def type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') description = Column(Text) source_path = Column(Text) @@ -1490,24 +1490,24 @@ class ArtifactBase(InstanceModelMixin): @declared_attr def properties(cls): - return cls._declare_many_to_many('parameter', prefix='properties', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') # region foreign_keys @declared_attr def type_fk(cls): """For Artifact many-to-one to Type""" - return cls._declare_fk('type') + return relationships.fk('type') @declared_attr def node_fk(cls): """For Node one-to-many to Artifact""" - return cls._declare_fk('node') + return relationships.fk('node') @declared_attr def artifact_template_fk(cls): """For Artifact many-to-one to ArtifactTemplate""" - return cls._declare_fk('artifact_template', nullable=True) + return relationships.fk('artifact_template', nullable=True) # endregion http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b4b1127e/aria/modeling/service_template.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py index b365720..d93cbf3 100644 --- a/aria/modeling/service_template.py +++ b/aria/modeling/service_template.py @@ -35,6 +35,7 @@ from ..parser.reading import deepcopy_with_locators from ..utils import collections, formatting, console from .mixins import TemplateModelMixin from . import ( + relationships, utils, types as modeling_types ) @@ -104,67 +105,68 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public @declared_attr def meta_data(cls): # Warning! We cannot use the attr name "metadata" because it's used by SqlAlchemy! - return cls._declare_many_to_many('metadata', dict_key='name') + return relationships.many_to_many(cls, 'metadata', dict_key='name') @declared_attr def node_templates(cls): - return cls._declare_one_to_many('node_template', dict_key='name') + return relationships.one_to_many(cls, 'node_template', dict_key='name') @declared_attr def group_templates(cls): - return cls._declare_one_to_many('group_template', dict_key='name') + return relationships.one_to_many(cls, 'group_template', dict_key='name') @declared_attr def policy_templates(cls): - return cls._declare_one_to_many('policy_template', dict_key='name') + return relationships.one_to_many(cls, 'policy_template', dict_key='name') @declared_attr def substitution_template(cls): - return cls._declare_one_to_one('substitution_template') + return relationships.one_to_one(cls, 'substitution_template') @declared_attr def inputs(cls): - return cls._declare_many_to_many('parameter', prefix='inputs', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') @declared_attr def outputs(cls): - return cls._declare_many_to_many('parameter', prefix='outputs', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='outputs', dict_key='name') @declared_attr def workflow_templates(cls): - return cls._declare_one_to_many('operation_template', dict_key='name') + return relationships.one_to_many(cls, 'operation_template', dict_key='name') @declared_attr def plugin_specifications(cls): - return cls._declare_one_to_many('plugin_specification', dict_key='name') + return relationships.one_to_many(cls, 'plugin_specification', dict_key='name') @declared_attr def node_types(cls): - return cls._declare_one_to_one('type', fk='node_type_fk', other_property=False) + return relationships.one_to_one(cls, 'type', fk='node_type_fk', other_property=False) @declared_attr def group_types(cls): - return cls._declare_one_to_one('type', fk='group_type_fk', other_property=False) + return relationships.one_to_one(cls, 'type', fk='group_type_fk', other_property=False) @declared_attr def policy_types(cls): - return cls._declare_one_to_one('type', fk='policy_type_fk', other_property=False) + return relationships.one_to_one(cls, 'type', fk='policy_type_fk', other_property=False) @declared_attr def relationship_types(cls): - return cls._declare_one_to_one('type', fk='relationship_type_fk', other_property=False) + return relationships.one_to_one(cls, 'type', fk='relationship_type_fk', + other_property=False) @declared_attr def capability_types(cls): - return cls._declare_one_to_one('type', fk='capability_type_fk', other_property=False) + return relationships.one_to_one(cls, 'type', fk='capability_type_fk', other_property=False) @declared_attr def interface_types(cls): - return cls._declare_one_to_one('type', fk='interface_type_fk', other_property=False) + return relationships.one_to_one(cls, 'type', fk='interface_type_fk', other_property=False) @declared_attr def artifact_types(cls): - return cls._declare_one_to_one('type', fk='artifact_type_fk', other_property=False) + return relationships.one_to_one(cls, 'type', fk='artifact_type_fk', other_property=False) # region orchestration @@ -178,42 +180,42 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public @declared_attr def substitution_template_fk(cls): """For ServiceTemplate one-to-one to SubstitutionTemplate""" - return cls._declare_fk('substitution_template', nullable=True) + return relationships.fk('substitution_template', nullable=True) @declared_attr def node_type_fk(cls): """For ServiceTemplate one-to-one to Type""" - return cls._declare_fk('type', nullable=True) + return relationships.fk('type', nullable=True) @declared_attr def group_type_fk(cls): """For ServiceTemplate one-to-one to Type""" - return cls._declare_fk('type', nullable=True) + return relationships.fk('type', nullable=True) @declared_attr def policy_type_fk(cls): """For ServiceTemplate one-to-one to Type""" - return cls._declare_fk('type', nullable=True) + return relationships.fk('type', nullable=True) @declared_attr def relationship_type_fk(cls): """For ServiceTemplate one-to-one to Type""" - return cls._declare_fk('type', nullable=True) + return relationships.fk('type', nullable=True) @declared_attr def capability_type_fk(cls): """For ServiceTemplate one-to-one to Type""" - return cls._declare_fk('type', nullable=True) + return relationships.fk('type', nullable=True) @declared_attr def interface_type_fk(cls): """For ServiceTemplate one-to-one to Type""" - return cls._declare_fk('type', nullable=True) + return relationships.fk('type', nullable=True) @declared_attr def artifact_type_fk(cls): """For ServiceTemplate one-to-one to Type""" - return cls._declare_fk('type', nullable=True) + return relationships.fk('type', nullable=True) # endregion @@ -412,7 +414,7 @@ class NodeTemplateBase(TemplateModelMixin): @declared_attr def type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') description = Column(Text) default_instances = Column(Integer, default=1) @@ -421,42 +423,42 @@ class NodeTemplateBase(TemplateModelMixin): @declared_attr def properties(cls): - return cls._declare_many_to_many('parameter', prefix='properties', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') @declared_attr def interface_templates(cls): - return cls._declare_one_to_many('interface_template', dict_key='name') + return relationships.one_to_many(cls, 'interface_template', dict_key='name') @declared_attr def artifact_templates(cls): - return cls._declare_one_to_many('artifact_template', dict_key='name') + return relationships.one_to_many(cls, 'artifact_template', dict_key='name') @declared_attr def capability_templates(cls): - return cls._declare_one_to_many('capability_template', dict_key='name') + return relationships.one_to_many(cls, 'capability_template', dict_key='name') @declared_attr def requirement_templates(cls): - return cls._declare_one_to_many('requirement_template', child_fk='node_template_fk', - child_property='node_template') + return relationships.one_to_many(cls, 'requirement_template', child_fk='node_template_fk', + child_property='node_template') target_node_template_constraints = Column(modeling_types.StrictList(FunctionType)) @declared_attr def plugin_specifications(cls): - return cls._declare_many_to_many('plugin_specification', dict_key='name') + return relationships.many_to_many(cls, 'plugin_specification', dict_key='name') # region foreign_keys @declared_attr def type_fk(cls): """For NodeTemplate many-to-one to Type""" - return cls._declare_fk('type') + return relationships.fk('type') @declared_attr def service_template_fk(cls): """For ServiceTemplate one-to-many to NodeTemplate""" - return cls._declare_fk('service_template') + return relationships.fk('service_template') # endregion @@ -575,33 +577,33 @@ class GroupTemplateBase(TemplateModelMixin): @declared_attr def type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') description = Column(Text) @declared_attr def node_templates(cls): - return cls._declare_many_to_many('node_template') + return relationships.many_to_many(cls, 'node_template') @declared_attr def properties(cls): - return cls._declare_many_to_many('parameter', prefix='properties', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') @declared_attr def interface_templates(cls): - return cls._declare_one_to_many('interface_template', dict_key='name') + return relationships.one_to_many(cls, 'interface_template', dict_key='name') # region foreign keys @declared_attr def type_fk(cls): """For GroupTemplate many-to-one to Type""" - return cls._declare_fk('type') + return relationships.fk('type') @declared_attr def service_template_fk(cls): """For ServiceTemplate one-to-many to GroupTemplate""" - return cls._declare_fk('service_template') + return relationships.fk('service_template') # endregion @@ -680,33 +682,33 @@ class PolicyTemplateBase(TemplateModelMixin): @declared_attr def type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') description = Column(Text) @declared_attr def node_templates(cls): - return cls._declare_many_to_many('node_template') + return relationships.many_to_many(cls, 'node_template') @declared_attr def group_templates(cls): - return cls._declare_many_to_many('group_template') + return relationships.many_to_many(cls, 'group_template') @declared_attr def properties(cls): - return cls._declare_many_to_many('parameter', prefix='properties', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') # region foreign keys @declared_attr def type_fk(cls): """For PolicyTemplate many-to-one to Type""" - return cls._declare_fk('type') + return relationships.fk('type') @declared_attr def service_template_fk(cls): """For ServiceTemplate one-to-many to PolicyTemplate""" - return cls._declare_fk('service_template') + return relationships.fk('service_template') # endregion @@ -777,18 +779,18 @@ class SubstitutionTemplateBase(TemplateModelMixin): @declared_attr def node_type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') @declared_attr def mappings(cls): - return cls._declare_one_to_many('substitution_template_mapping', dict_key='name') + return relationships.one_to_many(cls, 'substitution_template_mapping', dict_key='name') # region foreign keys @declared_attr def node_type_fk(cls): """For SubstitutionTemplate many-to-one to Type""" - return cls._declare_fk('type') + return relationships.fk('type') # endregion @@ -844,37 +846,37 @@ class SubstitutionTemplateMappingBase(TemplateModelMixin): @declared_attr def node_template(cls): - return cls._declare_one_to_one('node_template') + return relationships.one_to_one(cls, 'node_template') @declared_attr def capability_template(cls): - return cls._declare_one_to_one('capability_template') + return relationships.one_to_one(cls, 'capability_template') @declared_attr def requirement_template(cls): - return cls._declare_one_to_one('requirement_template') + return relationships.one_to_one(cls, 'requirement_template') # region foreign keys @declared_attr def substitution_template_fk(cls): """For SubstitutionTemplate one-to-many to SubstitutionTemplateMapping""" - return cls._declare_fk('substitution_template') + return relationships.fk('substitution_template') @declared_attr def node_template_fk(cls): """For SubstitutionTemplate one-to-one to NodeTemplate""" - return cls._declare_fk('node_template') + return relationships.fk('node_template') @declared_attr def capability_template_fk(cls): """For SubstitutionTemplate one-to-one to CapabilityTemplate""" - return cls._declare_fk('capability_template', nullable=True) + return relationships.fk('capability_template', nullable=True) @declared_attr def requirement_template_fk(cls): """For SubstitutionTemplate one-to-one to RequirementTemplate""" - return cls._declare_fk('requirement_template', nullable=True) + return relationships.fk('requirement_template', nullable=True) # endregion @@ -965,50 +967,52 @@ class RequirementTemplateBase(TemplateModelMixin): @declared_attr def target_node_type(cls): - return cls._declare_many_to_one('type', fk='target_node_type_fk', parent_property=False) + return relationships.many_to_one(cls, 'type', fk='target_node_type_fk', + parent_property=False) @declared_attr def target_node_template(cls): - return cls._declare_one_to_one('node_template', fk='target_node_template_fk', - other_property=False) + return relationships.one_to_one(cls, 'node_template', fk='target_node_template_fk', + other_property=False) @declared_attr def target_capability_type(cls): - return cls._declare_one_to_one('type', fk='target_capability_type_fk', other_property=False) + return relationships.one_to_one(cls, 'type', fk='target_capability_type_fk', + other_property=False) target_capability_name = Column(Text) target_node_template_constraints = Column(modeling_types.StrictList(FunctionType)) @declared_attr def relationship_template(cls): - return cls._declare_one_to_one('relationship_template') + return relationships.one_to_one(cls, 'relationship_template') # region foreign keys @declared_attr def target_node_type_fk(cls): """For RequirementTemplate many-to-one to Type""" - return cls._declare_fk('type', nullable=True) + return relationships.fk('type', nullable=True) @declared_attr def target_node_template_fk(cls): """For RequirementTemplate one-to-one to NodeTemplate""" - return cls._declare_fk('node_template', nullable=True) + return relationships.fk('node_template', nullable=True) @declared_attr def target_capability_type_fk(cls): """For RequirementTemplate one-to-one to NodeTemplate""" - return cls._declare_fk('type', nullable=True) + return relationships.fk('type', nullable=True) @declared_attr def node_template_fk(cls): """For NodeTemplate one-to-many to RequirementTemplate""" - return cls._declare_fk('node_template') + return relationships.fk('node_template') @declared_attr def relationship_template_fk(cls): """For RequirementTemplate one-to-one to RelationshipTemplate""" - return cls._declare_fk('relationship_template', nullable=True) + return relationships.fk('relationship_template', nullable=True) # endregion @@ -1148,24 +1152,24 @@ class RelationshipTemplateBase(TemplateModelMixin): @declared_attr def type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') description = Column(Text) @declared_attr def properties(cls): - return cls._declare_many_to_many('parameter', prefix='properties', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') @declared_attr def interface_templates(cls): - return cls._declare_one_to_many('interface_template', dict_key='name') + return relationships.one_to_many(cls, 'interface_template', dict_key='name') # region foreign keys @declared_attr def type_fk(cls): """For RelationshipTemplate many-to-one to Type""" - return cls._declare_fk('type', nullable=True) + return relationships.fk('type', nullable=True) # endregion @@ -1245,7 +1249,7 @@ class CapabilityTemplateBase(TemplateModelMixin): @declared_attr def type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') description = Column(Text) min_occurrences = Column(Integer, default=None) # optional @@ -1253,23 +1257,23 @@ class CapabilityTemplateBase(TemplateModelMixin): @declared_attr def valid_source_node_types(cls): - return cls._declare_many_to_many('type', prefix='valid_sources') + return relationships.many_to_many(cls, 'type', prefix='valid_sources') @declared_attr def properties(cls): - return cls._declare_many_to_many('parameter', prefix='properties', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') # region foreign keys @declared_attr def type_fk(cls): """For CapabilityTemplate many-to-one to Type""" - return cls._declare_fk('type') + return relationships.fk('type') @declared_attr def node_template_fk(cls): """For NodeTemplate one-to-many to CapabilityTemplate""" - return cls._declare_fk('node_template') + return relationships.fk('node_template') # endregion @@ -1376,39 +1380,39 @@ class InterfaceTemplateBase(TemplateModelMixin): @declared_attr def type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') description = Column(Text) @declared_attr def inputs(cls): - return cls._declare_many_to_many('parameter', prefix='inputs', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') @declared_attr def operation_templates(cls): - return cls._declare_one_to_many('operation_template', dict_key='name') + return relationships.one_to_many(cls, 'operation_template', dict_key='name') # region foreign keys @declared_attr def type_fk(cls): """For InterfaceTemplate many-to-one to Type""" - return cls._declare_fk('type') + return relationships.fk('type') @declared_attr def node_template_fk(cls): """For NodeTemplate one-to-many to InterfaceTemplate""" - return cls._declare_fk('node_template', nullable=True) + return relationships.fk('node_template', nullable=True) @declared_attr def group_template_fk(cls): """For GroupTemplate one-to-many to InterfaceTemplate""" - return cls._declare_fk('group_template', nullable=True) + return relationships.fk('group_template', nullable=True) @declared_attr def relationship_template_fk(cls): """For RelationshipTemplate one-to-many to InterfaceTemplate""" - return cls._declare_fk('relationship_template', nullable=True) + return relationships.fk('relationship_template', nullable=True) # endregion @@ -1495,14 +1499,14 @@ class OperationTemplateBase(TemplateModelMixin): @declared_attr def plugin_specification(cls): - return cls._declare_one_to_one('plugin_specification') + return relationships.one_to_one(cls, 'plugin_specification') implementation = Column(Text) dependencies = Column(modeling_types.StrictList(item_cls=basestring)) @declared_attr def inputs(cls): - return cls._declare_many_to_many('parameter', prefix='inputs', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') executor = Column(Text) max_retries = Column(Integer) @@ -1513,17 +1517,17 @@ class OperationTemplateBase(TemplateModelMixin): @declared_attr def service_template_fk(cls): """For ServiceTemplate one-to-many to OperationTemplate""" - return cls._declare_fk('service_template', nullable=True) + return relationships.fk('service_template', nullable=True) @declared_attr def interface_template_fk(cls): """For InterfaceTemplate one-to-many to OperationTemplate""" - return cls._declare_fk('interface_template', nullable=True) + return relationships.fk('interface_template', nullable=True) @declared_attr def plugin_specification_fk(cls): """For OperationTemplate one-to-one to PluginSpecification""" - return cls._declare_fk('plugin_specification', nullable=True) + return relationships.fk('plugin_specification', nullable=True) # endregion @@ -1616,7 +1620,7 @@ class ArtifactTemplateBase(TemplateModelMixin): @declared_attr def type(cls): - return cls._declare_many_to_one('type') + return relationships.many_to_one(cls, 'type') description = Column(Text) source_path = Column(Text) @@ -1626,19 +1630,19 @@ class ArtifactTemplateBase(TemplateModelMixin): @declared_attr def properties(cls): - return cls._declare_many_to_many('parameter', prefix='properties', dict_key='name') + return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') # region foreign keys @declared_attr def type_fk(cls): """For ArtifactTemplate many-to-one to Type""" - return cls._declare_fk('type') + return relationships.fk('type') @declared_attr def node_template_fk(cls): """For NodeTemplate one-to-many to ArtifactTemplate""" - return cls._declare_fk('node_template') + return relationships.fk('node_template') # endregion
