Flask學(xué)習(xí)「一」(按鈕,角色,菜單,用戶,權(quán)限)

FLASK學(xué)習(xí)

很榮幸有時間能靜下心來寫在這篇文章拾氓,前段時間寫了一些沒有營養(yǎng)的文章對那些關(guān)注我的同學(xué)來說非常抱歉,接下來的一段日子里會圍繞近期所做的Flask項目寫一系列的博客商虐,以記錄自己的不足。
鑒于可能有些小白可能會看到這篇文章圈盔,于是我盡量寫的通俗易懂罢猪。
接下來進入正題觅廓,我這篇文章要寫的是一個系統(tǒng)的權(quán)限部分仙逻。權(quán)限的控制對于一個優(yōu)秀的系統(tǒng)來說至關(guān)重要茂翔,但是對于權(quán)限的設(shè)計和把空是比較麻煩的混蔼。
一般如果我們不考慮按鈕的話,邏輯大致如下:
把菜單和權(quán)限珊燎、權(quán)限用戶關(guān)聯(lián)起來惭嚣。
1、用戶頁面俐末,可以增刪改查料按,并且還要有一個分配權(quán)限的按鈕。
2卓箫、權(quán)限頁面载矿,可以增刪改查,并且有一個分配用戶的按鈕和一個分配菜單的按鈕。
3闷盔、建立兩個表弯洗,分別為用戶權(quán)限表(保存用戶ID和權(quán)限ID)、權(quán)限菜單表(保存權(quán)限ID和菜單ID)逢勾。
4牡整、當(dāng)在用戶頁面中選中一個用戶,點擊用戶的“分配權(quán)限”按鈕時溺拱,打開展示所有權(quán)限的頁面(并把用戶ID傳進去)逃贝,左邊展示所有還沒有分配的權(quán)限列表,右邊展現(xiàn)已經(jīng)分配的權(quán)限列表迫摔,然后選擇需要分配的左邊權(quán)限后沐扳,點擊分配,把數(shù)據(jù)分配到右邊已分配的列表中句占,然后點擊“確定”按鈕沪摄,把用戶ID和選擇的權(quán)限ID保存到用戶權(quán)限表。
5纱烘、當(dāng)在權(quán)限頁面選中一個權(quán)限杨拐,并點擊“分配用戶”時,處理方式和4相同擂啥,當(dāng)選擇需要分配權(quán)限的用戶后哄陶,同樣把用戶ID和權(quán)限ID保存到用戶權(quán)限表。
6啤它、當(dāng)在權(quán)限頁面選中一個權(quán)限奕筐,并點擊“分配菜單”時舱痘,打開一個樹展現(xiàn)所有菜單的頁面变骡,每個樹節(jié)點前面有一個復(fù)選框,并把這個權(quán)限已經(jīng)分配的樹默認(rèn)選中芭逝,然后在要分配的菜單節(jié)點樹前面的復(fù)選框上選中塌碌,最后保存數(shù)據(jù),把權(quán)限Id和所有選中的菜單ID保存到權(quán)限菜單表旬盯。
7台妆、當(dāng)用戶登陸系統(tǒng)的時候,首先檢查用戶輸入的口令信息胖翰,如果口令正確接剩,再根據(jù)用戶倒查用戶權(quán)限表,再通過用戶權(quán)限表查到的權(quán)限萨咳,到權(quán)限菜單表查詢相應(yīng)的菜單懊缺,再把相應(yīng)的菜單展示出來。
上面便是不考慮按鈕的情況下的業(yè)務(wù)邏輯培他,其實加上按鈕的話也是差不多的鹃两,因為按鈕隸屬于菜單遗座,只有給某個用戶分配了某個角色,這個用戶才能在登錄的時候看到他所擁有角色對應(yīng)下的菜單和按鈕俊扳,這樣即完成了角色的權(quán)限控制途蒋。
接下來開始我們的項目。
首先根據(jù)上面的業(yè)務(wù)描述馋记,我們大概可以用到的表和字段如下:
user表(id,name,tel,email,password) # 用戶表
role表(id,name,description) # 角色表
user_role表(id,user_id,role_id) # 用戶角色表
menu表(id,parent_id,lay,name,code,description) # 菜單表
action表(id,menu_id,name,code,description) # 按鈕表
role_menu(id,role_id,menu_id) # 角色菜單表
role_action(id,role_id,action_id) # 角色按鈕表

user_id
role_id
role_id
menu_id
role_id
action_id
menu_id
user
user_role
role
role_menu
menu
role_action
action
emmm号坡,這幾張表的關(guān)系大概如上吧。
大概邏輯有了梯醒,現(xiàn)在開始寫代碼:

----------------------------------start-----------------------------------#

'''
我們的框架使用Flask+sqlalchemy+flask_restplus
sqlalchemy為ORM數(shù)據(jù)庫映射 PS:sqlalchemy真的非常強大 使用起來非常方便
flask_restplus是swagger所呈現(xiàn)出來的一種網(wǎng)頁端接口測試工具 最大的有點是可以避免寫接口文檔
'''

根據(jù)user_id查詢 required=True為必填項

page_parser.add_argument('user_id', type=int, required=True, location='args')

用戶角色post新增/修改傳入?yún)?shù)

user_role_model = api.model('RoleUserRole', {
'role_id_list': fields.String('role id list 以逗號隔開","'),
'user_id': fields.Integer
})

flask_restplus頁面展示url /flask路由注冊/需注冊到藍(lán)圖上

@api.route('/role_by_user')

flask_restplus定義每一個類名展現(xiàn)在swagger的NameSpace上

class RoleByUser(Resource):
@api.expect(page_parser)
‘’‘
查詢已經(jīng)分配過角色的用戶 以用戶為主體
’‘’
def get(self):
# 自定義驗證傳入?yún)?shù)是否合法
form = RoleByUserForm().validate_for_api()
#實現(xiàn)代碼模塊化 將可復(fù)用查詢條件拆分出來 放在最后定義成了一個單獨的方法
task_filter = _form_and_task(form)
user_id = form.user_id.data
# 增加查詢條件
task_filter.append(UserRole.user_id == user_id)
page = Role.query.outerjoin(UserRole, Role.id == UserRole.role_id)
# task_filter為可變參數(shù)筋帖,可以傳入元組/列表
.filter(
task_filter).order_by(
# sqlalchemy根據(jù)創(chuàng)建時間排序
text('role.create_time desc')
# paginate()分頁對象 傳入定義號的頁數(shù)
).paginate()
return Role().page(page)

@api.route('/role_by_not_user')
class RoleByNotUser(Resource):
# 和上面的類似 查詢未分配角色的用戶
@api.expect(page_parser)
def get(self):
form = RoleByUserForm().validate_for_api()
task_filter = _form_and_task(form)
user_id = form.user_id.data
# UserRole.user_id != user_id 查詢未分配角色的用戶
task_filter.append(UserRole.user_id != user_id)
page = Role.query.outerjoin(UserRole, Role.id == UserRole.role_id)
.filter(*task_filter).order_by(
text('role.create_time desc')
).paginate()
return Role().page(page)
# 新增 一個用戶可能對應(yīng)多個角色 傳入role_id_list
@api.expect(user_role_model)
def post(self):
form = RoleUserPostForm().validate_for_api()
user_id, role_id_list = form.user_id.data, form.role_id_list.data
# 傳入role_id_list使用“,”分開 使用split從每個“,”處分開
if role_id_list:
user_role_list = []
role_id_list = role_id_list.split(',')
# 遍歷role_id_list 將每個role_id存入上面定義的user_role_list列表中
# 調(diào)用我們自定義的save_all方法 將每個role_id存入UserRole表
for role_id in role_id_list:
user_role = UserRole()
user_role.role_id = role_id
user_role.user_id = int(user_id)
user_role_list.append(user_role)
UserRole().save_all(user_role_list)

’‘’
權(quán)限設(shè)置
’‘’
role_action_menu_parser = reqparse.RequestParser()
role_action_menu_parser.add_argument('role_id', type=int, required=True, location='args')

menu_action_lists = api.model('RoleActionMenuList', {
'mid': fields.Integer,
'type': fields.Integer
})

接收的參數(shù)為menu_action_list和role_id,menu_action_list中存的是mid和type

這里的type是為了區(qū)分菜單和按鈕 0-菜單 1-按鈕

role_action_menu_model = api.model('RoleActionMenu', {
'menu_action_list': fields.List(fields.Nested(menu_action_lists)),
'role_id': fields.Integer
})

‘’‘
namedtuple(命名元組)是繼承自tuple的子類 namedtuple創(chuàng)建一個和tuple類似的對象 而且對象擁有可訪問的屬性
普通tuple類型的成員 只能通過索引訪問 namedtuple在此基礎(chǔ)上還提供了通過名稱訪問的方式
’‘’

我們使用一個命名元組來定義按鈕和菜單的樹形集合

menu_action_tree = namedtuple('MenuActionTree', ['id', 'name', 'parent_id', 'lay', 'is_select', 'has_child', 'type'])

@api.route('/role_action_menu')
class RoleActionMenu(Resource):
@api.expect(role_action_menu_parser)
‘’‘
查詢該角色所能查到的所有的菜單和按鈕
’‘’
def get(self):
form = RoleIdForm().validate_for_api()
role_id = form.role_id.data
menus = Menu.query.filter().all() # 菜單
actions = Action.query.filter().all() # 按鈕
# 通過自定義樹形菜單和按鈕列表冤馏,通過role_id查詢拼接當(dāng)前角色所能看到的菜單和按鈕
# 分別構(gòu)造拼接菜單和按鈕樹形集合 并將菜單和按鈕的樹形合并
menu_action_trees = _menu_tree(role_id, menus)
menu_action_trees += _action_tree(role_id, actions, menus)
# 通過自定義get_tree方法將最后合并好的數(shù)據(jù)集合轉(zhuǎn)化為json傳給前臺
tree = get_tree(menu_action_trees)
return tree
‘’‘
新增角色菜單和按鈕
’‘’
@api.expect(role_action_menu_model)
def post(self):
form = RoleMenuActionForm().validate_for_api()
role_id, menu_action_list = form.role_id.data, form.menu_action_list.data
# 過濾 區(qū)分菜單和按鈕
if menu_action_list:
role_action = list(filter(lambda x: x['type'] == 1, menu_action_list))
role_menu = list(filter(lambda x: x['type'] == 0, menu_action_list))
# 使用自定義方法分別儲存菜單和按鈕到role_menu和role_action表
with db.auto_commit():
_save_menu(role_id, role_menu)
_save_action(role_id, role_action)

存儲菜單

def _save_menu(role_id, role_menu):
# 每次在存之前我們先刪除該角色之前存儲過的菜單
RoleMenu.query.filter_by(role_id=role_id).delete()
new_role_menu = []
# 遍歷role_menu表 通過role_id將給該角色添加菜單
for m in role_menu:
role_menu = RoleMenu()
role_menu.role_id = role_id
role_menu.menu_id = m['mid']
new_role_menu.append(role_menu)
RoleMenu().save_all(new_role_menu)

存儲按鈕

def _save_action(role_id, role_action):
# 和菜單一樣 在新增之前我們要刪除之前存儲過的按鈕
RoleAction.query.filter_by(role_id=role_id).delete()
new_role_action = []
# 遍歷role_acton表通過role_id將給該角色添加角色
for m in role_action:
role_action = RoleAction()
role_action.role_id = role_id
role_action.action_id = m['mid']
new_role_action.append(role_action)
RoleAction().save_all(new_role_action)

拼接action樹形

def _action_tree(role_id, actions, menus):
menu_action_trees = []
role_actions = RoleAction.query.filter(UserRole.role_id == role_id).all()
for action in actions:
action_parent = list(filter(lambda x: x.id == action.menu_id, menus))
# 判斷層級
if len(action_parent) > 0:
lay = action_parent[0].lay + 1
else:
lay = 0
# 是否選中 1-選中 0-未選中
is_select = [False for role_action in role_actions if role_action.action_id == action.id]
if is_select:
is_select = 1
else:
is_select = 0
# 按照前面定義命名元組參數(shù)順序按照順序傳入對應(yīng)參數(shù)
mct = menu_action_tree(
str(action.id) + 'action', # 為了區(qū)分按鈕和菜單id使用action分割
action.name,
action.menu_id,
lay,
is_select,
0, # 按鈕是最后一級
1 # 0-菜單 1-按鈕
)

    menu_action_trees.append(mct)
return menu_action_trees

拼接菜單樹形

def _menu_tree(role_id, menus):
menu_action_trees = []
role_menus = RoleMenu.query.filter(UserRole.role_id == role_id).all()
for menu in menus:
# 通過列表推導(dǎo)式判斷有無選中
is_select = [False for role_menu in role_menus if role_menu.menu_id == menu.id]
if is_select:
is_select = 1
else:
is_select = 0
mct = menu_action_tree(
menu.id,
menu.name,
menu.parent_id,
menu.lay,
is_select,
0, # 不好判斷暫定為0
0 # 0-菜單 1-按鈕
)

    menu_action_trees.append(mct)
return menu_action_trees

通過姓名模糊查詢

def _form_and_task(form):
name = form.name.data
task_filter = [
Role.name.like('%' + name + '%') if name is not None else text(''),
]
return task_filter

----------------------------------end------------------------------------#

這樣我們就完成了按鈕日麸,角色,菜單逮光,用戶代箭,權(quán)限的校驗,文中少數(shù)自定義類或方法由于寫在了基類中涕刚,等到后面會慢慢列出嗡综。另外文章前面是以用戶為主體的角色綁定用戶,在用戶頁面還應(yīng)該有以角色為主體的用戶綁定角色杜漠,但是兩者都不盡相同极景,因此在本文中暫不列出,后續(xù)如果有需要的話再補上盼樟!
————————————————
版權(quán)聲明:本文為CSDN博主「DesolatePoison」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議晨缴,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_40695642/article/details/103414498

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末击碗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子们拙,更是在濱河造成了極大的恐慌稍途,老刑警劉巖砚婆,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡殊者,警方通過查閱死者的電腦和手機与境,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猖吴,“玉大人,你說我怎么就攤上這事海蔽。” “怎么了拗引?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵幌衣,是天一觀的道長。 經(jīng)常有香客問我豁护,道長,這世上最難降的妖魔是什么楚里? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮蝴光,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蔑祟。我一直安慰自己苏携,他們只是感情好做瞪,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著著拭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪乳蛾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天肃叶,我揣著相機與錄音,去河邊找鬼因惭。 笑死,一個胖子當(dāng)著我的面吹牛激率,可吹牛的內(nèi)容都是我干的勿决。 我是一名探鬼主播乒躺,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼低缩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了健爬?” 一聲冷哼從身側(cè)響起么介,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎壤短,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體久脯,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡纳胧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年帘撰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片核行。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡蹬耘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出综苔,到底是詐尸還是另有隱情位岔,我是刑警寧澤堡牡,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站晤柄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏抓于。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一捉撮、第九天 我趴在偏房一處隱蔽的房頂上張望妇垢。 院中可真熱鬧,春花似錦闯估、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至侠姑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間莽红,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工醉蚁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留柳畔,地道東北人。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓薪韩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親罗捎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容

  • wyun_guest需要添加的地方 1: oauth_clients需要添加一條記錄image.png INSER...
    EddieZhang閱讀 683評論 0 0
  • 回首頁 第一部分 Drupal簡介 Drupal overview A tour of Drupal fundam...
    王乂閱讀 2,037評論 0 9
  • 效果圖 這一章節(jié)完成后的效果如下: 1.普通用戶 2.管理者 Github鏈接 https://github.co...
    happyte閱讀 2,247評論 0 2
  • JSPXCMS開發(fā)架構(gòu)介紹 V1 – 架構(gòu)概述 基本概述 配置文件目錄 /src/main/resources/...
    Java_Evan閱讀 4,402評論 0 0
  • 不要勉強自己去和不同圈子的人相處夭禽。 你只需要不斷去學(xué)習(xí), 提高個人品質(zhì)讹躯、氣質(zhì)和魅力, 自然會融進屬于你的圈子骗灶, 交...
    墨脫4444閱讀 136評論 0 0