欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python学习笔记:6.3.2 一对多关系

程序员文章站 2022-05-09 09:02:35
...

前言:本文是学习网易微专业的《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.htmltemplates/category/edit.htmltemplates/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.模型关系

我们现在已经创建了多个模型:用户模型,文章模型,分类模型。
通常我们会在这些模型之间建立一种联系:
比如:
每篇文章都属于一个作者(作者为用户模型中的一条记录)
每篇文章都属于某一个分类(分类为分类模型中的一条记录)
Python学习笔记:6.3.2 一对多关系

2.3.1.数据表关系

主键: 自己的主键或者唯一键是另一个表的外键,称为主表。
从表: 自己的某个字段值为另一个表的主键字段值,称为从表。
外键要求: 外键必须对应关联表中的主键或者具有唯一值的字段。
关联关系:
一对多关系:一条user成员可以拥有多条article记录,这种关系称为一对多关系。
多对一关系:站在article表来说,多条文章属于一个user,就是多对一关系。
Python学习笔记:6.3.2 一对多关系

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)

Python学习笔记:6.3.2 一对多关系

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的方法可以来定义,实现一对多的关系。另外还需要在从表里定义外键,将主表和从表联系在一起。