为什么 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_session
和
Base = 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)
一切正常。
设置: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_session
和
Base = 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)
一切正常。