關於自動化平臺的動態選單設計

jeanron100發表於2018-01-06

最近幾天是馬不停蹄的做一些事情,今天計劃把通用功能的部分先改進一些,比如說選單的許可權配置。目前使用的方案是使用者可以看到所有的選單,如果沒有許可權,則返回一個許可權不足的頁面。

因為整個開發後面會有多個模組,而且有些功能其實放在一起會看起來有些龐大,所以我們可以做裁剪,就是你登入以後,你能看到你擁有許可權的選單。

事情可以天馬行空的想,但是落到實處做的時候,發現還是有一些差別。比如我考慮了幾個方案:

  1. 重寫目前的登入校驗邏輯,不使用預設的admin模板的使用者

  2. 複用已有的使用者模型,然後新增幾個定製欄位來滿足需求

根據目前的功能實現,如果重寫user的模型,會有較大的難度,所以第一點可行但是代價太高,第二點的考慮是使用者的密碼校驗一個是預設實現的邏輯,程式碼改動後會不穩定而且改動效率較低,因為原來的使用者是一個抽象類,需要重新改動,代價也不低。

所以,思來想去,怎麼做都代價不小,於是乎,換了一個思路。原來的邏輯是靜態的,根據提供的選單列表來得到一些可選的許可權,我們可以保留這個邏輯,重新定製一下選單的部分,選單和使用者為多對多的關係,原來的使用者表也不用改動,只需要定製關係表就可以了。

關於自動化平臺的動態選單設計

所以權衡再三的想法是如果沒許可權就不要直接看到選單,這部分的關係在多對多的對映中體現。

到了這個程度,其實就離具體實現不遠了,我們繼續來理一下。

系統中預設建立的使用者只有普通許可權,需要登入到系統啟用才可以使用,所以我們的系統的想法是不要求一步到位,類似於邀請制,指定的使用者才可以配置相應的許可權,所以不會出現一下子建立出來多個超級使用者的情況,原來的邏輯就不用改動,繼續保留。

在這個基礎上配置一個選單管理頁面,把每個選單的標題,url,對映資訊都和使用者對映起來。

前端顯示的部分則透過首頁的index.html根據使用者的資訊動態匹配得到一個較新的選單列表,意味著每個人看到的選單可能不同。

使用者和選單之間是多對多的關聯關係

所以Django中的models.py的內容如下:

class Menu(models.Model):

menu_type_choices = (

( 'dashborad', u'平臺看板') ,

( 'cmdb', u'資產管理') ,

( 'env_deploy', u'環境部署') ,

( 'backup', u'資料備份') ,

( 'recover', u'資料恢復') ,

( 'scheduler', u'任務排程') ,

( 'global_config', u'全域性配置') ,

( 'user_center', u'使用者中心') ,

)

server_status_choices = (

( '1', 'valid') ,

( '0', 'invalid') ,

)

menu_id = models.SmallIntegerField( primary_key= True,verbose_name= '選單ID')

menu_title = models.CharField( max_length= 50,verbose_name= '選單標題')

menu_url = models.CharField( max_length= 50,verbose_name= '選單URL',unique= False)

menu_level = models.SmallIntegerField( blank= True,null= False,verbose_name= '選單等級')

menu_parent_level = models.SmallIntegerField( blank= True,null= False,verbose_name= '選單父等級')

menu_style = models.CharField( max_length= 50,verbose_name= '選單選用的元件風格')

menu_type = models.CharField( max_length= 50,choices=menu_type_choices ,null= False,verbose_name= '選單大類')

create_date = models.DateTimeField( auto_now_add= True)

update_date = models.DateTimeField( auto_now_add= True)

user_role = models.ManyToManyField(User)

menu_status = models.SmallIntegerField( choices=server_status_choices ,null= False,verbose_name= '選單狀態')

class Meta:

db_table = 'menu'

permissions = (

( "can_read_menu", "讀取選單許可權") ,

( "can_change_menu", "更改選單許可權") ,

( "can_add_menu", "新增選單許可權") ,

( "can_delete_menu", "刪除選單許可權") ,

)

verbose_name = '選單配置'

verbose_name_plural = '選單配置'

ORM對映生成的SQL類似下面的形式:

BEGIN;

CREATE TABLE `menu` (`menu_id` smallint NOT NULL PRIMARY KEY , `menu_title` varchar( 50) NOT NULL , `menu_url` varchar( 50) NOT NULL , `menu_level` smallint NULL , `menu_parent_level` smallint NULL , `menu_style` varchar( 50) NOT NULL , `menu_type` smallint NOT NULL , `create_date` datetime( 6) NOT NULL , `update_date` datetime( 6) NOT NULL , `menu_status` smallint NOT NULL);

CREATE TABLE `menu_user_role` (` id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY , `menu_id` smallint NOT NULL , `user_id` integer NOT NULL , UNIQUE (`menu_id` , `user_id`));

ALTER TABLE `menu_user_role` ADD CONSTRAINT `menu_user_role_menu_id_15dc921823a77a3f_fk_menu_menu_id` FOREIGN KEY (`menu_id`) REFERENCES `menu` (`menu_id`);

ALTER TABLE `menu_user_role` ADD CONSTRAINT `menu_user_role_user_id_52439a830d70516d_fk_auth_user_id` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (` id`);

COMMIT;

有了這些基本的設計,我們來模擬測試一下,當前的menu為空。

>>> from django.contrib.auth.models import User

>>> from db_ops.models import Menu

>>> Menu.objects.all()

( 0.001) QUERY = u'SELECT "menu"."menu_id", "menu"."menu_title", "menu"."menu_url", "menu"."menu_level", "menu"."menu_parent_level", "menu"."menu_style", "menu"."menu_type", "menu"."create_date", "me

nu "."update_date ", "menu "."menu_status " FROM "menu " LIMIT 21' - PARAMS = (); args=()

[]

我們呼叫API生成一個user,一個menu資訊。

User.objects.create(password= 'admin2',is_superuser= 0,username= 'admin2',first_name= 'admin',last_name= 'admin',email= 'aa@aa.com',is_staff= 0,is_active= 1,date_joined= '2018-01-04')

Menu.objects.create( menu_id= 1,menu_title= '後設資料管理',menu_url= 'cmdb_manage',menu_level= 2,menu_parent_level= 1,menu_type= 'cmdb',create_date= '2018-01-04',update_date= '2018-01-04',menu_status= 1)

如果要做多對多的對映,則是使用add方法,注意生成的insert語句。

user = User.objects.get( pk= 2)

menu = Menu.objects.get( pk= 1) >>> menu.user_role.add(user)

( 0.000) QUERY = u'BEGIN' - PARAMS = (); args= None

( 0.000) QUERY = u'SELECT "menu_user_role"."user_id" FROM "menu_user_role" WHERE ("menu_user_role"."menu_id" = %s AND "menu_user_role"."user_id" IN (%s))' - PARAMS = ( 1, 2); args=( 1, 2)

( 0.081) QUERY = u'INSERT INTO "menu_user_role" ("menu_id", "user_id") SELECT %s AS "menu_id", %s AS "user_id"' - PARAMS = ( 1, 2); args=( 1, 2)

如果要查詢中間表的資料,可以做關聯,比如下面的形式。

>>>User.objects.filter( menu__user_role= 1)

後續繼續補充完善。

關於自動化平臺的動態選單設計

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/23718752/viewspace-2149781/,如需轉載,請註明出處,否則將追究法律責任。

相關文章