require works well in sqlalchemy storage
[tiramisu.git] / tiramisu / storage / sqlalchemy / option.py
index 54c3256..1b7fbca 100644 (file)
 from tiramisu.i18n import _
 
 from sqlalchemy.ext.declarative import declarative_base, declared_attr
+from sqlalchemy.ext.associationproxy import association_proxy
 from sqlalchemy import create_engine, Column, Integer, String, Boolean, \
     PickleType, ForeignKey, Table
 from sqlalchemy.orm import relationship, backref
 from sqlalchemy.orm import sessionmaker
+from sqlalchemy.orm.collections import attribute_mapped_collection
 
 
 #FIXME
@@ -39,68 +41,91 @@ SqlAlchemyBase = declarative_base()
 
 
 #_Base : object dans la base de donnée
-# => _RequireOption => il y a une liste d'espect dans _RequireExpected
 # => _PropertyOption => liste des propriétés
-# => _Information => dictionnaire avec clef valeur
 # => _CallbackParam avec des Options
-require_table = Table('require', SqlAlchemyBase.metadata,
-                      Column('left_id', Integer, ForeignKey('requireoption.id')),
-                      Column('right_id', Integer, ForeignKey('baseoption.id'))
-                      )
-
-
-class _RequireExpected(SqlAlchemyBase):
-    __tablename__ = 'expected'
+def load_requires(collection_type, proxy):
+    def getter(obj):
+        if obj is None:
+            return None
+        ret = []
+        requires = getattr(obj, proxy.value_attr)
+        for require in requires:
+            option = session.query(_Base).filter_by(id=require.option).first()
+            ret.append(tuple([option, require.expected, require.action, require.inverse, require.transitive, require.same_action]))
+        return tuple(ret)
+
+    def setter(obj, value):
+        setattr(obj, proxy.value_attr, value)
+    return getter, setter
+
+
+class _Require(SqlAlchemyBase):
+    __tablename__ = "require"
     id = Column(Integer, primary_key=True)
-    expected = Column(PickleType)
-    require = Column(Integer, ForeignKey('requireoption.id'))
+    requires_id = Column(Integer, ForeignKey("baseoption.id"), nullable=False)
+    requires = relationship('_RequireOption')
 
-    def __init__(self, expected):
-        self.expected = expected
+    def __init__(self, requires):
+        for require in requires:
+            self.requires.append(_RequireOption(require))
 
 
 class _RequireOption(SqlAlchemyBase):
     __tablename__ = 'requireoption'
     id = Column(Integer, primary_key=True)
-    option = relationship('_Base', lazy='joined', cascade="all, delete-orphan")
-    #option = relationship('_Base')
-    expected = relationship("_RequireExpected")
+    require_id = Column(Integer, ForeignKey("require.id"), nullable=False)
+    option = Column(Integer, nullable=False)
+    _expected = relationship("_RequireExpected", collection_class=list,
+                             cascade="all, delete-orphan")
+    expected = association_proxy("_expected", "expected")
+    #expected = Column(String)
     action = Column(String, nullable=False)
     inverse = Column(Boolean, default=False)
     transitive = Column(Boolean, default=True)
     same_action = Column(Boolean, default=True)
 
-    def __init__(self, option, expected, action, inverse, transitive,
-                 same_action):
-        #self.r_opt = option.id
-        self.option = option
-        for expect in expected:
-            self.expected.append(_RequireExpected(expect))
+    def __init__(self, values):
+        option, expected, action, inverse, transitive, same_action = values
+        self.option = option.id
+        self.expected = expected
         self.action = action
         self.inverse = inverse
         self.transitive = transitive
         self.same_action = same_action
 
-    def get_expected(self):
-        for expected in self.expected:
-            yield(expected.expected)
 
-    #def get_option(self, config):
-    #    return config.cfgimpl_get_description().impl_get_opt_by_id(self.r_opt)
+class _RequireExpected(SqlAlchemyBase):
+    __tablename__ = 'expected'
+    id = Column(Integer, primary_key=True)
+    require = Column(Integer, ForeignKey('requireoption.id'), nullable=False)
+    expected = Column(PickleType)
+
+    def __init__(self, expected):
+        #FIXME ne pas creer plusieurs fois la meme _expected_
+        #FIXME pareil avec calc_properties
+        self.expected = expected
+
+
+class _CalcProperties(SqlAlchemyBase):
+    __tablename__ = 'calcproperty'
+    id = Column(Integer, primary_key=True)
+    require = Column(Integer, ForeignKey('baseoption.id'), nullable=False)
+    name = Column(PickleType)
+
+    def __init__(self, name):
+        #FIXME ne pas creer plusieurs fois la meme _expected_
+        #FIXME pareil avec calc_properties
+        self.name = name
 
 
 #____________________________________________________________
 #
 # properties
-property_table = Table('property', SqlAlchemyBase.metadata,
-                       Column('left_id', Integer, ForeignKey('propertyoption.name')),
-                       Column('right_id', Integer, ForeignKey('baseoption.id'))
-                       )
-
-
 class _PropertyOption(SqlAlchemyBase):
     __tablename__ = 'propertyoption'
-    name = Column(String, primary_key=True)
+    id = Column(Integer, primary_key=True)
+    option = Column(Integer, ForeignKey('baseoption.id'), nullable=False)
+    name = Column(String)
 
     def __init__(self, name):
         self.name = name
@@ -112,7 +137,7 @@ class _PropertyOption(SqlAlchemyBase):
 class _Information(SqlAlchemyBase):
     __tablename__ = 'information'
     id = Column(Integer, primary_key=True)
-    option = Column(Integer, ForeignKey('baseoption.id'))
+    option = Column(Integer, ForeignKey('baseoption.id'), nullable=False)
     key = Column(String)
     value = Column(PickleType)
 
@@ -184,11 +209,15 @@ class _Base(SqlAlchemyBase):
     __tablename__ = 'baseoption'
     id = Column(Integer, primary_key=True)
     _name = Column(String)
-    _informations = relationship('_Information')
+    #FIXME not autoload
+    _infos = relationship("_Information",
+                          collection_class=attribute_mapped_collection('key'),
+                          cascade="all, delete-orphan")
+    _informations = association_proxy("_infos", "value")
     _default = Column(PickleType)
     _default_multi = Column(PickleType)
-    _requires = relationship('_RequireOption', secondary=require_table,
-                             backref=backref('self_option', enable_typechecks=False))
+    _reqs = relationship("_Require", collection_class=list)
+    _requires = association_proxy("_reqs", "requires", getset_factory=load_requires)
     _multi = Column(Boolean)
     _multitype = Column(String)
     _callback = Column(PickleType)
@@ -197,8 +226,14 @@ class _Base(SqlAlchemyBase):
     _validator_params = relationship('_CallbackParam')
     _parent = Column(Integer, ForeignKey('baseoption.id'))
     _children = relationship('BaseOption', enable_typechecks=False)
-    _properties = relationship('_PropertyOption', secondary=property_table,
-                               backref=backref('options', enable_typechecks=False))
+    #FIXME pas 2 fois la meme properties dans la base ...
+    #FIXME not autoload
+    #FIXME normalement tuple ... transforme en set !
+    _props = relationship("_PropertyOption", collection_class=set)
+    _properties = association_proxy("_props", "name")
+    #FIXME fusion avec expected
+    _calc_props = relationship("_CalcProperties", collection_class=set)
+    _calc_properties = association_proxy("_calc_props", "name")
     _warnings_only = Column(Boolean)
     _readonly = Column(Boolean, default=False)
     _consistencies = relationship('_Consistency', secondary=consistency_table,
@@ -206,7 +241,6 @@ class _Base(SqlAlchemyBase):
     _choice_values = Column(PickleType)
     _choice_open_values = Column(Boolean)
     _type = Column(String(50))
-    _r_option = Column(Integer, ForeignKey('requireoption.id'))
     __mapper_args__ = {
         'polymorphic_identity': 'option',
         'polymorphic_on': _type
@@ -227,8 +261,8 @@ class _Base(SqlAlchemyBase):
             prop_obj = _PropertyOption(propname)
         return prop_obj
 
-    def _add_require(self, require):
-        self._requires.append(_RequireOption(*require))
+    #def _add_require(self, require):
+    #    self._requires.append(_RequireOption(*require))
 
     def _add_callback(self, key, values):
         self._callback_params.append(_CallbackParam(key, values))
@@ -282,7 +316,6 @@ class StorageOptionDescription(object):
 
     def impl_get_path_by_opt(self, opt):
         try:
-            print opt, type(opt)
             return self._cache_paths[1][self._cache_paths[0].index(opt.id)]
         except ValueError:
             raise AttributeError(_('no option {0} found').format(opt))