Flask-SQLAlchemy入门

安装

pip install flask-sqlalchemy

windows如果出现安装错误:ERROR: Cannot unpack file xxxx,使用下面命令安装成功

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn flask-sqlalchemy

简单入门

init.py:初始化的文件
insertUserTable.py:创建所有的表,插入用户数据,查询用户数据

# init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import sys
import os

app = Flask(__name__)

# 创建数据库,并连接flask程序
WIN = sys.platform.startswith('win')
if WIN:
    prefix = 'sqlite:///'
else:
    prefix = 'sqlite:////'
dev_db = prefix + os.path.join(os.path.dirname(app.root_path), 'data.db')
print("dev_db:", dev_db)
app.config['SQLALCHEMY_DATABASE_URI'] = dev_db
app.config['SECRET_KEY'] = 'secret string'
# 设置每次请求结束后会自动提交数据库的改动
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# 查询时显示原始SQL语句
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)

# 声明用户表
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(120), unique=True)


    def __init__(self, username, email):
        self.username = username
        self.email = email


    # 此方法是实例化对象的自我描述信息,相当于java的toString(),默认情况下返回:类名+object at 内存地址
    def __repr__(self):
        return '<User: %s %s >' % (self.username, self.email)



# insertUserTable.py
from demo.init import db, User
'''
创建用户数据并插入用户表,然后查询用户表中的数据
'''
db.drop_all()  # 删除所有的表
db.create_all()  # 创建所有生命的表

# 创建用户数据
user1 = User('user1', 'user1@qq.com')
user2 = User(username='user2', email='user2@qq.com')

# 将创建好的用户数据插入到用户表中
db.session.add(user1)
db.session.add(user2)

db.session.commit()

# 查询用户表所有的用户数据
users = User.query.all()
print(users)

执行输出如下:

[<User: user1 user1@qq.com >, <User: user2 user2@qq.com >]

常用的sqlalchemy字段类型

类型名 python中的类型 说明
Integer int 整数类型,32位
SmallInteger int 取值范围小的整数,16位
BigInteger int或long 不限制精度的整数
Float float 浮点数
Numeric decimal.Decimal 整数,32位
String str 变长字符串
Text str 变长字符串,对较长或不限制长度的字符串做了优化
Unicode unicode 变长Unicode字符串
UnicodeText unicode 变长Unicode字符串,对较长或不限长度的字符串做了优化
Boolean bool 布尔值
Date datetime.date 时间
Time datetime.datetime 日期和时间
LargeBinary str 二进制文件

常用的列表项

选项名 说明
primary_key True表示表的主键
unique True表示这列不允许出现重复的值
index True表示为这列创建索引,提高查询效率
nullable True表示允许有空值,False表示不允许有空值
default 设置默认值

常用的关系选项

选项名 说明
backref 在关系的另一模型中添加反向引用
primary join 明确指定两个模型之间使用的联结条件
uselist 如果为False,不使用列表,而使用标量值
order_by 指定关系中记录的排序方式
secondary 指定多对多关系中关系表的名字
secondary join 在SQLALchemy中无法自行决定时,指定多对多关系中的二级联结条件

flask-sqlalchemy配置项

配置项的key 说明
SQLALCHEMY_DATABASE_URI 用于连接数据库,例如:sqlite:////tmp/test.db(linux或mac),mysql://username:password@server/db
SQLALCHEMY_BINDS 一个映射绑定 (bind) 键到 SQLAlchemy 连接 URIs 的字典。 更多的信息请参阅 绑定多个数据库。
SQLALCHEMY_ECHO 设置成 True,显示原始SQL语句日志
SQLALCHEMY_POOL_SIZE 数据库连接池的大小。默认是数据库引擎的默认值 (通常是 5)
SQLALCHEMY_RECORD_QUERIES 可以用于显式地禁用或者启用查询记录。查询记录 在调试或者测试模式下自动启用。更多信息请参阅 get_debug_queries()
SQLALCHEMY_NATIVE_UNICODE 可以用于显式地禁用支持原生的 unicode。这是 某些数据库适配器必须的(像在 Ubuntu 某些版本上的 PostgreSQL),当使用不合适的指定无编码的数据库 默认值时。
SQLALCHEMY_POOL_RECYCLE 自动回收连接的秒数。这对 MySQL 是必须的,默认 情况下 MySQL 会自动移除闲置 8 小时或者以上的连接。 需要注意地是如果使用 MySQL 的话, Flask-SQLAlchemy 会自动地设置这个值为 2 小时
SQLALCHEMY_MAX_OVERFLOW 控制在连接池达到最大值后可以创建的连接数。当这些额外的 连接回收到连接池后将会被断开和抛弃
SQLALCHEMY_TRACK_MODIFICATIONS 如果设置成 True (默认情况),Flask-SQLAlchemy 将会追踪对象的修改并且发送信号。这需要额外的内存, 如果不必要的可以禁用它

一对多关系

假设有两张表:班级表Classe和学生表Student,Classe存储班级信息,Student存储学生信息;每个班级对应多个学生,
一个学生只能对应一个班级,班级表Classe与学生表Students是一对多关系

db.relationship()关系函数定义关系属性,
这里让Classe指向Student类并加载多个,如果想要一对一关系,使用userlist=False,并设置给relationship()。

backref是在一个Student类上声明新属性的简单方法,也可以通过stu._class来获取该学生所属班级
lazy决定了SQLALCHEMY什么时候从数据库加载数据:

  • select : 默认属性。按需加载,即在访问到属性的时候,才会全部加载该属性的数据
  • joined : 加载记录,但使用联结
  • subquery : 与joined类似,立即加载,但使用子查询。
  • dynamic : 不加载记录,而是提供加载记录的查询,即生成一个query对象,方便进行条件过滤
from demo.init import db

class Student(db.Model):
    __tablename__ = 'students'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), nullable=False)
    # 定义外键(表明.字段名)
    class_id = db.Column(db.Integer, db.ForeignKey('classes.id'))

    def __repr__(self):
        return '<Student: %r>' % self.name

class Classe(db.Model):
    __tablename__ = 'classes'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), nullable=False)
    # 使用关系函数定义关系属性
    students = db.relationship('Student', backref='_class', lazy='select')

    def __repr__(self):
        return '<Classe %r>' % self.name

# 创建所有的表
db.create_all()

# 删除Student表和Classe表的所有行
Student.query.delete()
Classe.query.delete()

# 创建数据,并插入数据库
stu1 = Student(name='stu1')
stu2 = Student(name='stu2')
stu3 = Student(name='stu3')

class1 = Classe(name='class1', students=[stu1])
class2 = Classe(name='class2', students=[stu2, stu3])

db.session.add_all([stu1, stu2, stu3, class1, class2])
db.session.commit()

# 查询所有学生
stu_all = Student.query.all()
print('全部学生:', stu_all)

# 查询第一个学生所属班级
stu_01 = stu_all[0]
print("该学生所属班级:", stu_01._class)

# 查询二班所有的学生
stus_of_class2 = Student.query.filter_by(_class=class2).all()
print('二班的学生:', stus_of_class2)

# 查询全部班级
cls_all = Classe.query.all()
print('全部班级:', cls_all)

# 查询二班所有的学生
cls2 = Classe.query.filter_by(name='class2').first()
print('二班学生:', cls2.students)

多对多关系

假设一堆学生学习不同的课程,这就是多对多关系
多对多的关系中需要定义一个用于关系的辅助表,这个辅助表不使用模型,而是采用一个实际的表

from demo.init import db

'''
多对多关系
假设一堆学生学习不同的课程,这就是多对多关系
'''
tb_student_course = db.Table('tb_student_course',
                             db.Column('student_id', db.Integer, db.ForeignKey('student.id')),
                             db.Column('course_id', db.Integer, db.ForeignKey('courses.id')))

class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)

    # 关联属性,多对多的情况,可以写在任意一个模型类型中,含有正向引用和反向引用
    relate_courses = db.relationship('Course',
                                     secondary=tb_student_course,
                                     backref='relate_student',
                                     lazy='dynamic')
    def __repr__(self):
        return '<Student %r>' % self.name

class Course(db.Model):
    __tablename__ = 'courses'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)

    def __repr__(self):
        return '<Course %r>' % self.name

# 创建所有表
db.create_all()

# 删除Student表和Course表的所有行
Student.query.delete()
Course.query.delete()

# 添加数据
stu1 = Student(name='张三')
stu2 = Student(name='李四')
stu3 = Student(name='王五')

cou1 = Course(name='物理')
cou2 = Course(name='化学')
cou3 = Course(name='生物')

# 添加关系
stu1.relate_courses = [cou2, cou3]
stu2.relate_courses = [cou2]
stu3.relate_courses = [cou1, cou2, cou3]

# 添加到数据库
db.session.add_all([stu1, stu2, stu3, cou1, cou2, cou3])
db.session.commit()

# 查询张三学习的所有课程
stu = Student.query.filter_by(name='张三').first()
for course in stu.relate_courses:
    print('张三学习的课程:', course.name)

# 查询学习生物这门课程的所有学生
course = Course.query.filter_by(name='生物').first()
for student in course.relate_student:
    print('学习生物的学生', student.name)

操作数据库:增删改查

from demo.init import db
from sqlalchemy import and_
'''
操作数据库,增删改查
'''

class Kids(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=False, unique=True)
    age = db.Column(db.Integer, nullable=False)

    def __repr__(self):
        return '<Kids name:%r age:%r>' % (self.name, self.age)

# 查询所有的kid
def queryAllKid():
    kids = Kids.query.all()
    print(kids)

# 添加数据到数据库
def add_data():
    kid1 = Kids(name='kid1', age=4)
    kid2 = Kids(name='kid2', age=5)
    kid3 = Kids(name='kid3', age=9)
    kid4 = Kids(name='kid4', age=12)
    kid5 = Kids(name='kid5', age=12)
    kid6 = Kids(name='kid6', age=12)

    db.session.add_all([kid1, kid2, kid3, kid4, kid5, kid6])
    db.session.commit()

# 创建数据库表
db.create_all()

# 删除Kids表所有行
Kids.query.delete()

# ----------- 增 -------------
# 添加一条数据
kid1 = Kids(name='kid1', age=4)
db.session.add(kid1)
db.session.commit()

# 添加多条数据(添加的是数组)
kid2 = Kids(name='kid2', age=5)
kid3 = Kids(name='kid3', age=9)
kid4 = Kids(name='kid4', age=12)

db.session.add_all([kid2, kid3, kid4])
db.session.commit()

queryAllKid()

# ----------- 删 -------------

# 先查询后删除
kid_del_1 = Kids.query.filter_by(name='kid1').first()
if kid_del_1 != None:
    db.session.delete(kid_del_1)
    db.session.commit()
queryAllKid()

# 删除Kids表所有行
Kids.query.delete()
queryAllKid()

# ----------- 改 -------------
add_data()
# 修改,先查询后修改
kid_update_1 = Kids.query.filter_by(name='kid1').first()
if kid_update_1 != None:
    kid_update_1.name = 'kid_1_update'
    db.session.commit()
queryAllKid()

# ----------- 查 -------------

# 原生slq语句查询,待补充
sql = 'select * from kids'
result_1 = db.session.execute(sql)

# 查询全部
result_2 = Kids.query.all()
print('查询全部:', result_2)

# 主键查询
result_3 = Kids.query.get(1)
print('主键查询:', result_3)

# 单条件查询
result_4 = Kids.query.filter_by(name='kid2').first()
print('条件查询:', result_4)

# 多条件查询
result_5 = Kids.query.filter(and_(Kids.name == 'kid2', Kids.age == 5)).first()
print('多条件查询:', result_5)

# 比较查询 .__lt__(5):小于5,__le__(5):小于等于5,__gt__(5):大于5,__ge__(5):大于等于5
result_6 = Kids.query.filter(Kids.age.__gt__(5)).all()
print('比较查询:', result_6)

# in查询
result_7 = Kids.query.filter(Kids.name.in_(['kid1', 'kid2', 'kid3'])).all()
print('in查询:', result_7)

# 排序
result_8 = Kids.query.order_by('age').all()
print('排序:', result_8)

# 限制查询,在符合age=12的结果中,跳过一个,剩下的结果选取前2个
result_9 = Kids.query.filter_by(age=12).offset(1).limit(2).all() 
print('限制查询:', result_9)

源码地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容

  • 转载,觉得这篇写 SQLAlchemy Core,写得非常不错。不过后续他没写SQLAlchemy ORM... ...
    非梦nj阅读 5,387评论 1 14
  • SQLAlchemy学习笔记(二) SQLAlchemy Core 现在我们可以连接到数据库了,接下来让我们看看如...
    麦卡鲁阅读 1,741评论 0 2
  • SQLAlchemy介绍和基本使用 数据库时一个网站的基础,在 flask 中可以自由的使用MySQL、Postg...
    宽哥好阅读 1,256评论 0 4
  • Flask-SQLAlchemy的使用: ORM的好处:可以让我们操作数据库跟操作对象是一样的,非常方便,因为一个...
    Dozing阅读 23,258评论 3 23
  • 印象里七夕应该是异地恋人们的专属节日,对于能在这一天相聚的恋人们,七夕自然是快乐的。对于不能在这一天相聚的恋人们,...
    弋Yi莹阅读 172评论 1 3