Github user mxmrlv commented on a diff in the pull request:

    https://github.com/apache/incubator-ariatosca/pull/31#discussion_r90778979
  
    --- Diff: aria/storage/structures.py ---
    @@ -27,281 +27,189 @@
         * Model - abstract model implementation.
     """
     import json
    -from itertools import count
    -from uuid import uuid4
    -
    -from .exceptions import StorageError
    -from ..logger import LoggerMixin
    -from ..utils.validation import ValidatorMixin
    -
    -__all__ = (
    -    'uuid_generator',
    -    'Field',
    -    'IterField',
    -    'PointerField',
    -    'IterPointerField',
    -    'Model',
    -    'Storage',
    +
    +from sqlalchemy import VARCHAR
    +from sqlalchemy.ext.mutable import Mutable
    +from sqlalchemy.orm import relationship, backref
    +from sqlalchemy.ext.declarative import declarative_base
    +# pylint: disable=unused-import
    +from sqlalchemy.ext.associationproxy import association_proxy
    +from sqlalchemy import (
    +    schema,
    +    Column,
    +    Integer,
    +    Text,
    +    DateTime,
    +    Boolean,
    +    Enum,
    +    String,
    +    PickleType,
    +    Float,
    +    TypeDecorator,
    +    ForeignKey,
    +    orm,
     )
     
    +Model = declarative_base()
     
    -def uuid_generator():
    -    """
    -    wrapper function which generates ids
    -    """
    -    return str(uuid4())
     
    +def foreign_key(foreign_key_column, nullable=False):
    +    """Return a ForeignKey object with the relevant
     
    -class Field(ValidatorMixin):
    +    :param foreign_key_column: Unique id column in the parent table
    +    :param nullable: Should the column be allowed to remain empty
         """
    -    A single field implementation
    +    return Column(
    +        ForeignKey(foreign_key_column, ondelete='CASCADE'),
    +        nullable=nullable
    +    )
    +
    +
    +def one_to_many_relationship(child_class,
    +                             parent_class,
    +                             foreign_key_column,
    +                             backreference=None):
    +    """Return a one-to-many SQL relationship object
    +    Meant to be used from inside the *child* object
    +
    +    :param parent_class: Class of the parent table
    +    :param child_class: Class of the child table
    +    :param foreign_key_column: The column of the foreign key
    +    :param backreference: The name to give to the reference to the child
         """
    -    NO_DEFAULT = 'NO_DEFAULT'
    -
    -    try:
    -        # python 3 syntax
    -        _next_id = count().__next__
    -    except AttributeError:
    -        # python 2 syntax
    -        _next_id = count().next
    -    _ATTRIBUTE_NAME = '_cache_{0}'.format
    -
    -    def __init__(
    -            self,
    -            type=None,
    -            choices=(),
    -            validation_func=None,
    -            default=NO_DEFAULT,
    -            **kwargs):
    -        """
    -        Simple field manager.
    -
    -        :param type: possible type of the field.
    -        :param choices: a set of possible field values.
    -        :param default: default field value.
    -        :param kwargs: kwargs to be passed to next in line classes.
    -        """
    -        self.type = type
    -        self.choices = choices
    -        self.default = default
    -        self.validation_func = validation_func
    -        super(Field, self).__init__(**kwargs)
    -
    -    def __get__(self, instance, owner):
    -        if instance is None:
    -            return self
    -        field_name = self._field_name(instance)
    -        try:
    -            return getattr(instance, self._ATTRIBUTE_NAME(field_name))
    -        except AttributeError as exc:
    -            if self.default == self.NO_DEFAULT:
    -                raise AttributeError(
    -                    str(exc).replace(self._ATTRIBUTE_NAME(field_name), 
field_name))
    -
    -        default_value = self.default() if callable(self.default) else 
self.default
    -        setattr(instance, self._ATTRIBUTE_NAME(field_name), default_value)
    -        return default_value
    -
    -    def __set__(self, instance, value):
    -        field_name = self._field_name(instance)
    -        self.validate_value(field_name, value, instance)
    -        setattr(instance, self._ATTRIBUTE_NAME(field_name), value)
    -
    -    def validate_value(self, name, value, instance):
    -        """
    -        Validates the value of the field.
    -
    -        :param name: the name of the field.
    -        :param value: the value of the field.
    -        :param instance: the instance containing the field.
    -        """
    -        if self.default != self.NO_DEFAULT and value == self.default:
    -            return
    -        if self.type:
    -            self.validate_instance(name, value, self.type)
    -        if self.choices:
    -            self.validate_in_choice(name, value, self.choices)
    -        if self.validation_func:
    -            self.validation_func(name, value, instance)
    -
    -    def _field_name(self, instance):
    -        """
    -        retrieves the field name from the instance.
    -
    -        :param Field instance: the instance which holds the field.
    -        :return: name of the field
    -        :rtype: basestring
    -        """
    -        for name, member in vars(instance.__class__).iteritems():
    -            if member is self:
    -                return name
    +    backreference = backreference or child_class.__tablename__
    +    return relationship(
    +        parent_class,
    +        primaryjoin=lambda: parent_class.storage_id == foreign_key_column,
    +        # The following line make sure that when the *parent* is
    +        # deleted, all its connected children are deleted as well
    +        backref=backref(backreference, cascade='all')
    +    )
     
     
    -class IterField(Field):
    +class Dict(TypeDecorator):
         """
    -    Represents an iterable field.
    +    Dict representation of type.
         """
    -    def __init__(self, **kwargs):
    -        """
    -        Simple iterable field manager.
    -        This field type don't have choices option.
     
    -        :param kwargs: kwargs to be passed to next in line classes.
    -        """
    -        super(IterField, self).__init__(choices=(), **kwargs)
    +    def process_literal_param(self, value, dialect):
    +        pass
     
    -    def validate_value(self, name, values, *args):
    -        """
    -        Validates the value of each iterable value.
    +    @property
    +    def python_type(self):
    +        return dict
     
    -        :param name: the name of the field.
    -        :param values: the values of the field.
    -        """
    -        for value in values:
    -            self.validate_instance(name, value, self.type)
    +    impl = VARCHAR
     
    +    def process_bind_param(self, value, dialect):
    +        if value is not None:
    +            value = json.dumps(value)
    +        return value
     
    -class PointerField(Field):
    -    """
    -    A single pointer field implementation.
    +    def process_result_value(self, value, dialect):
    +        if value is not None:
    +            value = json.loads(value)
    +        return value
     
    -    Any PointerField points via id to another document.
    +
    +class MutableDict(Mutable, dict):
    +    """
    +    Enables tracking for dict values.
         """
    +    @classmethod
    +    def coerce(cls, key, value):
    +        "Convert plain dictionaries to MutableDict."
     
    -    def __init__(self, type, **kwargs):
    -        assert issubclass(type, Model)
    -        super(PointerField, self).__init__(type=type, **kwargs)
    +        if not isinstance(value, MutableDict):
    +            if isinstance(value, dict):
    +                return MutableDict(value)
     
    +            # this call will raise ValueError
    +            return Mutable.coerce(key, value)
    +        else:
    +            return value
     
    -class IterPointerField(IterField, PointerField):
    -    """
    -    An iterable pointers field.
    +    def __setitem__(self, key, value):
    +        "Detect dictionary set events and emit change events."
     
    -    Any IterPointerField points via id to other documents.
    -    """
    -    pass
    +        dict.__setitem__(self, key, value)
    +        self.changed()
     
    +    def __delitem__(self, key):
    +        "Detect dictionary del events and emit change events."
     
    -class Model(object):
    -    """
    -    Base class for all of the storage models.
    -    """
    -    id = None
    +        dict.__delitem__(self, key)
    +        self.changed()
     
    -    def __init__(self, **fields):
    -        """
    -        Abstract class for any model in the storage.
    -        The Initializer creates attributes according to the (keyword 
arguments) that given
    -        Each value is validated according to the Field.
    -        Each model has to have and ID Field.
     
    -        :param fields: each item is validated and transformed into 
instance attributes.
    -        """
    -        self._assert_model_have_id_field(**fields)
    -        missing_fields, unexpected_fields = self._setup_fields(fields)
    +class SQLModelBase(Model):
    +    """
    +    Abstract base class for all SQL models that allows [de]serialization
    +    """
    +    # SQLAlchemy syntax
    +    __abstract__ = True
     
    -        if missing_fields:
    -            raise StorageError(
    -                'Model {name} got missing keyword arguments: 
{fields}'.format(
    -                    name=self.__class__.__name__, fields=missing_fields))
    +    # Does the class represent a resource (Blueprint, Deployment, etc.) or 
a
    +    # management table (User, Tenant, etc.), as they are handled 
differently
    +    is_resource = False
    --- End diff --
    
    remove


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastruct...@apache.org or file a JIRA ticket
with INFRA.
---

Reply via email to