postgresql 如何对sqlite和postgres使用相同的代码

5t7ly7z5  于 2023-01-12  发布在  PostgreSQL
关注(0)|答案(1)|浏览(76)

我的sqlalchemy代码需要同时支持sqlite和postgres,但是现在它不支持sqlite。
sqlalchemy.exc.StatementError:(buildins.TypeError)SQLite DateTime类型只接受Python日期时间和日期对象作为输入。
我检查了Error - "SQLite DateTime type only accepts Python " "datetime and date objects as input.",但在整个代码库中进行此更改是不可能的,因为它有多个地方使用了日期字符串而不是日期时间
这是我的代码,它适用于postgres引擎,但不适用于sqlite,我可以修改上面链接建议以外的任何内容,以便我的代码在sqlite和postgres上运行吗

from sqlalchemy import create_engine, Column, Integer
from sqlalchemy.orm import declarative_base, Session
from sqlalchemy.types import DateTime

Base = declarative_base()

class Foo(Base):
    __tablename__ = "foo"
    id = Column(Integer, primary_key=True)
    col = Column(DateTime)

engine = create_engine("postgresql://tony:tony@localhost:5432")
# engine = create_engine("sqlite:///db.db")
Base.metadata.create_all(engine)

with Session(engine) as session:
    session.add(Foo(col='2023-01-07T11:08:31Z'))
    session.commit()
pcww981p

pcww981p1#

DB-API规范要求SQL DATETIME以python datetime.datetime(docs)的形式提供。
我相信psycopg2提供了一个扩展,可以处理ISO 8601格式的字符串,但这只是一个扩展。
如果您想获得最大的兼容性,请使用datetime.datetime对象来传递和检索日期。
另外,为什么要从sqlalchemy.types导入DateTime呢?它可以直接在sqlalchemy下获得。

from datetime import datetime

from sqlalchemy import Column, Integer, create_engine, DateTime
from sqlalchemy.orm import Session, declarative_base

Base = declarative_base()

class Foo(Base):
    __tablename__ = "foo"
    id = Column(Integer, primary_key=True)
    col = Column(DateTime)

engine = create_engine("postgresql+psycopg2://postgres:postgres@localhost:5432/postgres") # OK
engine = create_engine("sqlite:///db.db") # OK

Base.metadata.create_all(engine)

with Session(engine) as session:
    session.add(Foo(col=datetime.fromisoformat("2023-01-07T11:08:31Z")))
    session.commit()

如果需要运行时(而不是测试)的兼容性,可以使用下面的TypeDecorator
如果您不想强制绑定参数,请进一步查看文档,但这将允许您在SQLite中输入ISO 8601 str。

from datetime import datetime

from sqlalchemy import Column, Integer, create_engine, DateTime
from sqlalchemy.orm import Session, declarative_base
from sqlalchemy.types import TypeDecorator

Base = declarative_base()

class ISO8601DateTime(TypeDecorator):
    impl = DateTime
    cache_ok = True

    def process_bind_param(self, value, dialect):
        if dialect.name == "sqlite" and isinstance(value, str):
            value = datetime.fromisoformat(value)
        return value

class Foo(Base):
    __tablename__ = "foo"
    id = Column(Integer, primary_key=True)
    col = Column(ISO8601DateTime)  # NOTE: decorated type

engine = create_engine("postgresql+psycopg2://postgres:postgres@localhost:5432/postgres") # OK
engine = create_engine("sqlite:///db.db") # OK

Base.metadata.create_all(engine)

with Session(engine) as session:
    session.add(Foo(col=datetime.fromisoformat("2023-01-07T11:08:31Z")))
    session.add(Foo(col="2023-01-07T11:08:31Z"))
    session.commit()

**NB.**对于测试,如果是单元测试,则模拟数据库适配器,如果是集成(和更高)测试,则模拟use the same DB as you'll use in production

相关问题