Repository: incubator-ariatosca
Updated Branches:
  refs/heads/ARIA-105-integrate-modeling 16a945383 -> f6da64acb


New relationship decorators


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

Branch: refs/heads/ARIA-105-integrate-modeling
Commit: f6da64acbf8baa02be486b0d15a62606662811d0
Parents: 16a9453
Author: Tal Liron <[email protected]>
Authored: Mon Mar 13 20:31:52 2017 -0500
Committer: Tal Liron <[email protected]>
Committed: Mon Mar 13 20:31:52 2017 -0500

----------------------------------------------------------------------
 aria/modeling/mixins.py           |   7 +-
 aria/modeling/relationship.py     | 421 +++++++++++++++++++++++++++++++++
 aria/modeling/service_common.py   |   2 +-
 aria/modeling/service_template.py | 141 +++++------
 4 files changed, 487 insertions(+), 84 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f6da64ac/aria/modeling/mixins.py
----------------------------------------------------------------------
diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py
index b9e5079..9133a85 100644
--- a/aria/modeling/mixins.py
+++ b/aria/modeling/mixins.py
@@ -145,13 +145,12 @@ class ModelMixin(object):
         ))
 
         return cls._create_relationship(cls.__tablename__, None, 
relationship_kwargs,
-                                        backreference='', dict_key=dict_key)
+                                        backreference=False, dict_key=dict_key)
 
     @classmethod
     def _create_one_to_one_relationship(cls,
                                         other_table,
                                         key=None,
-                                        foreign_key=None,
                                         backreference=None,
                                         backref_kwargs=None,
                                         relationship_kwargs=None):
@@ -159,7 +158,7 @@ class ModelMixin(object):
         backref_kwargs.setdefault('uselist', False)
 
         return cls._create_relationship(other_table, backref_kwargs, 
relationship_kwargs,
-                                        backreference, key=key, 
foreign_key=foreign_key)
+                                        backreference, key=key)
 
     @classmethod
     def _create_one_to_many_relationship(cls,
@@ -282,7 +281,7 @@ class ModelMixin(object):
             relationship_kwargs.setdefault('collection_class',
                                            
attribute_mapped_collection(dict_key))
 
-        if backreference == '':
+        if backreference is False:
             return relationship(
                 lambda: cls._get_cls_by_tablename(table),
                 **relationship_kwargs

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f6da64ac/aria/modeling/relationship.py
----------------------------------------------------------------------
diff --git a/aria/modeling/relationship.py b/aria/modeling/relationship.py
new file mode 100644
index 0000000..2fdbe0c
--- /dev/null
+++ b/aria/modeling/relationship.py
@@ -0,0 +1,421 @@
+# 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/f6da64ac/aria/modeling/service_common.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py
index e6b3c8d..7422211 100644
--- a/aria/modeling/service_common.py
+++ b/aria/modeling/service_common.py
@@ -191,7 +191,7 @@ class TypeBase(InstanceModelMixin):
         self._append_raw_children(types)
         return types
 
-    def dump(self,):
+    def dump(self):
         context = ConsumptionContext.get_thread_local()
         if self.name:
             console.puts(context.style.type(self.name))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f6da64ac/aria/modeling/service_template.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_template.py 
b/aria/modeling/service_template.py
index 09da8e1..d2d6c4b 100644
--- a/aria/modeling/service_template.py
+++ b/aria/modeling/service_template.py
@@ -38,6 +38,8 @@ from . import (
     types as modeling_types
 )
 
+from . import relationship
+
 
 class ServiceTemplateBase(TemplateModelMixin): # pylint: 
disable=too-many-public-methods
     """
@@ -100,76 +102,70 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: 
disable=too-many-public
     description = Column(Text)
     main_file_name = Column(Text)
 
-    @declared_attr
+    @relationship.many_to_many('metadata', dict_key='name')
     def meta_data(cls):
         # Warning! We cannot use the attr name "metadata" because it's used by 
SqlAlchemy!
-        return cls._create_many_to_many_relationship('metadata', 
dict_key='name')
+        pass
 
-    @declared_attr
+    @relationship.one_to_many('node_template')
     def node_templates(cls):
-        return cls._create_one_to_many_relationship('node_template')
+        pass
 
-    @declared_attr
+    @relationship.one_to_many('group_template')
     def group_templates(cls):
-        return cls._create_one_to_many_relationship('group_template')
+        pass
 
-    @declared_attr
+    @relationship.one_to_many('policy_template')
     def policy_templates(cls):
-        return cls._create_one_to_many_relationship('policy_template')
+        pass
 
-    @declared_attr
+    @relationship.one_to_one('substitution_template')
     def substitution_template(cls):
-        return cls._create_one_to_one_relationship('substitution_template')
+        pass
 
-    @declared_attr
+    @relationship.many_to_many('parameter', prefix='inputs', dict_key='name')
     def inputs(cls):
-        return cls._create_many_to_many_relationship('parameter', 
table_prefix='inputs',
-                                                     dict_key='name')
+        pass
 
-    @declared_attr
+    @relationship.many_to_many('parameter', prefix='outputs', dict_key='name')
     def outputs(cls):
-        return cls._create_many_to_many_relationship('parameter', 
table_prefix='outputs',
-                                                     dict_key='name')
+        pass
 
-    @declared_attr
+    @relationship.one_to_many('operation_template', dict_key='name')
     def workflow_templates(cls):
-        return cls._create_one_to_many_relationship('operation_template', 
dict_key='name')
+        pass
 
-    @declared_attr
+    @relationship.one_to_many('plugin_specification')
     def plugin_specifications(cls):
-        return cls._create_one_to_many_relationship('plugin_specification')
+        pass
 
-    @declared_attr
+    @relationship.one_to_one('type', fk='node_type_fk', other_property=False)
     def node_types(cls):
-        return cls._create_one_to_one_relationship('type', key='node_type_fk', 
backreference='')
+        pass
 
-    @declared_attr
+    @relationship.one_to_one('type', fk='group_type_fk', other_property=False)
     def group_types(cls):
-        return cls._create_one_to_one_relationship('type', 
key='group_type_fk', backreference='')
+        pass
 
-    @declared_attr
+    @relationship.one_to_one('type', fk='policy_type_fk', other_property=False)
     def policy_types(cls):
-        return cls._create_one_to_one_relationship('type', 
key='policy_type_fk', backreference='')
+        pass
 
-    @declared_attr
+    @relationship.one_to_one('type', fk='relationship_type_fk', 
other_property=False)
     def relationship_types(cls):
-        return cls._create_one_to_one_relationship('type', 
key='relationship_type_fk',
-                                                   backreference='')
+        pass
 
-    @declared_attr
+    @relationship.one_to_one('type', fk='capability_type_fk', 
other_property=False)
     def capability_types(cls):
-        return cls._create_one_to_one_relationship('type', 
key='capability_type_fk',
-                                                   backreference='')
+        pass
 
-    @declared_attr
+    @relationship.one_to_one('type', fk='interface_type_fk', 
other_property=False)
     def interface_types(cls):
-        return cls._create_one_to_one_relationship('type', 
key='interface_type_fk',
-                                                   backreference='')
+        pass
 
-    @declared_attr
+    @relationship.one_to_one('type', fk='artifact_type_fk', 
other_property=False)
     def artifact_types(cls):
-        return cls._create_one_to_one_relationship('type', 
key='artifact_type_fk',
-                                                   backreference='')
+        pass
 
     # region orchestration
 
@@ -180,54 +176,37 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: 
disable=too-many-public
 
     # region foreign keys
 
-    __private_fields__ = ['substitution_template_fk',
-                          'node_type_fk',
-                          'group_type_fk',
-                          'policy_type_fk',
-                          'relationship_type_fk',
-                          'capability_type_fk',
-                          'interface_type_fk',
-                          'artifact_type_fk']
-
-    # ServiceTemplate one-to-one to SubstitutionTemplate
-    @declared_attr
+    @relationship.fk('substitution_template', nullable=True)
     def substitution_template_fk(cls):
-        return cls._create_foreign_key('substitution_template', nullable=True)
+        """For ServiceTemplate one-to-one to SubstitutionTemplate"""
 
-    # ServiceTemplate one-to-one to Type
-    @declared_attr
+    @relationship.fk('type', nullable=True)
     def node_type_fk(cls):
-        return cls._create_foreign_key('type', nullable=True)
+        """For ServiceTemplate one-to-one to Type"""
 
-    # ServiceTemplate one-to-one to Type
-    @declared_attr
+    @relationship.fk('type', nullable=True)
     def group_type_fk(cls):
-        return cls._create_foreign_key('type', nullable=True)
+        """For ServiceTemplate one-to-one to Type"""
 
-    # ServiceTemplate one-to-one to Type
-    @declared_attr
+    @relationship.fk('type', nullable=True)
     def policy_type_fk(cls):
-        return cls._create_foreign_key('type', nullable=True)
+        """For ServiceTemplate one-to-one to Type"""
 
-    # ServiceTemplate one-to-one to Type
-    @declared_attr
+    @relationship.fk('type', nullable=True)
     def relationship_type_fk(cls):
-        return cls._create_foreign_key('type', nullable=True)
+        """For ServiceTemplate one-to-one to Type"""
 
-    # ServiceTemplate one-to-one to Type
-    @declared_attr
+    @relationship.fk('type', nullable=True)
     def capability_type_fk(cls):
-        return cls._create_foreign_key('type', nullable=True)
+        """For ServiceTemplate one-to-one to Type"""
 
-    # ServiceTemplate one-to-one to Type
-    @declared_attr
+    @relationship.fk('type', nullable=True)
     def interface_type_fk(cls):
-        return cls._create_foreign_key('type', nullable=True)
+        """For ServiceTemplate one-to-one to Type"""
 
-    # ServiceTemplate one-to-one to Type
-    @declared_attr
+    @relationship.fk('type', nullable=True)
     def artifact_type_fk(cls):
-        return cls._create_foreign_key('type', nullable=True)
+        """For ServiceTemplate one-to-one to Type"""
 
     # endregion
 
@@ -343,6 +322,10 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: 
disable=too-many-public
         utils.coerce_dict_values(container, self.workflow_templates, 
report_issues)
 
     def dump(self):
+        #print dir(self)
+        #from inspect import getdoc
+        #print getdoc(self.__class__.substitution_template_fk)
+        #exit()
         context = ConsumptionContext.get_thread_local()
         if self.description is not None:
             console.puts(context.style.meta(self.description))
@@ -980,17 +963,17 @@ class RequirementTemplateBase(TemplateModelMixin):
     @declared_attr
     def target_node_type(cls):
         return cls._create_many_to_one_relationship('type', 
key='target_node_type_fk',
-                                                    backreference='')
+                                                    backreference=False)
 
     @declared_attr
     def target_node_template(cls):
         return cls._create_one_to_one_relationship('node_template', 
key='target_node_template_fk',
-                                                   backreference='')
+                                                   backreference=False)
 
     @declared_attr
     def target_capability_type(cls):
         return cls._create_one_to_one_relationship('type', 
key='target_capability_type_fk',
-                                                   backreference='')
+                                                   backreference=False)
 
     target_capability_name = Column(Text)
     target_node_template_constraints = 
Column(modeling_types.StrictList(FunctionType))
@@ -1196,13 +1179,13 @@ class RelationshipTemplateBase(TemplateModelMixin):
 
     def instantiate(self, container):
         from . import models
-        relationship = models.Relationship(type=self.type,
-                                           relationship_template=self)
+        relationship_model = models.Relationship(type=self.type,
+                                                 relationship_template=self)
         utils.instantiate_dict(container,
-                               relationship.properties, self.properties)
+                               relationship_model.properties, self.properties)
         utils.instantiate_dict(container,
-                               relationship.interfaces, 
self.interface_templates)
-        return relationship
+                               relationship_model.interfaces, 
self.interface_templates)
+        return relationship_model
 
     def validate(self):
         # TODO: either type or name must be set

Reply via email to