這周學(xué)的內(nèi)容是flask,項(xiàng)目需要在原來的基礎(chǔ)上增加一個(gè)新功能,實(shí)際上就是在原來的一系列文件上改改就能實(shí)現(xiàn),功能也很簡單,連接頁面與數(shù)據(jù)庫,并且在頁面上實(shí)現(xiàn)增刪改查等功能。
項(xiàng)目是用flask實(shí)現(xiàn)的,所以先學(xué)了flask,好在flask是一個(gè)相對(duì)來說功能比較簡單的框架,因此學(xué)的時(shí)候基礎(chǔ)內(nèi)容比較少,但是還是在理解上遇到一些困難。在這里記錄一下flask的原理和案例
# -*- coding:utf-8 -*-
from flask import Flask, render_template, flash, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
# python2中的固定寫法,當(dāng)程序中出現(xiàn)中文時(shí),用來修改默認(rèn)編碼方式為utf-8
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
# 固定寫法,實(shí)例化一個(gè)flask類
app = Flask(__name__)
# 數(shù)據(jù)庫配置: 數(shù)據(jù)庫地址
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1/flask_books'
#關(guān)閉自動(dòng)跟蹤修改(提升運(yùn)行速度)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
#設(shè)置跨網(wǎng)頁傳輸?shù)拿荑€,不設(shè)會(huì)報(bào)錯(cuò)
app.secret_key = 'itheima'
# 創(chuàng)建數(shù)據(jù)庫對(duì)象
db = SQLAlchemy(app)
'''
1. 配置數(shù)據(jù)庫
a. 導(dǎo)入SQLAlchemy擴(kuò)展
b. 創(chuàng)建db對(duì)象, 并配置參數(shù)
c. 終端創(chuàng)建數(shù)據(jù)庫
2. 添加書和作者模型
a. 模型繼承db.Model
b. __tablename__:表名
c. db.Column:字段
d. db.relationship: 關(guān)系引用
3. 添加數(shù)據(jù)
4. 使用模板顯示數(shù)據(jù)庫查詢的數(shù)據(jù)
a. 查詢所有的作者信息, 讓信息傳遞給模板
b. 模板中按照格式, 依次for循環(huán)作者和書籍即可 (作者獲取書籍, 用的是關(guān)系引用)
5. 使用WTF顯示表單
a. 自定義表單類
b. 模板中顯示
c. secret_key / 編碼 / csrf_token
6. 實(shí)現(xiàn)相關(guān)的增刪邏輯
a. 增加數(shù)據(jù)
b. 刪除書籍 url_for的使用 / for else的使用 / redirect的使用
c. 刪除作者
'''
# 定義書和作者模型
# 作者模型
class Author(db.Model):
# 表名
__tablename__ = 'authors'
# 字段
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(16), unique=True)
# 關(guān)系引用(反關(guān)聯(lián),一對(duì)多時(shí)在一的那邊設(shè)置,反關(guān)聯(lián)根據(jù)外鍵生成的約束連接)
# books是給自己(Author模型)用的, author是給Book模型用的,即可以使用author.books和book.author但在表中不會(huì)生成真正的字段
books = db.relationship('Book', backref='author')
# 提升打印時(shí)的美觀度
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(16), unique=True)
# 外鍵(括號(hào)里指定主鍵是哪個(gè)表的哪個(gè)字段)
author_id = db.Column(db.Integer, db.ForeignKey('authors.id'))
def __repr__(self):
return 'Book: %s %s' % (self.name, self.author_id)
# 自定義表單類
class AuthorForm(FlaskForm):
author = StringField('作者', validators=[DataRequired()])
book = StringField('書籍', validators=[DataRequired()])
submit = SubmitField('提交')
# 刪除作者
# app.route這個(gè)裝飾器會(huì)將后綴為/delete_author/<author_id>的request都使用 delete_author這個(gè)函數(shù)來執(zhí)行
# <author_id>捕獲url里傳過來的參數(shù),放到后續(xù)的delete_author里
app.route('/delete_author/<author_id>')
def delete_author(author_id):
# 查詢數(shù)據(jù)庫, 是否有該ID的作者, 如果有就刪除(先刪書, 再刪作者), 沒有提示錯(cuò)誤
# 1. 查詢數(shù)據(jù)庫
author = Author.query.get(author_id)
# 2. 如果有就刪除(先刪書, 再刪作者)
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
# html中有捕獲flash閃現(xiàn)的語句
flash('刪除作者出錯(cuò)')
db.session.rollback()
else:
# 3. 沒有提示錯(cuò)誤
flash('作者找不到')
# redirect中是一個(gè)重定向的網(wǎng)頁,url_for是提取這個(gè)通過route綁定了“index”這個(gè)函數(shù)的網(wǎng)頁
return redirect(url_for('index'))
# 刪除書籍 --> 網(wǎng)頁中刪除-->點(diǎn)擊需要發(fā)送書籍的ID給刪除書籍的路由 --> 路由需要接受參數(shù)
@app.route('/delete_book/<book_id>')
def delete_book(book_id):
# 1. 查詢數(shù)據(jù)庫, 是否有該ID的書, 如果有就刪除, 沒有提示錯(cuò)誤
book = Book.query.get(book_id)
# 2. 如果有就刪除
if book:
try:
db.session.delete(book)
db.session.commit()
except Exception as e:
print e
flash('刪除書籍出錯(cuò)')
db.session.rollback()
else:
# 3. 沒有提示錯(cuò)誤
flash('書籍找不到')
# redirect: 重定向, 需要傳入網(wǎng)絡(luò)/路由地址
# url_for('index'): 需要傳入視圖函數(shù)名, 返回改視圖函數(shù)對(duì)應(yīng)的路由地址
print url_for('index')
return redirect(url_for('index'))
# 如何返回當(dāng)前網(wǎng)址 --> 重定向
# return redirect('wwww.itheima.com')
# return redirect('/')
@app.route('/', methods=['GET', 'POST'])
def index():
# 創(chuàng)建自定義的表單類
author_form = AuthorForm()
'''
表單填充和顯示的流程:
1、打開網(wǎng)頁,會(huì)向URL發(fā)送一個(gè)GET請(qǐng)求,因?yàn)槭荊ET請(qǐng)求,所以author_form為空,網(wǎng)頁上的表單中只顯示框架,框架中無值
2、在表單中鍵入值,發(fā)送POST請(qǐng)求,滿足條件后,author_form中保存了表單中的值,return 之后又把值傳回給頁面,頁面再渲染上值,保證了鍵入的值一直存在于表單中,而框架里也可以隨時(shí)調(diào)用。
'''
驗(yàn)證邏輯:
1. 調(diào)用WTF的函數(shù)實(shí)現(xiàn)驗(yàn)證
2. 驗(yàn)證通過獲取數(shù)據(jù)
3. 判斷作者是否存在
4. 如果作者存在, 判斷書籍是否存在, 沒有重復(fù)書籍就添加數(shù)據(jù), 如果重復(fù)就提示錯(cuò)誤
5. 如果作者不存在, 添加作者和書籍
6. 驗(yàn)證不通過就提示錯(cuò)誤
'''
# 1. 調(diào)用WTF的函數(shù)實(shí)現(xiàn)驗(yàn)證
if author_form.validate_on_submit():
# 2. 驗(yàn)證通過獲取數(shù)據(jù)
author_name = author_form.author.data
book_name = author_form.book.data
# 3. 判斷作者是否存在
author = Author.query.filter_by(name=author_name).first()
# 4. 如果作者存在
if author:
# 判斷書籍是否存在
book = Book.query.filter_by(name=book_name).first()
# 如果重復(fù)就提示錯(cuò)誤
if book:
flash('已存在同名書籍')
# 沒有重復(fù)書籍就添加數(shù)據(jù)
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:
# 5. 如果作者不存在, 添加作者和書籍
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:
# 6. 驗(yàn)證不通過就提示錯(cuò)誤
if request.method == 'POST':
flash('參數(shù)不全')
# 查詢所有的作者信息, 讓信息傳遞給模板
authors = Author.query.all()
return render_template('books.html', authors=authors, form=author_form)
if __name__ == '__main__':
db.drop_all()
db.create_all()
# 生成數(shù)據(jù)
au1 = Author(name='老王')
au2 = Author(name='老惠')
au3 = Author(name='老劉')
# 把數(shù)據(jù)提交給用戶會(huì)話
db.session.add_all([au1, au2, au3])
# 提交會(huì)話
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)
# 把數(shù)據(jù)提交給用戶會(huì)話
db.session.add_all([bk1, bk2, bk3, bk4, bk5])
# 提交會(huì)話
db.session.commit()
app.run(debug=True)