为啥 SQLAlchemy Postgres ORM 需要 __init__(self) 作为声明性基础?
Posted
技术标签:
【中文标题】为啥 SQLAlchemy Postgres ORM 需要 __init__(self) 作为声明性基础?【英文标题】:Why is SQLAlchemy Postgres ORM requiring __init__(self) for a Declarative Base?为什么 SQLAlchemy Postgres ORM 需要 __init__(self) 作为声明性基础? 【发布时间】:2021-12-18 08:29:46 【问题描述】:设置:Postgres 13、Python 3.7、SQLAlchemy 1.4
当前结构使用base.py
创建引擎、Scoped Session 和 Augmented Base。所有这些都被调用
在models.py
中,我们定义了一个表类,并在inserts.py
中再次调用,我们测试将新值插入到
使用 ORM 的数据库。所有这些都运行良好。
我的问题是关于models.py
表类中的def __init__(self)
函数。如果没有这个函数,代码会报错TypeError: __init__() takes 1 positional argument but 5 were given
只要我包含__init__
函数,代码就会正常工作。
鉴于所有模型都是通过声明性系统定义的,我很困惑为什么会产生此错误
这意味着我们的 Class 应该得到一个 __init__()
方法构造函数,它自动接受关键字名称
匹配我们映射的列。
我怀疑我在编写 db_session = scoped_session
和
Base = declarative_base(cls=Base, metadata=metadata_obj)
和 Base
的方式在 class NumLimit(Base)
中传递。
我无法完全解决这个问题,如果被引导到我创建此错误的位置,我将不胜感激。谢谢!
base.py
from sqlalchemy import Column, create_engine, Integer, MetaData
from sqlalchemy.orm import declared_attr, declarative_base, scoped_session, sessionmaker
engine = create_engine('postgresql://user:pass@localhost:5432/dev', echo=True)
db_session = scoped_session(
sessionmaker(
bind=engine,
autocommit=False,
autoflush=False
)
)
# Augment the base class by using the cls argument of the declarative_base() function so all classes derived
# from Base will have a table name derived from the class name and an id primary key column.
class Base:
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
id = Column(Integer, primary_key=True)
# Write all tables to schema 'collect'
metadata_obj = MetaData(schema='collect')
# Instantiate a Base class for our classes definitions
Base = declarative_base(cls=Base, metadata=metadata_obj)
models.py
from base import Base
from sqlalchemy import Column, DateTime, Integer, Text
from sqlalchemy.dialects.postgresql import UUID
import uuid
class NumLimit(Base):
org = Column(UUID(as_uuid=True), default=uuid.uuid4, unique=True)
limits = Column(Integer)
limits_rate = Column(Integer)
rate_use = Column(Integer)
def __init__(self, org, limits, allowance_rate, usage, last_usage):
super().__init__()
self.org = org
self.limits = limits
self.limits_rate = limits_rate
self.rate_use = rate_use
def __repr__(self):
return f'<NumLimit(org=self.org, limits=self.limits, limits_rate=self.limits_rate,' \
f' rate_use=self.rate_use)>'
insert.py
def insert_num_limit():
# Generate database schema based on definitions in models.py
Base.metadata.create_all(bind=engine)
# Create instances of the NumLimit class
a_num_limit = NumLimit('123e4567-e89b-12d3-a456-426614174000', 20, 4, 8)
another_limit = NumLimit('123e4567-e89b-12d3-a456-426614174660', 7, 2, 99)
# Use the current session to persist data
db_session.add_all([a_num_limit, another_limit])
# Commit current session to database and close session
db_session.commit()
db_session.close()
return
【问题讨论】:
【参考方案1】:sqlalchemy 生成的__init__()
的所有参数都是关键字。好像定义是:
def __init__(self, *, id=None, org=None, limits=None, limits_rate=None, rate_use=None):
self.id = id
self.org = org
# etc...
因此,当您尝试按位置提供参数时:NumLimit('123e4567-e89b-12d3-a456-426614174000', 20, 4, 8)
,您将收到错误 TypeError: __init__() takes 1 positional argument but 5 were given
,因为 __init__()
确实只接受一个位置参数。但是,如果您将它们作为关键字参数提供:NumLimit(id='123e4567-e89b-12d3-a456-426614174000', org=20, limits=4, limits_rate=8)
一切正常。
【讨论】:
感谢您解释清楚,这就是解决方案!以上是关于为啥 SQLAlchemy Postgres ORM 需要 __init__(self) 作为声明性基础?的主要内容,如果未能解决你的问题,请参考以下文章