You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ariatosca.apache.org by em...@apache.org on 2017/03/14 23:41:42 UTC

[3/3] incubator-ariatosca git commit: Many cleanups; use dict for many models

Many cleanups; use dict for many models


Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/0f040a2b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/0f040a2b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/0f040a2b

Branch: refs/heads/ARIA-105-integrate-modeling
Commit: 0f040a2b9a72cd7ed66b067a69df63d0bb367f0d
Parents: f6da64a
Author: Tal Liron <ta...@gmail.com>
Authored: Tue Mar 14 18:40:55 2017 -0500
Committer: Tal Liron <ta...@gmail.com>
Committed: Tue Mar 14 18:40:55 2017 -0500

----------------------------------------------------------------------
 aria/cli/commands.py                            |   7 +-
 aria/cli/dry.py                                 |   2 +-
 aria/modeling/mixins.py                         | 398 +++++++++------
 aria/modeling/orchestration.py                  |  97 ++--
 aria/modeling/relationship.py                   | 421 ----------------
 aria/modeling/service_changes.py                |  79 +--
 aria/modeling/service_common.py                 |  30 +-
 aria/modeling/service_instance.py               | 493 ++++++++++---------
 aria/modeling/service_template.py               | 460 ++++++++---------
 aria/modeling/types.py                          |  30 +-
 aria/orchestrator/workflows/api/task.py         | 162 +++---
 .../simple_v1_0/modeling/__init__.py            |  35 +-
 tests/end2end/test_orchestrator.py              |   7 +-
 tests/mock/models.py                            |   8 +-
 tests/modeling/test_models.py                   |  10 +-
 tests/orchestrator/context/test_operation.py    |  10 +-
 tests/orchestrator/context/test_serialize.py    |   2 +-
 tests/orchestrator/context/test_toolbelt.py     |   2 +-
 tests/orchestrator/workflows/api/test_task.py   |  14 +-
 .../workflows/builtin/test_execute_operation.py |   2 +-
 tests/orchestrator/workflows/core/test_task.py  |   3 +-
 .../executor/test_process_executor_extension.py |   4 +-
 .../test_process_executor_tracked_changes.py    |   4 +-
 tests/storage/test_resource_storage.py          |  58 +--
 24 files changed, 1029 insertions(+), 1309 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/0f040a2b/aria/cli/commands.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands.py b/aria/cli/commands.py
index f9d3053..1eef61d 100644
--- a/aria/cli/commands.py
+++ b/aria/cli/commands.py
@@ -235,12 +235,7 @@ class WorkflowCommand(BaseCommand):
                 workflow_name))
             inputs = {}
         else:
-            workflow = None
-            for policy in context.modeling.instance.policies:
-                if policy.name == workflow_name:
-                    workflow = policy
-                    break
-
+            workflow = context.modeling.instance.policies.get(workflow_name)
             if workflow is None:
                 raise AttributeError('workflow policy does not exist: "{0}"'.format(workflow_name))
             if workflow.type.role != 'workflow':

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/0f040a2b/aria/cli/dry.py
----------------------------------------------------------------------
diff --git a/aria/cli/dry.py b/aria/cli/dry.py
index 114ab7a..82faf42 100644
--- a/aria/cli/dry.py
+++ b/aria/cli/dry.py
@@ -34,7 +34,7 @@ def convert_to_dry(service):
     for workflow in service.workflows:
         convert_operation_to_dry(workflow)
 
-    for node in service.nodes:
+    for node in service.nodes.itervalues():
         for interface in node.interfaces.itervalues():
             for oper in interface.operations.itervalues():
                 convert_operation_to_dry(oper)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/0f040a2b/aria/modeling/mixins.py
----------------------------------------------------------------------
diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py
index 9133a85..06f2497 100644
--- a/aria/modeling/mixins.py
+++ b/aria/modeling/mixins.py
@@ -13,6 +13,8 @@
 # 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
@@ -35,13 +37,13 @@ from sqlalchemy import (
     Table,
 )
 
-from . import utils
+from .utils import classproperty
 from ..utils import formatting
 
 
 class ModelMixin(object):
 
-    @utils.classproperty
+    @classproperty
     def __modelname__(cls):                                                                         # pylint: disable=no-self-argument
         return getattr(cls, '__mapiname__', cls.__tablename__)
 
@@ -57,9 +59,9 @@ class ModelMixin(object):
         """
         Return a dict representation of the model
 
-        :param suppress_error: If set to True, sets `None` to attributes that
-                               it's unable to retrieve (e.g., if a relationship wasn't established
-                               yet, and so it's impossible to access a property through it)
+        :param suppress_error: If set to True, sets `None` to attributes that it's unable to
+                               retrieve (e.g., if a relationship wasn't established yet, and so it's
+                               impossible to access a property through it)
         """
 
         res = dict()
@@ -95,22 +97,64 @@ class ModelMixin(object):
         return fields - set(getattr(cls, '__private_fields__', []))
 
     @classmethod
-    def _create_foreign_key(cls, parent_table, nullable=False):
+    def _iter_association_proxies(cls):
+        for col, value in vars(cls).items():
+            if isinstance(value, associationproxy.AssociationProxy):
+                yield col
+
+    def __repr__(self):
+        return '<{cls} id=`{id}`>'.format(
+            cls=self.__class__.__name__,
+            id=getattr(self, self.name_column_name()))
+
+    # Model property declaration helpers
+
+    @classmethod
+    def _declare_fk(cls,
+                    other_table,
+                    nullable=False):
         """
-        Return a ForeignKey object.
+        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".
 
-        :param parent_table: Parent table name
-        :param nullable: Should the column be allowed to remain empty
+        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=parent_table),
-                                 ondelete='CASCADE'),
+                      ForeignKey('{table}.id'.format(table=other_table), ondelete='CASCADE'),
                       nullable=nullable)
 
     @classmethod
-    def _create_relationship_to_self(cls,
-                                     column_name,
-                                     relationship_kwargs=None):
+    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(
@@ -121,11 +165,11 @@ class ModelMixin(object):
         primaryjoin = '{remote_side} == {cls}.{column}'.format(
             remote_side=remote_side,
             cls=cls.__name__,
-            column=column_name
+            column=fk
         )
 
         return relationship(
-            cls._get_cls_by_tablename(cls.__tablename__).__name__,
+            cls._get_class_for_table(cls.__tablename__).__name__,
             primaryjoin=primaryjoin,
             remote_side=remote_side,
             post_update=True,
@@ -133,100 +177,197 @@ class ModelMixin(object):
         )
 
     @classmethod
-    def _create_one_to_many_relationship_to_self(cls,
-                                                 key,
-                                                 dict_key=None,
-                                                 relationship_kwargs=None):
+    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=key
+            remote_column=fk
         ))
 
-        return cls._create_relationship(cls.__tablename__, None, relationship_kwargs,
-                                        backreference=False, dict_key=dict_key)
+        return cls._declare_relationship(cls.__tablename__, None, relationship_kwargs,
+                                         other_property=False, dict_key=dict_key)
 
     @classmethod
-    def _create_one_to_one_relationship(cls,
-                                        other_table,
-                                        key=None,
-                                        backreference=None,
-                                        backref_kwargs=None,
-                                        relationship_kwargs=None):
+    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._create_relationship(other_table, backref_kwargs, relationship_kwargs,
-                                        backreference, key=key)
+        return cls._declare_relationship(other_table, backref_kwargs, relationship_kwargs,
+                                         other_property, fk=fk, other_fk=other_fk)
 
     @classmethod
-    def _create_one_to_many_relationship(cls,
-                                         child_table,
-                                         key=None,
-                                         foreign_key=None,
-                                         dict_key=None,
-                                         backreference=None,
-                                         backref_kwargs=None,
-                                         relationship_kwargs=None):
+    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._create_relationship(child_table, backref_kwargs, relationship_kwargs,
-                                        backreference, key=key, foreign_key=foreign_key,
-                                        dict_key=dict_key)
+        return cls._declare_relationship(child_table, backref_kwargs, relationship_kwargs,
+                                         child_property, other_fk=child_fk, dict_key=dict_key)
 
     @classmethod
-    def _create_many_to_one_relationship(cls,
-                                         parent_table,
-                                         key=None,
-                                         foreign_key=None,
-                                         backreference=None,
-                                         backref_kwargs=None,
-                                         relationship_kwargs=None):
+    def _declare_many_to_one(cls,
+                             parent_table,
+                             fk=None,
+                             parent_fk=None,
+                             parent_property=None,
+                             relationship_kwargs=None,
+                             backref_kwargs=None):
         """
-        Return a one-to-many SQL relationship object
-        Meant to be used from inside the *child* object
+        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: Name of the parent table
-        :param foreign_key: The column of the foreign key (from the child table)
-        :param backreference: The name to give to the reference to the child (on the parent table)
+        :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 backreference is None:
-            backreference = formatting.pluralize(cls.__tablename__)
+        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')
-        # The following line make sure that when the *parent* is deleted, all its connected children
-        # are deleted as well
-        backref_kwargs.setdefault('cascade', 'all')
+        backref_kwargs.setdefault('cascade', 'all') # delete children when parent is deleted
 
-        return cls._create_relationship(parent_table, backref_kwargs, relationship_kwargs,
-                                        backreference, key=key, foreign_key=foreign_key)
+        return cls._declare_relationship(parent_table, backref_kwargs, relationship_kwargs,
+                                         parent_property, fk=fk, other_fk=parent_fk)
 
     @classmethod
-    def _create_many_to_many_relationship(cls,
-                                          other_table,
-                                          table_prefix=None,
-                                          key=None,
-                                          dict_key=None,
-                                          backreference=None,
-                                          backref_kwargs=None,
-                                          relationship_kwargs=None):
+    def _declare_many_to_many(cls,
+                              other_table,
+                              prefix=None,
+                              dict_key=None,
+                              other_property=None,
+                              relationship_kwargs=None,
+                              backref_kwargs=None):
         """
-        Return a many-to-many SQL relationship object
+        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.
 
-        Notes:
+        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.
 
-        1. The backreference name is the current table's table name
-        2. This method creates a new helper table in the DB
+        *This utility method should only be used during class creation.*
 
-        :param cls: The class of the table we're connecting from
-        :param other_table: The class of the table we're connecting to
-        :param table_prefix: Custom prefix for the helper table name and the backreference name
-        :param dict_key: If provided, will use a dict class with this column as the key
+        :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__
@@ -236,87 +377,87 @@ class ModelMixin(object):
         other_column_name = '{0}_id'.format(other_table)
         other_foreign_key = '{0}.id'.format(other_table)
 
-        helper_table = '{0}_{1}'.format(this_table, other_table)
+        secondary_table = '{0}_{1}'.format(this_table, other_table)
 
-        if backreference is None:
-            backreference = formatting.pluralize(this_table)
-            if table_prefix:
-                helper_table = '{0}_{1}'.format(table_prefix, helper_table)
-                backreference = '{0}_{1}'.format(table_prefix, backreference)
+        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', cls._get_secondary_table(
+        relationship_kwargs.setdefault('secondary', ModelMixin._get_secondary_table(
             cls.metadata,
-            helper_table,
+            secondary_table,
             this_column_name,
             other_column_name,
             this_foreign_key,
             other_foreign_key
         ))
 
-        return cls._create_relationship(other_table, backref_kwargs, relationship_kwargs,
-                                        backreference, key=key, dict_key=dict_key)
+        return cls._declare_relationship(other_table, backref_kwargs, relationship_kwargs,
+                                         other_property, dict_key=dict_key)
 
     @classmethod
-    def _create_relationship(cls, table, backref_kwargs, relationship_kwargs, backreference,
-                             key=None, foreign_key=None, dict_key=None):
+    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 key:
+        if fk:
             relationship_kwargs.setdefault('foreign_keys',
                                            lambda: getattr(
-                                               cls._get_cls_by_tablename(cls.__tablename__),
-                                               key))
+                                               cls._get_class_for_table(cls.__tablename__),
+                                               fk))
 
-        elif foreign_key:
+        elif other_fk:
             relationship_kwargs.setdefault('foreign_keys',
                                            lambda: getattr(
-                                               cls._get_cls_by_tablename(table),
-                                               foreign_key))
+                                               cls._get_class_for_table(other_table),
+                                               other_fk))
 
         if dict_key:
             relationship_kwargs.setdefault('collection_class',
                                            attribute_mapped_collection(dict_key))
 
-        if backreference is False:
+        if other_property is False:
+            # No backref
             return relationship(
-                lambda: cls._get_cls_by_tablename(table),
+                lambda: cls._get_class_for_table(other_table),
                 **relationship_kwargs
             )
         else:
-            if backreference is None:
-                backreference = cls.__tablename__
+            if other_property is None:
+                other_property = cls.__tablename__
             backref_kwargs = backref_kwargs or {}
             return relationship(
-                lambda: cls._get_cls_by_tablename(table),
-                backref=backref(backreference, **backref_kwargs),
+                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,
-                             helper_table,
+                             name,
                              first_column,
                              second_column,
                              first_foreign_key,
                              second_foreign_key):
-        """
-        Create a helper table for a many-to-many relationship
-
-        :param helper_table: The name of the table
-        :param first_column_name: The name of the first column in the table
-        :param second_column_name: The name of the second column in the table
-        :param first_foreign_key: The string representing the first foreign key,
-               for example `blueprint.storage_id`, or `tenants.id`
-        :param second_foreign_key: The string representing the second foreign key
-        :return: A Table object
-        """
-
         return Table(
-            helper_table,
+            name,
             metadata,
             Column(
                 first_column,
@@ -330,33 +471,6 @@ class ModelMixin(object):
             )
         )
 
-    @classmethod
-    def _iter_association_proxies(cls):
-        for col, value in vars(cls).items():
-            if isinstance(value, associationproxy.AssociationProxy):
-                yield col
-
-    @classmethod
-    def _get_cls_by_tablename(cls, tablename):
-        """
-        Return class reference mapped to table.
-
-        :param tablename: String with name of table.
-        :return: Class reference or None.
-        """
-
-        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
-
-    def __repr__(self):
-        return '<{cls} id=`{id}`>'.format(
-            cls=self.__class__.__name__,
-            id=getattr(self, self.name_column_name()))
-
 
 class ModelIDMixin(object):
     id = Column(Integer, primary_key=True, autoincrement=True)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/0f040a2b/aria/modeling/orchestration.py
----------------------------------------------------------------------
diff --git a/aria/modeling/orchestration.py b/aria/modeling/orchestration.py
index d8bdd3c..683c526 100644
--- a/aria/modeling/orchestration.py
+++ b/aria/modeling/orchestration.py
@@ -100,28 +100,33 @@ class ExecutionBase(ModelMixin):
     workflow_name = Column(Text)
 
     @declared_attr
-    def service_template(cls):
-        return association_proxy('service', 'service_template')
+    def service(cls):
+        return cls._declare_many_to_one('service')
+
+    # region foreign keys
 
     @declared_attr
-    def service(cls):
-        return cls._create_many_to_one_relationship('service')
+    def service_fk(cls):
+        return cls._declare_fk('service')
+
+    # endregion
+
+    # region association proxies
 
     @declared_attr
     def service_name(cls):
+        """Required for use by SqlAlchemy queries"""
         return association_proxy('service', cls.name_column_name())
 
     @declared_attr
-    def service_template_name(cls):
-        return association_proxy('service', 'service_template_name')
-
-    # region foreign keys
-
-    __private_fields__ = ['service_fk']
+    def service_template(cls):
+        """Required for use by SqlAlchemy queries"""
+        return association_proxy('service', 'service_template')
 
     @declared_attr
-    def service_fk(cls):
-        return cls._create_foreign_key('service')
+    def service_template_name(cls):
+        """Required for use by SqlAlchemy queries"""
+        return association_proxy('service', 'service_template_name')
 
     # endregion
 
@@ -132,6 +137,11 @@ class ExecutionBase(ModelMixin):
             self.status
         )
 
+    __private_fields__ = ['service_fk',
+                          'service_name',
+                          'service_template',
+                          'service_template_name']
+
 
 class PluginBase(ModelMixin):
     """
@@ -186,37 +196,24 @@ class TaskBase(ModelMixin):
     INFINITE_RETRIES = -1
 
     @declared_attr
-    def node_name(cls):
-        return association_proxy('node', cls.name_column_name())
-
-    @declared_attr
     def node(cls):
-        return cls._create_many_to_one_relationship('node')
-
-    @declared_attr
-    def relationship_name(cls):
-        return association_proxy('relationship', cls.name_column_name())
+        return cls._declare_many_to_one('node')
 
     @declared_attr
     def relationship(cls):
-        return cls._create_many_to_one_relationship('relationship')
+        return cls._declare_many_to_one('relationship')
 
     @declared_attr
     def plugin(cls):
-        return cls._create_many_to_one_relationship('plugin')
+        return cls._declare_many_to_one('plugin')
 
     @declared_attr
     def execution(cls):
-        return cls._create_many_to_one_relationship('execution')
-
-    @declared_attr
-    def execution_name(cls):
-        return association_proxy('execution', cls.name_column_name())
+        return cls._declare_many_to_one('execution')
 
     @declared_attr
     def inputs(cls):
-        return cls._create_many_to_many_relationship('parameter', table_prefix='inputs',
-                                                     dict_key='name')
+        return cls._declare_many_to_many('parameter', prefix='inputs', dict_key='name')
 
     status = Column(Enum(*STATES, name='status'), default=PENDING)
 
@@ -260,26 +257,40 @@ class TaskBase(ModelMixin):
 
     # region foreign keys
 
-    __private_fields__ = ['node_fk',
-                          'relationship_fk',
-                          'plugin_fk',
-                          'execution_fk']
-
     @declared_attr
     def node_fk(cls):
-        return cls._create_foreign_key('node', nullable=True)
+        return cls._declare_fk('node', nullable=True)
 
     @declared_attr
     def relationship_fk(cls):
-        return cls._create_foreign_key('relationship', nullable=True)
+        return cls._declare_fk('relationship', nullable=True)
 
     @declared_attr
     def plugin_fk(cls):
-        return cls._create_foreign_key('plugin', nullable=True)
+        return cls._declare_fk('plugin', nullable=True)
 
     @declared_attr
     def execution_fk(cls):
-        return cls._create_foreign_key('execution', nullable=True)
+        return cls._declare_fk('execution', nullable=True)
+
+    # endregion
+
+    # region association proxies
+
+    @declared_attr
+    def node_name(cls):
+        """Required for use by SqlAlchemy queries"""
+        return association_proxy('node', cls.name_column_name())
+
+    @declared_attr
+    def relationship_name(cls):
+        """Required for use by SqlAlchemy queries"""
+        return association_proxy('relationship', cls.name_column_name())
+
+    @declared_attr
+    def execution_name(cls):
+        """Required for use by SqlAlchemy queries"""
+        return association_proxy('execution', cls.name_column_name())
 
     # endregion
 
@@ -298,3 +309,11 @@ class TaskBase(ModelMixin):
     @staticmethod
     def retry(message=None, retry_interval=None):
         raise TaskRetryException(message, retry_interval=retry_interval)
+
+    __private_fields__ = ['node_fk',
+                          'relationship_fk',
+                          'plugin_fk',
+                          'execution_fk',
+                          'node_name',
+                          'relationship_name',
+                          'execution_name']

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/0f040a2b/aria/modeling/relationship.py
----------------------------------------------------------------------
diff --git a/aria/modeling/relationship.py b/aria/modeling/relationship.py
deleted file mode 100644
index 2fdbe0c..0000000
--- a/aria/modeling/relationship.py
+++ /dev/null
@@ -1,421 +0,0 @@
-# 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, unused-argument
-
-from functools import wraps
-
-from sqlalchemy.orm import relationship, backref
-from sqlalchemy.orm.collections import attribute_mapped_collection
-from sqlalchemy.ext.declarative import declared_attr
-from sqlalchemy import (
-    Column,
-    ForeignKey,
-    Integer,
-    Table
-)
-
-from ..utils import formatting
-
-
-def fk(other_table,
-       nullable=False):
-    """
-    Decorator for 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. If you do not do so, SqlAlchemy will fail to create
-    the relationship 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.
-
-    :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
-    """
-
-    def decorator(fn):
-        @declared_attr
-        @wraps(fn)
-        def attr(cls):
-            if not hasattr(cls, '__private_fields__'):
-                cls.__private_fields__ = []
-            cls.__private_fields__.append(fn.__name__)
-            return Column(Integer,
-                          ForeignKey('{table}.id'.format(table=other_table), ondelete='CASCADE'),
-                          nullable=nullable)
-        return attr
-    return decorator
-
-
-def one_to_one_self(fk,
-                    relationship_kwargs=None):
-    """
-    Decorator for 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.
-
-    :param fk: Foreign key name
-    :type fk: basestring
-    :param relationship_kwargs: Extra kwargs for SqlAlchemy `relationship`
-    :type relationship_kwargs: {}
-    """
-
-    scope = locals()
-    def decorator(fn):
-        @declared_attr
-        @wraps(fn)
-        def attr(cls):
-            relationship_kwargs = scope['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(
-                _get_class_for_table(cls, cls.__tablename__).__name__,
-                primaryjoin=primaryjoin,
-                remote_side=remote_side,
-                post_update=True,
-                **relationship_kwargs
-            )
-        return attr
-    return decorator
-
-
-def one_to_many_self(fk,
-                     dict_key=None,
-                     relationship_kwargs=None):
-    """
-    Decorator for 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.
-
-    :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: {}
-    """
-
-    scope = locals()
-    def decorator(fn):
-        @declared_attr
-        @wraps(fn)
-        def attr(cls):
-            relationship_kwargs = scope['relationship_kwargs'] or {}
-
-            relationship_kwargs.setdefault('remote_side', '{cls}.{remote_column}'.format(
-                cls=cls.__name__,
-                remote_column=fk
-            ))
-
-            return _create_relationship(cls, cls.__tablename__, None, relationship_kwargs,
-                                        other_property=False, dict_key=dict_key)
-        return attr
-    return decorator
-
-
-def one_to_one(other_table,
-               fk=None,
-               other_fk=None,
-               other_property=None,
-               relationship_kwargs=None,
-               backref_kwargs=None):
-    """
-    Decorator for 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).
-
-    :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: {}
-    """
-
-    scope = locals()
-    def decorator(fn):
-        @declared_attr
-        @wraps(fn)
-        def attr(cls):
-            backref_kwargs = scope['backref_kwargs'] or {}
-            backref_kwargs.setdefault('uselist', False)
-
-            return _create_relationship(cls, other_table, backref_kwargs, relationship_kwargs,
-                                        other_property, fk=fk, other_fk=other_fk)
-        return attr
-    return decorator
-
-
-def one_to_many(child_table,
-                child_fk=None,
-                dict_key=None,
-                child_property=None,
-                relationship_kwargs=None,
-                backref_kwargs=None):
-    """
-    Decorator for 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 decorator 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.
-
-    :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: {}
-    """
-
-    scope = locals()
-    def decorator(fn):
-        @declared_attr
-        @wraps(fn)
-        def attr(cls):
-            backref_kwargs = scope['backref_kwargs'] or {}
-            backref_kwargs.setdefault('uselist', False)
-
-            return _create_relationship(cls, child_table, backref_kwargs, relationship_kwargs,
-                                        child_property, other_fk=child_fk, dict_key=dict_key)
-        return attr
-    return decorator
-
-
-def many_to_one(parent_table,
-                fk=None,
-                parent_fk=None,
-                parent_property=None,
-                relationship_kwargs=None,
-                backref_kwargs=None):
-    """
-    Decorator for 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 decorator 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 list; if you need it to be a dict, use
-    the :func:`one_to_many` decorator on that model instead.
-
-    :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: {}
-    """
-
-    scope = locals()
-    def decorator(fn):
-        @declared_attr
-        @wraps(fn)
-        def attr(cls):
-            parent_property = scope['parent_property']
-            if parent_property is None:
-                parent_property = formatting.pluralize(cls.__tablename__)
-
-            backref_kwargs = scope['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 _create_relationship(cls, parent_table, backref_kwargs, relationship_kwargs,
-                                        parent_property, fk=fk, other_fk=parent_fk)
-        return attr
-    return decorator
-
-
-def many_to_many(other_table,
-                 prefix=None,
-                 dict_key=None,
-                 other_property=None,
-                 relationship_kwargs=None,
-                 backref_kwargs=None):
-    """
-    Decorator for 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 decorator 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 list; if you need it to be a dict, use
-    the `many_to_many` decorator again on that model.
-
-    :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: {}
-    """
-
-    scope = locals()
-    def decorator(fn):
-        @declared_attr
-        @wraps(fn)
-        def attr(cls):
-            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)
-
-            other_property = scope['other_property']
-            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 = scope['backref_kwargs'] or {}
-            backref_kwargs.setdefault('uselist', True)
-
-            relationship_kwargs = scope['relationship_kwargs'] or {}
-            relationship_kwargs.setdefault('secondary', _get_secondary_table(
-                cls.metadata,
-                secondary_table,
-                this_column_name,
-                other_column_name,
-                this_foreign_key,
-                other_foreign_key
-            ))
-
-            return _create_relationship(cls, other_table, backref_kwargs, relationship_kwargs,
-                                        other_property, dict_key=dict_key)
-        return attr
-    return decorator
-
-
-def _create_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(
-                                           _get_class_for_table(cls, cls.__tablename__),
-                                           fk))
-
-    elif other_fk:
-        relationship_kwargs.setdefault('foreign_keys',
-                                       lambda: getattr(
-                                           _get_class_for_table(cls, 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(cls, other_table),
-            **relationship_kwargs
-        )
-    else:
-        if other_property is None:
-            other_property = cls.__tablename__
-        backref_kwargs = backref_kwargs or {}
-        return relationship(
-            lambda: _get_class_for_table(cls, other_table),
-            backref=backref(other_property, **backref_kwargs),
-            **relationship_kwargs
-        )
-
-
-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)
-        )
-    )
-
-
-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))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/0f040a2b/aria/modeling/service_changes.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_changes.py b/aria/modeling/service_changes.py
index b83a376..eb7bb27 100644
--- a/aria/modeling/service_changes.py
+++ b/aria/modeling/service_changes.py
@@ -65,33 +65,35 @@ class ServiceUpdateBase(ModelMixin):
 
     @declared_attr
     def execution(cls):
-        return cls._create_many_to_one_relationship('execution')
+        return cls._declare_many_to_one('execution')
 
     @declared_attr
-    def execution_name(cls):
-        return association_proxy('execution', cls.name_column_name())
+    def service(cls):
+        return cls._declare_many_to_one('service', parent_property='updates')
+
+    # region foreign keys
 
     @declared_attr
-    def service(cls):
-        return cls._create_many_to_one_relationship('service',
-                                                    backreference='updates')
+    def execution_fk(cls):
+        return cls._declare_fk('execution', nullable=True)
 
     @declared_attr
-    def service_name(cls):
-        return association_proxy('service', cls.name_column_name())
+    def service_fk(cls):
+        return cls._declare_fk('service')
 
-    # region foreign keys
+    # endregion
 
-    __private_fields__ = ['service_fk',
-                          'execution_fk']
+    # region association proxies
 
     @declared_attr
-    def execution_fk(cls):
-        return cls._create_foreign_key('execution', nullable=True)
+    def execution_name(cls):
+        """Required for use by SqlAlchemy queries"""
+        return association_proxy('execution', cls.name_column_name())
 
     @declared_attr
-    def service_fk(cls):
-        return cls._create_foreign_key('service')
+    def service_name(cls):
+        """Required for use by SqlAlchemy queries"""
+        return association_proxy('service', cls.name_column_name())
 
     # endregion
 
@@ -101,6 +103,11 @@ class ServiceUpdateBase(ModelMixin):
         dep_update_dict['steps'] = [step.to_dict() for step in self.steps]
         return dep_update_dict
 
+    __private_fields__ = ['service_fk',
+                          'execution_fk',
+                          'execution_name',
+                          'service_name']
+
 
 class ServiceUpdateStepBase(ModelMixin):
     """
@@ -133,20 +140,22 @@ class ServiceUpdateStepBase(ModelMixin):
 
     @declared_attr
     def service_update(cls):
-        return cls._create_many_to_one_relationship('service_update',
-                                                    backreference='steps')
+        return cls._declare_many_to_one('service_update', parent_property='steps')
+
+    # region foreign keys
 
     @declared_attr
-    def service_update_name(cls):
-        return association_proxy('service_update', cls.name_column_name())
+    def service_update_fk(cls):
+        return cls._declare_fk('service_update')
 
-    # region foreign keys
+    # endregion
 
-    __private_fields__ = ['service_update_fk']
+    # region association proxies
 
     @declared_attr
-    def service_update_fk(cls):
-        return cls._create_foreign_key('service_update')
+    def service_update_name(cls):
+        """Required for use by SqlAlchemy queries"""
+        return association_proxy('service_update', cls.name_column_name())
 
     # endregion
 
@@ -177,6 +186,9 @@ class ServiceUpdateStepBase(ModelMixin):
             return self.entity_type == 'relationship' and other.entity_type == 'node'
         return False
 
+    __private_fields__ = ['service_update_fk',
+                          'service_update_name']
+
 
 class ServiceModificationBase(ModelMixin):
     """
@@ -201,19 +213,24 @@ class ServiceModificationBase(ModelMixin):
 
     @declared_attr
     def service(cls):
-        return cls._create_many_to_one_relationship('service',
-                                                    backreference='modifications')
+        return cls._declare_many_to_one('service', parent_property='modifications')
+
+    # region foreign keys
 
     @declared_attr
-    def service_name(cls):
-        return association_proxy('service', cls.name_column_name())
+    def service_fk(cls):
+        return cls._declare_fk('service')
 
-    # region foreign keys
+    # endregion
 
-    __private_fields__ = ['service_fk']
+    # region association proxies
 
     @declared_attr
-    def service_fk(cls):
-        return cls._create_foreign_key('service')
+    def service_name(cls):
+        """Required for use by SqlAlchemy queries"""
+        return association_proxy('service', cls.name_column_name())
 
     # endregion
+
+    __private_fields__ = ['service_fk',
+                          'service_name']

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/0f040a2b/aria/modeling/service_common.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py
index 7422211..6214bb2 100644
--- a/aria/modeling/service_common.py
+++ b/aria/modeling/service_common.py
@@ -16,7 +16,6 @@
 # pylint: disable=no-self-argument, no-member, abstract-method
 
 import cPickle as pickle
-import logging
 
 from sqlalchemy import (
     Column,
@@ -68,9 +67,9 @@ class ParameterBase(TemplateModelMixin):
             return None
         try:
             return pickle.loads(self._value)
-        except BaseException:
+        except BaseException as e:
             raise exceptions.ValueFormatException('bad format for parameter of type "{0}": {1}'
-                                                  .format(self.type_name, self._value))
+                                                  .format(self.type_name, self._value), cause=e)
 
     @value.setter
     def value(self, value):
@@ -79,9 +78,9 @@ class ParameterBase(TemplateModelMixin):
         else:
             try:
                 self._value = pickle.dumps(value)
-            except (pickle.PicklingError, TypeError):
-                logging.getLogger('aria').warn('Could not pickle parameter of type "{0}": {1}'
-                                               .format(self.type_name, value))
+            except (pickle.PicklingError, TypeError) as e:
+                #raise exceptions.ValueFormatException('bad format for parameter of type "{0}": {1}'
+                #                                      .format(self.type_name, self.value), cause=e)
                 self._value = pickle.dumps(str(value))
 
     def instantiate(self, container):
@@ -124,20 +123,18 @@ class TypeBase(InstanceModelMixin):
 
     @declared_attr
     def parent(cls):
-        return cls._create_relationship_to_self('parent_type_fk')
+        return cls._declare_one_to_one_self('parent_type_fk')
 
     @declared_attr
     def children(cls):
-        return cls._create_one_to_many_relationship_to_self('parent_type_fk')
+        return cls._declare_one_to_many_self('parent_type_fk')
 
     # region foreign keys
 
-    __private_fields__ = ['parent_type_fk']
-
-    # Type one-to-many to Type
     @declared_attr
     def parent_type_fk(cls):
-        return cls._create_foreign_key('type', nullable=True)
+        """For Type one-to-many to Type"""
+        return cls._declare_fk('type', nullable=True)
 
     # endregion
 
@@ -206,6 +203,8 @@ class TypeBase(InstanceModelMixin):
             types.append(raw_child)
             child._append_raw_children(types)
 
+    __private_fields__ = ['parent_type_fk']
+
 
 class MetadataBase(TemplateModelMixin):
     """
@@ -258,11 +257,10 @@ class PluginSpecificationBase(InstanceModelMixin):
 
     # region foreign keys
 
-    __private_fields__ = ['service_template_fk']
-
     @declared_attr
     def service_template_fk(cls):
-        return cls._create_foreign_key('service_template', nullable=True)
+        """For ServiceTemplate one-to-many to PluginSpecification"""
+        return cls._declare_fk('service_template', nullable=True)
 
     # endregion
 
@@ -272,3 +270,5 @@ class PluginSpecificationBase(InstanceModelMixin):
             if plugin.name == self.name:
                 return plugin
         return None
+
+    __private_fields__ = ['service_template_fk']