mkdir sqlalchemy_alembic
cd /xxx/xxx/sqlalchemy_alembic
# init后的名称随意取
alembic init alembic
创建目录
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
修改后再次执行迁移命令,生成迁移文件
然后根据迁移文件,进行数据库映射
RESTRICT
:若子表中有父表对应的关联数据,删除父表对应数据,会阻止删除。默认项
NO ACTION
:在MySQL中,同RESTRICT。
CASCADE
:级联删除。
SET NULL
:父表对应数据被删除,子表对应数据项会设置为NULL,前提是nullable=True
。
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:指明该关系是一对一)
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})"
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})"