Flask 笔记三:连接数据库,做一个简单的列表页

例子还是用一个通用的小项目,不涉及任何真实业务。我们就做一个最简单的 「备忘录列表」:能看列表,能新增一条。


1. 这篇学完你能做什么

  • 给 Flask 接上数据库
  • 定义一张表(模型)
  • 查询数据并显示在网页上
  • 简单分页(数据多的时候不会一页挤爆)

暂时不学登录、权限、部署。先把 「数据库 → 页面」 这条链路跑通。


2. 先装两个包

在项目目录里执行:

pip install Flask-SQLAlchemy Flask-Migrate

简单理解:

干什么
Flask-SQLAlchemy 让 Flask 方便地使用数据库
Flask-Migrate 以后改表结构时用(这篇先装着,下一篇再用)

入门阶段,数据库先用 SQLite 就够了------一个文件,不用装 MySQL,本地跑起来最快。


3. 改一下 app/__init__.py

在上一篇的基础上,加上数据库配置:

import os

from flask import Flask

from flask_sqlalchemy import SQLAlchemy

from flask_migrate import Migrate

app = Flask(name)

app.config"SECRET_KEY" = "dev-secret-key"

数据库文件会放在项目根目录的 instance 文件夹里

basedir = os.path.abspath(os.path.dirname(file))

app.config"SQLALCHEMY_DATABASE_URI" = "sqlite:///" + os.path.join(

basedir, "..", "instance", "app.db"

)

app.config"SQLALCHEMY_TRACK_MODIFICATIONS" = False

db = SQLAlchemy(app)

migrate = Migrate(app, db)

from app.home import home

from app.admin import admin

app.register_blueprint(home)

app.register_blueprint(admin, url_prefix="/admin")

这里记住两行就够了:

db = SQLAlchemy(app)

migrate = Migrate(app, db)

db 后面写模型、查数据都要用到它。


4. 定义一张表:app/models.py

新建这个文件,写一个「备忘录」模型:

from datetime import datetime

from app import db

class Note(db.Model):

tablename = "note"

id = db.Column(db.Integer, primary_key=True)

title = db.Column(db.String(100), nullable=False)

content = db.Column(db.Text)

addtime = db.Column(db.DateTime, default=datetime.now)

def repr(self):

return "<Note %r>" % self.title

可以把它理解成:

字段 含义
id 主键,每条记录的唯一编号
title 标题
content 正文(可以为空)
addtime 创建时间

类 = 表,属性 = 字段。 这是 ORM 最核心的想法。


5. 第一次创建数据库表

在项目根目录新建 init_db.py(只用一次):

from app import app, db

from app.models import Note

with app.app_context():

db.create_all()

print("数据库表创建成功")

先建目录,再运行:

mkdir -p instance

python init_db.py

成功后会出现 instance/app.db,说明 SQLite 数据库已经建好了。

小提示:db.create_all() 适合入门和本地练习。项目变大、要改表结构时,再用 Flask-Migrate,后面专门讲。


6. 手动塞几条测试数据(可选)

方便先看列表效果,可以临时跑一下:

from app import app, db

from app.models import Note

with app.app_context():

if Note.query.count() == 0:

db.session.add(Note(title="第一条", content="今天学 Flask 数据库"))

db.session.add(Note(title="第二条", content="做一个列表页"))

db.session.commit()

print("测试数据已写入")


7. 写列表页视图

app/home/views.py 里加一个路由:

from flask import render_template, request

from app.home import home

from app.models import Note

@home.route("/notes/")

def note_list():

page = request.args.get("page", 1, type=int)

page_data = Note.query.order_by(Note.addtime.desc()).paginate(

page=page,

per_page=10,

)

return render_template("home/note_list.html", page_data=page_data)

逐行看:

  1. Note.query --- 查 note
  2. .order_by(Note.addtime.desc()) --- 新的排在前面
  3. .paginate(page=page, per_page=10) --- 每页 10 条
  4. page_data --- 传给模板,里面既有数据,也有分页信息

8. 写模板:app/templates/home/note_list.html

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>备忘录列表</title>

<style>

body { font-family: sans-serif; max-width: 720px; margin: 40px auto; }

.item { border-bottom: 1px solid #eee; padding: 12px 0; }

.time { color: #888; font-size: 14px; }

.pager a { margin-right: 8px; }

</style>

</head>

<body>

<h1>备忘录</h1>

<p><a href="{{ url_for('home.note_add') }}">+ 新增一条</a></p>

{% for note in page_data.items %}

<div class="item">

<h3>{{ note.title }}</h3>

<p>{{ note.content or '(无内容)' }}</p>

<div class="time">{{ note.addtime.strftime('%Y-%m-%d %H:%M') }}</div>

</div>

{% else %}

<p>还没有数据,先去新增一条吧。</p>

{% endfor %}

<div class="pager">

{% if page_data.has_prev %}

<a href="{{ url_for('home.note_list', page=page_data.prev_num) }}">上一页</a>

{% endif %}

<span>第 {{ page_data.page }} / {{ page_data.pages or 1 }} 页</span>

{% if page_data.has_next %}

<a href="{{ url_for('home.note_list', page=page_data.next_num) }}">下一页</a>

{% endif %}

</div>

</body>

</html>

浏览器打开:

http://127.0.0.1:5000/notes/

能看到列表,说明 数据库 → Flask → 模板 已经通了。


9. 顺手做一个「新增」页面

列表有了,再加一个最简单的写入。

视图 app/home/views.py

from flask import render_template, request, redirect, url_for, flash

from app import db

from app.home import home

from app.models import Note

@home.route("/notes/add/", methods="GET", "POST")

def note_add():

if request.method == "POST":

title = (request.form.get("title") or "").strip()

content = (request.form.get("content") or "").strip()

if not title:

flash("标题不能为空")

return redirect(url_for("home.note_add"))

note = Note(title=title, content=content)

db.session.add(note)

db.session.commit()

return redirect(url_for("home.note_list"))

return render_template("home/note_add.html")

模板 app/templates/home/note_add.html

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>新增备忘录</title>

</head>

<body>

<h1>新增备忘录</h1>

{% with messages = get_flashed_messages() %}

{% for msg in messages %}

<p style="color:red;">{{ msg }}</p>

{% endfor %}

{% endwith %}

<form method="post">

<p>

标题:<br>

<input type="text" name="title" required>

</p>

<p>

内容:<br>

<textarea name="content" rows="5" cols="40"></textarea>

</p>

<button type="submit">保存</button>

<a href="{{ url_for('home.note_list') }}">返回列表</a>

</form>

</body>

</html>

新增数据的固定三步,以后会经常写:

db.session.add(note) # 1. 放进会话

db.session.commit() # 2. 真正写入数据库

return redirect(...) # 3. 跳回列表


10. 新手常踩的 4 个坑

坑 1:忘了 app.app_context()

在 Flask 外面直接 db.create_all()Note.query...,可能报错。

脚本里要包一层:

with app.app_context():

...

坑 2:改了模型,表没跟着变

改了 models.py 里的字段,旧表不会自动更新。

入门阶段可以删 instance/app.db 再跑 init_db.py;正式项目用 Migrate。

坑 3:db.session.add 后忘了 commit

addcommit,刷新页面数据还是空的。

坑 4:循环导入

models.pyfrom app import db__init__.py 里又 import models,顺序不对会报错。

按这篇的结构写一般没问题:先在 __init__.py 创建 db,再写 models.py


11. 小结

这一篇核心就四步:

定义模型 → 建表 → 视图里查询 → 模板里展示

对应代码:

步骤 在哪
定义模型 app/models.py
建表 db.create_all()
查数据 Note.query...paginate()
展示 render_template(...)

到这里,你已经有一个「能存、能看、能加、能分页」的小功能了。比纯静态页面进了一大步。