On Sat, Nov 25, 2017 at 9:04 AM, Olaf <bastien...@gmail.com> wrote:

> Hello,
>
> I tried to implement the table_per_related.py solution in my code but I
> face new problems related to metaclasses.
>


I haven't looked deeply yet but at first this looks like an overuse of
metaclasses.    The reason metaclasses are often useful (at least in my
experience) is that they give you a "hook" that fires off when a class is
first set up.   With SQLAlchemy ORM mappings, you have other, simpler ways
of intercepting this event, most directly the 'instrument_class" event:


Base = declarative_base()
@event.listens_for(Base, "instrument_class", propagate=True)def
on_new_class(mapper, cls_):
    " ... "


the docstring here is at
http://docs.sqlalchemy.org/en/latest/orm/events.html?highlight=instrument_class#sqlalchemy.orm.events.InstanceEvents.init,
but note you can apply this event to any Base, arbitrary mixin class or
whatever you want, as long as you add propagate=True it will take place
automatically for all inheriting classes.

Similarly, intercepting the __init__ of an existing class is very simple
using the "init" event:
http://docs.sqlalchemy.org/en/latest/orm/events.html?highlight=instrument_class#sqlalchemy.orm.events.InstanceEvents.init

where again, you can apply this to a Base or any mixin or whatever you want
with propagate=True in order for it to be applied automatically to all
inheriting classes.

If you can do away with having to create additional metaclasses, you avoid
a lot of the complexity in getting those all to work.




>
> Here is a schema to help you to understand the code :
>
>
> <https://lh3.googleusercontent.com/-NesWeuhrl1M/Whl4BN4xHMI/AAAAAAAAAPU/tU_Pd_T03cwsWHtamsb8GY7PjkLgotScwCLcBGAs/s1600/schema_animals%2526cars.png>
>
>
> My animal and car classes have to be inherited from a generic class called
> "Stockable" which use a metaclass called "MetaBase". In addition to this,
> my classes Animal and Car must also be inherited from the Base class of
> SQLAlchemy.
>
> Now, I would like to be able to do this kind of things in order to map the
> "Description" attribute of these classes :
>
> class Animal(Stockable, Base, metaclass=DeclDescription)
>
> or
>
> class Animal(Stockable, Base, HasDescription)
>
> I tried several things but until now, impossible to make all these classes
> work together...
>
> Here is the code :
>
> from sqlalchemy import create_engine
> from sqlalchemy.orm import sessionmaker
> from sqlalchemy.ext.declarative import as_declarative, declared_attr
> from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta
> from sqlalchemy import Column, Integer, String, ForeignKey
> from sqlalchemy.orm import relationship
>
>
> class MetaBase(type):
>
>
>     def __init__(cls, nom, bases, contenu):
>         type.__init__(cls, nom, bases, contenu)
>         print("GenericMetaClass - __init__")
>
>
> class DeclMetaBase(MetaBase, DeclarativeMeta):
>
>
>     def __init__(cls, nom, bases, contenu):
>         MetaBase.__init__(cls, nom, bases, contenu)
>         DeclarativeMeta.__init__(cls, nom, bases, contenu)
>
>
> @as_declarative()
> class Base(metaclass=DeclMetaBase):
>     """Base class which provides automated table name
>     and surrogate primary key column.
>
>
>     """
>     @declared_attr
>     def __tablename__(cls):
>         return cls.__name__.lower()
>     id = Column(Integer, primary_key=True)
>
>
> class Stockable(metaclass=MetaBase):
>
>
>     def __init__(self):
>         print("Stockable - __init__")
>
>
> class Description(Stockable):
>
>
>     name = Column(String)
>
>
>     def __init__(self, name):
>         self.name = name
>         self.parent = None
>
>
>     def __repr__(self):
>         return self.name
>
>
> class HasDescription(object):
>
>
>     @declared_attr
>     def descriptions(cls):
>         cls.Description = type(
>             "%sDescription" % cls.__name__,
>             (Description, Base,),
>             dict(
>                 __tablename__="%s_descriptions" %
>                             cls.__tablename__,
>                 parent_id=Column(Integer,
>                             ForeignKey("%s.id" % cls.__tablename__)),
>                 parent=relationship(cls)
>             )
>         )
>         return relationship(cls.Description)
>
>
> class DeclDescription(DeclMetaBase, HasDescription):
>
>
>     def __init__(cls, nom, bases, contenu):
>         DeclMetaBase.__init__(cls, nom, bases, contenu)
>
>
> class Animal(Stockable, Base, metaclass=DeclDescription):
>
>
>     tablename = "animals"
>     id = Column(Integer, primary_key=True)
>     name = Column(String)
>
>
>     def __init__(self, name):
>          self.name = name
>
>
>     def __repr__(self):
>         return self.name
>
>
> class Car(Stockable, Base, metaclass=DeclDescription):
>
>
>     tablename = "cars"
>     id = Column(Integer, primary_key=True)
>     name = Column(String)
>
>
>     def __init__(self, name):
>         self.name = name
>
>
>     def __repr__(self):
>         return self.name
>
>
> engine = create_engine('sqlite://', echo=True)
>
>
> Base.metadata.create_all(engine)
>
>
> Session = sessionmaker(bind=engine)
> session = Session()
>
>
> rex = Animal("Rex")
> swift = Car("Suzuki Swift")
>
>
> d1 = Description("Rex is a good dog.")
> d2 = Description("What a beautiful car !")
>
>
> rex.description = d1
> d1.parent = rex
> swift.description = d2
> d2.parent = swift
>
>
> session.add(rex)
> session.add(swift)
>
>
> session.commit()
>
>
> And the error :
>
> Traceback (most recent call last):
>   File "C:\Users\user\Desktop\sa_tests\Generic
> Associations\description.py", line 21, in <module>
>     class Base(metaclass=DeclMetaBase):
>   File "C:\Users\user\Desktop\sa_tests\Generic
> Associations\description.py", line 18, in _init_
>     DeclarativeMeta.__init__(cls, nom, bases, contenu)
>   File "C:\Python34\lib\site-packages\sqlalchemy\ext\declarative\api.py",
> line 64, in _init_
>     as_declarative(cls, classname, cls.__dict_)
>   File "C:\Python34\lib\site-packages\sqlalchemy\ext\declarative\base.py",
> line 88, in _as_declarative
>     MapperConfig.setup_mapping(cls, classname, dict)
>   File "C:\Python34\lib\site-packages\sqlalchemy\ext\declarative\base.py",
> line 103, in setup_mapping
>     cfg_cls(cls, classname, dict)
>   File "C:\Python34\lib\site-packages\sqlalchemy\ext\declarative\base.py",
> line 125, in _init_
>     clsregistry.add_class(self.classname, self.cls)
>   File "C:\Python34\lib\site-packages\sqlalchemy\ext\
> declarative\clsregistry.py", line 34, in add_class
>     if classname in cls._decl_class_registry:
> AttributeError: type object 'Base' has no attribute '_decl_class_registry'
>
>
> Does someone have an idea ?
>
> Thank you !
>
> Olaf
>
> --
> SQLAlchemy -
> The Python SQL Toolkit and Object Relational Mapper
>
> http://www.sqlalchemy.org/
>
> To post example code, please provide an MCVE: Minimal, Complete, and
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full
> description.
> ---
> You received this message because you are subscribed to the Google Groups
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to sqlalchemy+unsubscr...@googlegroups.com.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> Visit this group at https://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/d/optout.
>

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to