Python Flask之旅

《Pyhton Flask之旅》

以前學flask時做的總結,搬運到這裏,markdown格式寫的有點亂,湊合看吧。

參考博客
http://blog.csdn.net/nunchakushuang/article/details/74645094

項目配置

Flask項目的配置,都是通過app.config對象來進行配置的。比如要配置一個項目處於DEBUG模式下,那麼可以使用app.config['DEBUG'] = True來進行設置,
那麼Flask項目將以DEBUG模式運行。在Flask項目中,有四種方式進行項目的配置:

1. 直接硬編碼:
app = Flask(__name__)
app.config['DEBUG'] = True

因爲app.config是flask.config.Config的實例,而Config類是繼承自dict,因此可以通過update方法:
app.config.update(
    DEBUG=True,
    SECRET_KEY='...'
)

如果你的配置項特別多,你可以把所有的配置項都放在一個模塊中,然後通過加載模塊的方式進行配置,假設有一個settings.py模塊,專門用來存儲配置項的,此時你可以通過app.config.from_object()方法進行加載,並且該方法既可以接收模塊的的字符串名稱,也可以模塊對象:
2. 通過模塊字符串
app.config.from_object('settings')
3. 通過模塊對象
import settings
app.config.from_object(settings)
也可以通過另外一個方法加載,該方法就是app.config.from_pyfile(),該方法傳入一個文件名,通常是以.py結尾的文件,但也不限於只使用.py後綴的文件:
app.config.from_pyfile('settings.py',silent=True)
#silent=True表示如果配置文件不存在的時候不拋出異常,默認是爲False,會拋出異常。
開啓DEBUG模式有三種方式
1.直接在應用對象上設置:
app.debug = True
    app.run()
2.在執行run方法的時候,傳遞參數進去:
app.run(debug=True)

3.在config屬性中設置:
app.config.update(DEBUG=True)

URL和view

# 類型有以下幾種:
string: 默認的數據類型,接受沒有任何斜槓“\/”的文本。
int: 接受整形。
float: 接受浮點類型。
path: 和string的類似,但是接受斜槓。
uuid: 只接受uuid字符串。
any:可以指定多種路徑。
#item這個函數可以接受兩個URL,一個是/article/,另一個是/blog/。並且,一定要傳url_path參數,url_path的名稱可以隨便。
@app.route('/<any(article,blog):url_path>/')
def item(url_path):
    return url_path

#url中參數的獲取
通過傳統的?=的形式來傳遞參數,例如:/article?id=xxx,這種情況下,可以通過request.args.get('id')來獲取id的值。如果是post方法,則可以通過request.form.get('id')來進行獲取。

響應(Response)

視圖函數的返回值會被自動轉換爲一個響應對象,Flask的轉換邏輯如下:

如果返回的是一個合法的響應對象,則直接返回。
如果返回的是一個字符串,那麼Flask會重新創建一個werkzeug.wrappers.Response對象,Response將該字符串作爲主體,狀態碼爲200,MIME類型爲text/html,然後返回該Response對象。
如果返回的是一個元組,元祖中的數據類型是(response,status,headers),只能包含一個元素。status值會覆蓋默認的200狀態碼,headers可以是一個列表或者字典,作爲額外的消息頭。
如果以上條件都不滿足,Flask會假設返回值是一個合法的WSGIt應用程序,並通過Response.force_type(rv,request.environ)轉換爲一個請求對象。


0.最常用的render_template,也可以自定義返回碼
from flask import Flask,render_template
@app.route('/about/')
def about():
    return render_template('about.html'), 205

1.直接使用Response創建:
from werkzeug.wrappers import Response
@app.route('/about/')
def about():
    resp = Response(response='about page',status=200,content_type='text/html;charset=utf-8')
    return resp

2.可以使用make_response函數來創建Response對象,這個方法可以設置額外的數據,比如設置cookie,header信息等:
from flask import make_response
@app.route('/about/')
def about():
    return make_response('about page')

3.通過返回元組的形式:
@app.errorhandler(404)
def not_found():
    return 'not found',404
    
4.自定義響應。自定義響應必須滿足三個條件:
必須繼承自Response類。
必須實現類方法force_type(cls,rv,environ=None)。
必須指定app.response_class爲你自定義的Response
#http://blog.csdn.net/nunchakushuang/article/details/74645094

模板

關於模板的幾個博客

http://blog.csdn.net/nunchakushuang/article/details/74645592
http://blog.csdn.net/kikaylee/article/details/53540352?locationNum=6&fps=1
http://www.cnblogs.com/Erick-L/p/6873129.html

if __name__ == '__main__':
    app.run(debug=True)
__name__=='__main__' 是 Python 的慣常用法,在這裏確保直接執行這個腳本時才啓動開發Web 服務器。
如果這個腳本由其他腳本引入,程序假定父級腳本會啓動不同的服務器,因此不會執行app.run()。


#如果想更改模板和靜態文件地址,應該在創建app的時候,給Flask傳遞關鍵字參數template_folder、static_folder
from flask import Flask,render_template
app = Flask(__name__,template_folder=r'C:\templates',static_folder='/tmp')

@app.route('/about/')
def about():
    return render_template('about.html')

#Jinja數據類型:
Jinja支持許多數據類型,包括:字符串、整型、浮點型、列表、元組、字典、true/false

#運行Jinja2的語句;               {%...%}     
#在頁面中打印Jinja2運行的結果: {{…}}       
#註釋:                            {#...#}     

#if控制
{% if user %}
    Hello, {{ user }}
{% else %}
    Hello, Stranger!
{% endif %}

#for循環
<ul>
    {% for comment in comments %}
        <li>{{ comment }}</li>
    {% endfor %}
</ul>

#宏類似 python 中的函數
{% macro render_comment(comment) %}
    <li>{{ comment }}</li>
{% endmacro %}

<ul>
    {% for comment in comments %}
        {{ render_comment(comment) }}
    {% endfor %}
</ul>


#遍歷字典:
<dl>
{% for key, value in my_dict.iteritems() %}
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
{% endfor %}
</dl>

#如果序列中沒有值的時候,進入else:
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% else %}
<li><em>no users found</em></li>
{% endfor %}
</ul>


#jinja2 能識別所有類型的變量,比如列表,字典,對象
<p>{{ mylist[3]}}</p>
<p>{{ mydict['key']}}</p>
<p>{{ mylist['key']}}</p>
<p>{{ myobj.somemethod() }}</p>


#include語句:
include語句可以把一個模板引入到另外一個模板中,類似於把一個模板的代碼copy到另外一個模板的指定位置,看以下例子:
{% include 'header.html' %}
Body
{% include 'footer.html' %}
#賦值(set)語句:
有時候我們想在在模板中添加變量,這時候賦值語句(set)就派上用場了,先看以下例子:
{% set name='xiaotuo' %}
那麼以後就可以使用name來代替xiaotuo這個值了,同時,也可以給他賦值爲列表和元組:
{% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
賦值語句創建的變量在其之後都是有效的,如果不想讓一個變量污染全局環境,可以使用with語句來創建一個內部的作用域,將set語句放在其中,這樣創建的變量只在with代碼塊中才有效,看以下示例:
{% with %}
{% set foo = 42 %}
{{ foo }} foo is 42 here
{% endwith %}
也可以在with的後面直接添加變量,比如以上的寫法可以修改成這樣:
{% with foo = 42 %}
{{ foo }}
{% endwith %}
這兩種方式都是等價的,一旦超出with代碼塊,就不能再使用foo這個變量了。

#過濾器
過濾器是通過管道符號(|)進行使用的,例如:{{ name|length }},將返回name的長度。
safe    渲染值時不轉義
capitalize 把值的首字母轉換成大寫,其他字母小寫
lower      把值轉換成小寫形式
upper     把值轉換成大寫形式
title       把值中每個單詞的首字母變成大寫
trim      把值的首尾空格去掉
striptags  渲染之前把所有的HTML標籤都刪除

常用的過濾器進行講解:

abs(value):返回一個數值的絕對值。示例:-1|abs
default(value,default_value,boolean=false):如果當前變量沒有值,則會使用參數中的值來代替。示例:name|default('xiaotuo')——如果name不存在,則會使用xiaotuo來替代。boolean=False默認是在只有這個變量爲undefined的時候纔會使用default中的值,如果想使用python的形式判斷是否爲false,則可以傳遞boolean=true。也可以使用or來替換。
escape(value)或e:轉義字符,會將<、>等符號轉義成HTML中的符號。示例:content|escape或content|e。
first(value):返回一個序列的第一個元素。示例:names|first
format(value,*arags,**kwargs):格式化字符串。比如:
{{ "%s" - "%s"|format('Hello?',"Foo!") }}
將輸出:Helloo? - Foo!
last(value):返回一個序列的最後一個元素。示例:names|last。
length(value):返回一個序列或者字典的長度。示例:names|length。
join(value,d=u''):將一個序列用d這個參數的值拼接成字符串。
safe(value):如果開啓了全局轉義,那麼safe過濾器會將變量關掉轉義。示例:content_html|safe。
int(value):將值轉換爲int類型。
float(value):將值轉換爲float類型。
lower(value):將字符串轉換爲小寫。
upper(value):將字符串轉換爲小寫。
replace(value,old,new): 替換將old替換爲new的字符串。
truncate(value,length=255,killwords=False):截取length長度的字符串。
striptags(value):刪除字符串中所有的HTML標籤,如果出現多個空格,將替換成一個空格。
trim:截取字符串前面和後面的空白字符。
string(value):將變量轉換成字符串。
wordcount(s):計算一個長字符串中單詞的個數。


#測試器:
測試器主要用來判斷一個值是否滿足某種類型,並且這種類型一般通過普通的if判斷是有很大的挑戰的。語法是:if...is...,先來簡單的看個例子:

{% if variable is escaped%}
value of variable: {{ escaped }}
{% else %}
variable is not escaped
{% endif %}
以上判斷variable這個變量是否已經被轉義了,Jinja中內置了許多的測試器,看以下列表:

callable(object):是否可調用。
defined(object):是否已經被定義了。
escaped(object):是否已經被轉義了。
upper(object):是否全是大寫。
lower(object):是否全是小寫。
string(object):是否是一個字符串。
sequence(object):是否是一個序列。
number(object):是否是一個數字。
odd(object):是否是奇數。
even(object):是否是偶數。
#宏:
模板中的宏跟Python中的函數類似,可以傳遞參數,但是不能有返回值,可以將一些經常用到的代碼片段放到宏中,然後把一些不固定的值抽取出來當成一個變量,以下將用一個例子來進行解釋:

{% macro input(name, value='', type='text') %}
<input type="{{ type }}" name="{{ name }}" value="{{value|e }}">
{% endmacro %}
以上例子可以抽取出了一個input標籤,指定了一些默認參數。那麼我們以後創建input標籤的時候,可以通過他快速的創建:
<p>{{ input('username') }}</p>
<p>{{ input('password', type='password') }}</p>


#轉義:
轉義的概念是,在模板渲染字符串的時候,字符串有可能包括一些非常危險的字符比如<、>等,這些字符會破壞掉原來HTML標籤的結構,更嚴重的可能會發生XSS跨域腳本攻擊,
因此如果碰到<、>這些字符的時候,應該轉義成HTML能正確表示這些字符的寫法,比如>在HTML中應該用&lt;來表示等。
但是Flask中默認沒有開啓全局自動轉義,針對那些以.html、.htm、.xml和.xhtml結尾的文件,如果採用render_template函數進行渲染的,則會開啓自動轉義。
並且當用render_template_string函數的時候,會將所有的字符串進行轉義後再渲染。
#Jinja2默認沒有開啓全局自動轉義。

#靜態文件配置
<link href="{{ url_for('static',filename='about.css') }}">

}

數據庫

http://blog.csdn.net/nunchakushuang/article/details/74647673

在Flask中可以自由的使用MySQL、PostgreSQL、SQLite、Redis、MongoDB來寫原生的語句實現功能,也可以使用更高級別的數據庫抽象方式,如SQLAlchemy或MongoEngine這樣的OR(D)M。

使用sqlite數據庫的兩種姿勢
import sqlite3
from contextlib import closing

app.config.update(
    DATABASE = 'my.db',      #相對於文件所在目錄
    DEBUG=True,
)

def connect_db():
    return sqlite3.connect(app.config['DATABASE'])

def init_db():
    with closing(connect_db()) as db:
        with app.open_resource('schema.sql', 'r') as f:
            db.cursor().executescript(f.read())
        db.commit()

def get_db():
    db = connect_db()
    cur = db.cursor()
    return db, cur

db, cur = get_db()
x = cur.execute('SELECT * FROM users WHERE username = ?', [username])
x.fetchall()

cur.execute("INSERT INTO users (username, password, email) VALUES(?,?,?)", [username, password, email])
db.commit()
cur.close()
db.close()
####################################################################

import sqlite3
from flask import Flask, request, session, g, redirect, url_for, abort, render_template, flash,make_response
# 設置數據庫存儲路徑
DATABASE = r'C:\\Users\\lWX307086\\Desktop\\python_learn-master\\未上傳文件\\myweb\\flaskr.db'
SECRET_KEY = "zheshiyigemima"

app = Flask(__name__)
app.config.from_object(__name__)
##########################################數據庫部分##########################
def connect_db():
    return sqlite3.connect(app.config['DATABASE'])

def init_db():
    with closing(connect_db()) as db:
        with app.open_resource('./schema.sql', mode='r')as f:
            db.cursor().executescript(f.read())
        db.commit()

# 有必要在請求之前初始化連接,並在請求後關閉連接
@app.before_request
def before_request():
    g.db = connect_db()


@app.after_request
def after_request(exception):
    db = getattr(g, 'db', None)  # Getattr用於返回一個對象屬性,或者方法
    if db is not None:
        db.close()
    g.db.close()
##########################################文章管理##########################
@app.route('/')
def show_web():
    # execute是執行sql語句
    cur = g.db.execute('select title, text,id from entries order by id desc')
    # 字典初始化,key爲title,value爲text。cur.fetchall()是接收全部的返回結果行
    entries = [dict(title=row[0], text=row[1],id=row[2]) for row in cur.fetchall()]
    print("entries:",entries)
    # 1.cucursor()獲取操作遊標
    # 2.execute執行SQL,括號裏的是sql語句
    # 3.fetchall()返回查詢到的所有記錄
    return render_template('show_entries.html', entries=entries)
使用引擎連接mysql數據庫
from sqlalchemy import create_engine

# 將要連接的數據庫的配置
HOSTNAME = '10.120.189.164'
PORT = '15432'
DATABASE = 'golang'
USERNAME = 'root'
PASSWORD = 'root'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
#dialect+driver://username:password@host:port/database
#dialect是數據庫的實現,比如MySQL、PostgreSQL、SQLite,並且轉換成小寫。driver是Python對應的驅動,如果不指定,會選擇默認的驅動,比如MySQL的默認驅動是MySQLdb。
#username是連接數據庫的用戶名,password是連接數據庫的密碼,host是連接數據庫的域名,port是數據庫監聽的端口號,database是連接哪個數據庫的名字。
#connect(),創建連接
#close(),關閉數據庫連接
#commit(),提交
#rollback(),回滾/取消當前

# 創建數據庫引擎
engine = create_engine(DB_URI)

#創建連接
with engine.connect() as con:
    rs = con.execute('SELECT 1')
    print (rs.fetchone())

with engine.connect() as con:
    # 先刪除users表
    con.execute('drop table if exists users')
    # 創建一個users表,有自增長的id和name
    con.execute('create table users(id int primary key auto_increment,'
    'name varchar(25))')
    # 插入兩條數據到表中
    con.execute('insert into users(name) values("xiaoming")')
    con.execute('insert into users(name) values("xiaotuo")')
    # 執行查詢操作
    rs = con.execute('select * from users')
    # 從查找的結果中遍歷
    for row in rs:
        print ("row:",row)


# ORM(數據庫映射式)
{
###################### ORM #######################
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
from flask import Flask
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
import os

basedir = os.path.abspath(os.path.dirname(__file__))

SQLALCHEMY_DATABASE_URI = "sqlite:///" + os.path.join(basedir, "ORM.db")
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db')
SQLALCHEMY_TRACK_MODIFICATIONS = True
CSRF_ENABLED = True
SECRET_KEY = "wito_python_api"
UPLOAD_FOLDER = "app\\static\\upload"

app = Flask(__name__)
app.config.from_object(__name__)
app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER

###################### ORM #######################
db = SQLAlchemy(app)

#db.Column 中其餘的參數指定屬性的配置選項。
"""
primary_key 如果設爲 True,這列就是表的主鍵
unique  如果設爲 True,這列不允許出現重複的值
index   如果設爲 True,爲這列創建索引,提升查詢效率
nullable    如果設爲 True,這列允許使用空值;如果設爲 False,這列不允許使用空值
default 爲這列定義默認值
"""
class User(db.Model):
    # 定義表名爲users
    __tablename__ = 'users'
    user_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    user_name = db.Column(db.String(64),unique=True)
    password = db.Column(db.String(64))
    status = db.Column(db.Integer)
    level = db.Column(db.Integer)
    # 讓打印出來的數據更好看,可選的
    def __repr__(self):
        return "<User(user_id='%s',user_name='%s',password='%s',status='%s',level='%s')>" % (self.user_id,self.user_name,self.password,self.status,self.level)

class Parameter(db.Model):
    parameter_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    interface_id = db.Column(db.Integer, db.ForeignKey("user.user_id"))
    parameter_type = db.Column(db.String(64))

db.create_all()


add = User(user_name="lgj",password="123",status=1,level=2)
add2 = User(user_name="lgj2",password="12233",status=2,level=4)
#會話由 db.session 表示
db.session.add(add)#增
db.session.add(add2)
add.user_name = 'LGJ'#改
db.session.delete(add2)#刪
db.session.commit()
user_all = User.query.filter_by(user_id=1).all()#查
print(user_all)
print(user_all[0].user_id,user_all[0].user_name,user_all[0].password,user_all[0].level)


#添加數據庫管理
manage = Manager(app)
migrate = Migrate(app, db)
manage.add_command('db', MigrateCommand)

if __name__ == '__main__':
    manage.run()
爲了導出數據庫遷移命令,Flask-Migrate 提供了一個 MigrateCommand 類,可附加到 Flask- Script 的 manager 對象上。
在這個例子中,MigrateCommand 類使用 db 命令附加。
在維護數據庫遷移之前,要使用 init 子命令創建遷移倉庫:
python hello.py db init    # 將嚮應用添加一個`migrations`文件夾。文件夾中的文件需要和其他源文件一起進行版本控制。
這個命令會創建 migrations 文件夾,所有遷移腳本都存放其中。


可在 query 對象上調用的常用過濾器。
過濾器 說明
filter()    把過濾器添加到原查詢上,返回一個新查詢
filter_by() 把等值過濾器添加到原查詢上,返回一個新查詢
limit() 使用指定的值限制原查詢返回的結果數量,返回一個新查詢
offset()    偏移原查詢返回的結果,返回一個新查詢
order_by()  根據指定條件對原查詢結果進行排序,返回一個新查詢
group_by()  根據指定條件對原查詢結果進行分組,返回一個新查詢
在查詢上應用指定的過濾器後,通過調用 all() 執行查詢,以列表的形式返回結果。除了 all() 之外,還有其他方法能觸發查詢執行。

常用查詢執行函數
方法  說明
all()   以列表形式返回查詢的所有結果
first() 返回查詢的第一個結果,如果沒有結果,則返回 None
first_or_404()  返回查詢的第一個結果,如果沒有結果,則終止請求,返回 404 錯誤響應
get()   返回指定主鍵對應的行,如果沒有對應的行,則返回 None
get_or_404()    返回指定主鍵對應的行,如果沒找到指定的主鍵,則終止請求,返回 404 錯誤響應
count() 返回查詢結果的數量
paginate()  返回一個 Paginate 對象,它包含指定範圍內的結果

sqlalchemy常用數據類型:{

Integer:整形。
Boolean:傳遞True/False進去。
Date:傳遞datetime.date()進去。
DateTime:傳遞datetime.datetime()進去。
Float:浮點類型。
String:字符類型,使用時需要指定長度,區別於Text類型。
Text:文本類型。
Time:傳遞datetime.time()進去。
}

過濾條件:
過濾是數據提取的一個很重要的功能,以下對一些常用的過濾條件進行解釋,並且這些過濾條件都是隻能通過filter方法實現的:
1.equals:
query.filter(User.name == 'ed')

2.not equals:
query.filter(User.name != 'ed')

3.like:
query.filter(User.name.like('%ed%'))

4.in:
query.filter(User.name.in_(['ed','wendy','jack']))
# 同時,in也可以作用於一個Query
query.filter(User.name.in_(session.query(User.name).filter(User.name.like('%ed%'))))

5.not in:
query.filter(~User.name.in_(['ed','wendy','jack']))

6.is null:
query.filter(User.name==None)
# 或者是
query.filter(User.name.is_(None))

7.is not null:
query.filter(User.name != None)
# 或者是
query.filter(User.name.isnot(None))

8.and:
from sqlalchemy import and_
query.filter(and_(User.name=='ed',User.fullname=='Ed Jones'))
# 或者是傳遞多個參數
query.filter(User.name=='ed',User.fullname=='Ed Jones')
# 或者是通過多次filter操作
query.filter(User.name=='ed').filter(User.fullname=='Ed Jones')

9.or:
from sqlalchemy import or_ query.filter(or_(User.name=='ed',User.name=='wendy'))
文本SQL
SQLAlchemy還提供了使用文本SQL的方式來進行查詢,而文本SQL要裝在一個text()方法中,看以下例子:

from sqlalchemy import text
for user in session.query(User).filter(text("id<244")).order_by(text("id")).all():
print user.name
如果過濾條件比如上例中的244存儲在變量中,這時候就可以通過傳遞參數的形式進行構造:
session.query(User).filter(text("id<:value and name=:name")).params(value=224,name='ed').order_by(User.id)
在文本SQL中的變量前面使用了:來區分,然後使用params方法,指定需要傳入進去的參數。另外,使用from_statement方法可以把過濾的函數和條件函數都給去掉,使用純文本的SQL:
sesseion.query(User).from_statement(text("select * from users where name=:name")).params(name='ed').all()
使用from_statement方法一定要注意,from_statement返回的是一個text裏面的查詢語句,一定要記得調用all()方法來獲取所有的值。
計數(Count)
Query對象有一個非常方便的方法來計算裏面裝了多少數據:

session.query(User).filter(User.name.like('%ed%')).count()
當然,有時候你想明確的計數,比如要統計users表中有多少個不同的姓名,那麼簡單粗暴的採用以上count是不行的,因爲姓名有可能會重複,但是處於兩條不同的數據上,如果在原生數據庫中,可以使用distinct關鍵字,那麼在SQLAlchemy中,可以通過func.count()方法來實現:
from sqlalchemy import func
session.query(func.count(User.name),User.name).group_by(User.name).all()
# 輸出的結果
> [(1, u'ed'), (1, u'fred'), (1, u'mary'), (1, u'wendy')]
另外,如果想實現select count(*) from users,可以通過以下方式來實現:
session.query(func.count(*)).select_from(User).scalar()
當然,如果指定了要查找的表的字段,可以省略select_from()方法:
session.query(func.count(User.id)).scalar()
分頁導航
Flask-SQLALchemy的Pagination對象可以方便的進行分頁,
對一個查詢對象調用pagenate(page, per_page=20, error_out=True)函數可以得到pagination對象,第一個參數表示當前頁,第二個參數代表每頁顯示的數量,error_out=True的情況下如果指定頁沒有內容將出現404錯誤,否則返回空的列表

#從get方法中取得頁碼
page = request.args.get('page', 1, type = int)
#獲取pagination對象
    pagination = Post.query.order_by(Post.timestamp.desc()).paginate(page, per_page=10, error_out = False)
#pagination對象的items方法返回當前頁的內容列表
    posts = pagination.items

pagination對象常用方法:
has_next :是否還有下一頁
has_prev :是否還有上一頁
items : 返回當前頁的所有內容
next(error_out=False) : 返回下一頁的Pagination對象
prev(error_out=False) : 返回上一頁的Pagination對象
page : 當前頁的頁碼(從1開始)
pages : 總頁數
per_page : 每頁顯示的數量
prev_num : 上一頁頁碼數
next_num :下一頁頁碼數
query :返回 創建這個Pagination對象的查詢對象
total :查詢返回的記錄總數
iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2)
#在模版中使用
{% macro render_pagination(pagination, endpoint) %}
  <div class=pagination>
  {%- for page in pagination.iter_pages() %}
    {% if page %}
      {% if page != pagination.page %}
        <a href="{{ url_for(endpoint, page=page) }}">{{ page }}</a>
      {% else %}
        <strong>{{ page }}</strong>
      {% endif %}
    {% else %}
      <span class=ellipsis>…</span>
    {% endif %}
  {%- endfor %}
  </div>
{% endmacro %}

}

Flask-Script

from flask_script import Shell
from flask_script import Manager
app=create_app()
manager = Manager(app)

# from flask_migrate import Migrate, MigrateCommand
# manager.add_command('db', MigrateCommand)
# #manaer 是Flask-Script的實例,這條語句在flask-script 中添加一個db命令

#增加命令行方式一
from flask_script import Command,prompt_bool
class Hello1(Command):
    "prints hello world"

    def run(self):
        print ("增加命令行方式一 : hello world 1")
manager.add_command('hello1', Hello1())
#manager.run({'hello1' : Hello1()}) #效果同上


#增加命令行方式二
@manager.option('-n', '--name', dest='name', default='joe')
@manager.option('-u', '--url', dest='url', default=None)
def hello2(name, url):
    if url is None:
        print ("hello2", name)
    else:
        print ("hello2", name, "from", url)
#python factory.py hello2 -u lgj

#增加命令行方式三 不帶參數
#初始化數據庫
@manager.command
def initdb():
    init_db()
    print('Initialized the database.')
#python factory.py initdb

#增加命令行方式三 帶參數
@manager.command
def hello3(name="lgj"):
    print ("hello3", name)
#python factory.py hello3 --name=llllll
#python factory.py hello3 -n 1111111

#增加命令行方式三 帶交互
@manager.command
def dropdb():
    if prompt_bool("Are you sure you want to lose all your data"):
        drop_db()
        print("刪除數據庫成功!")
#python factory.py dropdb

if __name__ == '__main__':
    app.debug = True
    manager.run()

# if __name__ == '__main__':
#     app=create_app()
#     app.run(debug=True, host='127.0.0.1', port=5000)
1.使用@command裝飾器

@manager.option('-n','--name',dest='name')
def hello(name):
    print ('hello ',name)

2.使用類繼承自Command類:

2.1 必須繼承自Command基類。
2.2 必須實現run方法。
2.3 必須通過add_command方法添加命令。
from flask_script import Command,Manager
from your_app import app

manager = Manager(app)

class Hello(Command):
    def run(self):
        print ("hello world")

manager.add_command('hello',Hello())

from flask_Flask import Comman,Manager,Option
class Hello(Command):
    def __init__(self,default_name='Joe'):
        self.default_name = default_name

    def get_options(self):
        return [
        Option('-n','--name',dest='name',default=self.default_name),
        ]

    def run(self,name):
        print ('hello',name)

Flask 常用點

1.判斷method方式
request.method  'POST', 'GET'
 
2.獲取form內容
request.form['form_name']
 
3.獲取url參數(?key=value) 
request.args.get('key', '')
request.args["url"]
 
4.獲取上傳的文件
確保在html表單中設置 enctype="multipart/form-data"屬性,f = request.files['the_file'],   from werkzeug import secure_filename,  file_name = secure_filename(f.filename) 
 
5.獲取cookies內容
request.cookies.get('cook_name')
request.cookies['cook_name']
 
6.設置 cookies 內容
resp = make_response(render_template(...)) 
rep.set_cookie('key', 'value')
---------------------------------------------------
res = app.make_response('hello lg')
res.set_cookie('username', value='lgphp', max_age=20000)
----------------------------------------------------------------------
7.刪除cooikes
res = app.make_response(render_template(...))
res.delete_cookie('username')   
#也可以
res.set_cookie('username', value='lgphp', expires=0)
 
8.判斷cookie存在?
if (request.cookies):

Flask 六種常用響應方式

1. 直接返回字符串,可以返回狀態碼
@app.route('/testresponse', methods=['GET', 'POST'])
def testresponse():
    return "xxxxxxxx", 400
    
2. 響應Response對象,利用make_reponse()函數接受字符串和錯誤碼,返回一個Response對象,利用這種方法,不但可以成功處理請求,還可以進一步設置響應,如設置cookie等等
from flask import make_response 
@app.route('/testresponse', methods=['GET', 'POST'])
def testresponse():
    print type( request.cookies )
    if request.cookies and request.cookies.get('hyman'):
        response=make_response('cookies has been set!')
    else:
        response=make_response('set cookies!')
        response.set_cookie('hyman','123')
    return response
    
3. 返回重定向類型redirect
@app.route('/testresponse', methods=['GET', 'POST'])
def testresponse():
    return redirect('http://www.baidu.com')
    
4. 返回處理錯誤碼 
from flask import abort
@app.route('/testresponse', methods=['GET', 'POST'])
def testresponse():
     abort(404)

5.渲染模板可以帶參數
@app.route('/image')
def image():
    nav_list = ['首頁', '頭條', '娛樂', '新聞']
    img = url_for('static', filename='1.png')
    username = request.cookies.get('username')
    if username != None:
        return render_template('test.html', img=img, nav=nav_list, name=username)
    return render_template('test.html', img=img, nav=nav_list), 205 #也可以自定義返回碼,默認爲200

6.返回json數據
@app.route("/api/v1.0/test/simple", methods=('POST','GET'))
def test_simple_view():
    return jsonify({'request.method': request.method, 'echo_msg': 'successful' } ), 201     

web表單的幾種方式

1.前端式,通過request.form獲取提交的數據
#login.html
{% extends "layout.html" %}
{% block body %}
  <h2>Login</h2>
  {% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
  <form action="{{ url_for('login') }}" method=post>
    <dl>
      <dt>Username:
      <dd><input type=text name=username>
      <dt>Password:
      <dd><input type=password name=password>
      <dd><input type=submit value=Login>
    </dl>
  </form>
{% endblock %}

# 登錄
@app.route('/login', methods=['GET', 'POST'])
def login():
    error = None
    if request.method == 'POST':
        #通過request.form獲取提交的數據
        u=request.form['username']
        p=request.form['password']
        try:
            pp = g.db.execute('select password from users where name=(?)',[u]).fetchone()[0]
            #print(pp)
        except Exception as e:
            error ='Invalid password or username!'
        else:
            user=checkpwd()
            user.password_hash=pp
            user.password=p
            if user.verify_password()==False:
                error = 'Invalid password or username!'
            else:
                session['username'] = u
                flash('You were logged in')
                return redirect(url_for('show_web'))
    return render_template('login.html', error=error)

    
2.FlaskForm式+bootstrap/wtf的quick_form
#index.html
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

{% block title %}Flasky{% endblock %}

{% block page_content %}
<div class="page-header">
    <h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1>
    {% if not known %}
    <p>Pleased to meet you!</p>
    {% else %}
    <p>Happy to see you again!</p>
    {% endif %}
</div>
{{ wtf.quick_form(form) }}
{% endblock %}

from flask_wtf import FlaskForm
from wtforms import StringField, BooleanField,SubmitField
from wtforms.validators import DataRequired,Required,Length
#在這裏定義form需要提交的數據的數目、類型、過濾規則等。
class NameForm(FlaskForm):
    name = StringField('name:', validators=[Required(),Length(3)])
    password=PasswordField("password:", validators=[Required(),Length(5)])
    submit = SubmitField('Submit')

# StringField 類表示type="text"的<input>元素
# StringField 構造函數的參數validators指定一個由驗證函數組成的列表,函數Required()確保提交不爲空
# SubmitField 類表示type="submit"的<input>元素


@app.route('/index',methods=['GET', 'POST'])
@app.route('/', methods=['GET', 'POST'])
def index():
    name = None
    form = NameForm()
    if form.validate_on_submit():
        name = form.name.data
        form.name.data = ''
    return render_template('index.html', form=form, name=name)

3.FlaskForm式+前端式,通過form.XXX使用參數
#login.html
<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<h1>Sign In</h1>
<form action="" method="post" name="login">
    {{form.hidden_tag()}}
    <p>
        Please enter your OpenID:<br>
        {{form.openid(size=80)}}<br>
        Please enter your password:<br>
        {{form.password(size=80)}}<br>
        {% for error in form.errors.openid %}
        <span style="color: red;">[{{error}}]</span>
        {% endfor %}<br>
    </p>
    <p>{{form.remember_me}} Remember Me</p>
    <p><input type="submit" value="Sign In"></p>
</form>
{% endblock %}

#在這裏定義form需要提交的數據的數目、類型、過濾規則。
class LoginForm(FlaskForm):
    openid = StringField('openid', validators = [DataRequired(),Length(3)])
    password = StringField('password', validators = [DataRequired(),Length(3)])
    remember_me = BooleanField('remember_me', default = False)
    
@app.route('/login', methods = ['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        flash('Login requested for OpenID="' + form.openid.data + form.password.data +'", remember_me=' + str(form.remember_me.data))
        return redirect('/index')
    return render_template('login.html',title = 'Sign In',form = form)

    
from flask_wtf import FlaskForm
from wtforms import StringField, BooleanField,SubmitField,PasswordField
from wtforms.validators import DataRequired,Required,Length

WTForms支持的HTML標準字段

StringField                 文本字段
TextAreaField               多行文本字段
PasswordField               密碼文本字段
HiddenField                 隱藏文本字段
DateField                   文本字段,值爲datetime.date格式
DateTimeField               文本字段,值爲datetime.datetime格式
IntegerField                文本字段,值爲整數
DecimalField                文本字段,值爲decimal.Decimal
FloatField                  文本字段,值爲浮點數
BooleanField                複選框
RadioField                  一組單選框
SelectField                 下拉列表
SelectMultipleField         下拉列表,可選擇多個值
FileField                   文本上傳字段
SubmitField                 表單提交
FormField                   把表單作爲字段嵌入另一個表單
FieldList                   一組指定類型的字段

WTForms.validators 驗證函數

Email             驗證電子郵件地址
EqualTo           比較兩字段值,常用於要求輸入兩次密碼確認
IPAddress         驗證IPv4網絡地址
Length            驗證輸入字符串長度
NumberRange       驗證輸入的值在數字範圍內
Optional          無輸入值時跳過其他驗證函數
Required          確保字段中的數據
Regexp            使用正則表達式驗證輸入值
URL               驗證URL
AnyOf             確保輸入值在可選值列表中
NoneOf            確保輸入值不在可選值列表中

    

文件的上傳下載

http://www.cnblogs.com/Erick-L/p/7015783.html

1 後臺程序直接生成文件內容
from flask import make_response
@app.route('/testdownload', methods=['GET'])
def testdownload():
    content = "long text"
    response = make_response(content)
    response.headers["Content-Disposition"] = "attachment; filename=myfilename.txt"
return response
 
2 讀取一個服務器上的文件,供用戶下載
from flask import make_response , send_file
@app.route('/testdownload', methods=['GET'])
def testdownload():
    response = make_response(send_file("views.py"))
    response.headers["Content-Disposition"] = "attachment; filename=views.py;"
return response

3.secure_filename原文件名存儲
# coding:utf-8

from flask import Flask,render_template,request,redirect,url_for
from werkzeug.utils import secure_filename
import os

app = Flask(__name__)

@app.route('/upload', methods=['POST', 'GET'])
def upload():
    if request.method == 'POST':
        f = request.files['file']
        basepath = os.path.dirname(__file__)  # 當前文件所在路徑
        upload_path = os.path.join(basepath, 'static\uploads',secure_filename(f.filename))  #注意:沒有的文件夾一定要先創建,不然會提示沒有該路徑
        f.save(upload_path)
        return redirect(url_for('upload'))
    return render_template('upload.html')

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

#upload.html    
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>文件上傳示例</h1>
    <form action="" enctype='multipart/form-data' method='POST'>
        <input type="file" name="file">
        <input type="submit" value="上傳">
    </form>
</body>
</html> 
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章