Python学习笔记:6.3.2 一对多关系
前言:本文是学习网易微专业的《python全栈工程师 - Flask高级建站》课程的笔记,欢迎学习交流。同时感谢老师们的精彩传授!
一、课程目标
- 分类管理
- 操作结果提示
- 一对多模型关系
二、详情解读
2.1.分类管理
步骤:
1.创建分类模型(M
部分)
2.创建分类模版(V
部分)
3.创建分类视图(C
部分)
实操:Step1
:在models.py
中添加以下内容:
class Category(db.Model):
cate_id = db.Column(db.Integer, primary_key=True)
# unique=True,表示此字段值不能重复
cate_name = db.Column(db.String, unique=True)
cate_order = db.Column(db.Integer, default=0)
Step2
:在templates
目录下新增三个文件:templates/category/add.html
,templates/category/edit.html
,templates/category/list.html
在add.html
添加以下内容:
{% extends "base.html" %}
<!DOCTYPE html>
<html>
<body>
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="main col-md-12 col-lg-12 col-xs-12 col-sm-12" >
<h3>添加分类</h3>
{% if message %}
<div class="alert alert-info" role="alert">
{{ message }}
</div>
{% endif %}
<div class="body">
<form action="" method="post">
<div class="form-group">
<label for="title">分类名称:</label>
<input class="form-control" type="text" name="name" id="name" value="" />
</div>
<div class="form-group">
<label for="order">显示顺序:</label>
<input class="form-control"type="text" name="order" id="order" value="0" />
</div>
<div class="form-group">
<input class="btn" type="submit" name="submit" id="btn1" value="添加" />
<input class="btn" type="reset" name="reset" id="btn2" value="重设" />
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
</body>
</html>
在edit.html
添加以下内容:
{% extends "base.html" %}
<!DOCTYPE html>
<html>
<body>
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="main col-md-12 col-lg-12 col-xs-12 col-sm-12" >
<h3>修改分类</h3>
<div class="body">
<form action="" method="post">
<div class="form-group">
<label for="title">分类名称:</label>
<input class="form-control" type="text" name="name" id="name" value="{{ category.cate_name }}" />
</div>
<div class="form-group">
<label for="order">显示顺序:</label>
<input class="form-control"type="text" name="order" id="order" value="{{ category.cate_order }}" />
</div>
<div class="form-group">
<input class="btn" type="submit" name="submit" id="btn1" value="修改" />
<input class="btn" type="reset" name="reset" id="btn2" value="重设" />
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
</body>
</html>
在list.html
添加以下内容:
{% extends "base.html" %}
{% block style %}
input[type='number']{
width: 100px;
}
.btn{
margin-right: 30px;
}
{% endblock %}
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="col-md-2 hidden-xs hidden-sm">
<ul class="nav nav-pills " id="left_menu">
<li class="active"><a href="">标准模块</a></li>
<li ><a href="#" data-toggle="dropdown">socket编程</a></li>
<li><a href="">异步编程</a></li>
<li><a href="">并发编程</a></li>
</ul>
</div>
<div class="col-md-6">
<!-- 媒体列表 -->
<h3>分类管理
<small>
<a href="{{ url_for("article_app.addCate") }}">
添加分类
</a>
</small>
</h3>
<table class="table table-bordered ">
<tr>
<th>分类id</th>
<th>分类名</th>
<th>分类顺序</th>
<th>管理操作</th>
</tr>
{% for cate in cates %}
<tr>
<td>{{ cate.cate_id }}</td>
<td>{{ cate.cate_name }}</td>
<td>{{ cate.cate_order }}</td>
<td>
<a href="{{ url_for("article_app.editCate", cate_id=cate.cate_id) }}">修改</a>
<a href="{{ url_for("article_app.deleteCate", cate_id=cate.cate_id) }}">删除</a>
</td>
</tr>
{% endfor %}
</table>
<ul class="pagination">
{% for page in pageList %}
{% if page!=None %}
<li><a href="{{ url_for("article_app.cateList", page=page) }}">{{ page }}</a></li>
{% else %}
<li><a href="#">...</a></li>
{% endif %}
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
{% endblock %}
</body>
</html>
Step3
:在articles.py
文件末尾添加以下内容:
# 分类视图部分
# 添加分类
@article_app.route("/add_cate", methods=['GET', 'POST'])
def addCate():
message = None
if request.method == 'POST':
cate_name = request.form['name']
cate_order = request.form['order']
category = Category(
cate_name = cate_name,
cate_order = cate_order,
)
try:
db.session.add(category)
db.session.commit()
message = cate_name + '添加成功'
except Exception as e:
message = '发生了错误:' + str(e)
# 如果插入失败,进行回滚操作
db.session.rollback()
return render_template("category/add.html", message=message)
# 获得分类列表
@article_app.route("/cate_list")
def cateList():
cates = Category.query.order_by(Category.cate_order.desc()).all()
return render_template("category/list.html", cates=cates)
# 删除分类
@article_app.route("/cate_delete/<int:cate_id>")
def deleteCate(cate_id):
cate = Category.query.get(cate_id)
db.session.delete(cate)
db.session.commit()
return redirect(url_for(".cateList"))
# 分类修改
@article_app.route("/cate_edit/<int:cate_id>", methods=['GET', 'POST'])
def editCate(cate_id):
category = Category.query.get(cate_id)
if request.method == 'POST':
category.cate_name = request.form['name']
category.cate_order = request.form['order']
db.session.commit()
return redirect(url_for(".cateList"))
return render_template("category/edit.html", category=category)
2.1.1.分类模型
单级分类模型字段
1.主键id
- 字段名 cate_id
字段模型 integer
2.分类名称 - 字段名 cate_name
字段类型 string
,字段值必须唯一
3.分类排序 - 字段名 cate_order
字段类型 integer
重要提示:不要忘记使用migrate工具更新数据表
2.1.2.数据提交失败
通常并不是每次数据库提交动作都是成功的,比如多次输入同一个分类名,违反了分类名必须是唯一值的约定,就会导致数据库提交失败。数据提交失败需要进行回滚操作:
# 表示撤销刚才对数据库的操作
db.session.rollback()
2.2.为文章提供分类选择
1.从分类表中读取分类数据,并附给添加文章模版,生成下拉选择框
2.在网站导航条上,使用分类数据生成导航
3.对于导航功能,所有的视图,都需要显示导航,因此在每个视图中,都需要分类数据
4.因此,可以使用模版上下文装饰器@app.context_processor
在app.py
中添加以下内容:
.
.
.
from models import Category
app = Flask(__name__)
.
.
.
@app.context_processor
def getCateList():
cates = Category.query.all()
return {"cates": cates}
修改template/base.html
,将导航部分修改为以下内容:
.
.
.
<div class="collapse navbar-collapse" id="navmenu">
<!-- 必须设置 navbar-nav-->
<ul class="nav navbar-nav" >
<li class="active"><a href="{{ url_for("index") }}">首页</a></li>
<!-- 必须设置dropdown -->
{% for cate in cates %}
<li><a href="">{{ cate.cate_name }}</a></li>
{% endfor %}
<li><a href="{{ url_for("article_app.list") }}">文章管理</a></li>
<li><a href="{{ url_for("user_app.userList") }}">用户管理</a></li>
<li><a href="{{ url_for("article_app.cateList") }}">分类管理</a></li>
</ul>
<ul class="nav navbar-nav pull-right">
{% if not username %}
<li><a href="{{ url_for("login") }}">登录</a></li>
<li><a href="{{ url_for("user_app.register") }}">注册</a></li>
{% else %}
<li><a href="/usercenter">{{ username }}</a></li>
{% endif %}
</ul>
</div>
.
.
.
2.3.模型关系
我们现在已经创建了多个模型:用户模型,文章模型,分类模型。
通常我们会在这些模型之间建立一种联系:
比如:
每篇文章都属于一个作者(作者为用户模型中的一条记录)
每篇文章都属于某一个分类(分类为分类模型中的一条记录)
2.3.1.数据表关系
主键: 自己的主键或者唯一键是另一个表的外键,称为主表。
从表: 自己的某个字段值为另一个表的主键字段值,称为从表。
外键要求: 外键必须对应关联表中的主键或者具有唯一值的字段。
关联关系:
一对多关系:一条user
成员可以拥有多条article
记录,这种关系称为一对多关系。
多对一关系:站在article
表来说,多条文章属于一个user
,就是多对一关系。
2.4.模型关系创建
在从表定义外键 - db.Foreignkey("主表.主键名")
:
class Article(db.Model):
...
cate_id = db.Column(db.Integer, db.ForeignKey('category.cate_id')
在主表定义关系 - db.relationship("从表模型名")
:
class Category(db.Model):
...
articles = db.relationship("Article")
重要提示:relationship
有很多参数,暂时不用去理解不要忘记使用migrate工具更新数据表
实操:Step1
:将models.py
中的Article
模型和Category
模型修改为以下内容:
class Article(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
intro = db.Column(db.String)
content = db.Column(db.Text)
author = db.Column(db.String)
pubdate = db.Column(db.DateTime, default=datetime.utcnow)
# 新增这一行
cate_id = db.Column(db.Integer, db.ForeignKey("category.cate_id"))
class Category(db.Model):
cate_id = db.Column(db.Integer, primary_key=True)
# unique=True,表示此字段值不能重复
cate_name = db.Column(db.String, unique=True)
cate_order = db.Column(db.Integer, default=0)
# 新增这一行
articles = db.relationship("Article")
Step2
:更新数据库:
flask db migrate -m "Category与Article建立一对多关系"
注意:SQLite
的坑
由于我们在创建关系时,修改了文章表的字段(增加了一个cate_id
字段),而SQLite
对修改字段支持不是太好,所以我们需要修改一下app.py
中的migrate
对象:
migrate = Migrate(app, db, render_as_batch=True)
2.5.实现文章分类
2.5.1.给文章添加分类选择
Step1
:发布文章的时候选择分类 - 发布文章模版post.html
<h3>文章发布</h3>
<div class="body">
<form action="" method="post">
<div class="form-group">
<label for="name">文章分类:</label>
<select name="cate" id="cate" class="form-control">
{% for cate in cates %}
<option value="{{ cate.cate_id }}">{{ cate.cate_name }}</opiton>
{% endfor %}
</select>
</div>
.
.
.
Step2
:修改文章的时候选择分类 - 文章修改模版edit_article.html
<h3>文章修改</h3>
<div class="body">
<form action="" method="post">
<div class="form-group">
<label for="name">文章分类:</label>
<select name="cate" id="cate" class="form-control">
{% for cate in cates %}
<option value="{{ cate.cate_id }}">
{% if article.cate_id == cate.cate_id %}selected{% endif %}
</opiton>
{% endfor %}
</select>
</div>
.
.
.
Step3
:增加文章分类列表显示视图 - articles.py
.
.
.
# 根据文章cate_id显示文章列表
@article_app.route("/cate/<int:cate_id>/<int:page>")
@article_app.route("/", defaults={"cate_id":0, "page":1})
def getArticleList(cate_id, page):
if cate_id == 0:
res = Article.query.paginate(page, 20)
else:
res = Article.query.filter_by(cate_id=cate_id).paginate(page,20)
articles = res.items
pageList = res.iter_pages()
return render_template("index.html",
articles=articles,
pageList=pageList
)
Step4
:点击分类导航进入分类文章页面 - base.html
,修改导航链接
<li class="active"><a href="{{ url_for('index') }}">首页</a></li>
{% for cate in cates %}
<li><a href="{{ url_for('article_app.getArticleList', cate_id=cate.cate_id, page=1) }}>{{ cate.cate_name }}</a></li>
{% endfor %}
2.6.级联删除
如果我们删除一个分类,那么该分类下的文章怎么处理
1.删除之前,修改这些文章分类
2.随着分类删除被删除
随着分类删除被删除称为级联。relationship
方法提供了一个参数:cascade
具有以下值:
1.save-update
默认行为
2.delete
一起删除
3.delete-orphan
解除关联时,删除从表数据
4.all
包含save-update, merge, refresh-expire, expunge, delete
三.课程小结
- 关联表
通过创建关联表的方式,为文章添加分类的功能。所谓关联表是指,有一个主表跟一个从表,从表里的多条记录都属于主表里的某一个记录。 - 一对多
- relationship
通过relationship的方法可以来定义,实现一对多的关系。另外还需要在从表里定义外键,将主表和从表联系在一起。
下一篇: Python连接DB2数据库