SQLALchemy:数据导入后属性错误从何而来?

SQLALchemy: Where does the attribute error after data import come from?

我使用 SQLAlchemy 的对象关系映射器编写了一个应用程序来存储和访问来自 SQLite3 数据库的数据。

我做错了什么?

这是应用程序的简单版本:

orm_test.py

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

def orm_setup():
    Base = declarative_base()    
    engine = create_engine('sqlite:///:main:', echo=True)
    Base.metadata.create_all(bind=engine)
    Session = sessionmaker(bind=engine)
    session = Session()
    return Base, engine, session

orm_test_class.py

from sqlalchemy import Column, Integer, String
from orm_test import orm_setup

Base = orm_setup()[0]
engine = orm_setup()[1]

class User(Base):
    __tablename__ = 'person'
    id = Column('id', Integer, primary_key=True)
    username = Column('username', String, unique=True)

Base.metadata.create_all(bind=engine)

orm_test_functions.py

from orm_test_class import User
from orm_test import orm_setup

session = orm_setup()[2]

def add_user(name):
    u = User()
    user_name = str(name)
    u.username = user_name
    session.add(u)
    session.commit()

def get_users():
    users = session.query(User).all()
    for user in users:
        print(user.id, user.username)
    session.close()

main.py

import fire
from orm_test_functions import add_user, get_users

if __name__ == '__main__':
    fire.Fire()

data_import.py

import fire
import pandas as pd
from orm_test import orm_setup

# import engine from orm
engine = orm_setup()[1]

def data_import():
    file = 'Data.xlsx'
    df_user = pd.read_excel(file, sheet_name = 'User')
    df_user.to_sql('person', engine, if_exists='replace', index=False)


# Command line interface
if __name__ == '__main__':
    fire.Fire()

您应该使用 AUTOINCREMENT 关键字设置列 id,请参阅 https://docs.sqlalchemy.org/en/14/dialects/sqlite.html#using-the-autoincrement-keyword

问题是 df_to_sql 删除了原始的 table,它定义了一个主键,并将其替换为 table not定义一个主键。

来自dataframe_to_sql docs

replace: Drop the table before inserting new values.

您可以通过设置 if_exists='append' 而不是 if_exists='replace' 来解决这个问题。

df_user.to_sql('person', engine, if_exists='append', index=False)

如有必要,您可以通过在导入数据之前从 table 中删除任何现有记录来模拟“替换”行为。


这是我用来复现解析的代码:

import io
import sqlalchemy as sa
from sqlalchemy import orm
import pandas as pd

Base = orm.declarative_base()


class User(Base):
    __tablename__ = 'person'

    id = sa.Column('id', sa.Integer, primary_key=True)
    username = sa.Column('username', sa.String, unique=True)


engine = sa.create_engine('sqlite://', echo=True, future=False)
# Drop all is redundant for in-memory db
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
sessionmaker = orm.sessionmaker(engine)


def add_user(name):
    session = sessionmaker()
    u = User()
    user_name = str(name)
    u.username = user_name
    session.add(u)
    session.commit()


def get_users():
    session = sessionmaker()
    users = session.query(User).all()
    for user in users:
        print(user.id, user.username)
    print()
    session.close()


DATA = """\
id,username
1,Alice
2,Bob
"""

buf = io.StringIO(DATA)
df_user = pd.read_csv(buf)
df_user.to_sql('person', engine, if_exists='append', index=False)


users = get_users()
add_user('Carol')
users = get_users()

engine.dispose()