《Head First Python》Ch10:函數(shù)修飾符

版權(quán)聲明:本文為CSDN博主「一笑照夜」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/erwugumo/article/details/95965235

1、web的狀態(tài)與訪問權(quán)限

我們在上一章已經(jīng)完成了日志的記錄與SQL的處理。并且可以通過viewlog網(wǎng)址查詢所有的日志。但是現(xiàn)在問題來了:作為日志數(shù)據(jù)這樣較為敏感較為有價(jià)值的信息,應(yīng)該是所有人可以隨意看到的嗎?

不應(yīng)該。我們應(yīng)添加一個功能,使得只有認(rèn)證用戶可以查詢?nèi)罩尽?/p>

最初的想法可以是這樣的:在我們的webapp中維護(hù)一個全局變量,如果這個變量值為True,說明有權(quán)限;如果變量值為False,說明沒有權(quán)限。

看上去好想可以用。

但是實(shí)際上不可以。為什么呢?

之前我們討論過所謂的變量作用域。即使是代碼中的全局變量,它的作用范圍也僅限于該程序。你總不能讓程序A中的全局變量a在程序B中也可以使用吧。那樣程序B不就把程序A給綠了。這不和諧。

那這樣,只要有用戶在訪問我的網(wǎng)站,我的程序A就一直運(yùn)行,全局變量a就一直在作用域內(nèi),一直都是有效的。這樣不行嗎?

不行。為什么不行?因?yàn)槟銢]法保證“程序A一直運(yùn)行”。

在我們自己的電腦上,我們打開一個程序,不自己關(guān)閉它,它就一直在內(nèi)存里,但是在服務(wù)器上不是。服務(wù)器會根據(jù)需要,隨時(shí)運(yùn)行你的代碼,當(dāng)然也可能會隨時(shí)關(guān)閉。這樣變量作用域就會時(shí)有時(shí)無,一會是True一會是False。這樣用戶可能一會可以看,一會又不能看。很麻煩。

那我們把這個變量保存在SQL中吧。需要驗(yàn)證權(quán)限的時(shí)候就讀取一下。

這樣是可行的,而且在你的網(wǎng)站僅有一個用戶的時(shí)候完全可行。這個唯一的用戶對應(yīng)唯一的變量,根據(jù)用戶是否登錄與登出改變變量的值。聽起來好慘

要是用戶多了就很麻煩。每個用戶都要維護(hù)這樣一個變量,有一種大炮打蚊子的感覺。

為什么會出現(xiàn)這種情況呢?因?yàn)樵诜?wù)器看來,使用web時(shí)保存變量是一個很浪費(fèi)的操作,為了實(shí)現(xiàn)高并發(fā),就很難有保存變量的效率空間了。二者不可得兼。web選擇了效率,因此它自己不會保存變量。這里談到的變量,專業(yè)詞匯應(yīng)該為“狀態(tài)”。web是無狀態(tài)的。

那我們要用什么方法實(shí)現(xiàn)權(quán)限檢查???也就是說,我們要用什么方法分別保存多個用戶的變量???

大多數(shù)web應(yīng)用開發(fā)框架都提供了一種名為session(會話)的技術(shù)來滿足這個需求。

可以把會話看作是無狀態(tài)web上面的一種狀態(tài)。

session的原理:服務(wù)器給用戶一小段認(rèn)證數(shù)據(jù),cookie,并且在服務(wù)器上建立一個與cookie對應(yīng)的一段認(rèn)證數(shù)據(jù),會話ID。這樣就可以讓每個用戶都有和服務(wù)器的唯一連接,可以持久儲存數(shù)據(jù),而且不同的用戶也不會混淆了。

2、session的機(jī)制

我們提供了一段成品代碼來演示session的會話機(jī)制如何工作。

from flask import Flask,session
 
app=Flask(__name__)
 
app.secret_key='YouWillNeverGuess'#秘密密鑰
 
@app.route('/setuser/<user>')
def setuser(user:str)->str:
    session['user']=user
    return 'User value set to: '+session['user']
 
 
@app.route('/getuser')
def getuser()->str:
    return 'User value is currently set to:'+session['user']
 
if __name__=='__main__':
    app.run(debug=True)

首先要從flask中導(dǎo)入session模塊。不用對session很害怕,可以把session看作是一個全局的python字典,我們用到的功能就是保存變量罷了。flask可以保證,無論應(yīng)用的代碼加載和卸載多少次,session中的變量都能夠一直存在。

其次,存儲在session中的所有數(shù)據(jù)都有一個唯一的瀏覽器cookie作為密鑰,這就確保了并不是web應(yīng)用的每一個用戶都能訪問你的會話數(shù)據(jù)。為了得到這些密鑰,我們需要為flask提供一個秘密密鑰作為種子,flask會用這個秘密密鑰加密所有的cookie,保護(hù)它不被外人刺探。也就是說,我作為一個用戶,我的會話數(shù)據(jù)保存在session中,因?yàn)槲矣衏ookie,所以我可以看我的數(shù)據(jù)。我的cookie又是經(jīng)過flask加密的,這樣就減少了泄露的概率。

在設(shè)置完秘密密鑰之后,就可以直接把session當(dāng)做一個字典使用了。

@app.route('/setuser/<user>')

這行代碼看起來很奇怪,因?yàn)橹拔覀兊膗rl中都沒有尖括號包括的代碼。它的用途是希望用戶提供一個值來賦給user變量。得到user變量后,會在setuser函數(shù)中使用該變量,并在字典中保存這個值。之后但會保存成功的提示。

注意一點(diǎn),雖然所有用戶共用這一行保存變量的代碼,而且表面上看session字典中的鍵都是user,但并不表示字典中只有一個名為user的鍵,這個鍵只有一個值。實(shí)際上,不同的用戶有不同的鍵值,但是在這里不需要考慮鍵名問題,只需要當(dāng)它是僅為一個用戶服務(wù)的就好,其他的由session自己完成。

如果我們想查看user這個變量現(xiàn)在的值,訪問/getuser這個url即可。它會調(diào)用一個函數(shù)訪問字典中的user鍵,并返回它的值。

既然我們已經(jīng)可以實(shí)現(xiàn)變量的保存和讀取了,那么接下來可以進(jìn)行測試了。

首先注意一點(diǎn),web服務(wù)器把一個瀏覽器當(dāng)做一個用戶,因此你電腦上的firefox和chrome會被認(rèn)作是兩個用戶。這樣就很容易測試了:我們打開多個瀏覽器分別訪問不同的/setuser/<user>,就會保存多個user值,然后在分別查看,如果它們的值不同,就說明已經(jīng)成功的進(jìn)行了分別保存。

效果如下:


首先設(shè)置第一個user為hry。使用chrome瀏覽器。
然后設(shè)置第二個user為chy,使用firefox。
設(shè)置第三個user為chy,使用edge。

接下來分別查看這三個user的值,如下:


image

image

image

可以看出,不同的瀏覽器訪問相同的網(wǎng)址,得到了不同的結(jié)果,說明變量保存成功,并且未發(fā)生混淆。

3、用session來控制登錄

使用一個簡單的實(shí)驗(yàn)代碼來進(jìn)行我們的探索:

from flask import Flask,session
 
app=Flask(__name__)
 
app.secret_key='YouWillNeverGuess'
 
@app.route('/')
def hello()->str:
    return 'Hello from the simple webapp.'
 
@app.route('/page1')
def page1()->str:
    return 'This is page 1.'
 
@app.route('/page2')
def page2()->str:
    return 'This is page 2.'
 
@app.route('/page3')
def page3()->str:
    return 'This is page 3.'
 
if __name__=='__main__':
    app.run(debug=True)

可以看到,代碼創(chuàng)建了四個url。我們希望page1、page2、page3都只對登錄用戶可見。因此,我們首先要寫一個登錄頁面。如下:

@app.route('/login')
def do_login()->str:
    session['logged_in']=True
    return 'You are now logged in'

很簡單,實(shí)現(xiàn)的功能就是如果你訪問了該url,則置session字典中的logged_in為True,并返回一個已登錄的提示。

接下來寫一個注銷頁面,如下:

@app.route('/logout')
def do_logout()->str:
    #session['logged_in'].clear()
    session.pop('logged_in')
    return 'You are now logged out.'

注銷邏輯有兩種:第一種是將logged_in的值由True改成False,第二種是直接刪去logged_in。在這里我們選擇后者。原因稍后再講。另外注意一點(diǎn),python中,刪除字典的某一項(xiàng)使用的方法為pop,刪除字典內(nèi)所有數(shù)據(jù)才會用clear。

然后是一個查看當(dāng)前狀態(tài)的頁面:

@app.route('/status')
def check_status()->str:
    #if session['logged_in']==True:
    if 'logged_in' in session:
        return 'You are currently logged in.'
    return 'You are NOT logged in.'

也有兩種邏輯:第一種是判斷鍵值是否為True,第二種是判斷鍵值是否存在。同樣選擇第二種。為什么都選第二種呢?

因?yàn)閜ython的字典機(jī)制:如果字典中某個鍵名不存在,就不能檢查它的值。注銷和查看狀態(tài)中的第一種方法都會檢查字典中某一鍵名的值,但是如果鍵名不存在呢?那樣程序就會崩潰。而且我們無法要求用戶一定是先訪問login再訪問status或logout,一旦不按這種順序訪問,網(wǎng)站就會出錯,也不會返回You are NOT logged in的信息,那樣就很不喜人。

因此我們選擇直接判斷是否存在,這樣就避免了檢查鍵值的操作。

接下來開始測試:

首先我們不登錄:


image

返回正確。

接下來我們登錄:


image

看狀態(tài):


image

注銷:
image

看狀態(tài):


image

這樣就簡單實(shí)現(xiàn)了登陸的操作。

4、引入函數(shù)修飾符

在3中,我們實(shí)現(xiàn)了登陸與注銷,對于status,登陸與注銷后,它顯示的內(nèi)容不同,這就是限制訪問url的雛形。現(xiàn)在想要實(shí)現(xiàn)對所有三個page實(shí)現(xiàn)這一點(diǎn)。

當(dāng)然很容易,把status中的check_status代碼在這三個url下面復(fù)制一下不就好了。

這樣是好了,但也沒好。為什么呢?

這樣做難以維護(hù),想一下要是我們要想改一下鍵名,或者更改返回的信息,那該多麻煩!

那把這寫代碼單獨(dú)拎出來怎么樣?寫一個函數(shù),這三個url下面只用一行代碼,調(diào)用這個函數(shù)如何?

本質(zhì)上沒有區(qū)別,后者就是把復(fù)制黏貼幾行代碼變成了復(fù)制黏貼一行代碼,仍然難以維護(hù),而且這兩者都會讓代碼真正要做的工作變得模糊:page1本來是返回一行通知的,你加的這個函數(shù)干嘛用啊?劣化了代碼的可讀性。

如果有一種方法,可以以某種方式為函數(shù)添加一個功能,比如說為page1、page2、page3這三個函數(shù)增加一個相同的檢查狀態(tài)功能就好了。為函數(shù)增加額外功能,這就是函數(shù)修飾符做的事情。

利用修飾符,可以用額外的代碼增強(qiáng)現(xiàn)有的函數(shù),從而改變現(xiàn)有函數(shù)的行為而不必修改它的代碼。

也就是說,我現(xiàn)在有一個高達(dá),給他加個噴氣模塊就能飛,而不用把它大卸八塊從內(nèi)而外的改造才能飛,這個噴氣模塊就是函數(shù)修飾符。

我們之前就有用過函數(shù)修飾符:所有的函數(shù)修飾符前面都有一個@作為前綴,它們很容易發(fā)現(xiàn)。

接下來我們將創(chuàng)建一個自己的函數(shù)修飾符。

5、創(chuàng)建函數(shù)修飾符的鋪墊

創(chuàng)建函數(shù)修飾符,需要我們了解三個問題:

如何把一個函數(shù)作為參數(shù)傳遞到另一個函數(shù)?

如何從函數(shù)返回一個函數(shù)?

如何處理任意數(shù)量和類型的函數(shù)參數(shù)?

首先來解決第一個問題:如何把一個函數(shù)作為參數(shù)傳遞到另一個函數(shù)?

咋一眼好像很奇怪?函數(shù)的參數(shù)也可以是函數(shù)嗎?當(dāng)然可以。python中的一切都是對象,函數(shù)自然也是對象,可以通過下面的試驗(yàn)代碼證明這一點(diǎn):


image

hello是一個函數(shù),id是另外一個函數(shù),我們調(diào)用id(hello)也會得到一個結(jié)果,而不會報(bào)錯。這里hello就是id的參數(shù)。但是注意一點(diǎn),雖然hello是id的參數(shù),但是id(hello)并沒有調(diào)用hello,而只是返回了一個地址,實(shí)際上,函數(shù)可以選擇是否調(diào)用它的函數(shù)參數(shù)。我們下面寫一個調(diào)用函數(shù)參數(shù)的函數(shù)apply。如下:

def apply(func:object,value:object)->object:
    return func(value)

它的第一個參數(shù)func就是一個函數(shù)對象,這里的注解object可以幫助理解這一點(diǎn),第二個參數(shù)value也是一個對象。雖然它們類型相同,但是通過名字可以看出,第一個參數(shù)應(yīng)該是一個函數(shù),而第二個參數(shù)應(yīng)是第一個函數(shù)參數(shù)所需要的參數(shù)。這是約定俗成的起名方法。效果如下:


image

可以看到apply的確調(diào)用了它的函數(shù)參數(shù),最后一個我是故意這么寫的,有點(diǎn)好奇它會不會死循環(huán)。

然而知道參數(shù)可以是函數(shù)有什么用呢?這個問題暫時(shí)按下不表。接下來看第二個問題:如何從一個函數(shù)返回一個函數(shù)?

如果之前只學(xué)過C的話,這是不可想象的。C中return只能是一個值,連數(shù)據(jù)結(jié)構(gòu)都不可以,怎么還可以是一個函數(shù)呢?的確可以。

為了從函數(shù)返回一個函數(shù),首先來學(xué)習(xí)一下嵌套函數(shù)的知識。

嵌套函數(shù),顧名思義,就是在一個函數(shù)中再定義一個函數(shù),舉例如下:

def outer():
    def inner():
        print("This is inner.")
 
    print("This is outer.")
    inner()

在outer內(nèi)定義一個函數(shù)inner,然后就可以在outer內(nèi)調(diào)用這個inner函數(shù)了。注意inner的作用域僅在outer內(nèi)部,你在外面是無法調(diào)用inner的。

這有什么用呢?我把inner的代碼寫在調(diào)用的地方不就好了。

的確是這樣。但是有一個問題,我們上文說過,函數(shù)可以作為返回值,你如何返回一大堆代碼呢?這不可能吧。

如果你想返回一個函數(shù)中的一部分代碼,一個做法就是把這個函數(shù)中要返回的這部分代碼打包成一個函數(shù),然后返回這個函數(shù)。

仍然以上面的函數(shù)為例,想返回inner。如下:

def outer():
    def inner():
        print("This is inner.")
 
    print("This is outer.")
    return inner

那么運(yùn)行它會發(fā)生什么呢?如下:


image

直接運(yùn)行outer。返回inner的類型和地址。因?yàn)閛uter有返回值,但我們沒有指定返回值賦給誰,所以會出現(xiàn)這種情況。

將outer的返回值賦給i。然后輸入i,情況和上面的類似。

輸入i(),這時(shí)才會調(diào)用inner。說明只有加上括號了,才能夠調(diào)用函數(shù)對象。只使用函數(shù)名就只是返回函數(shù)對象的類型和地址。

要想調(diào)用outer可以直接調(diào)用inner,只需要在return后面加上括號就行了。

這樣就體現(xiàn)出嵌套函數(shù)的作用了。

第三個問題:如何處理任意數(shù)量和類型的函數(shù)參數(shù)?

假設(shè)我現(xiàn)在有一個函數(shù),它可以接受任意多個參數(shù),例如沒有參數(shù),一個參數(shù),9527個參數(shù)。如何實(shí)現(xiàn)呢?總不可能是對每種情況分別寫一個函數(shù)吧。

python解決這一問題的方法是傳入一個參數(shù)元組,元組內(nèi)保存參數(shù)。元組內(nèi)的元素?cái)?shù)量可以是任意個,因此函數(shù)可以接收任意個參數(shù)。這里使用*代表任意數(shù)量,如下:

def myfunc(*args):
    for a in args:
        print(a,end=' ')
    if args:
        print()

其運(yùn)行效果如下:


image

注意,我們是直接輸入?yún)?shù)的,而不需要自己先聲明一個元組,初始化,然后再將元組作為參數(shù)輸入。將多個參數(shù)變?yōu)樵M這一步是由解釋器完成的,我們在調(diào)用時(shí)只需要把它看作是能夠接收任意個參數(shù)即可。而且類型不限哦。

那么問題來了,我提供一個列表行不行。比如說我在上文得到了一個列表,我想用這個函數(shù)處理列表中所有元素,難不成還得一個個拆開?當(dāng)然不用,要想處理列表中的所有參數(shù),只需要在調(diào)用時(shí),列表參數(shù)前面加一個*即可,函數(shù)會自動展開這個列表。

如下:


image

可以很明顯的看出來,前者是直接打印列表,也就是把列表當(dāng)做一個元素打印;后者則是把列表展開后一個個打印元素。

現(xiàn)在我們更貪心了,可不可以直接指定函數(shù)內(nèi)的若干個變量,直接讓參數(shù)賦給變量呢?畢竟,如果一個函數(shù)的參數(shù)太多的話,記住參數(shù)順序也有點(diǎn)煩,要是可以直接指定,那不就不用記住順序了。

可以的,我們在寫vsearch4web函數(shù)里就用過這種調(diào)用方式,這稱為接收一個函數(shù)字典。要想讓函數(shù)接收一個參數(shù)字典,需要在參數(shù)前面加兩個*。如下:

def myfunc(*args):
    for a in args:
        print(a,end=' ')
    if args:
        print()
 
def myfunc2(**kwargs):
    for k,v in kwargs.items():
        print(k,v,sep='->',end=' ')
    if kwargs:
        print()

這里的myfunc2就可以接收參數(shù)字典。效果如下:


image

但是注意,這種接收參數(shù)的方式只能用于字典,而不能給函數(shù)里面的某一個特定參數(shù)指定賦值,如下:

def myfunc2(**kwargs):
    print(sec)
    for k,v in kwargs.items():
        print(k,v,sep='->',end=' ')
    if kwargs:
        print()

報(bào)錯如下圖:


image

注意這里報(bào)的錯指的是print(sec)這一行的sec未定義,說明參數(shù)中的sec沒有被正確賦值,而是被加入到了參數(shù)字典中。若想指定sec,需要在定義函數(shù)時(shí)把參數(shù)更改如下:

def myfunc2(sec,**kwargs):
    print(sec)
    for k,v in kwargs.items():
        print(k,v,sep='->',end=' ')
    if kwargs:
        print()

運(yùn)行如下:

image

注意sec在定義函數(shù)中必須在kwargs之前,否則會報(bào)錯。在運(yùn)行的時(shí)候就沒有關(guān)系了。另外,在調(diào)用這個函數(shù)的時(shí)候也可以用直接傳入一個字典,之前傳送SQL配置的時(shí)候就用過,在這里不再贅述。

現(xiàn)在總結(jié)一下,若干參數(shù)的傳入其實(shí)就是在函數(shù)中先定義一個列表或者字典,然后用args或者*kwargs來填這個字典。

最后,我們可以把這兩者結(jié)合,傳入一個列表,再傳入一個字典,如下:

def myfunc3(*args,**kwargs):
    if args:
        for a in args:
            print(a,end=' ')
    if kwargs:
        for k,v in kwargs.items():
            print(k,v,sep='->',end=' ')

效果如下:


image

沒指定的默認(rèn)放在args中,指定的就放在kwargs中。

6、創(chuàng)建函數(shù)修飾符

前面鋪墊了這么一大堆,有什么用呢?在這里就可以描述一下函數(shù)修飾符的功能了:

函數(shù)修飾符的參數(shù)是一個函數(shù),它會自己定義一個函數(shù),稱作wrapper(包裝),在自己定義的這個函數(shù)里調(diào)用參數(shù)函數(shù),然后返回定義函數(shù)。也就是說,自己定義的這個函數(shù)wrapper,就是把參數(shù)函數(shù)包裝了一下,然后返回。因此需要用到以上的三大功能:

以函數(shù)為參數(shù),從而使得我們可以傳入一個函數(shù);

可以返回一個函數(shù),從而使得我們能夠得到新的函數(shù);

可以傳入任意參數(shù),使得wrapper能夠包裝任何函數(shù)。

下面開始創(chuàng)建函數(shù)修飾符。它本質(zhì)上還是一個函數(shù),它必須維護(hù)被修飾函數(shù)的簽名。什么叫被修飾函數(shù)的簽名?它返回的函數(shù)要和被修飾函數(shù)有同樣的參數(shù),個數(shù)和類型都得相同,參數(shù)的個數(shù)和類型就叫簽名。

代碼如下:

from flask import session
from functools import wraps
 
def check_logged_in(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        if 'logged_in' in session:
            return func(*args,**kwargs)
        return "You are NOT logged in"
    return wrapper

首先是import,要從flask中引入session,這是要實(shí)現(xiàn)函數(shù)功能必須的;引入wraps是函數(shù)修飾符的要求,這個有點(diǎn)復(fù)雜,我們只需要知道要先用一個這個修飾符,然后定義wrap函數(shù)。

然后就可以定義我們自己的函數(shù)修飾符了,因?yàn)槭呛瘮?shù),所以也用def定義,參數(shù)只有一個,就是被修飾的函數(shù)。

接下來調(diào)用修飾符,并定義wrap函數(shù),為了使其具有通用性,要用args和*kwargs來讓其可以接收任意參數(shù)。接下來是重頭戲,要給參數(shù)函數(shù)增加的功能就寫在這里面。我們的功能就是判斷字典中有沒有登錄記錄,有的話,就返回參數(shù)函數(shù),注意這里是有括號的,因此是調(diào)用;沒有的話,就返回通知。

最后返回定義的函數(shù)即可。這里沒有括號,因此不是調(diào)用。因?yàn)槲覀冃揎椧粋€函數(shù),要得到的應(yīng)該是一個新的函數(shù)對象,而不是直接就讓被修飾函數(shù)調(diào)用。

用修飾符修飾函數(shù)使得對page1、2、3進(jìn)行限制訪問的代碼如下:

from flask import Flask,session
from checker import check_logged_in
 
app=Flask(__name__)
 
app.secret_key='YouWillNeverGuess'
 
@app.route('/login')
def do_login()->str:
    session['logged_in']=True
    return 'You are now logged in'
 
@app.route('/logout')
def do_logout()->str:
    #session['logged_in'].clear()
    session.pop('logged_in')
    return 'You are now logged out.'
 
@app.route('/status')
def check_status()->str:
    #if session['logged_in']==True:
    if 'logged_in' in session:
        return 'You are currently logged in.'
    return 'You are NOT logged in.'
        
 
@app.route('/')
def hello()->str:
    return 'Hello from the simple webapp.'
 
@app.route('/page1')
@check_logged_in
def page1()->str:
    return 'This is page 1.'
 
@app.route('/page2')
@check_logged_in
def page2()->str:
    return 'This is page 2.'
 
@app.route('/page3')
@check_logged_in
def page3()->str:
    return 'This is page 3.'
 
if __name__=='__main__':
    app.run(debug=True)

先引入修飾符,再添加三行@即可,很方便。

7、更新我們的webapp代碼

from flask import Flask, render_template,request,redirect,escape,session
from vsearch import search4letters
from DBcm import UseDatabase
from checker import check_logged_in
 
app=Flask(__name__)
 
app.secret_key='YouWillNeverGuess'
 
app.config['dbconfig']={'host':'127.0.0.1',
                        'user':'vsearch',
                        'password':'vsearchpasswd',
                        'database':'vsearchlogDB',}
 
 
def log_request(req:'flask_request',res:str)->None:
    #with open('vsearch.log','a') as log:
        #print(req.form,req.remote_addr,req.user_agent,res,file=log,sep='|')
    with UseDatabase(app.config['dbconfig']) as cursor:
        _INSERT="""insert into log
                (phrase,letters,ip,browser_string,results)
                values
                (%s,%s,%s,%s,%s)"""
        cursor.execute(_INSERT,(req.form['phrase'],
                                req.form['letters'],
                                req.remote_addr,
                                req.user_agent.browser,
                                res,))
 
 
@app.route('/search4',methods=['POST'])
def do_search() -> 'html':
    phrase=request.form['phrase']
    letters=request.form['letters']
    results=str(search4letters(phrase,letters))
    log_request(request,results)
    return render_template('results.html',
                           the_title='Here are your results',
                           the_phrase=phrase,
                           the_letters=letters,
                           the_results=results)
 
 
@app.route('/')
@app.route('/entry')
def entry_page() -> 'html':
    return render_template('entry.html',
                           the_title='Welcome to search4letters on the web!')
 
 
@app.route('/viewlog')
@check_logged_in
def view_the_log()->str:
    #contents=[]
    with UseDatabase(app.config['dbconfig']) as cursor:
        _SELECT="""select phrase,letters,ip,browser_string,results from log"""
        cursor.execute(_SELECT)
        contents=cursor.fetchall()
    titles=('Phrase','Letters','Remote_addr','User_agent','Results')
    return render_template('viewlog.html',
                           the_title='View Log',
                           the_row_titles=titles,
                           the_data=contents,)  
                           
 
@app.route('/login')
def do_login()->str:
    session['logged_in']=True
    return 'You are now logged in'
    
 
@app.route('/logout')
def do_logout()->str:
    #session['logged_in'].clear()
    session.pop('logged_in')
    return 'You are now logged out.'
    
 
@app.route('/status')
def check_status()->str:
    #if session['logged_in']==True:
    if 'logged_in' in session:
        return 'You are currently logged in.'
    return 'You are NOT logged in.'
    
    
if __name__=='__main__':
    from werkzeug.contrib.fixers import ProxyFix
    app.wsgi_app=ProxyFix(app.wsgi_app)
    app.run()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容