2020-06-13--flask07--flask基础07

2020-06-14  本文已影响0人  program_white
  • 图书作者案例

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 = '用于快速多查一时调用')

图书作者案例

功能展示:
如果作者存在,书籍存在,不能添加
如果作者存在,书籍不存在,可以添加
如果作者不存在,书籍存在,可以添加
如果作者不存在,书籍不存在,可以添加

  1. 删除书籍
  2. 删除作者,同时删除作者所有的书籍
  3. 使用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.数据库

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方式提交表单,如果表单验证通过:

  1. 首先获取表单中的两个数据---作者name和书籍name。
  2. 判断如果作者存在,在数据库中查找是否有该作者写的该书籍,如果该书籍对象存在,那么就不能在添加了,如果该书籍不存在,那么就创建该书籍对象并添加到数据库中。
    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页面展示删除后的数据。

上一篇 下一篇

猜你喜欢

热点阅读