为什么 SQLAlchemy Postgres ORM 需要 __init__(self) 作为声明基础?

Why is SQLAlchemy Postgres ORM requiring __init__(self) for a Declarative Base?

设置:Postgres 13,Python3.7,SQLAlchemy 1.4

当前结构使用 base.py 创建引擎、Scoped Session 和 Augmented Base。所有这些都被称为 在 models.py 中,我们定义了一个 table Class,并在 inserts.py 中再次调用,我们在其中测试插入新值 数据库使用ORM。所有这些都运行良好。

我的问题是关于 models.py table Class 中的 def __init__(self) 函数。如果没有此功能,代码将出错 TypeError: __init__() takes 1 positional argument but 5 were given

只要我包含 __init__ 函数,代码就能正常工作。

鉴于所有模型都是通过声明式系统定义的,我对为什么会产生此错误感到困惑 这意味着我们的 Class 应该得到一个 __init__() 方法构造函数,它自动接受关键字名称 匹配我们映射的列。

我怀疑我对 db_session = scoped_sessionBase = declarative_base(cls=Base, metadata=metadata_obj) 以及在 class NumLimit(Base) 中传递 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

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) 一切正常。