文章目录
- [第5章: 数据库 关键知识梳理](#第5章: 数据库 关键知识梳理)
第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 数据库引擎
扩展了解
使用 SQLAlchemy操作数据库
安装Flask-SQLAlchemy
bash
pip3 install flask-sqlalchemy
初始操作
py
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:数据库配置
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)
扩展了解
DB Browser for SQLite 一款 连接SQLite的工具
DB Browser for SQLite 下载
创建数据库模型
借助SQLAIchemy你可以通过定义Python类来表示数据库里的一张表(类属性表示表中的字段/列),通过对这个类进行各种操作来代替SQL语句。这个类我们称之为模型类,类中的属性我们将称之为字段
模型类编写核心:
- 模型类要继承
db.Model - 字段要实例化
db.Column传入的参数为 字段的类型 - 字段约束配置
传入额外参数如primary_key`
app_create_db_model.py创建数据库模型:
py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import os
app = 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
bash
flask shell
注意 :这里在vscode IDE终端 中可以补全
bash
>>> from app import db
>>> db.create_all()
注意:需要重新生成数据库文件或表模式
bash
>>> db.drop_all()
>>> db.create_all()
扩展了解
代码实现
app_initdb.py 使用 时将其修改为app.py
py
"""注意需要在终端执行flask initdb命令来创建数据库文件"""
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import click
import os
app = 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)
@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.')
终端执行
bash
flask initdb
重新创建
bash
flask initdb --drop
对数据库表进行(增、删、改、查)
app.py
py
# coding = utf-8
from flask import Flask,render_template
from flask_sqlalchemy import SQLAlchemy
import click
import os
app = 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))
@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)
增(添加)
bash
>>> 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的记录
bash
>>> movie = Movie.query.get(1)
>>> db.session.delete(movie)
>>> db.session.delete()
>>> db.session.commit()
改(修改)
将id为2的修改
bash
>>> movie = Movie.query.get(2)
>>> movie.title = 'WALL-E'
>>> movie.year = '2008'
>>> db.session.commit()
查(读取/查找)
通用的调用方法
bash
<模型类>.query.<过滤方法(可选)>.<查询方法>
bash
>>> 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 对象,可以对记录进行分页处理 |
扩展
程序操作数据库
首页读取 数据库数据
修改app.py中的
py
@app.route("/")
def index():
return render_template("index.html",name=name,movies=movies)
修改结果 见 对数据库表进行增、删、改、查 的app.py
修改 index.html name修改为 user.name
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>
生成虚拟数据
生成虚拟数据的命令函数
py
"""注意需要在终端执行flask forge命令来创建数据库文件"""
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import click
import os
app = 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))
@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("创建虚拟数据 成功")
执行
py
flask forge
最终 修改俩文件
app.py
py
# coding = utf-8
from flask import Flask,render_template
from flask_sqlalchemy import SQLAlchemy
import click
import os
app = 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))
@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
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>