Flask學習「一」(按鈕,角色,菜單,用戶,權限)

FLASK學習

很榮幸有時間能靜下心來寫在這篇文章,前段時間寫了一些沒有營養(yǎng)的文章對那些關注我的同學來說非常抱歉,接下來的一段日子里會圍繞近期所做的Flask項目寫一系列的博客,以記錄自己的不足。
鑒于可能有些小白可能會看到這篇文章,于是我盡量寫的通俗易懂。
接下來進入正題,我這篇文章要寫的是一個系統(tǒng)的權限部分。權限的控制對于一個優(yōu)秀的系統(tǒng)來說至關重要,但是對于權限的設計和把空是比較麻煩的。
一般如果我們不考慮按鈕的話,邏輯大致如下:
把菜單和權限、權限用戶關聯(lián)起來。
1、用戶頁面,可以增刪改查,并且還要有一個分配權限的按鈕。
2、權限頁面,可以增刪改查,并且有一個分配用戶的按鈕和一個分配菜單的按鈕。
3、建立兩個表,分別為用戶權限表(保存用戶ID和權限ID)、權限菜單表(保存權限ID和菜單ID)。
4、當在用戶頁面中選中一個用戶,點擊用戶的“分配權限”按鈕時,打開展示所有權限的頁面(并把用戶ID傳進去),左邊展示所有還沒有分配的權限列表,右邊展現(xiàn)已經分配的權限列表,然后選擇需要分配的左邊權限后,點擊分配,把數據分配到右邊已分配的列表中,然后點擊“確定”按鈕,把用戶ID和選擇的權限ID保存到用戶權限表。
5、當在權限頁面選中一個權限,并點擊“分配用戶”時,處理方式和4相同,當選擇需要分配權限的用戶后,同樣把用戶ID和權限ID保存到用戶權限表。
6、當在權限頁面選中一個權限,并點擊“分配菜單”時,打開一個樹展現(xiàn)所有菜單的頁面,每個樹節(jié)點前面有一個復選框,并把這個權限已經分配的樹默認選中,然后在要分配的菜單節(jié)點樹前面的復選框上選中,最后保存數據,把權限Id和所有選中的菜單ID保存到權限菜單表。
7、當用戶登陸系統(tǒng)的時候,首先檢查用戶輸入的口令信息,如果口令正確,再根據用戶倒查用戶權限表,再通過用戶權限表查到的權限,到權限菜單表查詢相應的菜單,再把相應的菜單展示出來。
上面便是不考慮按鈕的情況下的業(yè)務邏輯,其實加上按鈕的話也是差不多的,因為按鈕隸屬于菜單,只有給某個用戶分配了某個角色,這個用戶才能在登錄的時候看到他所擁有角色對應下的菜單和按鈕,這樣即完成了角色的權限控制。
接下來開始我們的項目。
首先根據上面的業(yè)務描述,我們大概可以用到的表和字段如下:
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,這幾張表的關系大概如上吧。
大概邏輯有了,現(xiàn)在開始寫代碼:

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

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

根據user_id查詢 required=True為必填項

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

用戶角色post新增/修改傳入參數

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

flask_restplus頁面展示url /flask路由注冊/需注冊到藍圖上

@api.route('/role_by_user')

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

class RoleByUser(Resource):
@api.expect(page_parser)
‘’‘
查詢已經分配過角色的用戶 以用戶為主體
’‘’
def get(self):
# 自定義驗證傳入參數是否合法
form = RoleByUserForm().validate_for_api()
#實現(xiàn)代碼模塊化 將可復用查詢條件拆分出來 放在最后定義成了一個單獨的方法
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為可變參數,可以傳入元組/列表
.filter(
task_filter).order_by(
# sqlalchemy根據創(chuàng)建時間排序
text('role.create_time desc')
# paginate()分頁對象 傳入定義號的頁數
).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)
# 新增 一個用戶可能對應多個角色 傳入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列表中
# 調用我們自定義的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)

’‘’
權限設置
’‘’
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
})

接收的參數為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在此基礎上還提供了通過名稱訪問的方式
’‘’

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

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查詢拼接當前角色所能看到的菜單和按鈕
# 分別構造拼接菜單和按鈕樹形集合 并將菜單和按鈕的樹形合并
menu_action_trees = _menu_tree(role_id, menus)
menu_action_trees += _action_tree(role_id, actions, menus)
# 通過自定義get_tree方法將最后合并好的數據集合轉化為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
# 按照前面定義命名元組參數順序按照順序傳入對應參數
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:
# 通過列表推導式判斷有無選中
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------------------------------------#

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

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

相關閱讀更多精彩內容

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

友情鏈接更多精彩內容