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 :
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.
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)
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