目录

一、简介

二、使用

(一)采用sqlalchemy方式

1. 基本:通过SQLAlchemy连接数据库并取数据

2. ORM介绍(SQLAlchemy强大的地方)

(二)、采用Flask-SQLAlchemy方式


一、简介

SQLAlchemy是一个基于Python实现的ORM框架。该框架建立在 DB API之上,使用关系对象映射进行数据库操作,简言之便是:将类和对象转换成SQL,然后使用数据API执行SQL并获取执行结果。

SQLAlchemy本身无法操作数据库,其必须以来pymsql等第三方插件,Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:

MySQL-Pythonmysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>pymysqlmysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]MySQL-Connectormysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>cx_Oracleoracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]更多:http://docs.sqlalchemy.org/en/latest/dialects/index.html

操作数据库有两种方式,一是使用flask_sqlalchemy,另一个是直接使用原始的sqlalchemy。
       其中flask-sqlalchemy是对sqlalchemy进行了一些封装,提供了一些常用工具,使用更简洁。

二、使用

(一)采用sqlalchemy方式

1. 基本:通过SQLAlchemy连接数据库并取数据

# 0x01 通过SQLAlchemy连接数据库from sqlalchemy import create_engine# 为数据库配置变量
HOSTNAME = '127.0.0.1'
PORT = '3306'
DATABASE = 'select_test'
USERNAME = 'root'
PASSWORD = 'toor'
DB_URI = "mysql+pymysql://{}:{}@{}:{}/{}".format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)# 1 连接池的方式
# 创建数据库引擎, 创建数据库连接池,连接池中最大为5个。
# engine = create_engine(DB_URI, max_overflow = 0, pool_size = 5)# # 用with语句,只有连接成功才返回con,同时使用完毕后,自动结束连接
# # 连接数据库,执行sql语句,输出取得数据的第一条。
# with engine.contextual_connect() as con:
#     fs = con.execute('select * from teacher')
#     print(fs.fetchone())# 2 普通的方式
engine = create_engine(DB_URI)
conn = engine.connect()
# result = conn.execute('select 1')
result = conn.execute('select * from teacher')
print(result.fetchone())

执行原生SQL

# 0x02 执行原生sqlfrom sqlalchemy import create_engine
from zl_config import DB_URI# 为数据库配置变量,写入配置文件 zl_config.py中
# HOSTNAME = '127.0.0.1'
# PORT = '3306'
# DATABASE = 'select_test'
# USERNAME = 'root'
# PASSWORD = 'toor'
# DB_URI = "mysql+pymysql://{}:{}@{}:{}/{}".format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)# 创建数据库引擎, 创建数据库连接池,连接池中最大为5个。加入新参数echo=True(将操作数据库过程详细信息返回)
engine = create_engine(DB_URI, max_overflow = 0, pool_size = 5, echo=True)# 0x02
# 使用with语句连接数据库
with engine.contextual_connect() as con:# 查看users表是否存在,如果存在就删除  !这个句子有问题con.execute('drop table if exits users')# 创建users表,有字段id和namecon.execute('create table users(id int primary key auto_increment, name varchar(25))')# 插入两条数据con.execute('insert into users(name) values ("xiaoming")')con.execute('insert into users(name) values ("xiaozhao")')# # 执行查询操作rs = con.execute('select * from users')print(rs.fetchall())

问题:drop table if exits users

原生sql的缺陷:

  • sql语句重复利用率不高,越复杂的sql语句条件越多,会出现很多相近的sql语句。
  • 很多sql语句是在业务逻辑中出来的,如果有数据库要更改,就要去修改这些逻辑,这会容易漏掉对某些sql语句的修改。
  • 写sql时,容易忽略web安全问题,安全隐患!

2. ORM介绍(SQLAlchemy强大的地方)

ORM(Object Relationship Mapping),对象模型与数据库表的映射。 通过ORM,我们可以通过类的方式操作数据库,而不再写原生的SQL语句。通过把表映射成类,把行做实例,把字段作为属性,ORM在执行对象操作时,最终还是会把对应的操作转换为数据库原生语句。

优点:

  • 易用性:减少sql语句使用概率,代码直观清晰
  • 设计灵活:轻松写出复杂的查询
  • 可移植性:SQLAlchemy封装了底层的数据实现,支持多个关系数据库引擎,如MySQL、PostgreSQL、SQLite,可以很轻松的切换数据库。

原理思路:类  ->  数据库中的一张表
                       类中的属性(变量)   ->     数据库中那张表的字段
                       类中的method            ->      数据库中的操作
                       类创建的一个对象      ->     一条数据
                       用类的method 执行数据库操作

原理图:

ORM模型:

# 0x03 ORM模型from sqlalchemy import create_engine, Column, Integer, String
from zl_config import DB_URI# declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承)
from sqlalchemy.ext.declarative import declarative_base# 0.创建引擎,根据引擎创建基类
# 创建引擎(连接池的方式等后续教程)
engine = create_engine(DB_URI)# 知识点:对象继承于类,类继承于原类
# 用declarative_base函数根据engine创建一个ORM基类。
# 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现)
Base = declarative_base(engine)# 样表:
#      create table person(id int primary key autoincrement, name varchar(50), age int)
# 1.创建一个ORM模型,这个模型必须继承来自SQLAlchemy给我们提供的基类。
class Person(Base):# 该ORM模型映射到数据库中的表的名字__tablename__ = 'person' # 不写的话,该创建额表就是类名# 2.在这个ORM模型中创建一些属性,来跟表中的字段一一映射。这些属性必须是SQLAlchemy提供好的数据类型。id = Column(Integer, primary_key=True, autoincrement=True)name = Column(String(50))age = Column(Integer)
# 3.将创建好的ORM模型映射到数据库中。
Base.metadata.create_all()# 注意:一旦使用Base.metadata.create_all()将模型映射到数据库中后,即使改变了模型的字段,也不会重新映射了。

ORM数据类型:

  • Integer:映射到数据库中式INT类型。
  • Float:浮点类型,映射到数据库中是float类型,占据32位。会存在精度丢失的问题,如果要求精度请用DECIMAL。
  • Double:双精度浮点类型,映射到数据库是double类型,占据64位。
  • Boolean:布尔类型,映射到数据库中是tinyint类型。
  • DECIMAL:定点类型,解决float和double丢失精度问题(存的原理好像是小数点在哪,就存在哪),存储时需要两个参数,第一个参数用来标记这个字段总能存储多少个数字,第二个参数表示小数点后有多少位。
  • enum:枚举类型,映射到数据库中是enum类型。
  • Date:传递datetime.date()进去。存储时间,只能存储年月日,映射到数据库中为date类型,在python中可以使用datetime.date这个模块来指定。
  • DateTime:传递datetime.datetime()进去。存储时间,可以存储年月日时分秒,映射到数据库中也是datetime类型,在python中可以使用datetime.datetime来指定。
  • Time:传递datetime.time()进去。存储时间,可以存储时分秒,映射到数据库中是time类型,在python中可以使用datetime.time来指定。
  • String:可变字符类型,映射到数据库中是varchar类型。使用时需要指定长度,区别于Text类型。
  • Text:存储长字符串,一般可以存储6w多个字符,映射到数据库为text类型,如果超出范围可以使用longtext类型。
  • LONGTEXT:长文本类型 (需要导入)因为这个数据类型只在mysql中有,所以导入地方为:from sqlalchemy.dialects.mysql import LONGTEXT。
# 0x04 ORM模型中映射到数据库中的数据类型
from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DECIMAL, Enum, Date, DateTime, Time, Text
from zl_config import DB_URI# declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 使用长文本类型
from sqlalchemy.dialects.mysql import LONGTEXT# 采用py3中的enum模块
import enum
# 时间模块
from datetime import date
from datetime import datetime
from datetime import time# 0.创建引擎,根据引擎创建基类
# 创建引擎(连接池的方式等后续教程)
engine = create_engine(DB_URI)# 知识点:对象继承于类,类继承于原类
# 用declarative_base函数根据engine创建一个ORM基类。
# 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现)
Base = declarative_base(engine)# sessionmaker 功能待查
session = sessionmaker(engine)()# 定义自己的枚举
class TagEnum(enum.Enum):python = 'python'flask = 'flask'django = 'django'# 创建ORM模型
class Article(Base):__tablename__ = 'article'id = Column(Integer, primary_key=True, autoincrement=True)# price = Column(Float)is_delete = Column(Boolean) # Boolean类型对应数据库的tinyint类型# DECIMAL(10,4):存储时需要两个参数,第一个参数用来标记这个字段总能存储多少个数字,第二个参数表示小数点后有多少位。# ,位数超了就会报错。eg:100000.0001price = Column(DECIMAL(10, 4))# 后续存数据时,tag字段只能在‘python’、‘flask’、‘django’中取。# tag = Column(Enum('python', 'flask', 'django'))# 采用自己定义的枚举类型,好处是防止写字符串时,由于错误导致sql语句错误(比如,写成"python1",但其实没有python1,只有python)tag = Column(Enum(TagEnum))create_date = Column(Date)create_time = Column(DateTime)create_tiytime = Column(Time)content = Column(Text)content2 = Column(LONGTEXT)# 由于模型映射一次后,后续更改无效,故我们使用 flask-migrate,
# 由于还没学到flask-migrate,
# 所以使用Base.metadata.drop_all()把ORM模型删除
Base.metadata.drop_all()
# 映射到数据库
Base.metadata.create_all()# 插入数据
# article = Article(is_delete=True, price=9.9999, tag='python')
article = Article(is_delete=True, price=9.9999, tag=TagEnum.flask, create_date=date(2019, 9, 17), create_time=datetime(2019, 9, 17, 15, 2 ,10), create_tiytime=time(hour=15, minute=5, second=30), content="abd", content2="longabd")
session.add(article)
session.commit( )

 Column常用参数

  • default:默认值
  • nullable:是否可空
  • primary_key:是否为主键
  • unique:是否唯一
  • autoincrement:是否自增
  • onupdate:更新时执行的函数
  • name:该属性在数据库中的映射名,不指定的话使用这个属性名作为映射到数据库中的字段名

防止问题:请将这些指定参数放在数据类型后面。(至于问题,自己去查)

# 0x05 Column常用参数
from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DECIMAL, Enum, Date, DateTime, Time, Text
from zl_config import DB_URI# declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmakerfrom datetime import datetime# 0.创建引擎,根据引擎创建基类
# 创建引擎(连接池的方式等后续教程)
engine = create_engine(DB_URI)# 知识点:对象继承于类,类继承于原类
# 用declarative_base函数根据engine创建一个ORM基类。
# 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现)
Base = declarative_base(engine)# sessionmaker 功能待查
session = sessionmaker(engine)()class Article(Base):__tablename__ = "article"id = Column(Integer, primary_key=True, autoincrement=True)# default指定具体的值read_count = Column(Integer, default=11)# create_time = Column(DateTime)# default指定函数# 设置默认时间,注意是方法,不是方法的返回值# create_time = Column(DateTime, default=datetime.now)# nullable默认为Truetitle = Column(String(50), nullable=False, name='mytitle')# unique# tele = Column(String(50), unique=True)# !onupdate初次是为空的,只有更新了才会有值,故此处又加入了defaultupdate_time = Column(DateTime, onupdate=datetime.now, default=datetime.now)# 请搭配模型中字段值更改使用。(1. 2. 3.)
# 3.name参数测试
# 1.基本的测试
Base.metadata.drop_all()
Base.metadata.create_all()article = Article()
# article.create_time = datetime.now()
article.title = 'test'
session.add(article)
session.commit()# # 2.测试onupdate参数
# article = session.query(Article).first()
# print(article.update_time)
# # 更改title,以便查看是否更新了时间
# article.title = '563'
# session.commit()
# # 去数据库中看,是否更新时间了。
# article2 = session.query(Article).first()
# print(article2.update_time)

query参数

  1. 模型对象,指定查找这个模型中所有对象。
  2. 模型中的属性,指定只查找某个模型的其中几个属性。
  3. 聚合函数:
    1. func.count
    2. func.avg
    3. func.max
    4. func.min
    5. func.sum
# 6.filter
result = session.query(Article).filter(Article.id == 1).first()
print(result)
# 查看sql语句怎么写的,filter后不加其他。
result = session.query(Article).filter(Article.id == 1)
print(result)
result = session.query(Article).filter(Article.id != 1).all()
print(result)
# like、ilike(不区分大小写)
result = session.query(Article).filter(Article.title.like('title%')).all()
print(result)
result = session.query(Article).filter(Article.title.in_(['title_ 1', 'title2'])).all()
print(result)
# not in :notin_()  或者用取反 ~

外键

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

外键约束又以下几项:

  • RESTRICT:父表数据被删除,会阻止删除。(默认)
  • NO ACTION:在MySQL中,同RESTRICT。
  • CASCADE:级联删除。
  • SET NULL:父表数据被删除,子表数据会设置为NULL。
# 0x08 外键
from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DECIMAL, Enum, Date, DateTime, Time, Text, func, ForeignKey
from zl_config import DB_URI# declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker# 0.创建引擎,根据引擎创建基类
# 创建引擎(连接池的方式等后续教程)
engine = create_engine(DB_URI)# 知识点:对象继承于类,类继承于原类
# 用declarative_base函数根据engine创建一个ORM基类。
# 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现)
Base = declarative_base(engine)# sessionmaker 功能待查
session = sessionmaker(engine)()# user表
class User(Base):__tablename__ = "user"id = Column(Integer, primary_key=True, autoincrement=True)username = Column(String(50), nullable=False)# article表
class Article(Base):__tablename__ = "article"id = Column(Integer, primary_key=True, autoincrement=True)title = Column(String(50), nullable=False)content = Column(Text, nullable=False)# 外键uid,类型一定要和关联的键值保持类型一致。# 注意:ForeignKey()中写的是‘数据库’中的要关联的表和字段(!不是orm中!)# ondelete:用于传递外键约束(那四类里的)uid = Column(Integer, ForeignKey("user.id", ondelete="RESTRICT"))# __repr__与__str__很相似,不过打印一个列表__repr__也可以返回def __repr__(self):return "article.title:%s"%self.title# 1.模型映射
Base.metadata.drop_all()
Base.metadata.create_all()# 2.添加数据
user = User(username="zhaoming")
session.add(user)
session.commit()article = Article(title="abc", content="123", uid=1)
session.add(article)
session.commit()# 3.删除表测试
# sql:delete from user where id = 1;

ORM层外键和一对多关系

# 0x08 外键、ORM层外键和一对多关系
from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DECIMAL, Enum, Date, DateTime, Time, Text, func, ForeignKey
from zl_config import DB_URI# declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承)
from sqlalchemy.ext.declarative import declarative_base
# relationship用于外键查找
from sqlalchemy.orm import sessionmaker, relationship# 0.创建引擎,根据引擎创建基类
# 创建引擎(连接池的方式等后续教程)
engine = create_engine(DB_URI)# 知识点:对象继承于类,类继承于原类
# 用declarative_base函数根据engine创建一个ORM基类。
# 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现)
Base = declarative_base(engine)# sessionmaker 功能待查
session = sessionmaker(engine)()# user表
class User(Base):__tablename__ = "user"id = Column(Integer, primary_key=True, autoincrement=True)username = Column(String(50), nullable=False)# # 从一个作者找到其的所有文章 4.2# articles = relationship("Article")def __repr__(self):return "<User(id:%d username:%s)>"%(self.id, self.username)# article表
class Article(Base):__tablename__ = "article"id = Column(Integer, primary_key=True, autoincrement=True)title = Column(String(50), nullable=False)content = Column(Text, nullable=False)# 外键uid,类型一定要和关联的键值保持类型一致。# 注意:ForeignKey()中写的是‘数据库’中的要关联的表和字段(!不是orm中!)# ondelete:用于传递外键约束(那四类里的)uid = Column(Integer, ForeignKey("user.id", ondelete="RESTRICT"))# # 用于外键查找 4.1# author = relationship("User") # 4.3 backref反向引用author = relationship("User", backref="articles")# __repr__与__str__很相似,不过打印一个列表__repr__也可以返回def __repr__(self):return "<Article(id:%d, title:%s, content:%s uid:%d)>"%(self.id, self.title, self.content, self.uid)# # 1.模型映射
# Base.metadata.drop_all()
# Base.metadata.create_all()# # 2.添加数据
# user = User(username="zhaoming")
# session.add(user)
# session.commit()# article = Article(title="abc", content="123", uid=1)
# session.add(article)
# session.commit()# # 3.删除表测试
# # sql:delete from user where id = 1;# 4.一对多关系
# # 4.1传统做法
# article = session.query(Article).first()
# uid = article.uid
# print(article)
# # user = session.query(User).filter_by(id=uid).first()
# user = session.query(User).get(uid)
# print(uid)
# print(user)# # 4.2 ORM relationship方式
# # article = session.query(Article).first()
# # print(article.author.username)
# user = session.query(User).first()
# print(user)
# print(user.articles[0].title)# 4.3 不用两边都写relationship
user = session.query(User).first()
print(user)
print(user.articles[0].title)

通过"backref"指定反向访问的属性名。

注:articles有多个,故返回时时列表,取数据时要注意。

一对一关系

# 0x09 一对一关系
from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DECIMAL, Enum, Date, DateTime, Time, Text, func, ForeignKey
from zl_config import DB_URI# declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承)
from sqlalchemy.ext.declarative import declarative_base
# relationship用于外键查找
from sqlalchemy.orm import sessionmaker, relationship, backref# 0.创建引擎,根据引擎创建基类
# 创建引擎(连接池的方式等后续教程)
engine = create_engine(DB_URI)# 知识点:对象继承于类,类继承于原类
# 用declarative_base函数根据engine创建一个ORM基类。
# 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现)
Base = declarative_base(engine)# sessionmaker 功能待查
session = sessionmaker(engine)()# user表
class User(Base):__tablename__ = "user"id = Column(Integer, primary_key=True, autoincrement=True)username = Column(String(50), nullable=False)# # 3.1 设置一对一关系# extend = relationship("UserExtend", uselist=False)def __repr__(self):return "<User(id:%d username:%s)>"%(self.id, self.username)# 3.ORM层面
# 将user不常用的字段放在另一张表中
# 注:user和userextend是一对一的关系
class UserExtend(Base):__tablename__ = "userextend"id = Column(Integer, primary_key=True, autoincrement=True)school = Column(String(50))uid = Column(Integer, ForeignKey("user.id"))# # 3.1因为在User表中已经设置了,extend,所以不用反向写了# user = relationship("User")# 3.2 采用backref函数user = relationship("User", backref=backref("extend", uselist=False))# article表
class Article(Base):__tablename__ = "article"id = Column(Integer, primary_key=True, autoincrement=True)title = Column(String(50), nullable=False)content = Column(Text, nullable=False)# 外键uid,类型一定要和关联的键值保持类型一致。# 注意:ForeignKey()中写的是‘数据库’中的要关联的表和字段(!不是orm中!)# ondelete:用于传递外键约束(那四类里的)uid = Column(Integer, ForeignKey("user.id", ondelete="RESTRICT"))# # 用于外键查找 4.1# author = relationship("User") # 4.3 backref反向引用author = relationship("User", backref="articles")# __repr__与__str__很相似,不过打印一个列表__repr__也可以返回def __repr__(self):return "<Article(id:%d, title:%s, content:%s uid:%d)>"%(self.id, self.title, self.content, self.uid)# 1.模型映射
Base.metadata.drop_all()
Base.metadata.create_all()# # 2.基本
# # # 2.1创建数据:user添加两篇article
# # user = User(username = "xiaoli")# # article1 = Article(title="abc", content="abcda")
# # article2 = Article(title="czxc", content="zxcvbg")# # # user.articles是list类型
# # user.articles.append(article1)
# # user.articles.append(article2)# # session.add(user)
# # # 与user相关联的article1和article2都会自动添加
# # session.commit()# # 2.2反向添加:article添加user
# user = User(username="xixi")
# article = Article(title="sadf", content="werwe")
# article.author = user
# session.add(article)
# session.commit()# # 3.ORM层面
# # 3.1
# user = User(username='zhil')
# extend = UserExtend(school='zhil school')
# user.extend = extend# session.add(user)
# session.commit()# 3.2 采用backref函数
user = User(username='zhil')
extend = UserExtend(school='zhil school')
user.extend = extendsession.add(user)
session.commit()

多对多

思路:用中间表

  1. 先把两个需要做多对多的模型定义出来
  2. 使用Table定义一个中间表,中间表一般就是包含两个模型的外键字段,并让它们两个作为一个复合主键
  3. 在需要做多对多的模型之一上,定义一个relationship属性,来绑定三者之间的关系,在使用relationship时,需要传入一个secondary=中间表
# 0x010 多对多关系 (采用中间表)
from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DECIMAL, Enum, Date, DateTime, Time, Text, func, ForeignKey, Table
from zl_config import DB_URI# declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承)
from sqlalchemy.ext.declarative import declarative_base
# relationship用于外键查找
from sqlalchemy.orm import sessionmaker, relationship, backref# 0.创建引擎,根据引擎创建基类
# 创建引擎(连接池的方式等后续教程)
engine = create_engine(DB_URI)# 知识点:对象继承于类,类继承于原类
# 用declarative_base函数根据engine创建一个ORM基类。
# 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现)
Base = declarative_base(engine)# sessionmaker 功能待查
session = sessionmaker(engine)()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)
)# article表
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)# tag表
class Tag(Base):__tablename__ = "tag"id = Column(Integer, primary_key=True, autoincrement=True)name = Column(String(50), nullable=False)# 1.模型映射
Base.metadata.drop_all()
Base.metadata.create_all()article1 = Article(title="1")
article2 = Article(title="2")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.add(article2)
session.add(article1)
session.commit()# # 方便的获取其tag
# article = session.query(Article).first()
# article.tags

ORM层面删除数据注意事项

ORM层面删除数据,会无视mysql级别的外键约束,直接会将对应的数据删除,然后将从表中的那个外键设置为NULL,如果想要避免这种行为,应该将从表中的外键的“nullable=False"

# 0x010 多对多关系 (采用中间表)
from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DECIMAL, Enum, Date, DateTime, Time, Text, func, ForeignKey, Table
from zl_config import DB_URI# declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承)
from sqlalchemy.ext.declarative import declarative_base
# relationship用于外键查找
from sqlalchemy.orm import sessionmaker, relationship, backref# 0.创建引擎,根据引擎创建基类
# 创建引擎(连接池的方式等后续教程)
engine = create_engine(DB_URI)# 知识点:对象继承于类,类继承于原类
# 用declarative_base函数根据engine创建一个ORM基类。
# 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现)
Base = declarative_base(engine)# sessionmaker 功能待查
session = sessionmaker(engine)()# 创建模型
# user表
class User(Base):__tablename__ = "user"id = Column(Integer, primary_key=True, autoincrement=True)name = 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"))# 解决uid = Column(Integer, ForeignKey("user.id"), nullable=False)author = relationship("User", backref="articles")# Base.metadata.drop_all()
# Base.metadata.create_all()# user = User(name="lili")
# article = Article(title="asf")
# article.author = user# session.add(user)
# session.commit()# ORM层面会无视数据库层面的外键约束,这就是一个陷阱!
# 解决:将外键那里设置为不能为空。
user = session.query(User).first()
session.delete(user)
session.commit()

relationship方法中的casecade参数详解

在SQLAlchemy中,只要将一个数据添加到session中,和它关联的数据都可以一起存入数据库。实现原理:通过relationship的一个关键参数casecade可以设置这些属性:

  • save-update:默认选项。在添加一条数据时,会把其他和他关联的数据都添加到数据库中。
  • delete:表示当删除某一模型中的数据时,也删掉该模型中(关联的模型如果没有设置casecade="delete",那删除关联的模型执行的行为是默认行为,注意!单向通道的感觉)使用relationship和它关联的数据。
  • delete-orphan:表示当对一个ORM对象解除了父表中的关联对象时,自己便会被删除掉。当如果父表中的数据被删除,自己也会被删除。这个选项只能用在一对多上,不能用在多对多与多对一上。并且需要在子模型中的relationship中,增加一个single_parent=True的参数。
  • merge:默认选项,当在使用session.merge,合并一个对象的时候,会将使用了relationship相关联的对象也进行merge操作。
  • expunge:移除操作时,会将相关联的对象也进行移除。这个操作只是从session中移除,并不会真正的从数据库中删除。
  • all:是对save-updade,merge,refresh-expire,delete几种的缩写。
// 多个参数形式
casecade="save-update, delete"

排序(略)

limit、offset与切片操作(略)

复杂查询:group_by、having、join、subquery (略)

数据库查询懒加载技术

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

lazy可用的选项:

  • "select":这个是默认选项。例子:程序中user.articles返回的是一个列表。
  • "dynamic":返回一个“appendQuery”对象。

(二)、采用Flask-SQLAlchemy方式

1.基本

安装:

pip install flask_sqlalchemy

数据库连接:

  1. 和sqlalchemy一样,定义好数据库连接字符串DB_URI。
  2. 将定义好的数据库连接字符串DB_URI,通过"SQLALCHEMY_DATABASE_URI"这个键放到"app.config"中。
  3. 使用"flask_sqlalchemy.SQLAlchemy"这个类定义一个对象,并将"app"传入进去。
    db = SQLAlchemy(app)

创建ORM模型 

  1. 用法和sqlalchemy一样,定义模型,现在不再是需要使用“delarative_base”来创建一个基类,而是使用“db.Model”来作为基类。
  2. 在模型类中,‘Column’、‘String’、‘Integer’、‘relationship’等,都不需要导入,直接使用‘db’下面相应的属性名就可以了。
  3. 在定义模型时,可以不写‘__tablename__’,那么flask_sqlalchemy会默认使用当前模型的名字转换成小写来作为表的名字,且如果这个名字使用多个单词且是驼峰命名法,则会在多个单词之间使用下划线来进行连接。(最好还是用__tablename__)

ORM模型映射到数据库

  1. db.drop_all()
  2. db.create_all()

使用session

直接使用db.session

查询数据

如果查找数据只是查找一个模型上的数据,那么可以通过“模型.query”的方式进行查找。方法和sqlalchemy中的query方法一样。

from flask import Flask# 注意导入的是SQLAlchemy,这是一个类,不是sqlalchemy,这是一个包!
from flask_sqlalchemy import SQLAlchemyapp = Flask(__name__)# 配置不变: 不过可以放在配置文件中
HOSTNAME = '127.0.0.1'
PORT = '3306'
DATABASE = 'flaskdb'
USERNAME = 'root'
PASSWORD = 'toor'
DB_URI = "mysql+pymysql://{}:{}@{}:{}/{}".format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI
# 下面这个设置,请自己查,老师遇到了,自己写的没有遇到,估计是版本问题。
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)# 创建ORM模型
class User(db.Model):__tablename__ = 'user'# 因为封装了,所以不用单独再导入Column、Integer等等id = db.Column(db.Integer, primary_key=True, autoincrement=True)username = db.Column(db.String(50))def __repr__(self):return "<User:%s>"%self.usernameclass 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"), nullable=False)author = db.relationship("User", backref="articles")# # 将模型映射到数据库中
# db.drop_all()
# db.create_all()# # 添加数据
# user = User(username="cc")
# article = Article(title="luluxiu")
# article.author = user
# db.session.add(article)
# db.session.commit()# # 查找数据
# # User.query 和 db.session.query(User)效果一样
# users = User.query.all() # 返回list
# print(users)
# # 排序: users = User.query.order_by(User.id.desc()).all()
# # 其他                    .filter
#                         # .filter_by
#                         # .group_by
#                         # .join# # 修改
# user = User.query.filter(User.username=="cc").first()
# user.username = 'gg'
# db.session.commit()# 删除
article = Article.query.first()
db.session.delete(article)
db.session.commit()@app.route("/")
def index():return 'hello world'if __name__ == "__main__":app.run()

2.1(原生)alembic数据库迁移工具基本使用 (克服Base.drop_all()和Base.create_all()的缺陷)

alembic是sqlalchemy的作者开发的,用来做ORM模型与数据库的迁移和映射。使用方式和git类似,表现在两个方面:第一,alembic的所有命令都是以alembic开头;第二,alembic的迁移文件也是通过版本进行控制的。(其本质也是对Base.drop_all()和Base.create_all()的花式利用,故当出现错误时,可以从这里考虑。)

安装:pip install alembic

    使用

  1. 初始化alembic仓库:(注意,若是在虚拟环境安装的,那么就要在虚拟环境下初始化)在终端中,cd到你的项目目录,然后执行命令alembic init alembic,创建一个名叫alembic的仓库。(alembic init [仓库名字])默认用alembic
  2. 创建模型类:创建一个models.py模块,然后在里面定义你的模型类。
  3. 修改配置文件:
  • 在alembic(alembic.ini)中设置数据库连接,sqlalchemy.url = driver://user:pass@localhost/dbname  (sqlalchemy.url = mysql+pymysql://root:toor@localhost/almebic_demo?charset=utf8)。
  • 为了使用模型类更新数据,需要在env.py文件中设置target_metadata,默认为target_metadata=None。使用sys模块把当前项目的路径导入到path中。
  • import os
    import sys
    sys.path.append(os.path.dirname(os.path.dirname(__file__)))# 进行关联配置
    import zl_sqlalchemy13_alembic# 进行关联配置
    target_metadata = zl_sqlalchemy13_alembic.Base.metadata

    4.将ORM模型生成迁移脚本:使用alembic revision --autogenerate -m "message"   (message:自己写的名字要清晰的反应这次做的版本操作是什么,比如:第一次提交、增加了什么字段等等,方便对不同版本的数据库进行升级或降级还原等。)

alembic revision --autogenerate -m "first commit"

5.将生成的脚本映射到数据库中,使用alembic upgrade head 将刚刚生成的迁移文件,真正映射到数据库中;同理,如果要降级,那么使用alembic downgrade head

6.修改代码后:(修改代码后,更新数据库,重复4、5步)

# 添加ageage = Column(Integer, default=0)# 采用 alembic revision --autogenerate -m "add age column" # alembic upgrade head# 更新数据库

参考代码:

from sqlalchemy import Column, String, Integer, create_engine, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship# 0.安装alembic:pip install alembic (注意:如果是安装在虚拟环境)
# 1.初始化alembic仓库:alembic init alembic  (那么就要在虚拟环境中初始化)
# 2.创建模型类 class User(Base):...# 3.修改配置文件(alembic.ini):
# import os
# import sys
# sys.path.append(os.path.dirname(os.path.dirname(__file__)))
# # 进行关联配置
# import zl_sqlalchemy13_alembic
# # 进行关联配置
# target_metadata = zl_sqlalchemy13_alembic.Base.metadataHOSTNAME = '127.0.0.1'
PORT = '3306'
DATABASE = 'almebic_demo'
USERNAME = 'root'
PASSWORD = 'toor'
DB_URI = "mysql+pymysql://{}:{}@{}:{}/{}".format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)engine = create_engine(DB_URI)
Base = declarative_base(engine)class User(Base):__tablename__ = 'user'id = Column(Integer, primary_key=True, autoincrement=True)username = Column(String(50), nullable=False)# 添加ageage = Column(Integer, default=0)# 采用 alembic revision --autogenerate -m "add age column" # alembic upgrade head# 更新数据库# # 局限太大,后期上线不使用
# Base.metadata.create_all()# ORM->迁移脚本->映射到数据库# 4.将ORM模型生成迁移脚本:alembic revision --autogenerate -m "first commit"
# 5.将生成的脚本映射到数据库中,使用alembic upgrade head 将刚刚生成的迁移文件,真正映射到数据库中;同理,如果要降级,那么使用alembic downgrade head# 6.修改:重复4、5步。(注意:alembic revision --autogenerate -m ["message"] 中的message要根据具体情况更改)

常用命令

  • init:创建一个alembic仓库。
  • revision:创建一个新的版本文件。
  • -autogenerate:自动将当前模型的修改,生成迁移脚本。
  • -m:本次迁移做了哪些修改,用户可以指定这个参数,方便回帧。
  • upgrade:将指定版本的迁移文件映射到数据库中,会执行版本文件中的upgrade函数。如果有多个迁移脚本没有被映射到数据库中,那么会执行多个迁移脚本。
  • [head]:代表最新的迁移脚本的版本号。
  • downgrade:会执行指定版本的迁移文件中的downgrade函数。
  • heads:展示head指向的脚本文件版本号。
  • history:列出所有迁移版本及其信息。(只记录了upgrade的)
  • current:展示当前数据库中的版本号。

此外,在第一次执行upgrade时,会在数据库中创建一个名叫alembic_version表,这个表只会有一条数据,记录当前数据库映射的是哪个版本的迁移文件。

经典错误

错误描述 原因 解决方法
FAILED: Target database is not up to date. 主要是heads和current不相同。current落后于heads的版本。 将current移动到head上,alembic upgrade head
FAILED:Can't locate revision identified by '77524dsfsf' 数据库中存在版本号不在迁移脚本文件中 删除数据库的alembic_version表中的数据,重新执行alembic upgrade head
执行“upgrade head”命令报某个表已存在错误 执行这个命令时,会执行所有的迁移脚本,因为数据库中已经存在了这个表,然后迁移脚本中又包含了创建表的代码。 (1)删除versions中所有迁移文件。(2)修改迁移脚本中创建表的代码。

2.2 flask-SQLAlchemy下的alembic的配置

步骤:(安装一样)

  1. 初始化:alembic init alembic
  2. 写模型类
  3. 修改(alembic.ini):sqlalchemy.url = mysql+pymysql://root:toor@localhost/flaskdb?charset=utf8

(env.py)

import sys, os
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
import zl_flask_sqlalchemy2_alembictarget_metadata = zl_flask_sqlalchemy2_alembic.db.Model.metadata

4.5.6 (和之前一致)

Flask应用篇-数据库:SQLAlchemy 与 Flask_Sqlalchemy相关推荐

  1. Flask开发之数据库(SQLAlchemy)的配置以及增删改查

    Flask开发之数据库 (SQLAlchemy)的配置以及增删改查 1.在Flask中配置SQLAlchemy 1 安装flask-sqlalchemy 2 安装flask-mysqldb 3 在代码 ...

  2. Flask框架(flask中的数据库SQLAlchemy(python3),配置、模型类、创建表)

    1.  SQLAlchemy是一个关系型数据库框架, 它提供了高层的ORM和底层的原生数据库的操作. flask-sqlalchemy是一个简化了SQLAlchemy操作的flask扩展. 2. 安装 ...

  3. 从零开始搭建python flask+vue 小型web项目以及flask_sqlalchemy访问数据库

    重零开始搭建python flask+vue 小型web项目以及flask_sqlalchemy访问数据库 前言 作者是一个前端开发者,之前从未接触过python,也没接触过后端开发,所有这篇文章中有 ...

  4. eclipse连接mysql_专题一、flask构建mysql数据库正确姿势

    每周壹总结,一起共同充电第121篇 应用程序最核心的就是数据,每天我们写程序其实也是在处理数据的过程,那么很有必要系统性的讲讲和梳理python的flask框架是如何进行数据交互操作的. 趁这3天假期 ...

  5. Flask框架——ORM数据库

    目录 一.通过SQLAlchemy(ORM)操作数据库的流程 二.注意点 三.创建数据表类(继承db.Model) 四.增删改操作 五.查询数据库 六.relationship关联 七.数据库迁移 八 ...

  6. Flask 中的数据库迁移

    Flask 中的数据库迁移 在我之前使用 Flask 实现简单接口时,为了方便,我每次都会将数据表删除掉,然后重新创建表和添加数据.因为测试数据只有几条,所以可以使用删表重建的方式,但在实际的项目中, ...

  7. python的flask框架mysql数据库迁移

    最近在学习<flaskweb开发>,教程里采用 sqlite 数据库,我自己用的是mysql,在到数据库迁移部分有点懵了,想跳过去,但是到后面发现很不方便,于是折腾了好长时间,网上也找不到 ...

  8. Flask 蓝图,数据库链接

    蓝图 使用场景 如果代码非常多,要进行归类.不同的功能放在不同的文件,把相关的视图函数也放进去. 蓝图也就是对flask的目录结构进行分配(应用于小,中型的程序) 当然对于大型项目也可以通过   ur ...

  9. 【.NET Core项目实战-统一认证平台】第四章 网关篇-数据库存储配置(2)

    [.NET Core项目实战-统一认证平台]第四章 网关篇-数据库存储配置(2) 原文:[.NET Core项目实战-统一认证平台]第四章 网关篇-数据库存储配置(2) [.NET Core项目实战- ...

最新文章

  1. 这样配置,让你的 IDEA 好用到飞起来!
  2. [Java并发编程(二)] 线程池 FixedThreadPool、CachedThreadPool、ForkJoinPool?为后台任务选择合适的 Java executors...
  3. Boost:双图bimap与mi_bidirectional地图的测试程序
  4. ios获取手机 meid_共享充电宝并不简单,iOS也顶不住,隐私信息直接暴露
  5. Redis运维和开发学习笔记(7) 内存管理和过期策略
  6. Oracle安装 - shmmax和shmall设置
  7. 手机子王掩码和网关查找_C程序使用位掩码查找奇数或偶数
  8. 浅谈javascript数值类型转换
  9. 关于 MySQL 8.0 新特性“隐藏索引”的一点思考
  10. 固定 顶部_抗拉固定球铰支座优点与施工步骤
  11. 12个免费在线的Web网站性能测试工具
  12. 编译问题解决:mkdir: 无法创建目录/usr/local/share/man/man1: 文件已存在
  13. 【系统分析师之路】第五章 复盘软件工程(敏捷开发)
  14. 小米神隐模式破解(反系统息屏后网络中断)
  15. 网站域名服务器 地址查询,查看网站dns域名服务器ip地址查询
  16. 计算机教室条幅文字,教室横幅标语尺寸
  17. Python数据挖掘与机器学习实战——回归分析——线性回归及实例
  18. RK CPU调试技巧
  19. 阿里云天池python1
  20. Android8.0安装apk报错:Package xxx is currently frozen

热门文章

  1. 如何将手机录音文件转换成MP3格式
  2. AutoHotkey 与 AutoIt 的恩恩怨怨
  3. TCP 中 Flags 标志位 ACK、SYN 与 seq、ack
  4. 【软件架构】支持大规模系统的设计模式和原则
  5. 楼教主男人八题,告一段落
  6. [构造 找规律 孔明棋] Ural 1051 Simple Game on a Grid
  7. vue2.0 class声明组件_蘑菇街、滴滴、淘宝、微信的组件化架构解析,附源码Demo和PDF...
  8. IoT黑板报:面向5G无线 Xilinx发布射频级模拟技术
  9. 计算机中没有固态硬盘,为何我的固态硬盘没有预想中快?
  10. Codeforce-126B:Password(KMP模板题)