淺析 odoo用戶視圖中權(quán)限字段的實現(xiàn)

淺析 odoo用戶視圖中權(quán)限字段的實現(xiàn)

設(shè)置-用戶中編輯用戶信息是我們常用到的功能,但是當我們在開發(fā)者權(quán)限下查看用戶form視圖字段時,里面訪問權(quán)限字段設(shè)置卻顯示如下:

<group col="2">
        <separator colspan="2" string="應用程序"/>
        <field name="sel_groups_20_21_28" modifiers="{}"/>
        <newline/>
        <field name="sel_groups_3_10_11" modifiers="{}"/>
        <newline/>
        <field name="sel_groups_1_2" modifiers="{}"/>
        <newline/>
</group>

這確實很讓人困惑.

相關(guān)知識

在Ruter的這篇權(quán)限組的設(shè)置中介紹了權(quán)限組的實現(xiàn).
從這篇文章的實現(xiàn)中我們可以很直觀了解到在xml中設(shè)置model="ir.module.category"的record,用戶視圖中權(quán)限字段的顯示會多出一個新分類,在分類下可以選擇他們的子項(也就是model="res.groups"的record),當選擇完時,可以發(fā)現(xiàn)用戶被加入到了相關(guān)權(quán)限組中.
看到這里,也許你已經(jīng)大概猜到了我接下來要說什么了.

odoo用戶視圖中權(quán)限字段的實現(xiàn)

addons/base/res/res_users_view.xml中我們可以發(fā)現(xiàn)id="view_users_form"的record,是用戶表單視圖的基本實現(xiàn),通過對比源碼以及odoo開發(fā)者模式下的字段視圖獲取,他們之間的主要差別是源碼中的
<field name="groups_id"/>被替換成了開頭所說的讓人費解的字段.

在繼續(xù)從源碼中查找,我們還可以發(fā)現(xiàn)另外一段在同文件下繼承了id="view_users_form"的一個id="user_groups_view"的record.在這段record中,主要定義了

<field name="arch" type="xml">
    <!-- dummy, will be modified by groups -->
    <field name="groups_id" position="after"/>
 </field>

也就是說groups_id的字段的顯示部分已經(jīng)id="user_groups_view"被替換了.
從開發(fā)者模式下進入設(shè)置-頁面-視圖 中搜索這個id,可以發(fā)現(xiàn)這個視圖的結(jié)構(gòu)完全與被替換掉<field name="groups_id"/>的用戶視圖一致.

但是,在XML中,id = user_groups_view的record中并沒有定義出具體字段,那么odoo是如何精確的把相關(guān)分組設(shè)置加入到用戶的視圖呢?

addons/base/res/res_users.py中發(fā)現(xiàn)了一個繼承了res.groups的類GroupsView里面存在一個方法
_update_user_groups_view,在這個方法的介紹里,作者介紹了作用:

Modify the view with xmlid base.user_groups_view, which inherits
the user form view, and introduces the reified group fields.

下面給出這個方法的代碼:

    @api.model
    def _update_user_groups_view(self):
        """ Modify the view with xmlid ``base.user_groups_view``, which inherits
            the user form view, and introduces the reified group fields.
        """
        if self._context.get('install_mode'):
            # use installation/admin language for translatable names in the view
            user_context = self.env['res.users'].context_get()
            self = self.with_context(**user_context)

        # We have to try-catch this, because at first init the view does not
        # exist but we are already creating some basic groups.
        view = self.env.ref('base.user_groups_view', raise_if_not_found=False)
        if view and view.exists() and view._name == 'ir.ui.view':
            group_no_one = view.env.ref('base.group_no_one')
            xml1, xml2 = [], []
            xml1.append(E.separator(string=_('Application'), colspan="2"))
            for app, kind, gs in self.get_groups_by_application():
                # hide groups in categories 'Hidden' and 'Extra' (except for group_no_one)
                attrs = {}
                if app.xml_id in ('base.module_category_hidden', 'base.module_category_extra', 'base.module_category_usability'):
                    attrs['groups'] = 'base.group_no_one'

                if kind == 'selection':
                    # application name with a selection field
                    field_name = name_selection_groups(gs.ids)
                    xml1.append(E.field(name=field_name, **attrs))
                    xml1.append(E.newline())
                else:
                    # application separator with boolean fields
                    app_name = app.name or _('Other')
                    xml2.append(E.separator(string=app_name, colspan="4", **attrs))
                    for g in gs:
                        field_name = name_boolean_group(g.id)
                        if g == group_no_one:
                            # make the group_no_one invisible in the form view
                            xml2.append(E.field(name=field_name, invisible="1", **attrs))
                        else:
                            xml2.append(E.field(name=field_name, **attrs))

            xml2.append({'class': "o_label_nowrap"})
            xml = E.field(E.group(*(xml1), col="2"), E.group(*(xml2), col="4"), name="groups_id", position="replace")
            xml.addprevious(etree.Comment("GENERATED AUTOMATICALLY BY GROUPS"))
            xml_content = etree.tostring(xml, pretty_print=True, xml_declaration=True, encoding="utf-8")
            view.with_context(lang=None).write({'arch': xml_content})

這段代碼即便沒仔細的閱讀,我們也可以很輕松的了解到大體功能:得到所有application(也就是categroy),
構(gòu)造出field,其中field會根據(jù)該選項是否是selection類型以及其他處理(意味著選項是boolean)
最后,把構(gòu)造好的XML重新寫入數(shù)據(jù)庫中id = user_groups_view的記錄.

再仔細閱讀,可以發(fā)現(xiàn)方法中循環(huán)的主題是通過for app, kind, gs in self.get_groups_by_application():這段代碼生產(chǎn)的.這段代碼作者也給出了相關(guān)介紹:

Return all groups classified by application (module category), as a list::
[(app, kind, groups), ...],
where app and groups are recordsets, and kind is either
'boolean' or 'selection'. Applications are given in sequence
order. If kind is 'selection', groups are given in
reverse implication order.

至于看起來很奇怪的字段名,在源碼中有一段name_selection_groups的方法:

def name_selection_groups(ids):
    return 'sel_groups_' + '_'.join(map(str, ids))

可以發(fā)現(xiàn)_update_user_groups_view中在對字段處理的時候調(diào)用了該方法,所以field字段顯示的邏輯是各種不同權(quán)限中包含的的groups_id的組合.(對應implied_ids字段,分組包含其他分組的權(quán)限)

_update_user_groups_view方法會在頁面升級的時候調(diào)用一次生成視圖,此外,每當res.groups模型中執(zhí)行了創(chuàng)建,刪除,修改操作也會同時執(zhí)行該段函數(shù)更新視圖

應用

知道了原理,操作這部分就變得十分輕而易舉了.首先繼承res.groups模塊,然后重寫_update_user_groups_view方法

    @api.model
    def _update_user_groups_view(self):
        super(SpeGroupsView, self)._update_user_groups_view()

        # 修改base.user_groups_view視圖,把Admin設(shè)置只分配給超級管理員
        view = self.env.ref('base.user_groups_view', raise_if_not_found=False)
        etree = ET.fromstring(view.arch)
        change_field = etree.find(".//field[@name='sel_groups_1_2']")
        if change_field is not None:
            change_field.set('groups', 'base.group_system')
        xml_content = ET.tostring(etree, encoding='utf-8', method='xml')
        view.with_context(lang=None).write({'arch': xml_content})

這段代碼中等odoo的原本的處理完畢之后,我們再取出來,根據(jù)自己的需要操作XML(就跟平時編寫xml一樣),
在這里,我對 系統(tǒng)管理字段設(shè)置了groups,只有group_system權(quán)限組才可以看的到該視圖.
然后重新與原方法一樣,重新寫回數(shù)據(jù)庫中.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宫静,一起剝皮案震驚了整個濱河市侮叮,隨后出現(xiàn)的幾起案子瓶摆,更是在濱河造成了極大的恐慌邓梅,老刑警劉巖氧苍,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件退盯,死亡現(xiàn)場離奇詭異狱从,居然都是意外死亡改橘,警方通過查閱死者的電腦和手機破花,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門谦趣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疲吸,“玉大人,你說我怎么就攤上這事前鹅≌玻” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵舰绘,是天一觀的道長蹂喻。 經(jīng)常有香客問我,道長捂寿,這世上最難降的妖魔是什么口四? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮秦陋,結(jié)果婚禮上蔓彩,老公的妹妹穿的比我還像新娘。我一直安慰自己踱侣,他們只是感情好粪小,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抡句,像睡著了一般探膊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上待榔,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天逞壁,我揣著相機與錄音,去河邊找鬼锐锣。 笑死腌闯,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的雕憔。 我是一名探鬼主播姿骏,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼斤彼!你這毒婦竟也來了分瘦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤琉苇,失蹤者是張志新(化名)和其女友劉穎嘲玫,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體并扇,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡去团,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片土陪。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡昼汗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出旺坠,到底是詐尸還是另有隱情乔遮,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布取刃,位于F島的核電站蹋肮,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏璧疗。R本人自食惡果不足惜坯辩,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望崩侠。 院中可真熱鬧漆魔,春花似錦、人聲如沸却音。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽系瓢。三九已至阿纤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間夷陋,已是汗流浹背欠拾。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留骗绕,地道東北人藐窄。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像酬土,于是被迫代替她去往敵國和親荆忍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

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