flask綜合案例


一.項目准備

  1.新建項目目錄students,並創建虛擬環境

mkvirtualenv students

  2.安裝依賴環境

pip install flask==0.12.4
pip install redis
pip install flask-session
pip install flask-script
pip install flask-mysqldb
pip install flask-sqlalchemy
pip install flask-migrate
pip install flask_wtf

  3.在pycharm中打開項目目錄編寫manage.py啟動項目的文件

     (記得配置解釋器為我們的虛擬環境解釋器)

from flask import Flask

app = Flask(__name__)

@app.route('/index')
def index():
    return 'index'

if __name__ == '__main__':
    app.run()

  運行正常說明成功:

  4.創建目錄結構:

    mange.py終不能存放大量的開發代碼, 在開發中應該體現的是一種分工精神,所以我們可以把flask中各種功能代碼進行分類分文件存儲.

    創建項目目錄結構:

項目根目錄/
├── application/            # 項目主要邏輯代碼保存目錄
|   ├── settings/           # 項目配置存儲目錄
│   │   ├ dev.py            # 開發階段的配置文件
│   │   ├ prop.py           # 生產階段的配置文件
│   ├── __init__.py         # 項目初始化文件
├── manage.py               # 項目的終端管理腳本文件

  5.各配置文件的編寫

    配置文件

    settings/__init__.py代碼:

from redis import StrictRedis

class Config(object):
    """項目配置核心類"""
    # 調試模式
    DEBUG = True

    # todo 配置日志
    pass

    # mysql數據庫的配置信息
    SQLALCHEMY_DATABASE_URI = "mysql://root:mysql@127.0.0.1:3306/students?charset=utf8"
    # 動態追蹤修改設置,如未設置只會提示警告
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    # 查詢時會顯示原始SQL語句
    SQLALCHEMY_ECHO= False

    # 配置redis
    REDIS_HOST = '127.0.0.1'  # 項目上線以后,這個地址就會被替換成真實IP地址,mysql也是
    REDIS_PORT = 6379

    # 設置密鑰,可以通過 base64.b64encode(os.urandom(48)) 來生成一個指定長度的隨機字符串
    SECRET_KEY = "ghhBljAa0uzw2afLqJOXrukORE4BlkTY/1vaMuDh6opQ3uwGYtsDUyxcH62Aw3ju"

    # flask_session的配置信息
    SESSION_TYPE = "redis" # 指定 session 保存到 redis 中
    SESSION_USE_SIGNER = True # 讓 cookie 中的 session_id 被加密簽名處理
    SESSION_REDIS = StrictRedis(host=REDIS_HOST, port=REDIS_PORT,db=1) # 使用 redis 的實例
    PERMANENT_SESSION_LIFETIME = 24 * 60 * 60 # session 的有效期,單位是秒

    settings/dev.py代碼:

from . import Config
class DevelopementConfig(Config):
    """開發模式下的配置"""
    # 查詢時會顯示原始SQL語句
    SQLALCHEMY_ECHO= True

    settings/prop.py代碼:

from . import Config
class ProductionConfig(Config):
    """生產模式下的配置"""
    DEBUG = False

 

二.項目主應用中初始化項目

  1.application/__init__.py文件中,創建flask應用並加載配置

    

from flask import Flask
from application.settings.dev import DevelopementConfig
from application.settings.prop import ProductionConfig

config = {
    "dev": DevelopementConfig,
    "prop": ProductionConfig,
}

def init_app(config_name):
    """項目的初始化函數"""
    app = Flask(__name__)

    # 設置配置類
    Config = config[config_name]

    # 加載配置
    app.config.from_object(Config)

    return app

  在manage.py 中調用 init_app 函數,啟動項目

from application import init_app

app = init_app("dev")

@app.route("/")
def index():
    return "index"

if __name__ == '__main__':
    app.run()

  application/__init__.py項目初始化文件中加載redis或者mysql的初始化代碼

from flask import Flask
from redis import StrictRedis
from flask_wtf.csrf import CSRFProtect
from flask_session import Session

from application.settings.dev import DevelopementConfig
from application.settings.prop import ProductionConfig

config = {
    "dev": DevelopementConfig,
    "prop": ProductionConfig,
}

# 為了方便redis的連接對象在函數外部可以使用,預先設置一個全局變量,接下來在函數中用於保存redis的連接
redis_store = None

def init_app(config_name):
    """項目的初始化功能"""
    app = Flask(__name__)

    # 設置配置類
    Config = config[config_name]

    # 加載配置
    app.config.from_object(Config)

    # redis的鏈接初始化
    global redis_store
    redis_store = StrictRedis(host=Config.REDIS_HOST, port=Config.REDIS_PORT,db=0)

    # 開啟CSRF防范功能
    CSRFProtect(app)

    # 開啟session功能
    Session(app)

    # TODO 注冊藍圖對象到app應用中

    return app

    application/__init__.py項目初始化文件初始化mysql

# from flask import Flask
# from redis import StrictRedis
# from flask_wtf.csrf import CSRFProtect
# from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
# 
# from application.settings.dev import DevelopementConfig
# from application.settings.prop import ProductionConfig
# 
# config = {
#     "dev": DevelopementConfig,
#     "prop": ProductionConfig,
# }
# 
# # 為了方便redis的連接對象在函數外部可以使用,預先設置一個全局變量,接下來在函數中用於保存redis的連接
# redis_store = None
db = SQLAlchemy()
# 
# def init_app(config_name):
#     """項目的初始化功能"""
#     app = Flask(__name__)
# 
#     # 設置配置類
#     Config = config[config_name]
# 
#     # 加載配置
#     app.config.from_object(Config)
# 
#     # redis的鏈接初始化
#     global redis_store
#     redis_store = StrictRedis(host=Config.REDIS_HOST, port=Config.REDIS_PORT,db=0)
# 
#     # 開啟CSRF防范功能
#     CSRFProtect(app)
# 
#     # 開啟session功能
#     Session(app)
# 
    # 配置數據庫鏈接
  # 這個init_app是數據庫那個類自身的函數,並不是當前的這個函數
db.init_app(app) # # # TODO 注冊藍圖對象到app應用中 # # return app

  因為前面已經在settings中設置了數據庫的配置信息,所以接下來,創建對應的數據庫

create database students charset=utf8;

  2.在manage啟動文件中新增關於啟動過程中的相關功能(數據庫遷移等)

    在項目根目錄下`manage.py中設置項目啟動程序並調用__init__.py的app

from application import init_app,db
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand

app = init_app("dev")

# 使用終端腳本工具啟動和管理flask
manager = Manager(app)

# 啟用數據遷移工具
Migrate(app, db)
# 添加數據遷移的命令到終端腳本工具中
manager.add_command('db', MigrateCommand)


@app.route("/")
def index():
    return "index"

if __name__ == '__main__':
    manager.run()

 

三.日志的使用

  Python 自身提供了一個用於記錄日志的標准庫模塊:logging

FATAL/CRITICAL = 致命的,危險的
ERROR = 錯誤
WARNING = 警告
INFO = 信息
DEBUG = 調試
NOTSET = 沒有設置

  application/__init__.py項目初始化文件把日志設置封裝成一個函數

import logging
from logging.handlers import RotatingFileHandler

# 把日志相關的配置封裝成一個日志初始化函數
def setup_log(Config):
    # 設置日志的記錄等級
    logging.basicConfig(level=Config.LOG_LEVEL)  # 調試debug級
    # 創建日志記錄器,指明日志保存的路徑、每個日志文件的最大大小、保存的日志文件個數上限
    file_log_handler = RotatingFileHandler("logs/log", maxBytes=1024 * 1024 * 300, backupCount=10)
    # 創建日志記錄的格式 日志等級 輸入日志信息的文件名 行數 日志信息
    formatter = logging.Formatter('%(levelname)s %(filename)s:%(lineno)d %(message)s')
    # 為剛創建的日志記錄器設置日志記錄格式
    file_log_handler.setFormatter(formatter)
    # 為全局的日志工具對象(flaskapp使用的)添加日志記錄器
    logging.getLogger().addHandler(file_log_handler)

    在application/__init__.py項目初始化文件中,在init_app 方法中調用上一步創建的方法,並傳入 config_name

 # 啟用日志功能
    setup_log(Config)

 

  在配置文件settings/__init__.py中,設置默認日志等級

1 class Config(object):
2     """項目配置核心類"""
3     # 調試模式
4     DEBUG = True
5 
6     # todo 配置日志
7     LOG_LEVEL = "DEBUG"

  新增日志以后的項目目錄結構(logs這個文件需要手動創建,否則會報錯)

項目根目錄/
├── docs/                   # 項目開發相關文檔
├── logs/                   # 項目運行日志保存目錄
|   ├── log                 # 日志文件
├── application/            # 項目主要邏輯代碼保存目錄
|   ├── settings/           # 項目配置存儲目錄
│   │   ├ dev.py            # 開發階段的配置文件
│   │   ├ prop.py           # 生產階段的配置文件
│   ├── __init__.py         # 項目初始化文件
├── manage.py               # 項目的終端管理腳本文件

 

四.藍圖創建

  在applications下創建apps目錄,apps以后專門用於保存每一個項目的藍圖,

  並在apps創建index藍圖目錄,並在__init__.py文件中創建藍圖對象

from flask import Blueprint

index_blu = Blueprint("index_blu",__name__)

  在index藍圖目錄中新增對應的視圖文件vies.py,代碼:

from . import index_blu

@index_blu.route("/")
def index():
    return "首頁"

  __init__.py中引入當前藍圖下所有的視圖文件

from flask import Blueprint

index_blu = Blueprint("index_blu",__name__)

from .views import *

  在項目初始化文件application/__init__.py文件中的init_app函數,注冊藍圖對象

 # TODO 注冊藍圖對象到app應用中
    # 首頁模塊
    from .apps.index import index_blu
    app.register_blueprint(index_blu,url_prefix='')

  聲明了藍圖目錄以后的項目目錄結構

項目根目錄/
├── application/            # 項目主要邏輯代碼保存目錄
|   ├── settings/           # 項目配置存儲目錄
│   │   ├ dev.py            # 開發階段的配置文件
│   │   ├ prop.py           # 生產階段的配置文件
│   ├── __init__.py         # 項目初始化文件
│   ├── statics/            # 保存項目中所有的靜態資源文件[img/css/js]
│   ├── modules/            # 保存項目中所有藍圖的存儲目錄
│   │   ├── index           # 藍圖目錄
│   │   │   ├── __init__.py # 藍圖的初始化問年間
│   │   │   └── views.py    # 藍圖的視圖函數文件
│   │   ├── __init__.py     
├── manage.py               # 項目的終端管理腳本文件

    index的模型代碼:models.py

# coding=utf-8
from application import db

# 創建關系表,不再創建模型,一般用於表與表之間的多對多場景
"""
表關系變量 = db.Table(
    "關系表表名",
    db.Column('字段名', 字段類型, 字段選項),  # 普通字段
    db.Column("字段名", 字段類型, db.ForeignKey("表名.id")),
    db.Column("字段名", 字段類型, db.ForeignKey("表名.id")),
)
"""
achievement = db.Table(
    "achievement",
    db.Column('score', db.Numeric, comment="分數"),
    db.Column('student_id', db.Integer, db.ForeignKey('student.id')),
    db.Column('course_id', db.Integer, db.ForeignKey('course.id'))
)

class Student(db.Model):
    """學生信息"""
    __tablename__ = "student"
    id = db.Column(db.Integer, primary_key=True, comment="主鍵ID")
    name = db.Column(db.String(64), index=True, comment="姓名" )
    sex = db.Column(db.Boolean, default=True, comment="性別")
    class_number = db.Column(db.String(32), nullable=True, index=True, comment="班級")
    age = db.Column(db.SmallInteger, comment="年齡")
    description = db.Column(db.Text, comment="個性簽名")
    courses = db.relationship(
        'Course', # 模型名稱
        secondary=achievement, # 表關系變量
        backref='students', # 當外鍵反過來獲取主鍵信息時,使用的字段名稱,可以自定義,接下來的使用例如: course.students 獲取某個課程下所有的學生
        lazy='dynamic'
    )

class Course(db.Model):
    """課程信息"""
    __tablename__ = "course"
    id = db.Column(db.Integer, primary_key=True,comment="主鍵ID")
    name = db.Column(db.String(64), unique=True,comment="課程名稱")

   在主應用中帶入模型並遷移數據庫

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#author tom
from application import init_app, db
from flask_script import Manager
from flask_migrate import Migrate,MigrateCommand

app=init_app('dev')

# 使用終端腳本工具啟動和管理flask
manager=Manager(app)

#啟用數據遷移工具
Migrate(app,db)
#添加數據遷移的命令道終端腳本工具中
manager.add_command('db',MigrateCommand)

# 導入模型[為了進行數據遷移]
from application.apps.index import models


@app.route('/')
def index():
    return 'index'

if __name__ == '__main__':
    manager.run()

  遷移數據庫:

#因為上面已經把數據庫遷移夢里添加了:
#所以直接

#初始化
python manage.py  init

#遷移
python manage.py  migrate

#生成報表
python manage.py upgrade

  添加測試數據

insert into student values 
(1,"趙華",1,307,22,"對於勤奮的人來說,成功不是偶然;對於懶惰的人來說,失敗卻是必然。"),
(2,"程星雲",1,301,20,"人生應該如蠟燭一樣,從頂燃到底,一直都是光明的。"),
(3,"陳峰",1,504,21,"在不瘋狂,我們就老了,沒有記憶怎么祭奠呢?"),
(4,"蘇禮就",1,502,20,"不要為舊的悲傷,浪費新的眼淚。"),
(5,"張小玉",0,306,18,"沒有血和汗水就沒有成功的淚水。"),
(6,"吳傑",1,307,19,"以大多數人的努力程度之低,根本輪不到去拼天賦"),
(7,"張小辰",0,405,19,"人生的道路有成千上萬條, 每一條路上都有它獨自的風景。")

   查看數據:在index.view編寫視圖

from flask import render_template, request, flash

from application import db
from . import index_blu
from .models import Student

@index_blu.route('/')
def index():
    '''學生列表'''
    student_list=Student.query.all()
    data=[]
    for student in student_list:
        data.append({
            'id': student.id,
            'name':student.name,
            'age':student.age,
            'sex':'' if student.sex else '',
            'description':student.description,
            'class_number':student.class_number,
        })

    return render_template('index.html',students=data)

  index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="{{ url_for('index_blu.add') }}">新增學員</a>
    <table border="1" width="1000" align="center">
        <tr>
            <th>id</th>
            <th>姓名</th>
            <th>年齡</th>
            <th>性別</th>
            <th>班級</th>
            <th>個性簽名</th>
        </tr>
        {% for student in students  %}
            <tr>
                <td>{{ student.id }}</td>
                <td>{{ student.name }}</td>
                <td>{{ student.age }}</td>
                <td>{{ student.sex }}</td>
                <td>{{ student.class_number }}</td>
                <td>{{ student.description }}</td>
            </tr>
        {% endfor %}

    </table>

</body>
</html>
View Code

  添加數據:在index.view編寫視圖

@index_blu.route('/add',methods=['GET','POST'])
def add():
    if request.method=='POST':
        #接收數據
        name=request.form.get('username')
        age = int(request.form.get("age"))
        sex = True if request.form.get("sex") == '1' else False
        class_number = request.form.get("class_number")
        description = request.form.get("description")
        #驗證數據
        if age<0 or age >120:
            flash('非法年齡數據')
        #入庫
        student=Student(name=name,sex=sex,class_number=class_number,description=description)
        try:
            db.session.add(student)
            db.session.commit()
        except Exception as e:
            db.session.rollback()

    return render_template('add.html')

  add.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {% for message in get_flashed_messages() %}
    <span>{{ message }}</span>
    {% endfor %}
    <form action="" method="post">
        <table border="1" witdth="600" align="center">
            <tr>
                <td>姓名:</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>年齡:</td>
                <td><input type="text" name="age"></td>
            </tr>
            <tr>
                <td>性別:</td>
                <td>
                    <select name="sex">
                        <option value="1"></option>
                        <option value="0"></option>
                    </select>
                </td>
            </tr>
            <tr>
                <td>班級:</td>
                <td><input type="class_number"></td>
            </tr>
            <tr>
                <td>個性簽名:</td>
                <td><textarea name="description"></textarea></td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>
                    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
                    <input type="submit" value="注冊">
                </td>
            </tr>


        </table>
    </form>
</body>
</html>
View Code

   項目目錄結構圖:

 

 

 

五。閃現信息[flash]

  使用后,只會出現一次的信息,叫“閃現信息”,用於在驗證代碼失敗,或者一些只需要顯示一次性提示的場景。

  使用步驟:

  視圖中驗證有誤,則在顯示模板之前設置flash

  flash信息是通過session保存,也是從session中獲取的,所以要設置app.config["SECRE_KEY"]

# 視圖函數代碼
from flask import flash

flash("對不起,您尚未登錄,請登錄!")

  模板代碼:

# 模板代碼
{% for message in get_flashed_messages() %}
    <span>{{message}}</span>
{% endfor %}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM