Skip to content

如何在SQLAlchemy中追加新表和新字段,并确保旧表不变

在使用SQLAlchemy时,通常会需要向已有数据库中添加新表或新字段,而不影响现有的表结构和数据。这篇文章将介绍如何在SQLAlchemy中追加新表和新字段,并确保旧表保持不变。

首先,定义你想要添加的新表。在这个例子中,我们假设已有一个名为 users 的表,并且我们想要添加一个新的表 ChatConversation,同时向 users 表中添加一个新的字段 username

1. 定义新表

新的ChatConversation表定义:

python
from sqlalchemy import Column, Integer, String, Boolean, DateTime, JSON, func

class ChatConversation(Base):
    __tablename__ = "chat_conversations"  # 使用新的表名
    conversation_id = Column(Integer, primary_key=True, autoincrement=True, doc="对话的唯一标识符")
    uid = Column(Integer, doc="用户的唯一标识符")
    chat_model_name = Column(String, doc="用户选择的对话模型名称")
    title = Column(String, doc="对话的标题")
    messages = Column(JSON, doc="对话的消息记录,以 JSON 格式存储")
    ref_docs = Column(JSON, doc="引用的文档列表,以 JSON 格式存储")
    is_active = Column(Boolean, default=True, doc="对话是否处于活跃状态")
    has_good = Column(Boolean, default=False, doc="对话是否被点赞")
    has_bad = Column(Boolean, default=False, doc="对话是否被踩")
    create_at = Column(DateTime, default=func.current_timestamp(), doc="对话的创建时间")
    update_at = Column(DateTime, default=func.current_timestamp(), onupdate=func.current_timestamp(), doc="对话的最后更新时间")
    version = Column(String, doc="对话的版本信息")

创建或连接到数据库

使用SQLAlchemy的create_engine来创建或连接到现有的数据库。

python
engine = create_engine('sqlite:///example.db')  # 替换为你的数据库连接字符串

创建所有定义的新表

使用Base.metadata.create_all(engine)来创建所有定义的表。这将确保新的表被添加到数据库中,同时旧表保持不变。

python
Base.metadata.create_all(engine)

示例代码

以下是完整的示例代码,包括定义现有表、新表和新字段,并创建所有定义的表:

python
from sqlalchemy import Column, Integer, String, Boolean, DateTime, JSON, func, create_engine
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

# 定义已有的 User 表
class User(Base):
    __tablename__ = "users"
    __table_args__ = {'extend_existing': True}

    uid = Column(Integer, primary_key=True, autoincrement=True)
    email = Column(String, unique=True, index=True)
    password = Column(String)
    baidu_appkey = Column(String)
    dify_appkey = Column(String)
    deepseek_appkey = Column(String)
    email_auth = Column(Boolean, default=False)
    is_active = Column(Boolean, default=False)
    username = Column(String)  # 新添加的字段

# 定义新的 ChatConversation 表
class ChatConversation(Base):
    __tablename__ = "chat_conversations"  # 使用新的表名
    __table_args__ = {'extend_existing': True}
    conversation_id = Column(Integer, primary_key=True, autoincrement=True, doc="对话的唯一标识符")
    uid = Column(Integer, doc="用户的唯一标识符")
    chat_model_name = Column(String, doc="用户选择的对话模型名称")
    title = Column(String, doc="对话的标题")
    messages = Column(JSON, doc="对话的消息记录,以 JSON 格式存储")
    ref_docs = Column(JSON, doc="引用的文档列表,以 JSON 格式存储")
    is_active = Column(Boolean, default=True, doc="对话是否处于活跃状态")
    has_good = Column(Boolean, default=False, doc="对话是否被点赞")
    has_bad = Column(Boolean, default=False, doc="对话是否被踩")
    create_at = Column(DateTime, default=func.current_timestamp(), doc="对话的创建时间")
    update_at = Column(DateTime, default=func.current_timestamp(), onupdate=func.current_timestamp(), doc="对话的最后更新时间")
    version = Column(String, doc="对话的版本信息")

# 连接数据库并创建表
engine = create_engine('sqlite:///example.db')
Base.metadata.create_all(engine)

2. 已存在的表追加字段

上面的方案,我们发现username字段并没有被添加到users表中,这是因为我们使用__table_args__ = {'extend_existing': True}来定义表,这表示如果数据库中已经存在该表,则不会创建。 对于线上表,由于有数据存在,我们也不会重建表。 这个时候我们可能需要对表进行迁移,再添加字段。可以使用alembic进行迁移,可以自行搜索。 这里我们使用python来调用sql添加字段

python
def add_column_if_not_exists(table_name, column_name, column_type):
    import sqlite3
    path = ""  # 应当是正确的db路径: your/path/to/database.db
    conn = sqlite3.connect(path)
    cursor = conn.cursor()

    # Check if the column already exists
    cursor.execute(f"PRAGMA table_info({table_name});")
    columns = cursor.fetchall()
    column_names = [column[1] for column in columns]

    if column_name not in column_names:
        # Add the column if it does not exist
        add_column_query = f"""
        ALTER TABLE {table_name}
        ADD COLUMN {column_name} {column_type};
        """
        try:
            cursor.execute(add_column_query)
            conn.commit()
            print(f"新列 '{column_name}' 已成功添加到 '{table_name}' 表。")
        except sqlite3.Error as e:
            conn.rollback()
            print(f"添加列时发生错误: {e}")
    else:
        print(f"列 '{column_name}' 已存在于 '{table_name}' 表。")

    # Close the cursor and connection
    cursor.close()
    conn.close()

执行:add_column_if_not_exists(table_name=ChatConversation.__tablename__, column_name='working_goal', column_type='TEXT') 添加

总结

通过上述步骤,你可以在SQLAlchemy中添加新表和新字段而不影响现有表的数据和结构。定义现有表和新表、新字段,创建或连接到数据库,使用Base.metadata.create_all(engine)创建所有定义的表。这些步骤确保你的数据库在添加新表和新字段时保持一致性和完整性。通过这种方法,你可以在不断发展的应用程序中安全地管理数据库结构的变化,确保新旧表之间的和谐共存。

上次更新于: