On Fri, Oct 20, 2017 at 11:55 AM, Sven Dumay <[email protected]> wrote:
> Hello, > > I am actually working on an existing project (an online-game) which is > already advanced and pretty consequent. > > My goal is to replace the save system actually based on Pickle by > SQLAlchemy. Not so easy because I have to deal with the existing classes > and there is a lot of work to do (about 400 classes to persist). > > I'm not sure to know what is the best way to proceed and I think that I > require some help. > > *Let's look at the classes organization of the project :* > > > <https://lh3.googleusercontent.com/-rfFB3ExzCXg/WeoZakWziFI/AAAAAAAAAVM/RACvOxxZmSgySRTIswpSNtYO9fd_ht-PgCLcBGAs/s1600/schema2.png> > > > Every class which should be persistent has to be inherited from Stockable. > It is already designed this way and I think that it would be too > complicated to change that. Below Stockable, there is hundred of classes > with their own hierarchy. For example, Character is inherited from > Stockable and Player and NPC (Non-player Character) are inherited from > Character. > this is going to work very poorly. SQLAlchemy supports class inheritance mapped to databases but it's not really something that scales well to deep hierarchies. You can actually stretch this a lot by emphasizing single-table inheritance so that you aren't hobbled with dozens of joins, but this seems like it is still a very deep hierarchy even for that approach. What you need to do here is forget about your whole class hierarchy, and first design the database schema. You want to persist this data in a relational database. How? What do the tables look like? For any non-trivial application, this is where you need to design things from. > > My problem today is that I don't know how to proceed regarding the > metaclass "MetaBase". I am not able to use declarative_base() and MetaBase > at the same time. There is a metabase conflict. I found some other topics > about this problem on the internet and I tried several solutions, but > still, it never works for me. > > *To resume, here is how it basically works without SQLAlchemy :* > > class MetaBase(type): > > def __init__(cls, nom, bases, contenu): > type.__init__(cls, nom, bases, contenu) > pass > > class Stockable(metaclass = MetaBase): > > def __init__(self): > pass > > > class Character(Stockable): > > def __init__(self): > pass > > > *Here is what I would like to do with SQLAlchemy:* > > from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta > from sqlalchemy import Column, Integer > > Base = declarative_base() > > class MetaBase(DeclarativeMeta): > > def __init__(cls, nom, bases, contenu): > super(MetaBase, cls).__init__(nom, bases, contenu) > print("Init MetaBase") > > > class Stockable(metaclass = MetaBase): > > def __init__(self): > print("Init Stockable") > > class Character(Stockable, Base): > > __tablename__ = 'characters' > id = Column(Integer, primary_key=True) > > def __init__(self, name): > self.name = name > print("Init character") > > > jean = Character("Jean") > print(jean.name) > > > you need to pass your metaclass to declarative_base() so that the _decl_class_registry and other things are established: http://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/api.html?highlight=declarative_base#sqlalchemy.ext.declarative.declarative_base.params.metaclass Stockable then just descends from Base normally. Here is what I get : > > >>> > Traceback (most recent call last): > File "C:\Users\Sven\Desktop\SQL Alchemy Tests\test2.py", line 10, in > <module> > class Stockable(metaclass = MetaBase): > File "C:\Users\Sven\Desktop\SQL Alchemy Tests\test2.py", line 7, in > __init__ > super(MetaBase, cls).__init__(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 'Stockable' has no attribute > '_decl_class_registry' > >>> > > Does someone knows what it means and how it can be resolved ? > > *I tried other things and I found the following solution :* > > from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta > from sqlalchemy import Column, Integer > > class MetaBase(DeclarativeMeta): > > def __init__(cls, nom, bases, contenu): > super(MetaBase, cls).__init__(nom, bases, contenu) > print("Init MetaBase") > > Base = declarative_base(metaclass = MetaBase) > > class Stockable(Base): > > __abstract__ = True > > def __init__(self): > print("Init Stockable") > > class Character(Stockable): > > __tablename__ = 'characters' > id = Column(Integer, primary_key=True) > > def __init__(self, name): > self.name = name > print("Init character") > > > jean = Character("Jean") > print(jean.name) > > > It seems to work. I get the following result : > > >>> > Init MetaBase > Init MetaBase > Init MetaBase > Init compte > Jean > >>> > > However, the problem with this method is that I have to add *"__abstract__ > = True" *to every class which is inherited by Stockable... so, about 400 > classes. It is not very clean. Is it possible to avoid that by using > something similar to my first code ? It would be great ! > > Thank you very much. > > Sven > > -- > 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 [email protected]. > To post to this group, send email to [email protected]. > 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 [email protected]. To post to this group, send email to [email protected]. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.
