简体   繁体   English

如何在 SQLAlchemy 中创建一个以 Interval 作为主键的表?

[英]How to create a table with Interval as primary key in SQLAlchemy?

I'm trying to create a table for handling billing frequencies using the SQLAlchemy ORM and I can't seem to get it to be happy我正在尝试使用 SQLAlchemy ORM 创建一个用于处理计费频率的表,但我似乎不太高兴

The following works great in Postgres:以下在 Postgres 中效果很好:

create table test_interval(
    frequency interval primary key
);


insert into test_interval values ('1 MONTH'), ('1 YEAR');


select * from test_interval;
-- 0 years 1 mons 0 days 0 hours 0 mins 0.00 secs
-- 1 years 0 mons 0 days 0 hours 0 mins 0.00 secs

And I'm now trying to achieve the same thing in SQLAlchemy with this code我现在正在尝试使用此代码在 SQLAlchemy 中实现相同的目标

from typing import Any

from sqlalchemy import Column, Interval, PrimaryKeyConstraint
from sqlalchemy.ext.declarative import as_declarative, declared_attr


@as_declarative()
class Base:
    id: Any
    __name__: str

    # Generate __tablename__ automatically
    @declared_attr
    def __tablename__(cls) -> str:
        return cls.__name__.lower()



class BillingFrequency(Base):
    __tablename__ = "billing_frequency"
    # I've also tried this
    # __table_args__ = (PrimaryKeyConstraint("frequency"),)
    # frequency: Column(Interval(native=True), index=True, unique=True, nullable=False)
    frequency: Column(Interval(native=True), primary_key=True, nullable=False)


# seed.py
# -- I've not even managed to create the table so this is yet untested --
from sqlalchemy.orm import Session
from sqlalchemy.dialects.postgresql import insert

from app.models import BillingFrequency

def seed_billing(db: Session) -> None:
    # Monthy frequency
    stmt_month = insert(BillingFrequency).values(frequency="1 MONTH")
    stmt_month = stmt_month.on_conflict_do_nothing(
        index_elements=[BillingFrequency.frequency],
    )
    db.add(stmt_month)
    # Year frequency
    stmt_year = insert(BillingFrequency).values(frequency="1 YEAR")
    stmt_year = stmt_year.on_conflict_do_nothing(
        index_elements=[BillingFrequency.frequency],
    )
    db.add(stmt_year)
    db.commit()


Which results in the following error:这导致以下错误:

sqlalchemy.exc.ArgumentError: Mapper mapped class BillingFrequency->billing_frequency could not assemble any primary key columns for mapped table 'billing_frequency'

And if I try to use primary-key using __table_args__ I get the following error.如果我尝试使用__table_args__使用主键, __table_args__出现以下错误。

 KeyError: 'frequency'

Not sure how to handle this.不知道如何处理这个。 It's quite trivial in pure SQL but the ORM makes it a pain.这在纯 SQL 中非常简单,但 ORM 让它变得很痛苦。

You've made two minor errors, but the error messages are unfortunately a bit cryptic for this type of error.您犯了两个小错误,但不幸的是,对于此类错误,错误消息有点神秘。

The first problem is that you've used ...: Column ie as a type instead of ...= Column , assigning a value.第一个问题是您使用...: Column即作为类型而不是...= Column分配值。 This is what causes the sqlalchemy.exc.ArgumentError and the KeyError: 'frequency' , SQLAlchemy doesn't know that the column exists since it doesn't look in the type annotations for Column data.这就是导致sqlalchemy.exc.ArgumentErrorKeyError: 'frequency' ,SQLAlchemy 不知道该列存在,因为它没有查看 Column 数据的类型注释。

The second error you've made is to use db.add(…) for a statement, you should instead use db.execute(…) .您犯的第二个错误是使用db.add(…)作为语句,您应该使用db.execute(…) You would get this following error with db.add :使用db.add会出现以下错误:

AttributeError: 'Insert' object has no attribute '_sa_instance_state'

The above exception was the direct cause of the following exception:
...
sqlalchemy.orm.exc.UnmappedInstanceError: Class 'sqlalchemy.dialects.postgresql.dml.Insert' is not mapped

With these changes your code should look like this:通过这些更改,您的代码应如下所示:


from typing import Any

from sqlalchemy import Column, Interval, PrimaryKeyConstraint
from sqlalchemy.ext.declarative import as_declarative, declared_attr


@as_declarative()
class Base:
    id: Any
    __name__: str

    # Generate __tablename__ automatically
    @declared_attr
    def __tablename__(cls) -> str:
        return cls.__name__.lower()



class BillingFrequency(Base):
    __tablename__ = "billing_frequency"
    frequency = Column(Interval(native=True), primary_key=True, nullable=False)


# seed.py
# -- I've not even managed to create the table so this is yet untested --
from sqlalchemy.orm import Session
from sqlalchemy.dialects.postgresql import insert

from app.models import BillingFrequency

def seed_billing(db: Session) -> None:
    # Monthy frequency
    stmt_month = insert(BillingFrequency).values(frequency="1 MONTH")
    stmt_month = stmt_month.on_conflict_do_nothing(
        index_elements=[BillingFrequency.frequency],
    )
    db.execute(stmt_month)
    # Year frequency
    stmt_year = insert(BillingFrequency).values(frequency="1 YEAR")
    stmt_year = stmt_year.on_conflict_do_nothing(
        index_elements=[BillingFrequency.frequency],
    )
    db.execute(stmt_year)
    db.commit()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM