06-综合案例(一对多)
综合案例-图书管理
1、WTF表单
2、数据库操作
3、一对多关系演练
定义模型
模型表示程序使用的数据实体,在Flask-SQLAlchemy中,模型一般是Python类,继承自db.Model,db是SQLAlchemy类的实例,代表程序使用的数据库。
类中的属性对应数据库表中的列。id为主键,是由Flask-SQLAlchemy管理。db.Column类构造函数的第一个参数是数据库列和模型属性类型。
如果想修改数据库编码:
alter database 数据库名 CHARACTER SET utf8;
定义两个模型类:作者和书名:
from flask import Flask,render_template,redirect,url_for from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) #设置连接数据库 root是用户名 冒号后面是密码 app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:mysql@127.0.0.1:3306/flasksql" app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True #实例化SQLAlchemy对象,对谁操作 db = SQLAlchemy(app) #定义模型类-作者 class Author(db.Model): #设置表名:author __tablename__ = "authors" #设置 id列 id = db.Column(db.Integer,primary_key=True) name = db.Column(db.String(32),unique=True) #一对多,所以在一的地方声明关系,与哪张表,backref为类Author申明新属性的方法 au_book = db.relationship("Book",backref="author") def __repr__(self): return "Author:%s"%self.name #定义模型类-书名 多对一 class Book(db.Model): __tablename__ = "books" id = db.Column(db.Integer,primary_key=True) name = db.Column(db.String(32)) #关联那个外键表,多对一 首先外键对应的是 模型类小写点属性 au_book = db.Column(db.Integer,db.ForeignKey("author.id")) def __repr__(self): return "Book:%s,%s"%(self.name,self.id)
创建表
#创建表 if __name__ == '__main__': #建表之前需要将数据库中的表清除完 db.drop_all() db.create_all() app.run(debug=True)
查看表成功创建。
添加数据测试一下:
#生成数据 au1 = Author(name='老王') au2 = Author(name='老尹') au3 = Author(name='老刘') # 把数据提交给用户会话 db.session.add_all([au1, au2, au3]) # 提交会话 db.session.commit() bk1 = Book(name='老王回忆录', au_book=au1.id) bk2 = Book(name='我读书少,你别骗我', au_book=au1.id) bk3 = Book(name='如何才能让自己更骚', au_book=au2.id) bk4 = Book(name='怎样征服美丽少女', au_book=au3.id) bk5 = Book(name='如何征服英俊少男', au_book=au3.id) # 把数据提交给用户会话 db.session.add_all([bk1, bk2, bk3, bk4, bk5]) # 提交会话 db.session.commit()
数据显示&表单添加
定义路由函数,并将Author和Book的所有结果传到模板
@app.route('/',methods=['GET','POST']) def index(): author = Author.query.all() book = Book.query.all() return render_template('index.html',author=author,book=book)
模板:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for x in author %} <li>{{ x }}</li> {% endfor %} </ul> <hr> <ul> {% for x in book %} <li>{{ x }}</li> {% endfor %} </ul> </body> </html>
表单添加
定义表单类
from flask_wtf import FlaskForm from wtforms.validators import DataRequired from wtforms import StringField,SubmitField #创建表单类,用来添加信息 class Append(FlaskForm): au_info = StringField(validators=[DataRequired()]) bk_info = StringField(validators=[DataRequired()]) submit = SubmitField('添加')
传入模板中:
@app.route('/',methods=['GET','POST']) def index(): author = Author.query.all() book = Book.query.all() form = Append() return render_template('index.html',author=author,book=book,form=form)
模板中的代码;
<form method="post"> {{ form.csrf_token }} <p>作者:{{ form.au_info }}</p> <p>书名:{{ form.bk_info }}</p> <p>{{ form.submit }}</p> </form>
表单验证
from flask import Flask,render_template,redirect,url_for,flash,request from flask_sqlalchemy import SQLAlchemy from flask_wtf import FlaskForm from wtforms.validators import DataRequired from wtforms import StringField,SubmitField app = Flask(__name__) #设置密钥 app.config["SECRET_KEY"] = "SECRET_KEY" #设置连接数据库 app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:@127.0.0.1:3306/flasksql" app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True #查询时会显示原始SQL app.config["SQLALCHEMY_ECHO"] = True #实例化SQLAlchemy对象,对谁操作 db = SQLAlchemy(app) #定义模型类-作者 class Author(db.Model): #设置表名:author __tablename__ = "authors" #设置 id列 id = db.Column(db.Integer,primary_key=True) name = db.Column(db.String(32),unique=True) #一对多,所以在一的地方声明关系,与哪张表,backref为类Author申明新属性的方法 us = db.relationship("Book",backref="author") def __repr__(self): return "Author:%s"%self.name #定义模型类-书名 多对一 class Book(db.Model): __tablename__ = "books" id = db.Column(db.Integer,primary_key=True) name = db.Column(db.String(32)) #关联那个外键表,多对一 首先外键对应的是 创建的表名点属性 au_book = db.Column(db.Integer,db.ForeignKey("authors.id")) def __repr__(self): return "Book:%s,%s"%(self.name,self.id) #创建表单类,用来添加信息 class Append(FlaskForm): au_info = StringField(validators=[DataRequired()]) bk_info = StringField(validators=[DataRequired()]) #这里添加u才使得这个数据完整 submit = SubmitField(u'添加') # @app.route('/',methods=['GET','POST']) # def index(): # author = Author.query.all() # book = Book.query.all() # return render_template('index.html',author=author,book=book) # @app.route('/', methods=['get', 'post']) def index(): append_form = Append() if request.method == 'POST': if append_form.validate_on_submit(): #先确定值 author_name = append_form.au_info.data book_name = append_form.bk_info.data # 判断数据是否存在 author = Author.query.filter_by(name=author_name).first() if not author: try: # 先添加作者 author = Author(name=author_name) db.session.add(author) db.session.commit() # 再添加书籍 book = Book(name=book_name, au_book=author.id) db.session.add(book) db.session.commit() except Exception as e: db.session.rollback() print(e) flash("数据添加错误") else: #查询和这个作者相关的书名 book_names = [book.name for book in author.us] if book_name in book_names: flash('该作者已存在相同的书名') else: try: book = Book(name=book_name, author_id=author.id) db.session.add(book) db.session.commit() except Exception as e: db.session.rollback() print(e) flash('数据添加错误') else: flash('数据输入有问题') authors = Author.query.all() books = Book.query.all() return render_template('test2.html', authors=authors, books=books, append_form=append_form) #生成数据 # au1 = Author(name='老大') # au2 = Author(name='老二') # au3 = Author(name='老三') # # 把数据提交给用户会话 # db.session.add_all([au1, au2, au3]) # # 提交会话 # db.session.commit() # bk1 = Book(name='老王回忆录', au_book=au1.id) # bk2 = Book(name='我读书少,你别骗我', au_book=au1.id) # bk3 = Book(name='如何才能让自己更骚', au_book=au2.id) # bk4 = Book(name='怎样征服美丽少女', au_book=au3.id) # bk5 = Book(name='如何征服英俊少男', au_book=au3.id) # # 把数据提交给用户会话 # db.session.add_all([bk1, bk2, bk3, bk4, bk5]) # # 提交会话 # db.session.commit() #创建表 if __name__ == '__main__': #建表之前需要将数据库中的表清除完 # db.drop_all() # db.create_all() app.run(debug=True,port=8080)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {# 重新编写html文件展示列表书籍 #} <h2>书籍列表</h2> <ul> {% for author in authors %} <li> {{ author.name }} <ul> {% for book in author.us %} <li>{{ book.name }} {% else %} <li>无书籍</li> {% endfor %} </ul> </li> {% endfor %} </ul> <form method="post"> {{ append_form.csrf_token }} <p>作者:{{ append_form.au_info }}</p> <p>书名:{{ append_form.bk_info }}</p> <p>{{ append_form.submit }}</p> </form> {# 在form标签下添加 flash 消息的显示 #} {% for message in get_flashed_messages() %} {{ message }} {% endfor %} </body> </html>
在form标签下添加flash消息显示
{% for message in get_flashed_messages() %} {{ message }} {% endfor %}
删除数据
定义删除author和book的路由
# 删除作者 @app.route('/delete_author/<int:author_id>') def delete_author(author_id): author = Author.query.get(author_id) if not author: flash('数据不存在') else: try:
#要先删除这个作者的书籍,再删除作者 Book.query.filter_by(author_id=author_id).delete() #删除作者
db.session.delete(author) db.session.commit() except Exception as e: db.session.rollback() print(e) flash('操作数据库失败') return redirect(url_for('index')) # 删除书籍 @app.route('/delete_book/<int:book_id>') def delete_book(book_id): book = Book.query.get(book_id) if not book: flash('数据不存在') else: try: db.session.delete(book) db.session.commit() except Exception as e: db.session.rollback() print(e) flash('操作数据库失败') return redirect(url_for('index'))
在模板中添加删除的a标签链接:
<h2>书籍列表</h2> <ul> {% for author in authors %} <li> {{ author.name }} <a href="/delete_author/{{ author.id }}">删除</a> <ul> {% for book in author.books %} <li>{{ book.name }} <a href="/delete_book/{{ book.id }}">删除</a></li> {% else %} <li>无书籍</li> {% endfor %} </ul> </li> {% endfor %} </ul>