1.安装MySQL

直接去下载即可,如果是windows建可以下载msi,一路next即可。我已经安装过了,这里就不再演示了。

最后使用Navicat连接测试一下,我这里是没有问题的

2.SQLAlchemy的介绍和基本使用

sqlalchemy是一款orm框架

注意:SQLAlchemy本身是无法处理数据库的,必须依赖于第三方插件,比方说pymysql,cx_Oracle等等

SQLAlchemy等于是一种更高层的封装,里面封装了许多dialect(相当于是字典),定义了一些组合,比方说: 可以用pymysql处理mysql,也可以用cx_Oracle处理Oracle,关键是程序员使用什么 然后会在dialect里面进行匹配,同时也将我们高层定义的类转化成sql语句,然后交给对应的DBapi去执行。

除此之外,SQLAlchemy还维护一个数据库连接池,数据库的链接和断开是非常耗时的 SQLAlchemy维护了一个数据库连接池,那么就可以拿起来直接用

首先要安装sqlalchemy和pymysql,直接pip install  即可

我在mysql新建了一个名为satori的数据库

# 连接数据库需要创建引擎
from sqlalchemy import create_engine

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"

# 连接方式:数据库+驱动://用户名:密码@ip:端口/数据库
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")

# 测试连接是否成功
conn = engine.connect()
# 我们直接可以使用conn去获取数据
data = conn.execute("show databases")
print(data.fetchall()) # [('information_schema',), ('mysql',), ('performance_schema',), ('sakila',), ('satori',), ('sys',), ('world',)]

显然是可以获取成功的

3.orm介绍

orm: object relation mapping,就是可以把我们写的类转化成表。将类里面的元素映射到数据库表里面

4.定义orm模型并映射到数据库中

from sqlalchemy import create_engine

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")

# 需要以下步骤
'''
1.创建一个orm模型,这个orm模型必须继承SQLAlchemy给我们提供好的基类
2.在orm中创建一些属性,来跟表中的字段进行一一映射,这些属性必须是SQLAlchemy给我们提供好的数据类型
3.将创建好的orm模型映射到数据库中
'''
# 1.创建orm,继承基类
from sqlalchemy.ext.declarative import declarative_base
# 这个declarative_base只是一个函数,我们需要传入engine,然后返回基类
Base = declarative_base(engine)

class Girls(Base):
    # 首先是表名,创建表要指定名字,如何指定呢?
    __tablename__ = "girls"  # __tablename__就是我们的表名,我们指定为girls
    # 2.创建属性,来跟表中的字段一一映射
    # 首先我们需要有列,还要有字段的属性。注意:我这里是为了演示,工作中要写在开头。
    from sqlalchemy import Column, Integer, String
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(30))  # String也是一个类,里面可以传入数字表示最多可以存多少字节的字符串
    age = Column(Integer)
    gender = Column(String(1))
    anime = Column(String(50))

# 3.创建完毕,那么接下来就要映射到数据库中
Base.metadata.create_all()  # 表示将Base的所有子类的模型映射到数据库中,一旦映射,即使改变模型之后再次执行,也不会再次映射。

此时里面还没有表,我们执行一下看看

我们刚才创建的表已经在里面了

5.SQLAlchemy对数据的增删改查操作

from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

class Girls(Base):
    __tablename__ = "girls"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(30))
    age = Column(Integer)
    gender = Column(String(1))
    anime = Column(String(50))

# 实例化一个对象,是用来增删改查数据的
girls =Girls()

# 会得到一个Session类
Session = sessionmaker(bind=engine)
# 然后实例化得到一个Session对象,这个session使用来提交操作的
session = Session()

  

增:

girls.name = "satori"
girls.age = 16
girls.gender = "f"
girls.anime = "东方地灵殿"

# 这两步表示将girls做的操作全部提交上去执行
session.add(girls)
session.commit()

# 也可以批量操作
session.add_all([Girls(name="koishi", age=15, gender="f", anime="东方地灵殿"),
                 Girls(name="mashiro", age=16, gender="f", anime="樱花庄的宠物女孩"),
                 Girls(name="matsuri", age=400, gender="f", anime="sola"),
                 Girls(name="kurisu", age=20, gender="f", anime="命运石之门")])
session.commit()

  

 

查:

# session.query得到的是一个query对象,调用filter_by依旧是一个query对象
# 调用first方法获取第一条查询记录,如果不存在则为None
girls = session.query(Girls).filter_by(gender="f").first()
print(girls.name, girls.age, girls.anime)  # satori 16 东方地灵殿

  

改:

# 改和查比较类似
# 还是先获取相应的记录
girls = session.query(Girls).filter_by(gender="f").first()
# 直接修改即可
girls.name = "古明地觉"
# 一旦涉及到数据的变更,那么就必须要进行提交,查不涉及所以不需要
# 如何提交呢?我们只是改,所以不需要add,直接使用commit即可
session.commit()

  

删:

# 筛选出要删除的字段
girl = session.query(Girls).filter_by(name="古明地觉").first()
# 添加用add,删除用delete
session.delete(girl)
# 别忘了提交
session.commit()

  

6.SQLAlchemy属性常用数据类型

SQLAlchemy常用数据类型:
  Integer:整形,映射到数据库是int类型
  Float:浮点型,映射到数据库是float类型,占32位。
  Boolean:布尔类型,传递True or False,映射到数据库是tinyint类型
  DECIMAL:顶点类型,一般用来解决浮点数精度丢失的问题
  Enum:枚举类型,指定某个字段只能是枚举中指定的几个值,不能为其他值,
  Date:传递datetime.date()进去,映射到数据库中是Date类型
  DateTime:传递datetime.datetime()进去,映射到数据库中也是datatime类型
  String:字符类型,映射到数据库中varchar类型
  Text:文本类型,可以存储非常长的文本
  LONGTEXT:长文本类型(只有mysql才有这种类型),可以存储比Text更长的文本

7.Column常用参数

  default:默认值,如果没有传值,则自动为我们指定的默认值
  nullable:是否可空,False的话,表示不可以为空,那么如果不传值则报错
  primary_key:是否为主键
  unique:是否唯一,如果为True,表示唯一,那么传入的值存在的话则报错
  autoincrement:是否自动增长
  name:该属性在数据库中的字段映射,如果不写,那么默认为赋值的变量名。

8.query查询

1.模型,指定查找这个模型中所有的对象

2.模型中的属性,可以指定查找模型的某几个属性

3.聚合函数

  func.count:统计行的数量

  func.avg:求平均值

  func.max:求最大值

  func.min:求最小值

  func.sum:求总和

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, func
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)
# 以上这些都是必须的

class Girls(Base):
    __tablename__ = "girls"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(30))
    age = Column(Integer)
    gender = Column(String(1))
    anime = Column(String(50))

    # 以后就不会打印一个query对象了,而是会打印返回值
    def __repr__(self):
        return f"{self.name}--{self.age}--{self.anime}"

Session = sessionmaker(bind=engine)
session = Session()

# 这里指定了Girls,也就是模型,那么会查找所有的模型对象
for girl in session.query(Girls).all():
    print(girl)
    '''
    koishi--15--东方地灵殿
    mashiro--16--樱花庄的宠物女孩
    matsuri--400--sola
    kurisu--20--命运石之门
    '''

# 但如果我不想要所有属性,而是要部分属性怎么办呢?比如我只想要name和age,就可以这样
# 会获取一个有元组组成的字典
print(session.query(Girls.name, Girls.age).all())  # [('koishi', 15), ('mashiro', 16), ('matsuri', 400), ('kurisu', 20)]

# 聚合函数
# 得到query对象虽然只有一条数据,但还是要加上first
print(session.query(func.count(Girls.name)).first())  # (4,)
print(session.query(func.avg(Girls.age)).first())  # (Decimal('112.7500'),)
print(session.query(func.max(Girls.age)).first())  # (400,)
print(session.query(func.min(Girls.age)).first())  # (15,)
print(session.query(func.sum(Girls.age)).first())  # (Decimal('451'),)

  

9.filter方法常用过滤条件

为了方便演示,我这里有多建了几条数据

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

class Girls(Base):
    __tablename__ = "girls"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(30))
    age = Column(Integer)
    gender = Column(String(1))
    anime = Column(String(50))

    # 以后就不会打印一个query对象了,而是会打印返回值
    def __repr__(self):
        return f"{self.name}--{self.age}--{self.anime}"

Session = sessionmaker(bind=engine)
session = Session()

# 关于过滤,有两种方法,一种是filter,另一种是filter_by
# filter需要使用类名.属性的方式,filter_by只需要属性即可,这就意味着后者使用简单,但前者可以写出更复杂的查询

# 下面来看看都有哪些查询方法
# 1、==,由于可能有多条,我们只选择一条。
print(session.query(Girls).filter(Girls.age == 16).first())  # 古明地觉--16--东方地灵殿

# 2、!=
print(session.query(Girls).filter(Girls.age != 16).first())  # 四方茉莉--400--sola

# 3、like,%和sql里的一样。除此之外还要ilike,不区分大小写
print(session.query(Girls).filter(Girls.anime.like("%久%")).all())  # [雨宫优子--16--悠久之翼, 宫村宫子--15--悠久之翼]

# 4、in_,至于为什么多了多了一个_,这个bs4类似,为了避免和python里的关键字冲突。为什么不是_in,如果放在前面那代表不想被人访问
print(session.query(Girls).filter(Girls.age.in_([16, 400])).all())  # [古明地觉--16--东方地灵殿, 椎名真白--16--樱花庄的宠物女孩, 四方茉莉--400--sola, 春日野穹--16--缘之空, 雨宫优子--16--悠久之翼]

# 5、notin_,等价于~Girls.age.in_()
print(session.query(Girls).filter(Girls.age.notin_([16, 20, 400])).all())  # [立华奏--18--angelbeats, 古河渚--19--Clannad, 坂上智代--18--Clannad, 古明地恋--15--东方地灵殿, 宫村宫子--15--悠久之翼]

# 6、isnull
print(session.query(Girls).filter(Girls.age is None).first())  # None

# 7、isnotnull
print(session.query(Girls).filter(Girls.age is not None).first())  # 古明地觉--16--东方地灵殿

# 8、and
print(session.query(Girls).filter(and_(Girls.age == 16, Girls.anime == "樱花庄的宠物女孩")).first())  # 椎名真白--16--樱花庄的宠物女孩

# 9、or
print(session.query(Girls).filter(or_(Girls.age == 15, Girls.anime == "悠久之翼")).all())  # [古明地恋--15--东方地灵殿, 雨宫优子--16--悠久之翼, 宫村宫子--15--悠久之翼]

 

10.外键及其四种约束

在mysql中,外键可以使表之间的关系更加紧密。而SQLAlchemy中也同样支持外键,通过ForforeignKey来实现,并且可以指定表的外键约束

外键约束有以下几种:

1.RESTRICT:父表数据被删除,会阻止删除

2.NO ACTION:在mysql中,等同于RESTRICT

3.CASCADE:级联删除

4.SET NULL:父表数据被删除,zishuju字表数据会跟着删除,也就是设置为NULL

既然如此的话,我们是不是要有两张表啊,我这里新创建两张表

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

# 我现在新建两张表
# 表People--表language ==》父表--子表
class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可为空
    age = Column(Integer, nullable=False)

class Language(Base):
    __tablename__ = "Language"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)
    birthday = Column(Integer, nullable=False)
    type = Column(String(20), nullable=False)
    # 这个pid指的是People里面的id,所以要和People里的id保持一致
    pid = Column(Integer, ForeignKey("People.id"))  # 引用的表.引用的字段

Base.metadata.create_all()

  

  

  

我们也可以看看表的创建语法

那么下面添加记录

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可为空
    age = Column(Integer, nullable=False)

class Language(Base):
    __tablename__ = "Language"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)
    birthday = Column(Integer, nullable=False)
    type = Column(String(20), nullable=False)
    # 还记得约束的种类吗
    # 1.RESTRICT:父表删除数据,会阻止。
    '''
    因为在子表中,引用了父表的数据,如果父表的数据删除了,那么字表就会懵逼,不知道该找谁了。
    '''

    # 2.NO ACTION
    '''
    和RESTRICT作用类似
    '''

    # 3.CASCADE
    '''
    级联删除:我们Language表的pid关联了People的id。如果People中id=1的记录被删除了,那么Language中关联id=1的记录也会被删除
    就是我们关联了,我的pid关联了你的id,如果你删除了一条记录,那么根据你删除记录的id,我也会相应的删除一条,要死一块死。
    '''

    # 4.SET NULL
    '''
    设置为空:和CASCADE类似,就是我的pid关联你的id,如果id没了,那么pid会被设置为空,不会像CASCADE那样,把相应pid所在整条记录都给删了
    '''
    # 那么如何设置呢?在ForeignKey中有一个onedelete参数,可以接收上面四种中的一种
    pid = Column(Integer, ForeignKey("People.id", ondelete="RESTRICT"))

# 由于我们之前已经创建了,所以再次创建已不会修改,所以只能先删除,然后再次创建
Base.metadata.drop_all()
Base.metadata.create_all()

# 然后添加几条记录吧
Session = sessionmaker(bind=engine)
session = Session()

session.add_all([People(name="Guido van Rossum", age=62),
                 People(name="Dennis Ritchie", age=77),
                 People(name="James Gosling", age=63),
                 Language(name="Python", birthday=1991, type="解释型", pid=1),
                 Language(name="C", birthday=1972, type="编译型", pid=2),
                 Language(name="Java", birthday=1995, type="解释型", pid=3)])

session.commit()

我们下面删除数据,直接在Navicat里面演示

剩下的便不再演示了,都是比较类似的。另外,如果再ForeignKey中不指定onedelete,那么默认就是RESTRICT

11.ORM层外键和一对多关系

 

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可为空
    age = Column(Integer, nullable=False)

class Language(Base):
    __tablename__ = "Language"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)
    birthday = Column(Integer, nullable=False)
    type = Column(String(20), nullable=False)
    pid = Column(Integer, ForeignKey("People.id", ondelete="RESTRICT"))

    # 如果我想通过Language表查询People表的数据呢?
    # 可以通过relationship进行关联
    people = relationship("People")

Base.metadata.drop_all()
Base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()
session.add_all([People(name="Guido van Rossum", age=62),
                 People(name="Dennis Ritchie", age=77),
                 People(name="James Gosling", age=63),
                 Language(name="Python", birthday=1991, type="解释型", pid=1),
                 Language(name="C", birthday=1972, type="编译型", pid=2),
                 Language(name="Java", birthday=1995, type="解释型", pid=3)])

session.commit()

people = session.query(Language).all()
for i in people:
    # Language的people已经和People这个表关联了,我通过language依旧可以访问
    print(i.people.name, i.people.age)
    '''
    Guido van Rossum 62
    Dennis Ritchie 77
    James Gosling 63
    '''

  一般情况下,一们语言对应一个作者,但是一个作者可以有多个语言。

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可为空
    age = Column(Integer, nullable=False)
    language = relationship("Language")

    def __repr__(self):
        return f"{self.name}--{self.age}--{self.languages}"

class Language(Base):
    __tablename__ = "Language"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)
    birthday = Column(Integer, nullable=False)
    type = Column(String(20), nullable=False)
    pid = Column(Integer, ForeignKey("People.id", ondelete="RESTRICT"))

    people = relationship("People")

    def __repr__(self):
        return f"{self.name}--{self.birthday}--{self.type}"

Session = sessionmaker(bind=engine)
session = Session()

# 通过People里的language,查找Language表的记录
# 实际上这里的language还是所有People对象的集合
language = session.query(People).all()
# 遍历People对象
for i in language:
    # People的language已经和Language这个表关联了,我通过language依旧可以访问
    # 由于People和Language是一对多的关系,所以一个People记录可能对应多个Language记录,所以得到的都是一个列表
    # 通过i.language,直接会去查找Language表,从而打印出结果
    print(i.language)
    '''
    [Python--1991--解释型]
    [C--1972--编译型, unix--1973--操作系统]
    [Java--1995--解释型]
    '''

# 这里得到是每一个People对象
for i in language:
    # 这里遍历每一个People对象下的language,通过这里的language查找Language表的记录。
    # 而这里的两个language是不一样的,所以我这里起名字失误了,不应该都叫language的,容易引起歧义,まぁいいや
    for j in i.language:
        print(i.name, j.name, j.birthday, j.type)
        '''
        Guido van Rossum Python 1991 解释型
        Dennis Ritchie C 1972 编译型
        Dennis Ritchie unix 1973 操作系统
        James Gosling Java 1995 解释型
        '''

  在relationship中还有一个反向引用

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可为空
    age = Column(Integer, nullable=False)

    # 我们把这一行注释掉
    # language = relationship("Language")

    def __repr__(self):
        return f"{self.name}--{self.age}--{self.languages}"

class Language(Base):
    __tablename__ = "Language"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)
    birthday = Column(Integer, nullable=False)
    type = Column(String(20), nullable=False)
    pid = Column(Integer, ForeignKey("People.id", ondelete="RESTRICT"))

    # 我们在People表中把relationship给注释掉了,但是在Language表的relationship中添加了一个backref="language",反向引用
    # 意思是说,我们即便在People中不指定,也依旧可以通过People对象.language的方式来访问到Language表中的所有属性
    # 因为前面指定了People表示已经和People表关联了
    # 当然这里也可以不叫language,比方说我改成中文试试
    '''
    people = relationship("People", backref="language")
    '''
    people = relationship("People", backref="古明地盆")

    def __repr__(self):
        return f"{self.name}--{self.birthday}--{self.type}"

Session = sessionmaker(bind=engine)
session = Session()

language = session.query(People).all()
# 遍历People对象
for i in language:
    print(i.古明地盆)
    '''
    [Python--1991--解释型]
    [C--1972--编译型, unix--1973--操作系统]
    [Java--1995--解释型]
    '''
    # 可以看到即便用中文依旧可以访问,但是工作中千万别这么干,否则被技术leader看到了,要么把你骂一顿,要么把你给开除了

  

12.一对一关系实现

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可为空
    age = Column(Integer, nullable=False)

    # 我们把这一行注释掉
    # language = relationship("Language")

    def __repr__(self):
        return f"{self.name}--{self.age}--{self.languages}"

class Language(Base):
    __tablename__ = "Language"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)
    birthday = Column(Integer, nullable=False)
    type = Column(String(20), nullable=False)
    pid = Column(Integer, ForeignKey("People.id", ondelete="RESTRICT"))

    people = relationship("People", backref="language")

    def __repr__(self):
        return f"{self.name}--{self.birthday}--{self.type}"

Session = sessionmaker(bind=engine)
session = Session()

people = People(id=6, name="KenThompson", age=75)
language1 = Language(name="B", birthday=1968, type="编译型", pid=6)
language2 = Language(name="Go", birthday=2009, type="编译型", pid=6)

# 由于People和Language是关联的,并且通过language可以访问到Language表的属性
# 那么可以通过people.language.append将Language对象添加进去
people.language.append(language1)
people.language.append(language2)

# 然后只需要提交People对象people,就会自动将Language对象language1、language2也提交了
session.add(people)
session.commit()

  

但是如果我想反向添加怎么办呢?

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可为空
    age = Column(Integer, nullable=False)

    # 我们把这一行注释掉
    # language = relationship("Language")

    def __repr__(self):
        return f"{self.name}--{self.age}--{self.languages}"

class Language(Base):
    __tablename__ = "Language"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)
    birthday = Column(Integer, nullable=False)
    type = Column(String(20), nullable=False)
    pid = Column(Integer, ForeignKey("People.id", ondelete="RESTRICT"))

    people = relationship("People", backref="language")

    def __repr__(self):
        return f"{self.name}--{self.birthday}--{self.type}"

Session = sessionmaker(bind=engine)
session = Session()

people = People(id=7, name="松本行弘", age=53)
language = Language(name="ruby", birthday=1995, type="解释型", pid=7)

# 反向添加,刚才的是正向添加的。
# 正向:people.language.append,反向:language.people=people
# 正向里的language是backref,反向里的people是people = relationship("People", backref="language")的左值
language.people = people

# 然后只需要提交People对象people,就会自动将Language对象language1、language2也提交了
session.add(people)
session.commit()

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可为空
    age = Column(Integer, nullable=False)

    # 表示得到的不是一个列表,因为我们是一对一,只有一个元素
    extend = relationship("People_extend", uselist=False)

    def __repr__(self):
        return f"{self.name}--{self.age}--{self.languages}"

class People_extend(Base):
    __tablename__ = "People_extend"
    id = Column(Integer, primary_key=True, autoincrement=True)
    country = Column(String(20))
    uid = Column(Integer, ForeignKey("People.id"))

    # 并且这里不需要backref了,加了会报错
    people = relationship("People")

'''
people = People()
extend = People_extend()
然后通过people.extend = extend实现即可
'''

  

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可为空
    age = Column(Integer, nullable=False)

    # 表示得到的不是一个列表,因为我们是一对一,只有一个元素
    # extend = relationship("People_extend", uselist=False)

    def __repr__(self):
        return f"{self.name}--{self.age}--{self.languages}"

class People_extend(Base):
    __tablename__ = "People_extend"
    id = Column(Integer, primary_key=True, autoincrement=True)
    country = Column(String(20))
    uid = Column(Integer, ForeignKey("People.id"))

    # 或者把上面的注释掉,导入backref模块
    people = relationship("People", backref=backref("extend", uselist=False))

'''
用法和之前的一样
people = People()
extend = People_extend()
然后通过people.extend = extend实现即可

再使用People访问的时候,直接通过people.extend.来访问即可
'''

  

12.多对多关系实现

比如说博客园的文章,一篇文章可能对应多个标签。同理一个标签也会有有多个文章。这就是一个多对多的关系

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

# 现在我们的两张表建好了,既然要实现多对多,需要借助第三张表。
# SQLAlchemy已经为我们提供好了一个table,让我们去使用

Article_Tag = Table("Article_Tage", Base.metadata,
                    Column("article_id", Integer, ForeignKey("Article.id"), primary_key=True),
                    Column("tag_id", Integer, ForeignKey("Tag.id"), primary_key=True)
                    )

class Article(Base):
    __tablename__ = "Article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)

    tags = relationship("Tag", backref="articles", secondary=Article_Tag)

class Tag(Base):
    __tablename__ = "Tag"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), nullable=False)

'''
总结一下:
1、先把两个需要多对多的模型建立出来
2、使用Table定义一个中间表,中间表就是包含两个模型的外键字段就可以了,并且让它们两个做一个复合主键
3、在两个需要做多对多的模型中随便选择一个模型,定义一个relationship属性,来绑定三者之间的关系,在使用relationship的时候,需要传入一个secondary="中间表"
'''

Base.metadata.create_all()

   

看一下表的创建过程

添加数据

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

# 现在我们的两张表建好了,既然要实现多对多,需要借助第三张表。
# SQLAlchemy已经为我们提供好了一个table,让我们去使用

Article_Tag = Table("Article_Tag", Base.metadata,
                    Column("article_id", Integer, ForeignKey("Article.id"), primary_key=True),
                    Column("tag_id", Integer, ForeignKey("Tag.id"), primary_key=True)
                    )

class Article(Base):
    __tablename__ = "Article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)

    tags = relationship("Tag", backref="articles", secondary=Article_Tag)

class Tag(Base):
    __tablename__ = "Tag"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), nullable=False)

'''
总结一下:
1、先把两个需要多对多的模型建立出来
2、使用table定义一个中间表,中间表就是包含两个模型的外键字段就可以了,并且让它们两个做一个复合主键
3、在两个需要做多对多的模型中随便选择一个模型,定义一个relationship属性,来绑定三者之间的关系,在使用relationship的时候,需要传入一个secondary="中间表"
'''

Base.metadata.create_all()

article1 = Article(title="article1")
article2 = Article(title="article2")

tag1 = Tag(name="tag1")
tag2 = Tag(name="tag2")

# 每一篇文章,添加两个标签
article1.tags.append(tag1)
article1.tags.append(tag2)
article2.tags.append(tag1)
article2.tags.append(tag2)

Session = sessionmaker(bind=engine)
session = Session()

# 只需添加article即可,tag会被自动添加进去
session.add_all([article1, article2])
session.commit()

  

获取数据

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

# 现在我们的两张表建好了,既然要实现多对多,需要借助第三张表。
# SQLAlchemy已经为我们提供好了一个table,让我们去使用

Article_Tag = Table("Article_Tag", Base.metadata,
                    Column("article_id", Integer, ForeignKey("Article.id"), primary_key=True),
                    Column("tag_id", Integer, ForeignKey("Tag.id"), primary_key=True)
                    )

class Article(Base):
    __tablename__ = "Article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    '''
    Article对象.tags获取Tag里面的属性
    Tag对象.articles(backref)获取Article里面的属性
    '''
    tags = relationship("Tag", backref="articles", secondary=Article_Tag)

    def __repr__(self):
        return f"{self.title}"

class Tag(Base):
    __tablename__ = "Tag"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), nullable=False)

    def __repr__(self):
        return f"{self.name}"
'''
总结一下:
1、先把两个需要多对多的模型建立出来
2、使用table定义一个中间表,中间表就是包含两个模型的外键字段就可以了,并且让它们两个做一个复合主键
3、在两个需要做多对多的模型中随便选择一个模型,定义一个relationship属性,来绑定三者之间的关系,在使用relationship的时候,需要传入一个secondary="中间表"
'''

Base.metadata.create_all()

# article1 = Article(title="article1")
# article2 = Article(title="article2")
#
# tag1 = Tag(name="tag1")
# tag2 = Tag(name="tag2")
#
# # 每一篇文章,添加两个标签
# article1.tags.append(tag1)
# article1.tags.append(tag2)
# article2.tags.append(tag1)
# article2.tags.append(tag2)
#
# Session = sessionmaker(bind=engine)
# session = Session()
#
# # 只需添加article即可,tag会被自动添加进去
# session.add_all([article1, article2])
# session.commit()

Session = sessionmaker(bind=engine)
session = Session()
tag = session.query(Tag).first()
print(tag.articles)  # [article1, article2]

article = session.query(Article).first()
print(article.tags)  # [tag1, tag2]

# 可以看到,相应的数据全部获取出来了

  

13.ORM层面删除数据注意事项

我们知道一旦关联,那么删除父表里面的数据是无法删除的,只能先删除字表的数据,然后才能删除关联的父表数据。如果在orm层面的话,可以直接删除父表数据,因为这里等同于两步。先将字表中关联的字段设置为NULL,然后删除父表中的数据,等同于这两步。为了方便演示,我这里将所有表全部删除

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()

class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)

class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    # uid是为了建立外键,我们要和use表的id进行关联,所以类型也要和user表的id保持一致
    uid = Column(Integer, ForeignKey("user.id"))
    # 这个是为了我们能够通过一张表访问到另外一张表
    # 以后User对象便可以通过articles来访问Articles表的属性了
    author = relationship("User", backref="articles")

Base.metadata.create_all()

# 创建几条记录
user = User(username="guido van rossum")
article = Article(title="Python之父谈论python的未来")

article.author = user
# 然后使用session添加article即可,会自动添加user
session.add(article)
session.commit()

此时我们在数据库层面删除一下数据

我们试试在orm层面删除数据

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()

class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)

class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    # uid是为了建立外键,我们要和use表的id进行关联,所以类型也要和user表的id保持一致
    uid = Column(Integer, ForeignKey("user.id"))
    # 这个是为了我们能够通过一张表访问到另外一张表
    # 以后User对象便可以通过articles来访问Articles表的属性了
    author = relationship("User", backref="articles")

Base.metadata.create_all()

# # 创建几条记录
# user = User(username="guido van rossum")
# article = Article(title="Python之父谈论python的未来")
#
# article.author = user
# # 然后使用session添加article即可,会自动添加user
# session.add(article)
# session.commit()

# 因为只有一条数据,所以直接first
user = session.query(User).first()
session.delete(user)
session.commit()

  

删除父表的数据,这个过程相当于先将article中的uid设置为NULL,然后删除父表的数据

但是这样也有危险,如果不熟悉SQLAlchemy的话,会造成不可避免的后果,怎么办呢?直接将uid设置为不可为空即可,加上nullable=False

14.relationship中的cascade属性

ORM层面的cascade:

首先我们知道如果如果数据库的外键设置为RESTRICT,那么在orm层面,如果删除了父表的数据,字表的数据将会被设置为NULL,如果想避免这一点,那么只需要将nullable设置为False即可

但是在SQLAlchemy中,我们只需要将一个数据添加到session中,提交之后,与其关联的数据也被自动地添加到数据库当中了,这是怎么办到的呢?其实是通过relationship的时候,其关键字参数cascade设置了这些属性:

1.save-update:默认选项,在添加一条数据的时候,会自动把与其相关联的数据也添加到数据库当中。这种行为就是save-update所影响的

2.delete:表示删除某一个模型的数据时,是否也删掉使用relationship与其相关联的数据。

3.delete-orphan:表示当对一个orm对象解除了父表中的关联对象的时候,自己便会被删掉。当然父表中的数据被删除了,自己也会被删除。这个选项只能用在一对多上,不能用在多对多以及多对一上。并且还需要在子模型的relationship中,添加一个single_parent=True的选项

4.merge:默认选项,当使用session.merge选项合并一个对象的时候,会将使用了relationship相关联的对象也进行merge操作

5.expunge:移除操作的时候,会将相关联的对象也进行移除。这个操作只会从session中删除,并不会从数据库当中移除

6.all:以上五种情况的总和

下面我来逐一演示

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()

class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)

class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)

    uid = Column(Integer, ForeignKey("user.id"))

    # 可以看到其他都是不变的,只是这里我们显式的指定了cascade
    # 这里设置为"",这样就不会有save-update属性了。
    author = relationship("User", backref="articles", cascade="")

Base.metadata.drop_all()
Base.metadata.create_all()

# 创建几条记录
user = User(username="guido van rossum")
article = Article(title="Python之父谈论python的未来")

article.author = user
# 然后使用session添加article即可,此时就不会添加user了
session.add(article)
session.commit()

  

  

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()

class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)

class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)

    uid = Column(Integer, ForeignKey("user.id"))

    # 可以看到其他都是不变的,只是这里我们显式的指定了cascade
    # 我们也可以手动指定save-update,当然这样和不指定cascade是一样,因为默认就是save-update
    author = relationship("User", backref="articles", cascade="save-update")

Base.metadata.drop_all()
Base.metadata.create_all()

# 创建几条记录
user = User(username="guido van rossum")
article = Article(title="Python之父谈论python的未来")

article.author = user
# 然后使用session添加article即可,会添加user
session.add(article)
session.commit()

  

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()

class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)

class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)

    uid = Column(Integer, ForeignKey("user.id"))

    # 可以看到其他都是不变的,只是这里我们显式的指定了cascade
    # 我们试一下delete,先说一下,可以设置多个属性,属性之间使用英文的逗号分隔
    # 这样指定之后,如果当我删除了Article表的数据,那么与之相关联的User表中的数据也会被删除
    author = relationship("User", backref="articles", cascade="save-update,delete")

Base.metadata.drop_all()
Base.metadata.create_all()

# 创建几条记录
user = User(username="guido van rossum")
article = Article(title="Python之父谈论python的未来")

article.author = user

# 如果不加delete,那么我删除Article表的数据,User表的数据依旧会坚挺在那里
# 但是我现在加上了delete,那么当我删除Article表的数据,User表的数据也会被删除
session.add(article)
session.commit()

  

15.三种排序方式

1.order_by

正序:session.query(table).order_by("table.column").all() ,或者 session.query(table).order_by("column").all()

倒序:session.query(table).order_by("table.column.desc").all()  或者 session.query(table).order_by("-column").all()

2.在模型中定义

class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)
    age = Column(Integer)

    # 就以User为例,在SQLAlchemy中有一个属性叫做__mapper_args__
    # 是一个字典,我们可以指定一个叫做order_by的key,value则为一个字段
    __mapper_args__ = {"order_by": age}
    # 倒序的话,__mapper_args__ = {"order_by": age.desc()}
    # 以后再查找的时候直接session.query(table).all()即可,自动按照我们指定的排序

  

3.使用backref

16.limit、offset以及切片操作

limit:session.query(table).limit(10).all(),从开头开始取十条数据

offset:session.query(table).offset(10).limit(10).all(),从第十条开始取10条数据

slice:session.query(table).slice(1, 8).all(),从第一条数据开始取到第八条数据。或者session.query(table)[1:8]这样更简单,连all()都不用了

17.数据查询懒加载技术

在一对多或者多对多的时候,如果想要获取多的这一部分数据的时候,往往通过一个属性就可以全部获取了。比如有一个作者,如果想要获取这个作者的所有文章,那么通过user.articles就可以全部获取了。但有时候我们不想获取所有的数据,比如只获取这个作者今天发表的文章,那么这个时候就可以给relationship传递一个lazy=“dynamic”,以后通过user.articles获取到的就不是一个列表,而是一个AppendQuery对象了。以后就可以对这个对象再进行过滤和排序工作。

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref
from pprint import pprint

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()

class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)
    age = Column(Integer)

class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    uid = Column(Integer, ForeignKey("user.id"))

    author = relationship("User", backref=backref("articles"))

Base.metadata.drop_all()
Base.metadata.create_all()

user = User(username="guido", age=63)
for i in range(10):
    article = Article(title=f"title{i}")
    article.author = user
    session.add(article)

session.commit()

user = session.query(User).first()
pprint(user.articles)
'''
[<__main__.Article object at 0x000001B73F8A5208>,
 <__main__.Article object at 0x000001B73F8A5278>,
 <__main__.Article object at 0x000001B73F8A52E8>,
 <__main__.Article object at 0x000001B73F8A5358>,
 <__main__.Article object at 0x000001B73F8A53C8>,
 <__main__.Article object at 0x000001B73F8A5438>,
 <__main__.Article object at 0x000001B73F8A54A8>,
 <__main__.Article object at 0x000001B73F8A5550>,
 <__main__.Article object at 0x000001B73F8A55F8>,
 <__main__.Article object at 0x000001B73F804400>]
'''

  以上获取了全部数据,如果只获取一部分呢?

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref
from pprint import pprint

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()

class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)
    age = Column(Integer)

class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    uid = Column(Integer, ForeignKey("user.id"))

    # 如果转化成一个AppendQuery对象,获取部分数据呢?只需要加上一个lazy="dynamic"即可
    # 注意这里一定要写在backref里面,我们的目的是为了通过user取article,把article转成一个AppendQuery对象,所以要写在backref里面
    # 如果写在了外面,那么就相当于通过article去找user,把user变成一个AppendQuery对象了
    # 所以我们要导入backref,表示反向引用。backref="articles",是为了通过user取得article,所以明明定义在article里面,却反过来被user使用
    # 同理backref=backref("articles")和backref="articles"是一样的,但是之所以要加上里面的这个backref,是为了给user提供更好的属性,比如这里的懒加载
    # 是为了让user找article的时候进行懒加载,如果没有这个backref,那么这个lazy属性我们可能要定义在user里面了,这样很麻烦
    # 但是通过导入backref,我们可以直接在article里面定义,反过来让user去使用,一旦定义在了外面,不反向了就变成article的了
    # 等于通过article找user进行懒加载
    author = relationship("User", backref=backref("articles", lazy="dynamic"))

    def __repr__(self):
        return f"{self.title}"

Base.metadata.drop_all()
Base.metadata.create_all()

user = User(username="guido", age=63)
for i in range(10):
    article = Article(title=f"title{i}")
    article.author = user
    session.add(article)

session.commit()

user = session.query(User).first()
# 此时不再是InstrumentList,而是一个AppendQuery对象
print(type(user.articles))  # <class 'sqlalchemy.orm.dynamic.AppenderQuery'>
# 查看一下源码发现,AppenderQuery这个类继承在Query这个类,也就是说Query可使用的,它都能使用

print(user.articles.filter(Article.id > 5).all())  # [title5, title6, title7, title8, title9]

# 也可以动态添加数据
article = Article(title="100")
user.articles.append(article)
session.commit()  # 不用add,直接commit

print(user.articles.filter(Article.id > 5).all())  # [title5, title6, title7, title8, title9, 100]

'''
lazy有以下参数:
1.select:默认选项,以user.articles为例,如果没有访问user.articles属性,那么SQLAlchemy就不会从数据库中查找文章。一旦访问,就会查找所有文章,最为InstrumentList返回
2.dynamic:返回的不是一个InstrumentList,而是一个AppendQuery对象。
主要使用这两种情况
'''

  

18.group_by和having字句

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_, func
from sqlalchemy import Column, Integer, String, ForeignKey, Table, Enum
from sqlalchemy.orm import sessionmaker, relationship, backref
from pprint import pprint

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()

class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)
    age = Column(Integer)
    gender = Column(Enum("male", "female","secret"), default="male")

Base.metadata.drop_all()
Base.metadata.create_all()

user1 = User(username="神田空太", age=16, gender="male")
user2 = User(username="椎名真白", age=16, gender="female")
user3 = User(username="四方茉莉", age=400, gender="female")
user4 = User(username="木下秀吉", age=15, gender="secret")
user5 = User(username="牧濑红莉栖", age=18, gender="female")

session.add_all([user1, user2, user3, user4, user5])
session.commit()

# group_by:分组,比方说我想查看每个年龄对应的人数
print(session.query(User.age, func.count(User.id)).group_by(User.age).all())
'''
输出结果:
[(15, 1), (16, 2), (18, 1), (400, 1)]
'''

# having:在group_by分组的基础上进行进一步查询,比方说我想查看年龄大于16的每一个年龄段对应的人数
print(session.query(User.age, func.count(User.id)).group_by(User.age).having(User.age > 16).all())
'''
输出结果:
[(18, 1), (400, 1)]
'''

# 如果想看看底层的语句是什么写的,可以不加上all()进行查看
print(session.query(User.age, func.count(User.id)).group_by(User.age).having(User.age > 16))
'''
SELECT user.age AS user_age, count(user.id) AS count_1
FROM user GROUP BY user.age
HAVING user.age > %(age_1)s

从user表中选出age,计算count(user.id)作为count_1,按照user.age分组,并且user.age要大于我们指定的值
'''

  

19.join实现复杂查询

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_, func
from sqlalchemy import Column, Integer, String, ForeignKey, Table, Enum
from sqlalchemy.orm import sessionmaker, relationship, backref
from pprint import pprint

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()

class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)

class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    uid = Column(Integer, ForeignKey("user.id"))

    author = relationship("User", backref=backref("articles", lazy="dynamic"))

    def __repr__(self):
        return f"{self.title}"

Base.metadata.drop_all()
Base.metadata.create_all()

user1 = User(username="guido")
user2 = User(username="ken")

article1 = Article(title="python")
article1.author = user1

article2 = Article(title="B")
article2.author = user2
article3 = Article(title="go")
article3.author = user2

session.add_all([article1, article2, article3])
session.commit()

  

在数据库层面上进行查询,查询每个作者发表了多少文章,因为id是不重复的,所以对id进行count即可

# 找到所有用户,按照发表文章的数量进行排序
res = session.query(User.username, func.count(Article.id)).join(Article, User.id == Article.uid).\
    group_by(User.id).order_by(func.count(Article.id))
print(res)
'''
SELECT user.username AS user_username, count(article.id) AS count_1
FROM user INNER JOIN article ON user.id = article.uid GROUP BY user.id ORDER BY count(article.id)
'''
print(res.all())
'''
[('guido', 1), ('ken', 2)]
'''

  

20.subquery实现复杂查询

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_, func
from sqlalchemy import Column, Integer, String, ForeignKey, Table, Enum
from sqlalchemy.orm import sessionmaker, relationship, backref
from pprint import pprint

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()

class Girl(Base):
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), nullable=False)
    anime = Column(String(50), nullable=False)
    age = Column(Integer, nullable=False)

    def __repr__(self):
        return f"{self.name}--{self.anime}--{self.age}"

Base.metadata.create_all()

girl1 = Girl(name="雨宫优子", anime="悠久之翼", age=16)
girl2 = Girl(name="宫村宫子", anime="悠久之翼", age=16)
girl3 = Girl(name="古河渚", anime="clannad", age=19)
girl4 = Girl(name="牧濑红莉栖", anime="命运石之门", age=18)

session.add_all([girl1, girl2, girl3, girl4])
session.commit()

  

# 下面要寻找和雨宫优子在同一anime,并且age相同的记录
girl = session.query(Girl).filter(Girl.name == "雨宫优子").first()
# Girl.anime == girl.anime, Girl.age == girl.age,girl是我们查找的name="雨宫优子"的记录,Girl则是整个表
# 查找整个表中的anime等于girl.anime 并且 age等于girl.age的记录
expect_girls = session.query(Girl).filter(Girl.anime == girl.anime, Girl.age == girl.age, Girl.name != "雨宫优子").all()
print(expect_girls)  # [宫村宫子--悠久之翼--16]

  

# 使用subquery
# 这里也可以使用label取个别名,当然我这里取得还是anime
ygyz = session.query(Girl.anime.label("anime"), Girl.age).filter(Girl.name == "雨宫优子").subquery()
# 这里的ygyz.c的c代指的是column,是一个简写
res = session.query(Girl).filter(Girl.anime == ygyz.c.anime, Girl.age == ygyz.c.age)
print(res)
'''
SELECT girls.id AS girls_id, girls.name AS girls_name, girls.anime AS girls_anime, girls.age AS girls_age
FROM girls, (SELECT girls.anime AS anime, girls.age AS age
FROM girls
WHERE girls.name = %(name_1)s) AS anon_1
WHERE girls.anime = anon_1.anime AND girls.age = anon_1.age
可以看到除了起了一些名字之外,SQLAlchemy生成的sql语句与我们之前写的几乎类似
'''
print(res.all())
'''
[雨宫优子--悠久之翼--16, 宫村宫子--悠久之翼--16]
'''

  和我们之前写的代码貌似没啥区别,也要两步,而且这个貌似代码量还多一些。但是倒数据库里面,我们现在写的只需要进行一次查询,效率更高一些。

21.Flask-SQLAlchemy的使用

Flask-SQLAlchemy的一个插件,可以更加方便我们去使用,SQLAlchemy是可以独立于flask而存在的,这个插件是将SQLAlchemy集成到flask里面来。我们之前使用SQLAlchemy的时候,要定义Base,session,各个模型之类,使用这个插件可以简化我们的工作

这个插件首先要安装,直接pip install flask-sqlalchemy即可

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
db_url = f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}"
# 这个配置不直接与我们的SQLAlchemy这个类发生关系,而是要添加到app.config里面,这一步是少不了的
# 至于这里的key,作者规定就是这么写的
app.config["SQLALCHEMY_DATABASE_URI"] = db_url
# 并且还要加上这一段,不然会弹出警告
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

# 接收一个app,从此db便具有了app的功能
db = SQLAlchemy(app)

# 建立模型,肯定要继承,那么继承谁的,继承自db.Module,相当于之前的Base。这里操作简化了,不需要我们去创建了
class User(db.Model):
    __tablename__ = "user"
    # 可以看到,之前需要导入的通通不需要导入了,都在db下面。不过本质上调用的还是sqlalchemy模块里的类。
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(50), nullable=False)

class Article(db.Model):
    __tablename__ = "article"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(50), nullable=False)
    uid = db.Column(db.Integer, db.ForeignKey("user.id"))

    authror = db.relationship("User", backref="articles")
    # 所以我们发现这和SQLAlchemy框架中的使用方法基本上是一致的,只不过我们在SQLAlchemy中需要导入的,现在全部可以通过db来访问

# 那么如何映射到数据库里面呢?这里也不需要Base.metadata了,直接使用db即可。
db.drop_all()
db.create_all()

  

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
db_url = f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}"
# 这个配置不直接与我们的SQLAlchemy这个类发生关系,而是要添加到app.config里面,这一步是少不了的
# 至于这里的key,作者规定就是这么写的
app.config["SQLALCHEMY_DATABASE_URI"] = db_url
# 并且还要加上这一段,不然会弹出警告
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

# 接收一个app,从此db便具有了app的功能
db = SQLAlchemy(app)

# 建立模型,肯定要继承,那么继承谁的,继承自db.Module,相当于之前的Base。这里操作简化了,不需要我们去创建了
class User(db.Model):
    __tablename__ = "user"
    # 可以看到,之前需要导入的通通不需要导入了,都在db下面。不过本质上调用的还是sqlalchemy模块里的类。
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(50), nullable=False)

class Article(db.Model):
    __tablename__ = "article"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(50), nullable=False)
    uid = db.Column(db.Integer, db.ForeignKey("user.id"))

    author = db.relationship("User", backref="articles")
    # 所以我们发现这和SQLAlchemy框架中的使用方法基本上是一致的,只不过我们在SQLAlchemy中需要导入的,现在全部可以通过db来访问

# # 那么如何映射到数据库里面呢?这里也不需要Base.metadata了,直接使用db即可。
# db.drop_all()
# db.create_all()

# 下面添加数据
user = User(name="guido")
article = Article(title="python之父谈python的未来")
user.articles.append(article)

db.session.add(user)
'''
或者
article.author = user
db.session.add(article)
'''
db.session.commit()
# 跟我们之前使用SQLAlchemy的流程基本一致

  

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
db_url = f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}"
# 这个配置不直接与我们的SQLAlchemy这个类发生关系,而是要添加到app.config里面,这一步是少不了的
# 至于这里的key,作者规定就是这么写的
app.config["SQLALCHEMY_DATABASE_URI"] = db_url
# 并且还要加上这一段,不然会弹出警告
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

# 接收一个app,从此db便具有了app的功能
db = SQLAlchemy(app)

# 建立模型,肯定要继承,那么继承谁的,继承自db.Module,相当于之前的Base。这里操作简化了,不需要我们去创建了
class User(db.Model):
    __tablename__ = "user"
    # 可以看到,之前需要导入的通通不需要导入了,都在db下面。不过本质上调用的还是sqlalchemy模块里的类。
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(50), nullable=False)

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

class Article(db.Model):
    __tablename__ = "article"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(50), nullable=False)
    uid = db.Column(db.Integer, db.ForeignKey("user.id"))

    author = db.relationship("User", backref="articles")

    def __repr__(self):
        return f"{self.tit }"

# 如何查找数据呢?
# 首先我们可以想到db.session.query(User),这毫无疑问是可以的,但是我们的模型继承了db.Model,那么我们有更简单的方法
# 直接使用User.query即可,就等价于db.session.query(User)
users = User.query.all()
print(users)  # [guido]
'''
排序:User.query.order_by(User.id.desc()).all()
过滤:User.query.filter(User.id == 1).all()
修改:User.query.filter(User.id == 1).first().name == "guido van rossum"
删除:删除的话还是要依赖db.session()的,找到user,db.session.delete(user)
'''
# 另外忘记提了,如果没有指定表名,也就是__tablename__,那么默认会将模型的名字转成小写当成表名。
'''
User-->user
但如果是驼峰命名法,UserModel-->user_model
但是不推荐这样使用,还是显示的指定比较好,也符合python之禅,明言胜于暗喻。
'''

  

22.alembic数据库迁移工具的使用

alembic是SQLAlchemy作者所写的一款用于做ORM与数据库的迁移和映射的一个框架,类似于git。

首先肯定要安装,pip install alembic

这是我的项目结构,一个文件夹,里面一个py文件

进入文件夹里面,输入alembic init xxxx

接下来建立模型,这个是独立于flask的,所以我们这次还是使用SQLAlchemy做演示

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, func
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可为空
    age = Column(Integer, nullable=False)

    def __repr__(self):
        return f"{self.name}--{self.age}"

下面修改配置文件,alembic.ini

接下来生成迁移文件,alembic revision --autogenerate -m "message",这里的message是我们注释信息

下面就要更新数据库了,将刚才生成的迁移文件映射到数据库。至于为什么需要迁移文件,那是因为无法直接映射orm模型,需要先转化为迁移文件,然后才能映射到数据库当中。

alembic upgrade head,将刚刚生成的迁移文件映射到数据库当中

如果需要修改表的结构,不需要再drop_all,create_all了,如果里面有大量数据,不可能清空之后重新创建。那么在修改之后,直接再次生成迁移文件然后映射到数据库就可以了

总结一下,就是五个步骤:

1.定义好自己的模型

2.使用alembic init 仓库名,创建一个仓库

3.修改两个配置文件,在alembic.ini中配置数据库的连接方式,在env中修改target_metada=Base.metadata

4.生成迁移文件

5.将迁移文件映射到数据库

可以看出,这和Django非常类似。如果以后修改模型,那么重复4和5

23.alembic常用命令和经典错误解决办法

常用命令:

  init:创建一个alembic仓库

  revision:创建一个新的版本文件

  --autogenerate:自动将当前模型的修改,生成迁移脚本

  -m:本次迁移做了哪些修改,用户可以指定这个参数,方便回顾

  upgrade:将指定版本的迁移文件映射到数据库中,会执行版本文件中的upgrade函数。如果有多个迁移脚本没有被映射到数据库中,那么会执行多个迁移脚本

  [head]:代表最新的迁移脚本的版本号

  downgrade:降级,我们每一个迁移文件都有一个版本号,如果想退回以前的版本,直接使用alembic downgrade version_id

  heads:展示head指向的脚本文件

  history:列出所有的迁移版本及其信息

  current:展示当前数据库的版本号

经典错误:

  FAILED:Target database is not up to date。原因:主要是heads和current不相同。current落后于heads的版本。解决办法:将current移动到head上,alembic upgrade head

  FAILED:can’t locate revision identified by “78ds75ds7s”。原因:数据库中村的版本号不在迁移脚本文件中。解决办法:删除数据库中alembic_version表的数据,然后重新执行alembic upgrade head

  执行upgrade head 时报某个表已经存在的错误。解决办法:1.删除version中所有的迁移文件的代码,修改迁移脚本中创建表的代码

24.Flask-SQLAlchemy下alembic的配置

重新建一个项目

config.py,用于存放配置文件

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"

SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{username}:{password}@{hostname}:{port}/{database}"

  

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import config
app = Flask(__name__)
app.config.from_object(config)
db = SQLAlchemy(app)

@app.route("/index")
def hello():
    return "hello world"

class User(db.Model):
    __tablename__ = "user"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(50), nullable=False)

if __name__ == '__main__':
    app.run()

  alembic.ini

# A generic, single database configuration.

[alembic]
# path to migration scripts
script_location = alembic

# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s

# timezone to use when rendering the date
# within the migration file as well as the filename.
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =

# max length of characters to apply to the
# "slug" field
#truncate_slug_length = 40

# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false

# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false

# version location specification; this defaults
# to alembic/versions.  When using multiple version
# directories, initial revisions must be specified with --version-path
# version_locations = %(here)s/bar %(here)s/bat alembic/versions

# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8

sqlalchemy.url = mysql+pymysql://root:zgghyys123@localhost:3306/satori

# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console
qualname =

[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine

[logger_alembic]
level = INFO
handlers =
qualname = alembic

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

  

from __future__ import with_statement
from alembic import context
from sqlalchemy import engine_from_config, pool
from logging.config import fileConfig
import sys, os
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
import start

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config

# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = start.db.metadata

# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.

def run_migrations_offline():
    """Run migrations in 'offline' mode.

    This configures the context with just a URL
    and not an Engine, though an Engine is acceptable
    here as well.  By skipping the Engine creation
    we don't even need a DBAPI to be available.

    Calls to context.execute() here emit the given string to the
    script output.

    """
    url = config.get_main_option("sqlalchemy.url")
    context.configure(
        url=url, target_metadata=target_metadata, literal_binds=True)

    with context.begin_transaction():
        context.run_migrations()

def run_migrations_online():
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    connectable = engine_from_config(
        config.get_section(config.config_ini_section),
        prefix='sqlalchemy.',
        poolclass=pool.NullPool)

    with connectable.connect() as connection:
        context.configure(
            connection=connection,
            target_metadata=target_metadata
        )

        with context.begin_transaction():
            context.run_migrations()

if context.is_offline_mode():
    run_migrations_offline()
else:
    run_migrations_online()

  生成迁移文件

映射到数据库

25.flask-script讲解

flask-script的作用是可以通过命令行的方式来操作flask。例如通过命令来跑一个开发版本的服务器,设置数据库,定时任务等等。要使用的话,首先要安装,pip install flask-script

这个多了一个manage.py,可以类比Django,如果我们把所有东西都写在一个py文件里面,会非常的凌乱。那这个manage.py是干什么的呢?可以让我们通过命令行来工作

start.py

from flask import Flask
app = Flask(__name__)

@app.route("/index")
def index():
    return "hello satori"

if __name__ == '__main__':
    manager.run()  # 注意这里是manager.run(),不是app.run()

  

manage.py

from flask_script import Manager
from start import app

manager = Manager(app)

@manager.command
def hello():
    print("你好啊")

if __name__ == '__main__':
    app.run()

  

from flask_script import Manager
from start import app

manager = Manager(app)

@manager.command
def hello():
    return "你好啊"

@manager.option("--name", dest="username")
@manager.option("--age", dest="age")
def foo(username, age):
    # 类似于python里的optparse,可以见我的python常用模块,里面有介绍
    print(f"name={username}, age={age}")

if __name__ == '__main__':
    manager.run()

  

不传参,使用command,传参使用option

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import config
app = Flask(__name__)
app.config.from_object(config)
db = SQLAlchemy(app)

class BackendUser(db.Model):
    __tablename__ = "backend_user"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(50), nullable=False)
    email = db.Column(db.String(50), nullable=False)

# alembic很强大,但这里就不用了,直接使用create_all
db.create_all()

@app.route("/index")
def index():
    return "hello satori"

if __name__ == '__main__':
    app.run()
from flask_script import Manager
from start import app, BackendUser, db

manager = Manager(app)

@manager.command
def hello():
    return "你好啊"

@manager.option("--name", dest="username")
@manager.option("--email", dest="email")
def add_user(username, email):
    user = BackendUser(username=username, email=email)
    db.session.add(user)
    db.session.commit()

if __name__ == '__main__':
    manager.run()

  以后就可以通过终端来添加了

而且我们还可以模拟数据库迁移,映射等等。Django的manage.py不就是这么做的吗?python manage.py makemigrations迁移,然后再python manage.py migrate映射。

26.flask-migrate

在实际的数据库开发中,经常会出现数据表修改的行为。一般我们不会手动修改数据库,而是去修改orm模型,然后再把模型映射到数据库中。这个时候如果能有一个工具专门做这件事情就非常好了,而flask-migrate就是用来干这个的。flask-migrate是基于alembic的一个封装,并集成到flask当中,而所有的操作都是alembic做的,它能跟踪模型的变化,并将模型映射到数据库中。

from flask_migrate import Migrate, MigrateCommand

manager = Manager(app, db)  # 绑定app和db到flask_migrate

manager.add_command("db", MigrateCommand)  # 添加Migrate的所有子命令到db下

1.初始化一个环境:python manage.py db init

2.自动检测模型,生成迁移脚本,python manage.py db migrate

3.将迁移脚本映射到数据库中,python manage.py db upgrade

4.flask数据库的更多相关文章

  1. 实验3、Flask数据库操作-如何使用Flask与数据库

    1. 实验内容 数据库的使用对于可交互的Web应用程序是极其重要的,本节我们主要学习如何与各种主要数据库进行连接和使用,以及ORM的使用 2. 实验要点 掌握Flask对于各种主要数据库的连接方法 掌 ...

  2. 细说flask数据库迁移

    什么情况下要用数据库迁移? 在开发过程中,需要修改数据库模型,而且还要在修改之后更新数据库.最直接的方式就是删除旧表,但这样会丢失数据. 更好的解决办法是使用数据库迁移框架,它可以追踪数据库模式的变化 ...

  3. flask数据库迁移理解及命令

    前言: 使用数据库迁移,可以直接建表,而不用我们自己写sql语句用来建表.就是将关系型数据库的一张张表转化成了Python的一个个类. 在开发中经常会遇到需要修改原来的数据库模型,修改之后更新数据库, ...

  4. 03 flask数据库操作、flask-session、蓝图

    ORM ORM 全拼Object-Relation Mapping,中文意为 对象-关系映射.主要实现模型对象到关系数据库数据的映射. 1.优点 : 只需要面向对象编程, 不需要面向数据库编写代码. ...

  5. flask~数据库

    flask与数据库的连接基于flaks_sqlaichemy 扩展 首先要连接数据库的时候必须得先下载 pip install flask-sqlalchemy 这个扩展 flask框架与数据库的连接 ...

  6. flask数据库操作

    Python 数据库框架 大多数的数据库引擎都有对应的 Python 包,包括开源包和商业包.Flask 并不限制你使用何种类型的数据库包,因此可以根据自己的喜好选择使用 MySQL.Postgres ...

  7. Flask数据库

    一 数据库的设置 Web应用中普遍使用的是关系模型的数据库,关系型数据库把所有的数据都存储在表中,表用来给应用的实体建模,表的列数是固定的,行数是可变的.它使用结构化的查询语言.关系型数据库的列定义了 ...

  8. Flask 数据库迁移

    在开发过程中,需要修改数据库模型,而且还要在修改之后更新数据库.最直接的方式就是删除旧表,但这样会丢失数据. 更好的解决办法是使用数据库迁移框架,它可以追踪数据库模式的变化,然后把变动应用到数据库中. ...

  9. Flask数据库基本操作

    数据库基本操作 在Flak-SQLAlchemy中,插入.修改.删除操作,均由数据库会话管理. 会话用db.session表示.在准备把数据写入数据库前,需要先将数据添加到会话中然后调用commit( ...

随机推荐

  1. React错误总结解决方案(二)

    1.React native: Cannot add a child that doesn't have a YogaNode or parent node 该错误一般是因为render方法中注释语句 ...

  2. URAL 1932 The Secret of Identifier(容斥)

    Description Davy Jones: You've been captain of the Black Pearl for 13 years. That was our agreement. ...

  3. 使用ListOperations操作redis

    使用ListOperations对象操作redis list: 方法 c参数 s说明   List<V> range(K key, long start, long end); K key ...

  4. C#程序中SQL语句作为函数参数形式问题

    今天遇到一个神奇现象,目前正在写一个Demo,人事管理系统,首先肯定是初始化主页面,在初始化时,需要声明一个登陆窗体,但是当我在登陆窗体中填入登入名称和密码时直接就登陆成功了,但是发现我的status ...

  5. JMS实战——ActiveMQ实现Pub-Sub

    前言 上篇博客<JMS实战--ActiveMQ>介绍了ActiveMQ的安装,并实现了简单的PTP模型.这篇博客我们来看一下Pub-Sub模型,之后来总结一下JMS. 实现 项目结构 其中 ...

  6. GET传值

    发送页: <form id="form1" runat="server"> <div> <asp:TextBox ID=</ ...

  7. SQL语言:结构化查询语言

    SQL语言:结构化查询语言 程序员或者DBA(数据库管理员)使用SQL和DBBSM进行交互,操纵数据库中的资源 分类: 1.DDL 数据定义语言 结构 create  创建   database ta ...

  8. 12.25模拟赛T1

    可以区间dp,但是复杂度太高. 所以应该是贪心,怎么贪心呢? 这种题目,最好还是手玩找一些规律. 可以发现,由于保证可以m次填完,所以颜色之间没有相互包含关系. 比较像分治的模型. 所以考虑拿到一个区 ...

  9. 写一个JavaScript“返回顶部”功能

    在web页面中,如果页面较高,为了方便用户快速地返回顶部,都会添加一个返回顶部按钮. 效果演示可以查看本页.如果页面有滚动高度,右下角就会有一个含有“返回顶部”字样的黑色背景半透明的小条条.点击这里“ ...

  10. CentOS 7, Attempting to create directory /root/perl5

    By francis_hao    Apr 10,2017 在使用CentOS 7的时候,首次登陆会出现新建一个perl5文件夹的提示,删除该文件后,之后登陆还是会出现该提示并新建了perl5文件夹. ...