from flask import Flask,render_template, flash, request,url_for,redirect
# 導入數據庫工具
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
# 提交
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired,EqualTo
#編碼
# import sys
# sys.setdefaultencoding("utf-8")
'''
1、配置數據庫
a.導入SQLAlchemy擴展
b.創建db對象, 並設置參數
c.終端創建數據庫
2、添加書和作者模型
a.模型繼承 db.Model
b.__tablename__ :表明
c. db.Column :字段
3、添加數據
4、使用模板顯示數據庫查詢的數據
a.查詢所有的作者的信息,讓信息傳遞給模板
b.模板中按照格式,依次for循環作者的書籍即可,(作者獲取書籍, 用的是關系引用)
5、使用WTF顯示表單
a.自定義表單類
b.模板中的顯示
c.secret_key /編碼 /csrf_token
6、實現相關的增刪邏輯
a.增加數據
b.刪除書籍 url_for的使用 、 for else的使用 、redirect(重定向)的使用
'''
app = Flask(__name__)
# 配置數據庫的地址 數據庫
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:123456@localhost:3306/flask_books"
# 跟蹤數據庫的修改 -->不建議開啟、 開啟以后會性能會有影響、未來的版本會移除
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
#在flask項目中,Session, Cookies以及一些第三方擴展都會用到SECRET_KEY值,這是一個比較重要的配置值。
app.secret_key = "itheima"
#創建數據庫對象
db = SQLAlchemy(app)
'''
這個網站解釋了 flask框架封裝數據庫字段的解釋 https://blog.csdn.net/appleyuchi/article/details/79372240
'''
class Book(db.Model):
# 表名字
__tablename__ = "books"
# 字段
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(16), unique=True)
# 外鍵 就是Book類中添加了一個author_id變量,數據類型db.Integer,第二個參數指定外鍵是哪個表中哪個(authors)的id。
author_id = db.Column(db.Integer,db.ForeignKey("authors.id"))
def __repr__(self):
return "Book: %s %s" % (self.name,self.author_id)
#定義書和作者模型
class Author(db.Model):
#表名字
__tablename__ = "authors"
#字段
id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(16),unique=True)
#關系引用
books = db.relationship("Book",backref = "author")
'''
這句話比較復雜,仔細讀下面的話:
0.添加到Author模型中的books屬性代表這個關系的面向對象視角。對於一個Author類的實例,其books屬性將返回與角色相關聯的用戶組成的列表。
1.db.Relationship()第一個參數表明這個關系的另一端是哪個模型(類)。如果模型類尚未定義,可使用字符串形式指定。
2.db.Relationship()第二個參數backref,將向Book類中添加一個author屬性,從而定義反向關系。這一屬性可替代author_id訪問Author模型,此時獲取的是模型對象,而不是外鍵的值。
'''
def __repr__(self):
return "Author: %s" % self.name
#自定義表單類
class AuthorForm(FlaskForm):
#validators=[DataRequired()] 驗證函數
author = StringField("作者",validators=[DataRequired()])
book = StringField("書籍",validators=[DataRequired()])
submit = SubmitField("提交")
@app.route("/delete_author/<author_id>")
def delete_author(author_id):
#查詢數據庫,查看id的作者是否存在, 存在就刪除
author = Author.query.get(author_id)
#如果作者存在
if author:
try:
#查詢書籍查看是否存在, 並且刪除
Book.query.filter_by(author_id = author.id).delete()
#刪除作者
db.session.delete(author)
#刪除以后提交
db.session.commit()
except Exception as e:
print(e)
flash("刪除作者出錯")
db.session.rollback()
else:
flash("作者找不到")
return redirect(url_for("index"))
@app.route("/delete_book/<book_id>")
def delete_book(book_id):
#查詢數據庫、是否有改id的書籍
book = Book.query.get(book_id)
#如果存在就刪除、如果不存在就提示錯誤
if book:
try:
db.session.delete(book)
db.session.commit()
except Exception as e:
print(e)
flash("刪除書籍錯誤")
db.session.rollback()
else:
flash("書籍找不到")
print(url_for("index"))
#刪除以后重定向到index所對應的視圖
return redirect(url_for("index"))
#驗證就會出現GET/POST請求
@app.route('/',methods=["GET","POST"])
def index():
# 創建自定義的表單類
author_form = AuthorForm()
'''
6、 WTF可以一句話實現驗證數據:
1、調用WTF的函數實現驗證
2、驗證通過獲取數據
3、判斷作者是否存在
4、判斷書籍是否存在, 如果沒有重復書籍就添加數據,如果重復就提示錯誤
5、如果作者不存在, 添加作者和書籍
6、驗證不通過就提示錯誤
'''
#1、調用WTF的函數實現驗證, 提交的時候驗證 \ 驗證是否有數據
if author_form.validate_on_submit():
#2、驗證通過獲取數據
#拿到作者input信息
author_name = author_form.author.data
#書籍input信心
book_name = author_form.book.data
#3、判斷作者是否存在 (涉及到數據庫查詢操作、query.filter_by)
author = Author.query.filter_by(name = author_name).first()
#如果作者存在
if author:
#4、判斷書籍是否存在, 如果沒有重復書籍就添加數據,如果重復就提示錯誤
book = Book.query.filter_by( name = book_name).first()
if book:
flash("已經存在同名書籍")
else:
try:
#添加數據、 與數據庫交互操作
new_book = Book(name = book_name,author_id = author.id)
db.session.add(new_book)
db.session.commit()
except Exception as e:
print(e)
flash("添加書籍失敗")
#如果添加書籍失敗就回滾一下
db.session.rollback()
else:
#如果作者不存在, 添加作者和書籍
try:
#添加作者
new_author = Author(name = author_name)
db.session.add(new_author)
db.session.commit()
new_book = Book(name = book_name,author_id = new_author.id)
db.session.add(new_book)
db.session.commit()
except Exception as e:
print(e)
flash("添加作者和書籍失敗")
db.session.rollback()
else:
#flash("這個無論如何都會提示、因為沒有做啟動方式驗證、啟動方式是get或者post")
#6、如果是post請求, 才會提示有問題 驗證不通過就提示錯誤 加上from flask import Flask,render_template, flash, request
if request.method == "POST":
flash("參數不全")
#查詢所有的作者信息, 讓信息傳遞給模板
authors = Author.query.all()
return render_template("books.html",authors=authors, form = author_form)
if __name__ == '__main__':
# 為了演示方便,先刪除所有表,再創建
db.drop_all()
# 創建
db.create_all()
# 添加測試數據庫
# 生成數據
au1 = Author(name='老王')
au2 = Author(name='老尹')
au3 = Author(name='老劉')
# 把數據提交給用戶會話
db.session.add_all([au1, au2, au3])
# 提交會話
db.session.commit()
bk1 = Book(name='老王回憶錄', author_id=au1.id)
bk2 = Book(name='我讀書少,你別騙我', author_id=au1.id)
bk3 = Book(name='如何才能讓自己更騷', author_id=au2.id)
bk4 = Book(name='怎樣征服美麗少女', author_id=au3.id)
bk5 = Book(name='如何征服英俊少男', author_id=au3.id)
# 把數據提交給用戶會話
db.session.add_all([bk1, bk2, bk3, bk4, bk5])
# 提交會話
db.session.commit()
app.run(debug = True)