Flask入门教程——李辉 第5章: 数据库 关键知识梳理
文章目录
- 第5章: 数据库 关键知识梳理
- 写在前边
- 扩展了解
- 使用 SQLAlchemy操作数据库
- 安装Flask-SQLAlchemy
- 配置连接数据库
- 扩展了解
- 创建数据库模型
- 常用字段类型
- 常用字段约束配置
- 创建数据库表
- 命令实现
- 扩展了解
- 代码实现
- 对数据库表进行(增、删、改、查)
- 增(添加)
- 删(除)
- 改(修改)
- 查(读取/查找)
- 常用的过滤方法
- 常用的查找方法
- 扩展
- 程序操作数据库
- 首页读取 数据库数据
- 生成虚拟数据
- 生成虚拟数据的命令函数
- 最终 修改俩文件
- app.py
- `index.html`
第5章: 数据库 关键知识梳理
写在前边
这个篇文章是基于前三章 内容的,基础的建议 专栏从头看起
笔者学习该书的环境是:
- 系统:Fedora Linux 42 (Workstation Edition)
- IDE: vscode
- Python:3.13.X
- Conda: conda 25.7.0
- pip: pip 25.2
- 第三方库 : 最新
Flask作为Python Web的后端框架,也需要履行后端的核心职责之一——与数据库进行交互
Flask-SQLAlchemy是Flask的第三方扩展,用来简化Flask与SQLAlchemy的操作
SQLite 是一个软件库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎
扩展了解
数据库管理系统(DMS)
关系数据库管理系统(RDMS)
使用 SQLAlchemy操作数据库
安装Flask-SQLAlchemy
pip3 install flask-sqlalchemy
初始操作
from flask import Flask
from flask_sqlalchemy import SQLAlchemy # 导入扩展类app = Flask(__name__)# 初始化扩展 传入程序实例
db = SQLAlchemy(app)
配置连接数据库
- 配置数据库连接地址 =>
SQLALCHEMY_DATABASE_URI - 对模型修改的检测(可选) =>
SQLALCHEMY_TRACK_MODIFICATIONS
app_db_connect.py:数据库配置
import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy # 导入扩展类app = Flask(__name__)# 添加数据库URl 为当前路径下的data.db sqlite 文件
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(app.root_path,'data.db')# 关闭对模型修改的监控
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False# 初始化扩展 传入程序实例
db = SQLAlchemy(app)
扩展了解
Flask-SQLAlchemy文档的配置
Flask文档配置
DB Browser for SQLite 一款 连接SQLite的工具
DB Browser for SQLite 下载
创建数据库模型
借助SQLAIchemy你可以通过定义Python类来表示数据库里的一张表(类属性表示表中的字段/列),通过对这个类进行各种操作来代替SQL语句。这个类我们称之为模型类,类中的属性我们将称之为字段
模型类编写核心:
- 模型类要继承
db.Model - 字段要实例化
db.Column传入的参数为 字段的类型 - 字段约束配置
传入额外参数如primary_key`
app_create_db_model.py创建数据库模型:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import osapp = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] ='sqlite:////' + os.path.join(app.root_path,'data.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)class User(db.Model):"""生成User表:param db: 数据库模型"""id = db.Column(db.Integer,primary_key=True)name = db.Column(db.String(20))class Movie(db.Model):"""生成电影数据:param db: 数据库模型"""id = db.Column(db.Integer,primary_key=True)title = db.Column(db.String(60))year = db.Column(db.String(4))
常用字段类型
| 字段类 | 说明 |
|---|---|
Integer | 整型 |
String(size) | 字符串,size 为最大长度,比如 db.String(20) |
Text | 长文本 |
DateTime | 时间日期,Python datetime 对象 |
Float | 浮点数 |
Boolean | 布尔值 |
常用字段约束配置
| 约束选项 | 说明 |
|---|---|
primary_key=True | 设置为主键 |
unique=True | 唯一约束(列值不可重复) |
index=True | 创建索引(提升查询效率) |
nullable=False | 非空约束(默认允许NULL) |
default=值 | 默认值(支持函数如datetime.now) |
创建数据库表
声明:如下代码块中带有>>>的是在flask shell中执行,否则就在终端对话框中执行
命令实现
注意: app.py 为 app_create_db_model.py
flask shell
注意:这里在vscode IDE终端 中可以补全
>>> from app import db
>>> db.create_all()
注意:需要重新生成数据库文件或表模式
>>> db.drop_all()
>>> db.create_all()
扩展了解
数据库迁移工具 Alembic
代码实现
app_initdb.py 使用时将其修改为app.py
"""注意需要在终端执行flask initdb命令来创建数据库文件"""from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import click
import osapp = Flask(__name__)app.config['SQLALCHEMY_DATABASE_URI'] ='sqlite:////' + os.path.join(app.root_path,'data.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = Falsedb = SQLAlchemy(app)@app.cli.command()
@click.option('--drop',is_flag=True, help="Create after drop.")def initdb(drop):"""Initialize the database."""if drop:db.drop_all()db.create_all()click.echo('Initialized the database.')
终端执行
flask initdb
重新创建
flask initdb --drop
对数据库表进行(增、删、改、查)
app.py
# coding = utf-8
from flask import Flask,render_template
from flask_sqlalchemy import SQLAlchemy
import click
import osapp = Flask(__name__)app.config['SQLALCHEMY_DATABASE_URI'] ='sqlite:///' + os.path.join(app.root_path,'data.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = Falsedb = SQLAlchemy(app)class User(db.Model):"""生成User表:param db: 数据库模型"""id = db.Column(db.Integer,primary_key=True)name = db.Column(db.String(20))class Movie(db.Model):"""生成电影数据:param db: 电影数据模型"""id = db.Column(db.Integer,primary_key=True)title = db.Column(db.String(60))year = db.Column(db.String(4))@app.cli.command()
@click.option('--drop',is_flag=True, help="Create after drop.")def initdb(drop):"""初始化数据库表"""if drop:db.drop_all()db.create_all()click.echo('Initialized the database.')@app.route("/")
def index():user = User.query.first()movies = Movie.query.all()return render_template("index.html",user=user,movies=movies)app.run("127.0.0.1",5000,False)
增(添加)
>>> from app import User,Movie
>>> user = User(name="开心-开心急了")
>>> m1 = Movie(title="Leon",year="1994")
>>> m2 = Movie(title="Mahjong",year="1996")
>>> db.session.add(user)
>>> db.session.add(m1)
>>> db.session.add(m2)
>>> db.session.commit()
注意
- 在实例化模型时
SQLAlchemy会自动处理id字段。 db.session.commit()才会真的提交db.session.add(user)是添加到缓存里(这不是有点像gitee吗)
删(除)
删除id为1的记录
>>> movie = Movie.query.get(1)
>>> db.session.delete(movie)
>>> db.session.delete()
>>> db.session.commit()
改(修改)
将id为2的修改
>>> movie = Movie.query.get(2)
>>> movie.title = 'WALL-E'
>>> movie.year = '2008'
>>> db.session.commit()
查(读取/查找)
通用的调用方法
<模型类>.query.<过滤方法(可选)>.<查询方法>
>>> from app import Movie
>>> movie = Movie.query.first() # 获取 Movie 模型的第一个记录(返回模型类实例)
>>> movie.title # 对返回的模型类实例调用属性即可获取记录的各字段数据
'Leon'
>>> movie.year
'1994'
>>> Movie.query.all() # 获取 Movie 模型的所有记录,返回包含多个模型类实例的列表
[<Movie 1>, <Movie 2>]
>>> Movie.query.count() # 获取 Movie 模型所有记录的数量
2
>>> Movie.query.get(1) # 获取主键(id)值为 1 的记录
<Movie 1>
>>> Movie.query.filter_by(title='Mahjong').first() # 获取 title 字段值为 Mahjong 的记录
<Movie 2>
>>> Movie.query.filter(Movie.title=='Mahjong').first() # 等同于上面的查询,但使用不同的过滤方法
<Movie 2>
常用的过滤方法
| 过滤方法 | 说明 |
|---|---|
filter() | 使用指定的规则过滤记录,返回新产生的查询对象 |
filter_by() | 使用指定规则过滤记录(以关键字表达式的形式),返回新产生的查询对象 |
order_by() | 根据指定条件对记录进行排序,返回新产生的查询对象 |
group_by() | 根据指定条件对记录进行分组,返回新产生的查询对象 |
常用的查找方法
| 查询方法 | 说明 |
|---|---|
all() | 返回包含所有查询记录的列表 |
first() | 返回查询的第一条记录,如果未找到,则返回 None |
get(id) | 传入主键值作为参数,返回指定主键值的记录,如果未找到,则返回 None |
count() | 返回查询结果的数量 |
first_or_404() | 返回查询的第一条记录,如果未找到,则返回 404 错误响应 |
get_or_404(id) | 传入主键值作为参数,返回指定主键值的记录,如果未找到,则返回 404 错误响应 |
paginate() | 返回一个 Pagination 对象,可以对记录进行分页处理 |
扩展
更多数据库操作方法(Query API)
程序操作数据库
首页读取 数据库数据
修改app.py中的
@app.route("/")
def index():return render_template("index.html",name=name,movies=movies)
修改结果 见 对数据库表进行增、删、改、查的app.py
修改 index.html name修改为 user.name
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{{ user.name }}'s Watchlist</title><link rel="icon" href="{{ url_for('static',filename='dicos.ico')}}"><link rel="stylesheet" href="{{url_for( 'static', filename='style.css') }}" type="text/css">
</head>
<body><h2><img src="{{ url_for('static',filename='images/avatar.png')}}" alt="Avatar" class="avatar">{{ user.name }}'s Watchlist</h2>{# 使用 length 过滤器获取 movies 变量的长度 #}<ul class="movie-list">{% for movie in movies %} {# 迭代movies 变量 #}<li>{{ movie.title }} - {{ movie.year }}{# 等同于 movie['title'] #}{% endfor %} {# 不要忘记endfor 来结束for语句 #}</li></ul><img src="{{ url_for('static',filename='images/totoro.gif')}}" alt="totoro" class="totoro"><footer><small>© 2018 <a href="http://helloflask.com/tutorial">HelloFlask</a></small></footer>
</body>
</html>
生成虚拟数据
借助faker快速生成虚拟数据
生成虚拟数据的命令函数
"""注意需要在终端执行flask forge命令来创建数据库文件"""from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import click
import osapp = Flask(__name__)app.config['SQLALCHEMY_DATABASE_URI'] ='sqlite:////' + os.path.join(app.root_path,'data.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = Falsedb = SQLAlchemy(app)class User(db.Model):"""生成User表:param db: 数据库模型"""id = db.Column(db.Integer,primary_key=True)name = db.Column(db.String(20))class Movie(db.Model):"""生成电影数据:param db: 电影数据模型"""id = db.Column(db.Integer,primary_key=True)title = db.Column(db.String(60))year = db.Column(db.String(4))@app.cli.command()
def forge():"""创建虚拟 数据"""name = "开心开心-急了"movies = [{'title': '实现研究而且.', 'year': '1997'}, {'title': '重要出来不要不要广告.', 'year': '1987'},{'title': '活动这些城市.', 'year': '1995'},{'title': '历史网上你的来源选择销售没有.', 'year': '2006'},{'title': '这样简介个人如果信息怎么类型.', 'year': '2024'}, {'title': '能够时候两个.', 'year': '2004'}, {'title': '部分有限电影不要得到应用.', 'year': '2022'}, {'title': '设备处理最后大学为了.', 'year': '1996'}, {'title': '来自谢谢决定.', 'year': '2021'},{'title': '提供所以大家您的非常以后有关.', 'year': '2016'}]# 将用户添加到 会话(session)中user = User(name=name)db.session.add(user)# 将电影名和电影年份 添加到 会话(session)中for m in movies:movie = Movie(title= m['title'],year=m['year'])db.session.add(movie)# 提交 会话(session) 里的修改db.session.commit()click.echo("创建虚拟数据 成功")
执行
flask forge
最终 修改俩文件
app.py
# coding = utf-8
from flask import Flask,render_template
from flask_sqlalchemy import SQLAlchemy
import click
import osapp = Flask(__name__)app.config['SQLALCHEMY_DATABASE_URI'] ='sqlite:///' + os.path.join(app.root_path,'data.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = Falsedb = SQLAlchemy(app)class User(db.Model):"""生成User表:param db: 数据库模型"""id = db.Column(db.Integer,primary_key=True)name = db.Column(db.String(20))class Movie(db.Model):"""生成电影数据:param db: 电影数据模型"""id = db.Column(db.Integer,primary_key=True)title = db.Column(db.String(60))year = db.Column(db.String(4))@app.cli.command()
@click.option('--drop',is_flag=True, help="Create after drop.")def initdb(drop):"""初始化数据库表"""if drop:db.drop_all()db.create_all()click.echo('Initialized the database.')@app.cli.command()
def forge():"""创建虚拟 数据"""name = "开心开心-急了"movies = [{'title': '实现研究而且.', 'year': '1997'}, {'title': '重要出来不要不要广告.', 'year': '1987'},{'title': '活动这些城市.', 'year': '1995'},{'title': '历史网上你的来源选择销售没有.', 'year': '2006'},{'title': '这样简介个人如果信息怎么类型.', 'year': '2024'}, {'title': '能够时候两个.', 'year': '2004'}, {'title': '部分有限电影不要得到应用.', 'year': '2022'}, {'title': '设备处理最后大学为了.', 'year': '1996'}, {'title': '来自谢谢决定.', 'year': '2021'},{'title': '提供所以大家您的非常以后有关.', 'year': '2016'}]# 将用户添加到 会话(session)中user = User(name=name)db.session.add(user)# 将电影名和电影年份 添加到 会话(session)中for m in movies:movie = Movie(title= m['title'],year=m['year'])db.session.add(movie)# 提交 会话(session) 里的修改db.session.commit()click.echo("创建虚拟数据 成功")@app.route("/")
def index():user = User.query.first()movies = Movie.query.all()return render_template("index.html",user=user,movies=movies)app.run("127.0.0.1",5000,False)
index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{{ user.name }}'s Watchlist</title><link rel="icon" href="{{ url_for('static',filename='dicos.ico')}}"><link rel="stylesheet" href="{{url_for( 'static', filename='style.css') }}" type="text/css">
</head>
<body><h2><img src="{{ url_for('static',filename='images/avatar.png')}}" alt="Avatar" class="avatar">{{ user.name }}'s Watchlist</h2>{# 使用 length 过滤器获取 movies 变量的长度 #}<ul class="movie-list">{% for movie in movies %} {# 迭代movies 变量 #}<li>{{ movie.title }} - {{ movie.year }}{# 等同于 movie['title'] #}{% endfor %} {# 不要忘记endfor 来结束for语句 #}</li></ul><img src="{{ url_for('static',filename='images/totoro.gif')}}" alt="totoro" class="totoro"><footer><small>© 2018 <a href="http://helloflask.com/tutorial">HelloFlask</a></small></footer>
</body>
</html>
