- 关联查询
- 图书作者案例
1.项目基本结构
2.数据库
3.插入数据的表单类
4.编写作者,书籍的展示和添加
5.删除作者,删除书籍
关联查询
model层的表中存在一对一,一对多,多对多三种关系,其中一对多关系最为常见。
上一章中使用的一些查询都是单表查询,只是在一个表中根据某个字段进行查询,那么怎么快速的多表查询呢?
relationship
函数是sqlalchemy对关系之间提供的一种便利的调用方式
backref
:参数则对关系提供反向引用的声明。
实例:
#角色类
class Role(db.Model):
#定义表名
__tablename__ = 'role'
#id
id = db.Column(db.Integer,primary_key=True)
#name
name = db.Column(db.String(64),unique=True)
user = db.relationship('User',backref = 'role') #用于查询一对多 role.user
#用户类
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(64),unique=True)
email = db.Column(db.String(64),unique=True)
password = db.Column(db.String(64))
role_id = db.Column(db.Integer,db.ForeignKey('role.id')) #正向关联外键,role_id与role表中的id关联
详解:
realtionship函数定义在主表上。
realtionship描述了模型类Role类和User类的关系。在此文中
- 第一个参数为对应参照的类"User"(附表类名)
- 第二个参数backref为类User申明新属性role,也就是说当要用User类查询Role类中的数据时,可以直接调用 ----------- User类对象(user1).role,获取该用户所扮演的角色。
- 第三个参数lazy决定了什么时候SQLALchemy从数据库中加载数据
如果设置为子查询方式(subquery),则会在加载完Role对象后,就立即加载与其关联的对象,这样会让总查询数量减少,但如果返回的条目数量很多,就会比较慢
*设置为 subquery 的话,role.users 返回所有数据列表
*另外,也可以设置为动态方式(dynamic),这样关联对象会在被使用的时候再进行加载,并且在返回前进行过滤,如果返回的对象数很多,或者未来会变得很多,那最好采用这种方式
*设置为 dynamic 的话,role.users 返回查询对象,并没有做真正的查询,可以利用查询对象做其他逻辑,比如:先排序再返回结果
参数详解:
上图错误:relationship('附表类名',backref = '用于快速多查一时调用')
图书作者案例
功能展示:
如果作者存在,书籍存在,不能添加
如果作者存在,书籍不存在,可以添加
如果作者不存在,书籍存在,可以添加
如果作者不存在,书籍不存在,可以添加
- 删除书籍
- 删除作者,同时删除作者所有的书籍
- 使用wtf表单完成
1.项目基本结构
创建项目基本结构demo_library.py:
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
@app.route('/')
def index():
return render_template('library.html')
if __name__ == '__main__':
app.run(debug=True)
2.数据库
- 设置数据库
- 数据库基配置
- 编写model类
- 创建表
- 添加测试数据
from flask_sqlalchemy import SQLAlchemy
'''数据库'''
host = 'localhost'
port = '3306'
useranme = 'root'
password = 'root'
db = 'library'
mysqlpath = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(useranme,password,host,port,db)
app.config['SQLALCHEMY_DATABASE_URI'] = mysqlpath
db = SQLAlchemy(app)
'''数据库相关设置'''
# 设置每次请求后自动提交数据库的改动
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] =True
# 动态追踪设置
app.config['SQLALCHEMY_TRACK_MODUFICATIONS'] = True
# 显示原始sql
app.config['SQLALCHEMY_ECHO'] = True
'''model类'''
class Auther(db.Model):
__tablename__ = 'auther'
id = db.Column(db.Integer,primary_key=True) #主键
name = db.Column(db.String(64),unique=True)
db.relationship('Book',backref = 'auther') #用于查询一对多
class Book(db.Model):
__tablename__ = 'book'
id = db.Column(db.Integer,primary_key=True) #主键
name = db.Column(db.String(64))
au_book = db.Column(db.Integer,db.ForeignKey('auther.id'))
db.drop_all()
db.create_all()
auther1 = Auther(name='金庸')
auther2 = Auther(name='古龙')
db.session.add_all([auther1,auther2])
db.session.commit()
book1 = Book(name='天龙八部',au_book=auther1.id)
book2 = Book(name='射雕英雄传',au_book=auther1.id)
book3 = Book(name='小李飞刀',au_book=auther2.id)
book4 = Book(name='楚留香传奇',au_book=auther2.id)
db.session.add_all([book1,book2,book3,book4])
db.session.commit()
3.插入数据的表单类
'''表单验证类'''
#添加数据的表单验证类
class BookForm(FlaskForm):
author_name = StringField(label='作者:',validators=[DataRequired('作者不能为空')])
book_name = StringField(label='书籍:',validators=[DataRequired('书籍不能为空')])
submit = SubmitField(label='添加')
4.编写作者,书籍的展示和添加
思路导图:
@app.route('/',methods=['GET','POST'])
def index():
# 查询所有的作者
authors = Auther.query.all()
#实例化表单
form = BookForm()
if form.validate_on_submit():
#获取表单数据 也可以 author_name = form.author_name.data
author_name = request.form.get('author_name')
book_name = request.form.get('book_name')
#通过用户填写的作者name查询该作者对象
author = Auther.query.filter_by(name = author_name).all()
if author: #如果该作者存在,就判断该作者有没有用户填写的书籍
book = Book.query.filter(Book.name == book_name,Book.au_book == author[0].id).all()
if book: #如果该书籍存在,就不能添加
flash('该作者和书籍已存在')
else: #不存在,就添加到数据库
bookobj = Book(name=book_name, au_book=author[0].id) #创建该对象的书籍
db.session.add(bookobj) #插入数据库
db.session.commit()
else:
#创建作者
newau = Auther(name=author_name)
db.session.add(newau) # 插入数据库
db.session.commit()
#添加书籍
bookobj = Book(name=book_name, au_book=newau.id) # 创建该作者的书籍
db.session.add(bookobj) # 插入数据库
db.session.commit()
#不管是否添加成功,都要重新进入index页面
return redirect(url_for('index'))
return render_template('library.html',authors=authors,form=form)
分析:
当以get方式进入该视图函数时,要进行数据的展示页面,获取作者信息,以及表单的展示。然后返回library.html页面。
当用户填写完数据后,以post方式提交表单,如果表单验证通过:
- 首先获取表单中的两个数据---作者name和书籍name。
- 判断如果作者存在,在数据库中查找是否有该作者写的该书籍,如果该书籍对象存在,那么就不能在添加了,如果该书籍不存在,那么就创建该书籍对象并添加到数据库中。
3.如果该作者不存在,直接创建作者,提交数据库,创建该作者的书籍,提交数据库。
4.不管添加是否成功,都重定向到index页面。
library.html:
<body>
<h1>欢迎来到图书管理</h1>
<h3>添加书籍</h3>
<form action="" method="post">
{{ form.csrf_token() }}
{{ form.author_name.label }}
{{ form.author_name }}
<br>
{{ form.book_name.label }}
{{ form.book_name }}
<br>
{{ form.submit }}
<br>
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}
<br>
</form>
<ul>
{% for author in authors %}
<li>作者:{{ author.name }} <a href="{{ url_for('delete_author',author_id = author.id) }}">删除</a></li>
<ul>
{% for book in author.book %}
<li>书籍:{{ book.name }} <a href="{{ url_for('delete_book',book_id = book.id) }}">删除</a></li>
{% endfor %}
</ul>
{% endfor %}
</ul>
</body>
5.删除作者,删除书籍
#删除作者
@app.route('/delete_author/<int:author_id>')
def delete_author(author_id):
#获取要删除的作者对象
author = Auther.query.get(author_id)
for book in author.book:
db.session.delete(book)
db.session.delete(author)
db.session.commit()
return redirect(url_for('index'))
#删除书籍
@app.route('/delete_book/<int:book_id>')
def delete_book(book_id):
book = Book.query.get(book_id) #获取该书籍
db.session.delete(book)
db.session.commit()
return redirect(url_for('index'))
分析:
1.删除作者,不仅要删除该作者,还要删除该作者的所有书籍,那只要查找出来,删除即可。
首先根据前台传过来的作者id -- author_id 获取该作者对象,根据表之间的关联查询该作者有那几本书,循环遍历出来,并一一删除,最后在删除该作者。提交数据库即可。
重定向到index页面展示删除后的数据。
2.删除书籍,通过前台传过来的book_id查询该书籍对象,删除它,提交数据库即可。
重定向到index页面展示删除后的数据。
网友评论