基于 oracle 的 flask 項(xiàng)目(二)——用戶(hù)登錄

flask 相對(duì)于很多國(guó)企的 oracle 數(shù)據(jù)庫(kù)而言,是比較新的,因此很多古老的設(shè)計(jì)并不一定適合較新的 flask 的標(biāo)準(zhǔn),但作為后來(lái)者,你得向前兼容,你得適應(yīng)需求。
本章內(nèi)容就來(lái)解釋一下上一章——基于 oracle 的 flask 項(xiàng)目(一)——配置項(xiàng)目留下的彩蛋——數(shù)據(jù)庫(kù)到底留下了什么樣的坑?

項(xiàng)目描述

絕對(duì)大多數(shù)的網(wǎng)站需要管理功能的,這個(gè)功能是不對(duì)外開(kāi)放,需要有權(quán)限的用戶(hù)登錄后才能操作的。這個(gè)登錄功能對(duì)于大牛來(lái)說(shuō),肯定是操作 session, cookies 了,其實(shí)不必這么麻煩, flask-login 插件可以解決你的登錄問(wèn)題,但是要注意一些小細(xì)節(jié),本章內(nèi)容就是來(lái)討論一些細(xì)節(jié)內(nèi)容。

  • 本章主要幫你解決你的項(xiàng)目的 DBA 管理員沒(méi)有給你設(shè)置主鍵,主鍵也不是 id 的問(wèn)題。

創(chuàng)建登錄模板

  1. 在 app/templates/show 目錄下新建一個(gè) login.html 頁(yè)面。
  2. 同時(shí),對(duì)模板進(jìn)行分割,創(chuàng)建文件夾 common , 將主模板 base.html,提示模板 alert.thml 、頭文件 header.html,側(cè)邊欄 sidebar.html 等共用文件,都放入該文件夾。
  3. 其中使用到一些 css 和 js 的內(nèi)容,需要放在藍(lán)圖文件夾下的 static 文件夾里面。
  4. 個(gè)性化你的 web 主頁(yè),設(shè)置你的 index.html。

使用數(shù)據(jù)庫(kù)及映射類(lèi)

使用數(shù)據(jù)庫(kù)及映射類(lèi)的時(shí)候,需要用到 sqlalchemy 第三方庫(kù), flask 也提供了一個(gè)封裝好的插件 flask-sqlalchemy ,拿來(lái)使用即可。但是要使用 sqlalchemy, 在創(chuàng)建映射類(lèi)的時(shí)候,必須得設(shè)置主鍵。但是很多前輩的 DBA 們的眼里可是沒(méi)有 flask 的概念的,很多表是沒(méi)有設(shè)置主鍵的,我們?cè)撛趺崔k?

答:先對(duì) DBA 管理員翻個(gè)白眼,然后自己默默的做事吧。還能怎么辦呢?。?!
找個(gè)具有唯一性,不重復(fù)的字段,在創(chuàng)建映射類(lèi)的時(shí)候,把該字段定義為主鍵。也就是說(shuō),不管數(shù)據(jù)庫(kù)中是否定義了主鍵,只要映射類(lèi)種定義了即可。
當(dāng)然,有的同學(xué)會(huì)說(shuō),我們找不到具有唯一性,不重復(fù)的字段,該怎么辦?那就繼續(xù)給你的 DBA 管理員翻白眼唄,翻到他清醒為止。

還好,我的項(xiàng)目中的數(shù)據(jù)庫(kù)還是有主鍵的,無(wú)需在映射類(lèi)種,建立虛假的主鍵。

內(nèi)容不在贅述,請(qǐng)參加代碼。

設(shè)置 flask-login

初始化 flask-login

app/__init__.py 里進(jìn)行設(shè)置:

login_manager = LoginManager()
login_manager.session_protection = 'strong' # 可以設(shè)置None,'basic','strong'  以提供不同的安全等級(jí),一般設(shè)置strong,如果發(fā)現(xiàn)異常會(huì)登出用戶(hù)。
login_manager.login_view = 'show.login' # 這里填寫(xiě)你的登陸界面的路由

def create_app(config_name):
    """ 使用工廠函數(shù)初始化程序?qū)嵗?""
    ....
    login_manager.init_app(app=app)

很多時(shí)候,我們會(huì)遇到 remember_me 無(wú)效的情況,請(qǐng)將 login_manager.session_protection 設(shè)置成 basic 試試。

詳細(xì)設(shè)置請(qǐng)看程序注釋及源代碼02

配置 flask-login

models.py 中的用戶(hù)映射類(lèi)繼承 flask_login 中的 UserMixin 類(lèi),該類(lèi)實(shí)現(xiàn)了 4 個(gè)用戶(hù)方法,基本上能夠滿(mǎn)足用戶(hù)登錄的需求,如需其它的用戶(hù)方法,可自行定義。

class OusiStaff(UserMixin, db.Model):
    __tablename__ = 'ousi_staff'
    sid = db.Column(db.Integer, primary_key=True)
    department = db.Column(db.String(8))
    name = db.Column(db.String(8))
    password = db.Column(db.String(8))
    phone = db.Column(db.String(11))
    role = db.Column(db.String(8))

    def is_admin(self): # 自行定義的方法,用于權(quán)限判斷
        return self.role == 'admin'


class AnonymousUser(AnonymousUserMixin):
    '''
    繼承至該類(lèi)的用戶(hù)模型 將作為未登陸時(shí)的用戶(hù)模型,可以保持代碼的一致性。
    '''
    def is_admin(self): # 自行定義的方法,用于權(quán)限判斷
        return False


login_manager.anonymous_user = AnonymousUser

實(shí)現(xiàn)用戶(hù)的回調(diào)函數(shù)

也是在 models.py 里實(shí)現(xiàn):

@login_manager.user_loader
def load_user(user_id):
    return OusiStaff.query.get(int(user_id))

此處,不詳細(xì)講解,僅僅是實(shí)現(xiàn)了一個(gè)回調(diào)用戶(hù)的函數(shù)。

使用登錄權(quán)限限制

既然使用了登錄功能,那么肯定是有些內(nèi)容不能讓未登錄的用戶(hù)觀看,這就需要在試圖函數(shù)定義的時(shí)候加上一個(gè) login_required 裝飾器了。

這個(gè)功能的實(shí)現(xiàn)很簡(jiǎn)單,在 views.py 里進(jìn)行修改:

...
from flask_login import login_required, login_user, logout_user
...

@show.route('/', methods = ['GET', 'POST'])
@show.route('/index', methods = ['GET', 'POST'])
@login_required
def index():

    return render_template('show/index.html')

至此,你可以測(cè)試自己的項(xiàng)目了。


問(wèn)題

報(bào) NotImplementedError: No 'id' attribute - override 'get_id' 錯(cuò)誤的問(wèn)題

005.png

...

006.png

bug 如影隨從。這是你遇到的第一個(gè)錯(cuò)誤。你會(huì)發(fā)現(xiàn)登錄之后馬上就報(bào)這個(gè)錯(cuò)誤。那么兵來(lái)將擋水來(lái)土掩,找到問(wèn)題,解決問(wèn)題。

首先,報(bào)錯(cuò)的地點(diǎn)顯示是我們自己的代碼中的 login_user,而真正報(bào)錯(cuò)的地點(diǎn)是 minins.py 源碼中的 39 行,那么這一行的真面目是什么呢?

def get_id(self):
    try:
        return text_type(self.id)
    except AttributeError:
        raise NotImplementedError('No `id` attribute - override `get_id`')

你可以清晰的看到,這個(gè) get_id 函數(shù)的返回值是 return text_type(self.id) 是當(dāng)前用戶(hù)的 id。那么我們的 models.py 中定義的 OusiStaff 類(lèi)有 id 這個(gè)字段嗎?沒(méi)有。只有一個(gè)作為主鍵的 sid 字段??吹竭@里,估計(jì)你會(huì)對(duì)以前的 DBA 管理員問(wèn)候很多聲了,OK,稍安勿躁,問(wèn)候了之后還得解決問(wèn)題。很直觀的解決方法是,修改源碼,將 return text_type(self.id) 修改為 return text_type(self.sid)。但是這樣的方法很危險(xiǎn),很造成一些其它項(xiàng)目的兼容性問(wèn)題。顯得這個(gè)程序員很 low。

再次我們用一種更好、更優(yōu)雅的方法來(lái)解決。在 models.py 文件內(nèi)的 OusiStaff 映射類(lèi)中添加如下內(nèi)容:

class OusiStaff(UserMixin, db.Model):
    ...
    ...
    
    @property
    def id(self):
        return self.sid

增加一個(gè) id 屬性。解決問(wèn)題。

神奇的 @property。 彩蛋就如此簡(jiǎn)單的被解決。

打開(kāi)頁(yè)面,顯示正常。

007.png

源碼下載

下節(jié)更精彩,我們將講解使用 flask-sqlalchemy 來(lái)生成相關(guān)報(bào)表。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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