Flask-SQLAlchemy 基本使用

简介

Flask-SQLAlchemy 是 Python Web 框架 Flask 的一个扩展,简化了在 Flask 中对 ORM 数据库框架 SQLAlchemy 的操作。

基本使用

  • 一个最小型的应用:对于大多数只有一个 Flask 的应用来说,需要做的就是为 Flask 实例选择加载配置,然后把 SQLlchemy 实例传递给它即可:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# need to install pymysql first if using mysql
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:password@localhost/test'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
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

    def __repr__(self):
        return '<User %r>' % self.username


# create tables
db.create_all()

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

首先,要让 Flask-SQLAlchemy 根据模型类创建数据库。db.create_all()函数将寻找所有db.Model的子类,然后在数据库中创建对应的表,如果数据库中相应表已存在,则db.create_all()不会重新创建或者更新相应的表。
:上面的代码中我们已经在 MySql 数据库中创建了一个表user,接下来我们添加几条数据到user表中:

from yourapplication import User
admin = User(username='admin', email='admin@example.com')
guest = User(username='guest', email='guest@example.com')

db.session.add(admin)
db.session.add(guest)
db.session.commit()

:下面我们访问这个表,看下数据是否已插入成功:

>>> User.query.all()
[<User 'admin'>, <User 'guest'>]
>>> User.query.filter_by(username='admin').first()
<User 'admin'>

:下面我们删除掉username=guest这条数据:

guest = User.query.filter_by(username='guest').first()
db.session.delete(guest)
db.session.commit()

:下面我们修改username=admin这条数据,让其邮箱变更为admin@qq.com

# get User admin data from primary key
admin = User.query.get(1)
admin.email = 'admin@qq.com'
db.session.add(admin) # not necessary
db.session.commit()

更多 Flask-SQLAlchemy 对数据库进行增删改查的操作,请查看:Select, Insert, Delete

数据库表间关系简解

在关系型数据中,表与表之间存在三种关系:一对多,一对一和多对多。由于关系是建立在多个表之间的,因此当我们声明一个表单模型时,与之关联的表可能并未建立,因此可以使用字符串指向未创建的关联表名。下面简单介绍下这三种关系在 Flask-SQLAlchemy 中的实现方式:

  • 一对多:最常见的表间关系。假设现在有两张表:学生表students和班级表classesstudents存储学生信息,classes存储班级信息。每个学生都对应一个班级,每个班级对应多个学生,因此,班级表classes与学生表stuents是一对多关系:
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=True)

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

上面代码中,我们使用relationship方法建立了classes表和students表之间的一对多联系(一个班级对应多个学生),其中的backref为反向关系定义,为Studnet模型添加了一个_class属性,表示学生对应的班级(Student()._class)。而lazy指定了加载数据的时机,其共有四种选择:
 • select/True:默认属性。按需加载,即在访问到属性的时候,才会全部加载该属性的数据(即:class01.students返回一个学生列表)。
 • joined/False:加载记录,但使用联结。
 • subquery:与joined类似,立即加载,但使用子查询。
 • dynamic:不加载记录,而是提供加载记录的查询,即生成一个query对象,方便进行条件过滤(即:class01.students返回一个query)。
下面对我们创建的两个表进行数据注入,并进行使用,如下所示:

# insert data
stu01 = Student(name="one")
stu02 = Student(name="two")
stu03 = Student(name="three")

class01 = Classe(name="class01", students=[stu01])
class02 = Classe(name="class02", students=[stu02, stu03])

# update db
db.session.add_all([stu01, stu02, stu03, class01, class02])
db.session.commit()

# query
stu = Student.query.filter_by(_class=class01).first()
print(stu)
print(stu._class)

cls02 = Classe.query.filter_by(name='class02').first()
print(cls02.students)
  • 一对一关系:只需为relationship添加参数uselist=False即可建立一对一关系。

  • 多对多关系:若要定义多对多关系,需要借助一张中间表进行实现。强烈建议使用一张真实的表而不是表模型作为中间表。

tags = db.Table('tags',
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'), primary_key=True),
    db.Column('page_id', db.Integer, db.ForeignKey('page.id'), primary_key=True)
)

class Page(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    tags = db.relationship('Tag', secondary=tags, lazy='subquery',
        backref=db.backref('pages', lazy=True))

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)

更多详细信息,请查看:relationships

参考

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

推荐阅读更多精彩内容