I am trying to understand how Django uses python metaclasses for its database models (parameters) and came up with the following code snippet, which should roughly imitate Django logic.
class DatabaseOptions(object):
def __init__(self, opts):
if opts:
for key, val in opts.__dict__.items():
if not key.startswith('__') and not callable(val):
setattr(self, key, val)
class MetaModel(type):
def __new__(cls, name, bases, classdict):
result = super().__new__(cls, name, bases, dict(classdict))
opts = classdict.pop('DbMeta', None)
if opts:
setattr(result, '_db_meta', DatabaseOptions(opts))
return result
class Model(object, metaclass=MetaModel):
class DbMeta:
database = 'default'
migrate = True
class User(Model):
class DbMeta(Model.DbMeta):
database = 'user'
class GroupUser(User):
class DbMeta(User.DbMeta):
database = 'group_user'
Using the code above, I expect the following output:
print(Model._db_meta.database)
print(Model._db_meta.migrate)
print(User._db_meta.database)
print(User._db_meta.migrate)
print(GroupUser._db_meta.database)
print(GroupUser._db_meta.migrate)
Instead, I get the following exception
>>> python3 test.py
default
True
user
Traceback (most recent call last):
File "test.py", line 48, in <module>
print(User._db_meta.migrate)
AttributeError: 'DatabaseOptions' object has no attribute 'migrate'
My question is: why User.DbMeta
doesn't the attribute inherit migrate
from Model.DbMeta
? Is there any solution for this kind of problem?
Edit
According to Daniel, I came up with the following that worked for me:
class DatabaseOptions(object):
def __init__(self, opts):
if opts:
for key in dir(opts):
if not key.startswith('__'):
val = getattr(opts, key, None)
if not callable(val):
setattr(self, key, val)