4.flask数据库

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.模型,指定查找这个模型中全部的对象python

2.模型中的属性,能够指定查找模型的某几个属性mysql

3.聚合函数git

  func.count:统计行的数量程序员

  func.avg:求平均值sql

  func.max:求最大值数据库

  func.min:求最小值flask

  func.sum:求总和windows

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只须要属性便可,这就意味着后者使用简单,但前者能够写出更复杂的查询

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

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

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

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

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

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

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

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

# 九、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对象language一、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对象language一、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)

'''
总结一下:
一、先把两个须要多对多的模型创建出来
二、使用Table定义一个中间表,中间表就是包含两个模型的外键字段就能够了,而且让它们两个作一个复合主键
三、在两个须要作多对多的模型中随便选择一个模型,定义一个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)

'''
总结一下:
一、先把两个须要多对多的模型创建出来
二、使用table定义一个中间表,中间表就是包含两个模型的外键字段就能够了,而且让它们两个作一个复合主键
三、在两个须要作多对多的模型中随便选择一个模型,定义一个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}"
'''
总结一下:
一、先把两个须要多对多的模型创建出来
二、使用table定义一个中间表,中间表就是包含两个模型的外键字段就能够了,而且让它们两个作一个复合主键
三、在两个须要作多对多的模型中随便选择一个模型,定义一个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便可api

 

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文件ruby

进入文件夹里面,输入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

相关文章
相关标签/搜索