当前位置: 首页 > 工具软件 > alembic > 使用案例 >

sqlalchemy+alembic

韩嘉胜
2023-12-01

第一步:创建项目目录

mkdir sqlalchemy_alembic

第二步:初始化数据库版本管理器

cd /xxx/xxx/sqlalchemy_alembic
# init后的名称随意取
alembic init alembic

第三步:创建数据库目录databases,用于保存数据库模型

  • 创建目录

    cd /xxx/xxx/sqlalchemy_alembic
    mkdir databases
    
  • 创建模型,models.py内容如下

    from sqlalchemy import create_engine, Column, Integer, String
    from sqlalchemy.ext.declarative import declarative_base
    
    engine = create_engine("mysql+pymysql://admin:password@192.168.0.129/test?charset=utf8mb4")
    Base = declarative_base()
    
    
    class User(Base):
        __tablename__ = 'users'
    
        id = Column(Integer, primary_key=True)
        username = Column(String(64), nullable=False)
        password = Column(String(64), nullable=False)
        email = Column(String(64), nullable=False)
    
        def __repr__(self):
            return f"{self.__class__.__name__}(username: {self.username} | email: {self.email})"
    
  • 目录树如下

    F:\projects\sqlalchemy-alembic
    ├─almbic
    │  └─versions
    │  └─env.py
    │  └─script.py.mako
    │  └─README
    │─databases
    │  └─__init__.py
    │  └─models.py
    ├─__init__.py
    └─alembic.ini   
    

第四步:修改生成的配置文件

  • alembic.ini

    # 原文件
    sqlalchemy.url = driver://user:pass@localhost/dbname
    # 修改后
    sqlalchemy.url = mysql+pymysql://admin:password@192.168.0.129/test?charset=utf8mb4
    

    更多数据库链接方式点击这里

  • alembic/env.py

    import sys
    import os
    
    # os.path.dirname(__file__)  获取当前文件所在目录:alembic
    # os.path.abspath(os.path.dirname(__file__))  获取当前目录绝对路径:F:\projects\sqlalchemy-alembic\almbic
    # os.path.pardir  返回上级目录:..
    
    # 定位到到项目根目录
    root_path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                             os.path.pardir)
    
    # 将项目路径添加进环境变量
    sys.path.append(root_path)
    
    from databases import models
    
    # 原
    target_metadata = None
    # 修改后
    target_metadata = models.Base.metadata
    

第五步:初始化数据库,生成迁移文件

  • 仅生成了迁移文件,未真正对数据库进行操作

    alembic revision --autogenerate -m 'add table users'
    
  • 类似于Django的以下命令

    python3 manage.py makemigrations
    
  • 该命令执行后会在versions下生成一个py文件,目录如下

    • 前面是版本唯一标识,后面的_add_table_users是备注信息
    F:\projects\sqlalchemy-alembic
    ├─almbic
    │  └─versions
    │  │ └─65579825fb5d_add_table_users.py
    │  └─env.py
    │  └─script.py.mako
    │  └─README
    │─databases
    │  └─__init__.py
    │  └─models
    ├─__init__.py
    └─alembic.ini   
    

第六步:执行脚本迁移,生成数据表

  • 由迁移脚本文件生成对应的数据表

    alembic upgrade head
    
  • 类似于Django的以下命令

    python3 manage.py migrate
    

第七步:修改数据表模型,然后进行数据库迁移

  • 第一步:修改模型User,新增real_name字段

    real_name = Column(String(64), nullable=False, server_default='default')
    

    server_default 和 default说明

      server_default: 对于数据库原有数据,该列使用default默认值填充,新增的数据该列不能为空
      
      server_default: 会修改表结构
      
      server_default: 对于整型或者布尔型设置时需要特殊处理
      
      from sqlalchemy import text
      is_del = Column(Boolean,server_default=text('False'))
      sit_num = Column(Integer,server_default=text('80'))
      
      default:对于数据库原有数据不做处理,新增的数据该列不能为空
      
      default:不会修改表结构
    
  • 第二步:生成迁移文件

    alembic revision --autogenerate -m "add column realname with default"
    
  • 第三步:进行数据库迁移

    alembic upgrade head
    

迁移异常处理

  • 一般生成迁移文件时不会出错

  • 出错一般会在根据迁移文件映射到数据表时

  • 出错后执行以下命令,然后根据报错修改相应内容

    # 放弃本次迁移文件
    alembic stamp head
    
  • 修改后再次执行迁移命令,生成迁移文件

  • 然后根据迁移文件,进行数据库映射

SQLALchemy外键

外键约束有以下几项:

  1. RESTRICT:若子表中有父表对应的关联数据,删除父表对应数据,会阻止删除。默认项

  2. NO ACTION:在MySQL中,同RESTRICT。

  3. CASCADE:级联删除。

  4. SET NULL:父表对应数据被删除,子表对应数据项会设置为NULL,前提是nullable=True

1. 一对一关系

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(64), nullable=False)
    password = Column(String(64), nullable=False)

    def __repr__(self):
        return f"{self.__class__.__name__}(username: {self.username} | group: {self.group})"
class UserInfo(Base):
    __tablename__ = 'users'

	id = Column(Integer, primary_key=True, autoincrement=True)
    phone = Column(String(64), nullable=False)
    address = Column(String(64), nullable=False)
    real_name = Column(String(64), nullable=False, server_default='default')
    email = Column(String(64), nullable=False)
    # 一对一外键关联,与一对多主要区别uselist
    user_id = Column(Integer, ForeignKey('groups.id', ondelete='CASCADE'), nullable=False)
    user = relationship('User', backref=backref("userinfo", uselist=False))
  • 说明:

    backhref(‘反向查找时的变量名:即user.userinfo可以获取到用户的详细信息’,uselist=False:指明该关系是一对一)

2.一对多关系

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(64), nullable=False)
    password = Column(String(64), nullable=False)
    # 多对多外键关联
    group_id = Column(Integer, ForeignKey('groups.id', ondelete='CASCADE'), nullable=False)
    group = relationship('Group', backref='user')
    
    def __repr__(self):
        return f"{self.__class__.__name__}(username: {self.username} | group: {self.group})"
class Group(Base):
    __tablename__ = 'groups'

    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(64), nullable=False)
    # 此处加了这个关联关系,就可以通过group对象.user获取组内的所有用户
    # 此处不建议这样写,一般会定义在多表关系中的backhref属性中
    # user = relationship(User)

    def __repr__(self):
        return f"{self.__class__.__name__}(name: {self.name})"

3.多对多关系

  • 第一步:使用Table定义一个中间表,中间表一般就是包含两个模型的外键字段就可以了,并且让他们两个来作为一个“复合主键”
permission_group_mapping = Table(
    "perms_group",
    Base.metadata,
    Column('permissions_id', Integer, ForeignKey('permissions.id'), primary_key=True),
    Column('groups_id', Integer, ForeignKey('groups.id'), primary_key=True)
)
  • 第二步:定义权限表
class Permission(Base):
    __tablename__ = 'permissions'

    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(64), nullable=False)
    desc = Column(String(256), nullable=False)

    def __repr__(self):
        return f"{self.__class__.__name__}(name: {self.name})"
  • 第三步:定义组表(关联关系可以写在二者中的任意一张表中)
class Group(Base):
    __tablename__ = 'groups'

    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(64), nullable=False)
	# 关联关系
	permissions = relationship("permissions", backref="groups", secondary=permission_group_mapping)
	
    def __repr__(self):
        return f"{self.__class__.__name__}(name: {self.name})"
 类似资料: