如何将我自己的 Meta class 与作为父级的 SQLAlchemy-Model 一起使用 class
How to use my own Meta class together with SQLAlchemy-Model as a parent class
我最近开始使用 Flask 作为我的后端框架。但是,最近我遇到了一个问题,我想不出如何解决它。作为最后的手段,我想在这里尝试我的改变。如果你能帮助我,我将不胜感激。
所以,我有一个 class 继承自 SQLAlchemy 的 db.Model:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
...
我想创建自己的 Meta class 以自动向其中插入一些 Mixins class:
class MyMetaClass(type):
def __new__(meta, name, bases, class_dict):
bases += (Mixin1, Mixin2, Mixin3)
return type.__new__(meta, name, bases, class_dict)
但是,当我尝试将此 Meta class 与 User class:
一起使用时
class User(db.Model, metaclass=MyMetaClass):
...
我有以下错误:
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
有人知道如何解决这个问题吗?任何帮助将不胜感激。
(注:我不是Flask和Meta的高手classes,所以如果我理解有误,请原谅我的不理解)
终于解决了我的问题。如果其他人遇到同样的问题,我会在这里发布解决方案。
这是摘自 SQLAlchemy's website:
的片段
模型元class 负责在定义模型子classes 时设置SQLAlchemy 内部结构。 Flask-SQLAlchemy 通过 mixins 添加了一些额外的行为;它的默认元class,DefaultMeta,继承了它们。
- BindMetaMixin:bind_key 从 class 中提取并应用于 table。查看具有绑定的多个数据库。
- NameMetaMixin:如果模型没有指定 tablename 但指定了主键,则会自动生成一个名称。
您可以通过定义自己的元class 并自己创建声明性基础来添加自己的行为。确保仍然继承你想要的混合(或者只是继承默认的元class)。
如上所示,将声明性基础 class 而不是简单模型基础 class 传递给 base_class 将导致 Flask-SQLAlchemy 使用此基础而不是构造一个默认元class.
from flask_sqlalchemy import SQLAlchemy
from flask_sqlalchemy.model import DefaultMeta, Model
class CustomMeta(DefaultMeta):
def __init__(cls, name, bases, d):
# custom class setup could go here
# be sure to call super
super(CustomMeta, cls).__init__(name, bases, d)
# custom class-only methods could go here
db = SQLAlchemy(model_class=declarative_base(
cls=Model, metaclass=CustomMeta, name='Model'))
您还可以将您想要的任何其他参数传递给 declarative_base() 以根据需要自定义基础 class。
除此之外,我最初的目的实际上是为我的模型添加一些额外的功能,这些功能继承自 db.Model。实际上,您不需要使用 Meta classes 来做到这一点。因此,对于这种情况,我们可以扩展 db.Model class,如同一网页中所述。这是一个给每个模型一个整数主键,或者一个用于连接的外键的例子-table 继承:
from flask_sqlalchemy import Model, SQLAlchemy
import sqlalchemy as sa
from sqlalchemy.ext.declarative import declared_attr, has_inherited_table
class IdModel(Model):
@declared_attr
def id(cls):
for base in cls.__mro__[1:-1]:
if getattr(base, '__table__', None) is not None:
type = sa.ForeignKey(base.id)
break
else:
type = sa.Integer
return sa.Column(type, primary_key=True)
db = SQLAlchemy(model_class=IdModel)
class User(db.Model):
name = db.Column(db.String)
class Employee(User):
title = db.Column(db.String)
其实如果你对python中元类的机制很熟悉的话,问题就很简单了。我在SqlAlchemy
.
中举个例子
from sqlalchemy.ext.declarative import as_declarative, declared_attr
@as_declarative()
class Base():
id = Column(Integer, primary_key=True, index=True)
__name__: str
@declared_attr
def __tablename__(cls) -> str:
return cls.__name__.lower()
@property
def url(self):
return f'/{self.__class__.__name__.lower()}/{self.id}/'
class AnotherMetaClass(type):
def __new__(cls, name, bases, attrs):
pass
# do something
class ModelMeta(Base.__class__, AnotherMetaClass):
...
class User(Base, metaclass=ModelMeta):
pass
关键步骤是SqlAlchemy
中Base
的metaclass要和自己设计的metclass保持一致。一种解决方案是创建一个新的元类,它是它们的子类。
我最近开始使用 Flask 作为我的后端框架。但是,最近我遇到了一个问题,我想不出如何解决它。作为最后的手段,我想在这里尝试我的改变。如果你能帮助我,我将不胜感激。
所以,我有一个 class 继承自 SQLAlchemy 的 db.Model:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
...
我想创建自己的 Meta class 以自动向其中插入一些 Mixins class:
class MyMetaClass(type):
def __new__(meta, name, bases, class_dict):
bases += (Mixin1, Mixin2, Mixin3)
return type.__new__(meta, name, bases, class_dict)
但是,当我尝试将此 Meta class 与 User class:
一起使用时class User(db.Model, metaclass=MyMetaClass):
...
我有以下错误:
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
有人知道如何解决这个问题吗?任何帮助将不胜感激。
(注:我不是Flask和Meta的高手classes,所以如果我理解有误,请原谅我的不理解)
终于解决了我的问题。如果其他人遇到同样的问题,我会在这里发布解决方案。
这是摘自 SQLAlchemy's website:
的片段模型元class 负责在定义模型子classes 时设置SQLAlchemy 内部结构。 Flask-SQLAlchemy 通过 mixins 添加了一些额外的行为;它的默认元class,DefaultMeta,继承了它们。
- BindMetaMixin:bind_key 从 class 中提取并应用于 table。查看具有绑定的多个数据库。
- NameMetaMixin:如果模型没有指定 tablename 但指定了主键,则会自动生成一个名称。
您可以通过定义自己的元class 并自己创建声明性基础来添加自己的行为。确保仍然继承你想要的混合(或者只是继承默认的元class)。
如上所示,将声明性基础 class 而不是简单模型基础 class 传递给 base_class 将导致 Flask-SQLAlchemy 使用此基础而不是构造一个默认元class.
from flask_sqlalchemy import SQLAlchemy
from flask_sqlalchemy.model import DefaultMeta, Model
class CustomMeta(DefaultMeta):
def __init__(cls, name, bases, d):
# custom class setup could go here
# be sure to call super
super(CustomMeta, cls).__init__(name, bases, d)
# custom class-only methods could go here
db = SQLAlchemy(model_class=declarative_base(
cls=Model, metaclass=CustomMeta, name='Model'))
您还可以将您想要的任何其他参数传递给 declarative_base() 以根据需要自定义基础 class。
除此之外,我最初的目的实际上是为我的模型添加一些额外的功能,这些功能继承自 db.Model。实际上,您不需要使用 Meta classes 来做到这一点。因此,对于这种情况,我们可以扩展 db.Model class,如同一网页中所述。这是一个给每个模型一个整数主键,或者一个用于连接的外键的例子-table 继承:
from flask_sqlalchemy import Model, SQLAlchemy
import sqlalchemy as sa
from sqlalchemy.ext.declarative import declared_attr, has_inherited_table
class IdModel(Model):
@declared_attr
def id(cls):
for base in cls.__mro__[1:-1]:
if getattr(base, '__table__', None) is not None:
type = sa.ForeignKey(base.id)
break
else:
type = sa.Integer
return sa.Column(type, primary_key=True)
db = SQLAlchemy(model_class=IdModel)
class User(db.Model):
name = db.Column(db.String)
class Employee(User):
title = db.Column(db.String)
其实如果你对python中元类的机制很熟悉的话,问题就很简单了。我在SqlAlchemy
.
from sqlalchemy.ext.declarative import as_declarative, declared_attr
@as_declarative()
class Base():
id = Column(Integer, primary_key=True, index=True)
__name__: str
@declared_attr
def __tablename__(cls) -> str:
return cls.__name__.lower()
@property
def url(self):
return f'/{self.__class__.__name__.lower()}/{self.id}/'
class AnotherMetaClass(type):
def __new__(cls, name, bases, attrs):
pass
# do something
class ModelMeta(Base.__class__, AnotherMetaClass):
...
class User(Base, metaclass=ModelMeta):
pass
关键步骤是SqlAlchemy
中Base
的metaclass要和自己设计的metclass保持一致。一种解决方案是创建一个新的元类,它是它们的子类。