第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

首頁 慕課教程 Flask 框架教程 Flask 框架教程 Flask 項目實戰(zhàn) 2: 后端實現(xiàn)

Flask 項目實戰(zhàn) 2: 后端實現(xiàn)

上一節(jié)介紹了待做清單項目的功能、程序的總體結(jié)構(gòu),程序的總體結(jié)構(gòu)分為前端和后端兩個部分,本節(jié)講解后端的實現(xiàn)。

1. 數(shù)據(jù)庫設(shè)計

1.1 表的設(shè)計

在數(shù)據(jù)庫中存在兩張表:users 和 todos。

表 users 用于記錄已經(jīng)注冊的用戶,包含有如下字段:

字段 描述
userId 用戶的 ID,表的主鍵
name 姓名
password 密碼

表 todos 用于記錄待做事項,包含有如下字段:

字段 描述
todoId 待做事項的 ID,表的主鍵
userId 所屬用戶的 ID
status 待做事項的狀態(tài),“todo” 表示待做,“done” 表示已經(jīng)完成
title 待做事項的標(biāo)題

1.2 數(shù)據(jù)庫腳本 db.sql

創(chuàng)建文件 db.sql,內(nèi)容由如下部分構(gòu)成:

1. 創(chuàng)建數(shù)據(jù)庫 todoDB

SET character_set_database=utf8;
SET character_set_server=utf8;
DROP DATABASE IF EXISTS todoDB;
CREATE DATABASE todoDB;
USE todoDB;

如果數(shù)據(jù)庫 todoDB 已經(jīng)存在,則首先刪除,然后再創(chuàng)建數(shù)據(jù)庫 todoDB。

2. 創(chuàng)建表 users

CREATE TABLE users(
    userId INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(255),
    password VARCHAR(255),
    PRIMARY KEY(userId)
);

創(chuàng)建表 users,表 users 包含 userId、name、password 等字段。userId 是主鍵,設(shè)置為從 1 自動增長。

3. 創(chuàng)建表 todos

CREATE TABLE todos(
    todoId INT NOT NULL AUTO_INCREMENT,
    userId INT,
    status VARCHAR(255),
    title VARCHAR(255),
    PRIMARY KEY(todoId)
);

創(chuàng)建表 todos,表 todos 包含 todoId、userId、status、title 等字段。todoId 是主鍵,設(shè)置為從 1 自動增長。

4. 創(chuàng)建測試數(shù)據(jù)

INSERT INTO users(name, password) VALUES ("guest", "123");
INSERT INTO todos(userId, status, title) VALUES (1, "todo", "吃飯");
INSERT INTO todos(userId, status, title) VALUES (1, "todo", "睡覺");
INSERT INTO todos(userId, status, title) VALUES (1, "done", "作業(yè)");

為了方便測試,向數(shù)據(jù)庫中插入一些預(yù)定義的數(shù)據(jù)。

在第 1 行,向表 users 中增加一個用戶 guest、密碼為 “123”,因為該用戶是表 users 中的第 1 條數(shù)據(jù),所以 userId 為 1。

在第 2 行到第 3 行,向表 todos 中增加 3 個 userId 為 1 的記錄,相當(dāng)于為 guest 用戶增加 3 個記錄;在第 2 行,插入待做事項 “吃飯”;在第 3 行,插入待做事項 “睡覺”;在第 4 行,插入已完成事項 “作業(yè)”。

最后,啟動 mysql 數(shù)據(jù)庫,在數(shù)據(jù)庫中執(zhí)行 db.sql:

mysql> source db.sql

2. Flask 實例 app.py

from flask import Flask
from datetime import timedelta

app = Flask(__name__)
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = timedelta(seconds=1) 
app.config['SECRET_KEY'] = 'hard to guess string'

在程序 app.py 中創(chuàng)建 Flask 實例 app,并進行兩項配置:

  • config[‘SEND_FILE_MAX_AGE_DEFAULT’],配置緩存的有效時間;
  • config[‘SECRET_KEY’],在程序中使用到了 Session,需要使用 SECRET_KEY 進行加密。

3. 入口 main.py

創(chuàng)建文件 main.py,它是 Flask 程序的入口,源代碼由如下部分構(gòu)成:

3.1 導(dǎo)入相關(guān)模塊

#!/usr/bin/python3
from app import app
from flask import render_template, session

import db
import users 
import todos
app.register_blueprint(users.blueprint)
app.register_blueprint(todos.blueprint)

在第 2 行,從模塊 app.py 中導(dǎo)入變量 app,他是 Flask 應(yīng)用程序?qū)嵗辉诘?5 行,導(dǎo)入模塊 db.py,該模塊用于提供了數(shù)據(jù)庫訪問接口。

程序包括兩個藍(lán)圖:users 藍(lán)圖和 todos 藍(lán)圖,在第 8 行和第 9 行,在 Flask 實例中注冊這兩個藍(lán)圖。

3.2 頁面 / 的視圖函數(shù)

@app.route('/')
def index():
    hasLogin = session.get('hasLogin')
    if hasLogin:
        userId = session.get('userId')
        items = db.getTodos(userId)
        todos = [item for item in items if item.status == 'todo']
        dones = [item for item in items if item.status == 'done']
    else:
        items = []
        todos = []
        dones = []
    return render_template('index.html', hasLogin = hasLogin, todos = todos, dones = dones)

app.run()

設(shè)置網(wǎng)站的首頁面 / 的處理函數(shù)為 index,該函數(shù)首先查詢 Session 中的變量 hasLogin,如果為真,表示用戶已經(jīng)登錄,顯示用戶已經(jīng)輸入的待做事項和完成事項;如果為假,表示用戶沒有登錄,顯示待做事項和完成事項為空。

在第 5 行,查詢 Session 中的變量 userId,該變量表示已經(jīng)登錄用戶的 Id;在第 6 行,根據(jù) db.getTodos(userId) 獲取數(shù)據(jù)庫該用戶記錄的待做事項。

在第 7 行,獲取待做事項中 status 等于 ‘todo’ 的待做事項,保存在列表 todos 中;在第 8 行,獲取待做事項中 status 等于 ‘done’ 的待做事項,保存在列表 dones 中。

在第 13 行,渲染首頁模板 index.html,傳遞 3 個參數(shù):

  • hasLogin,用戶是否登錄;
  • todos,該用戶輸入的待做事項;
  • dones,該用戶輸入的完成事項。

4. 數(shù)據(jù)庫訪問 db.py

db.py 中完成數(shù)據(jù)庫訪問相關(guān)的函數(shù),db.py 分為如下幾個部分:

4.1 引入相關(guān)模塊并配置

from app import app
from flask_sqlalchemy import SQLAlchemy

user = 'root'
password = '123456'
database = 'todoDB'
uri = 'mysql+pymysql://%s:%s@localhost:3306/%s' % (user, password, database)
app.config['SQLALCHEMY_DATABASE_URI'] = uri
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
orm = SQLAlchemy(app)

變量 user 是數(shù)據(jù)庫的用戶名,變量 password 是數(shù)據(jù)庫的密碼,變量 database 是數(shù)據(jù)庫的名稱。在這個例子中,用戶是 root,密碼是 123456,請調(diào)整你的 mysql 設(shè)置。設(shè)置完這 3 個變量后,數(shù)據(jù)庫訪問的 URI 為:

mysql+pymysql://root:123456@localhost:3306/todoDB

4.2 映射表 users 和表 todos

class User(orm.Model):
    __tablename__ = 'users'
    userId = orm.Column(orm.Integer, primary_key=True)
    name = orm.Column(orm.String(255))
    password = orm.Column(orm.String(255))

class Todo(orm.Model):
    __tablename__ = 'todos'
    todoId = orm.Column(orm.Integer, primary_key=True)
    userId = orm.Column(orm.Integer)
    status = orm.Column(orm.String(255))
    title = orm.Column(orm.String(255))

使用類 User 映射數(shù)據(jù)庫中的表 users,該表包含 3 個字段 userId、name、password,與類 User 中相同名稱的 3 個屬性一一對應(yīng)。

使用類 Todo 映射數(shù)據(jù)庫中的表 todos,該表包含 4 個字段 todoId、userId、status、title,與類 Todo 中相同名稱的 4 個屬性一一對應(yīng)。

4.3 對表 users 進行操作

def login(name, password):
    users = User.query.filter_by(name = name, password = password)
    user = users.first()
    return user

def register(name, password):
    user = User(name = name, password = password)
    orm.session.add(user)
    orm.session.commit()
    return True

函數(shù) login 在表 users 中查找與 name、password 匹配的用戶,如果存在,則表示登錄成功。

函數(shù) register 根據(jù) name、password 創(chuàng)建一個新的用戶,然后插入到表 users 中。

4.4 對表 todos 進行操作

def getTodos(userId):
    todos = Todo.query.filter_by(userId = userId)
    return todos

def addTodo(userId, status, title):
    todo = Todo(userId = userId, status = status, title = title)
    orm.session.add(todo)
    orm.session.commit()
    return True

def updateTodo(todoId, status):
    todos = Todo.query.filter_by(todoId = todoId)
    todos.update({'status': status})
    orm.session.commit()
    return True

def deleteTodo(todoId):
    todos = Todo.query.filter_by(todoId = todoId)
    todos.delete()
    orm.session.commit()
    return True

函數(shù) getTodos(userId) 在表中查詢屬于指定用戶的待做事項。

函數(shù) addTodo(userId, status, title) 根據(jù) userId、status、title 創(chuàng)建一個新的待做事項,然后插入到表 todos 中。

函數(shù) updateTodo(todoId,status) 更新待做事項的 status,當(dāng)用戶完成一個待做事項時,需要將待做事項的 status 從 “todo” 更改為 “done”。

函數(shù) deleteTodo(todoId) 刪除待做事項。

5. 藍(lán)圖 users.py

藍(lán)圖 users 包含有 3 個頁面:/users/login、/users/register、/users/logout,代碼由如下部分構(gòu)成:

5.1 導(dǎo)入相關(guān)模塊

from flask import Flask, render_template, request, redirect, session
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField
from wtforms.validators import DataRequired, Length
from flask import Blueprint
import db

blueprint = Blueprint('users', __name__, url_prefix='/users')

導(dǎo)入相關(guān)模塊,然后創(chuàng)建藍(lán)圖對象 blueprint,參數(shù) ‘users’ 是藍(lán)圖的名稱,參數(shù) url_prefix 是頁面的前綴。

藍(lán)圖 users 包含有 3 個頁面 /users/login、/users/register、/users/logout,設(shè)置 url_prefix 為 /users 后,使用 @app.route 注冊頁面的處理函數(shù)時,使用 /login、/register、/logout 作為 URL 即可,省略了前綴 /users。

5.2 登錄表單

class LoginForm(FlaskForm):
    name = StringField(
        label = '姓名',
        validators = [
            DataRequired(message = '姓名不能為空')
        ]
    )

    password = PasswordField(
        label = '密碼',
        validators =[
            DataRequired(message = '密碼不能為空'),
            Length(min = 3, message = '密碼至少包括 3 個字符')
        ]
    )

    submit = SubmitField('登錄')

使用 WTForms 表單實現(xiàn)登錄表單,LoginForm 繼承于 FlaskForm,它包含 2 個字段 name 和 password。

name 字段的驗證器 DataRequired 要求字段不能為空;password 字段的驗證器 DataRequired 要求字段不能為空,驗證器 Length 要求密碼至少包括 3 個字符。

5.3 請求 /users/login 頁面

@blueprint.route('/login', methods = ['GET', 'POST'])
def login():
    if request.method == 'GET':
        form = LoginForm()
        return render_template('login.html', form = form)
    else:
        form = LoginForm()
        if form.validate_on_submit():
            name = form.name.data 
            password = form.password.data
            user = db.login(name, password)
            if user:
                session['hasLogin'] = True
                session['userId'] = user.userId
                return redirect('/')
        return render_template('login.html', form = form)

頁面 /users/login 有兩種請求方法:GET 和 POST。

使用 GET 方法請求頁面 /users/login 時,用于顯示登陸界面。在第 5 行,使用 render_template 渲染登陸頁面模板 login.html。

使用 POST 方法請求頁面 /users/login 時,用于向服務(wù)器提交登陸請求。在第 7 行,創(chuàng)建一個 LoginForm 實例,然后調(diào)用 form.validate_on_submit() 驗證表單中的字段是否合法;在第 11 行,調(diào)用 db.login(name, password) 在數(shù)據(jù)庫驗證用戶身份,如果登錄成功,則返回登錄的用戶 user。

在第 12 行,如果登錄成功,在 Session 中設(shè)置 hasLogin 為 Ture,設(shè)置 userId 為登錄用戶的 userId;在第 15 行,調(diào)用 redirect(’/’),用戶登錄成功后,瀏覽器重定向到網(wǎng)站根頁面。

5.4 注冊表單

class RegisterForm(FlaskForm):
    name = StringField(
        label = '姓名',
        validators = [
            DataRequired(message = '姓名不能為空')
        ]
    )

    password = PasswordField(
        label = '密碼',
        validators =[
            DataRequired(message = '密碼不能為空'),
            Length(min = 3, message = '密碼至少包括 3 個字符')
        ]
    )

    submit = SubmitField('注冊')

使用 WTForms 表單實現(xiàn)注冊表單,RegisterForm 繼承于 FlaskForm,它包含 2 個字段 name 和 password。

name 字段的驗證器 DataRequired 要求字段不能為空;password 字段的驗證器 DataRequired 要求字段不能為空,驗證器 Length 要求密碼至少包括 3 個字符。

5.5 請求 /users/register 頁面

@blueprint.route('/register', methods = ['GET', 'POST'])
def register():
    if request.method == 'GET':
        form = RegisterForm()
        return render_template('register.html', form = form)
    else:
        form = RegisterForm()
        if form.validate_on_submit():
            name = form.name.data 
            password = form.password.data
            if db.register(name, password):
                return redirect('/')
        return render_template('register.html', form = form)

頁面 /users/register 有兩種請求方法:GET 和 POST。

使用 GET 方法請求頁面 /users/register 時,用于顯示注冊界面。在第 5 行,使用 render_template 渲染注冊頁面模板 register.html。

使用 POST 方法請求頁面 /users/register 時,用于向服務(wù)器提交登陸請求。在第 7 行,創(chuàng)建一個 RegisterForm 實例,然后調(diào)用 form.validate_on_submit() 驗證表單中的字段是否合法;在第 11 行,調(diào)用 db.register(name, password) 在數(shù)據(jù)庫注冊一個新用戶,如果注冊成功,則返回 True。

在第 12 行,如果注冊成功,調(diào)用 redirect(’/’),用戶注冊成功后,瀏覽器重定向到網(wǎng)站根頁面。

5.6 退出系統(tǒng) /logout

@blueprint.route('/logout')
def logout():
    session['hasLogin'] = False
    return redirect('/')

訪問 /users/logout 頁面時,用戶退出系統(tǒng)。在 Session 中設(shè)置 hasLogin 為 False,調(diào)用 redirect(’/’),用戶退出系統(tǒng)后,瀏覽器重定向到網(wǎng)站根頁面。

6. 藍(lán)圖 todos.py

藍(lán)圖 todos 包含有 3 個頁面:/todos/add、/todos/update、/todos/delete,代碼由如下部分構(gòu)成:

6.1 導(dǎo)入相關(guān)模塊

from flask import Flask, render_template, request, redirect, session, jsonify
from flask import Blueprint
import db

blueprint = Blueprint('todos', __name__, url_prefix='/todos')

導(dǎo)入相關(guān)模塊,然后創(chuàng)建藍(lán)圖對象 blueprint,參數(shù) ‘todos’ 是藍(lán)圖的名稱,參數(shù) url_prefix 是頁面的前綴。

藍(lán)圖 todos 包含有 3 個頁面 /todos/add、/todos/update、/todos/delete,設(shè)置 url_prefix 為 /todos 后,使用 @app.route 注冊頁面的處理函數(shù)時,使用 /add、/update、/delete 作為 URL 即可,省略了前綴 /todos。

6.2 請求 /todos/add 頁面

@blueprint.route('/add', methods = ['POST'])
def addTodo():
    userId = session.get('userId')
    status = 'todo'
    title = request.json['title']
    db.addTodo(userId, status, title)
    return jsonify({'error': None});

使用 POST 方法請求 /todos/add 頁面用于新增一個待做事項,在第 6 行調(diào)用 db.addTodo(userId, status, title) 向表 todos 中插入一行。

在例子中忽略了錯誤處理,在第 7 行,返回錯誤為 None。

6.3 請求 /todos/update 頁面

@blueprint.route('/update', methods = ['POST'])
def updateTodo():
    todoId = request.json['todoId']
    status = 'done'
    db.updateTodo(todoId, status)
    return jsonify({'error': None});

當(dāng)用戶完成一個待做事項后,將待做事項移入到完成事項中,需要使用 POST 方法請求 /todos/update 頁面用于更新待做事項的 status,在第 5 行調(diào)用 db.updateTodo(todoId, status) 個更新待做事項的 status。

在例子中忽略了錯誤處理,在第 6 行,返回錯誤為 None。

6.4 請求 /todos/delete 頁面

@blueprint.route('/delete', methods = ['POST'])
def deleteTodo():
    todoId = request.json['todoId']
    db.deleteTodo(todoId)
    return jsonify({'error': None});

使用 POST 方法請求 /todos/delete 頁面用于刪除一個待做事項,在第 4 行調(diào)用 db.deleteTodo(todoId) 刪除指定的待做事項。

在例子中忽略了錯誤處理,在第 5 行,返回錯誤為 None。

7. 小結(jié)

本節(jié)講解了后端的實現(xiàn),使用思維導(dǎo)圖概括如下:

圖片描述