odoo前后端交互詳解


為了簡單敘述,暫時不考慮多個db的情況(主要是懶得說沒有db或者多個db實例的情況)當odoo指定數據庫開啟服務時(也就是odoo-bin -d <some_db_name> ),我們使用chrome的隱身模式訪問http://127.0.0.1:8069

 

1. 輸入http://127.0.0.1:8069/之后發生了什么

192.168.1.10, 這個是我的虛機ip地址,返回響應是200,可以通過源代碼中, 我們看到

# web/controllers/main.py:435

@http.route('/', type='http', auth='none')
def index(self, s_action=None, db=None, **kwargs):
        return http.local_redirect('/web', query=request.params, keep_hash=True)


按照代碼命名來看,應該返回一個30x的redirect[重定向]響應,為什么實際返回的確是200的響應頭呢?
繼續查看代碼,可以發現就是keep_hash=True 這個參數所引起的,在local_redirect 函數中由於keep_hash,

# odoo/http.py:156
def redirect_with_hash(url, code=303):
    if request.httprequest.user_agent.browser in ('firefox',):
        return werkzeug.utils.redirect(url, code)
    url = pycompat.to_text(url)
    if urls.url_parse(url, scheme='http').scheme not in ('http', 'https'):
        url = u'http://' + url
    url = url.replace("'", "%27").replace("<", "%3C")
    return "<html><head><script>window.location = '%s' + location.hash;</script></head></html>" % url

調用了redirect_with_hash 函數,而多數瀏覽器是不支持帶着hash redirect的,就直接返回了html,並且帶有script的js代碼,使得從js的角度實現帶有hash地址的redirect。
通過curl命令驗證,實際上確實是如此返回

 

2. 跳轉至/web之后

# web/controllers/main.py:442
@http.route('/web', type='http', auth="none")
def web_client(self, s_action=None, **kw):
    ensure_db()
    if not request.session.uid:
        return werkzeug.utils.redirect('/web/login', 303)
    if kw.get('redirect'):
        return werkzeug.utils.redirect(kw.get('redirect'), 303)

    request.uid = request.session.uid
    try:
        context = request.env['ir.http'].webclient_rendering_context()
        response = request.render('web.webclient_bootstrap', qcontext=context)
        response.headers['X-Frame-Options'] = 'DENY'
        return response
    except AccessError:
        return werkzeug.utils.redirect('/web/login?error=access')

ensure_db(),就是一些確定當前用戶是否選擇db,沒有就跳轉到/web/database/selector, 或者/web/database/create,由於上面我說了只使用一個db,因為其他情況不想多講,所以在ensure_db中,默認替我們選擇好了唯一一個db,並且存在request.session中了。接下來,由於我們尚未登陸,request.session.uid為空,就返回了 /web/login 303的響應。  

 

3. 跳轉至/web/login

# web/controllers/main.py:467
@http.route('/web/login', type='http', auth="none", sitemap=False)
def web_login(self, redirect=None, **kw):
    ensure_db()
    request.params['login_success'] = False

    # 如果是帶有redirect的get請求,並且session中有uid(也就是用戶已經登陸的情況下),返回redirect
    # 我們目前不是這個情況
    if request.httprequest.method == 'GET' and redirect and request.session.uid:
        return http.redirect_with_hash(redirect)

    # 設置request.uid
    if not request.uid:
        request.uid = odoo.SUPERUSER_ID

    values = request.params.copy()
    try:
        values['databases'] = http.db_list()
    except odoo.exceptions.AccessDenied:
        values['databases'] = None

    # 登陸的相關邏輯
    if request.httprequest.method == 'POST':
        old_uid = request.uid
        uid = request.session.authenticate(request.session.db, request.params['login'], request.params['password'])
        if uid is not False:
            request.params['login_success'] = True
            return http.redirect_with_hash(self._login_redirect(uid, redirect=redirect))
        request.uid = old_uid
        values['error'] = _("Wrong login/password")
    else:
        if 'error' in request.params and request.params.get('error') == 'access':
            values['error'] = _('Only employee can access this database. Please contact the administrator.')

    if 'login' not in values and request.session.get('auth_login'):
        values['login'] = request.session.get('auth_login')

    if not odoo.tools.config['list_db']:
        # 這個值決定了是否在登陸界面出現Manage Databases超鏈接
        values['disable_database_manager'] = True

    # GET請求,返回的響應就是這個了,
    response = request.render('web.login', values)
    response.headers['X-Frame-Options'] = 'DENY'
    return response

到這,就不得不提一下odoo自己的Qweb模板引擎了,這個'web.login',就是qweb view的id,具體可以通過全局查找定位到'web.login' 的位置,其實就是在web/views/webclient_templates.xml中301行的位置,qweb有它自己獨特的語法,這個要了解就去odoo官網查看吧,然后通過values對這個模板進行渲染,然后返回200的響應,就出現了基本的登錄界面了  

 

 

 

 

 

 

綜上,這就是基本的輸入界面到返回界面的一些過程。
例子 - 去除登陸頁面的Powered by Odoo鏈接

從上面的第3步,我們可以看到,最后登錄界面是由'web.login' 模板來顯示的,通過odoo的繼承方式,我們很容易的就可以去除這個鏈接,通過查找,這個鏈接實際是出現在'web.login_layout' qweb視圖中,

<template id='remove_login_odoo_link' inherit_id='web.login_layout'>
    <xpath expr='//div[@t-if="not disable_footer"]' position='replace'>
        <div class="text-right" t-if="not disable_footer">
            <t t-if="not disable_database_manager">
                <a class="" href="/web/database/manager">Manage Databases</a>
            </t>
        </div>
    </xpath>
</template> 
<odoo>
    <data>
        <template id="assets_backend" inherit_id="web.assets_backend" name="title assets backend">
            <xpath expr="." position="inside">
                <script type="text/javascript" src="/remove_login_odoo_link/static/src/title.js"></script>
            </xpath>
        </template>


        <!--移除/web/login界面中, Powered by Odoo的超鏈接-->
        <template id='remove_login_odoo_link' inherit_id='web.login_layout'>
            <xpath expr='//a[@href="https://www.odoo.com"]' position='replace'/>
            <xpath expr="//t[@t-if='not disable_database_manager']" position="replace">
                <a class="" href="/web/database/manager">Manage Databases</a>
            </xpath>
        </template>

        <!--替換/web/login下的 title-->
        <template id='remove_web_head_title' inherit_id='web.layout'>
            <xpath expr='//head/title' position='replace'>
                <title t-esc="title"/>
            </xpath>
        </template>

    </data>
</odoo>

 

出現登陸界面,在我們輸入用戶名密碼之后,odoo又做了什么,chrome中開發人員模式,看到請求如下

 

 

# web/controllers/main.py:483
        # 登陸邏輯,
        if request.httprequest.method == 'POST':
            old_uid = request.uid
            uid = request.session.authenticate(request.session.db, request.params['login'], request.params['password'])
            if uid is not False:
                request.params['login_success'] = True
                # 密碼驗證通過之后,返回一個帶hash的響應,實際上是200響應頭,js實現跳轉到/web
                return http.redirect_with_hash(self._login_redirect(uid, redirect=redirect))
            request.uid = old_uid
            values['error'] = _("Wrong login/password")
        else:
            if 'error' in request.params and request.params.get('error') == 'access':
                values['error'] = _('Only employee can access this database. Please contact the administrator.')

# 在/web route下
# web/controllers/main.py:443
@http.route('/web', type='http', auth="none")
    def web_client(self, s_action=None, **kw):
        ensure_db()
        if not request.session.uid:
            return werkzeug.utils.redirect('/web/login', 303)
        if kw.get('redirect'):
            return werkzeug.utils.redirect(kw.get('redirect'), 303)

        # /web/login成功之后的跳轉,session已經有uid, 返回html
        request.uid = request.session.uid
        try:
            # 獲取當前用戶的一些context,包括菜單之類的
            context = request.env['ir.http'].webclient_rendering_context()
            # 用模板渲染html
            response = request.render('web.webclient_bootstrap', qcontext=context)
            response.headers['X-Frame-Options'] = 'DENY'
            return response
        except AccessError:
            return werkzeug.utils.redirect('/web/login?error=access')
簡短就是,驗證登陸用戶名密碼成功之后,將用戶uid寫入session之中,然后跳轉至/web頁面;/web請求時,獲取用戶的一些context包括能看到的菜單等,然后通過模板渲染,最后返回響應。

'web.webclient_bootstrap'模板可以在web/views/webclient_templates.xml:597行找到,通過模板我們可以看到只生成了一些基本的靜態文件,以及當前登錄用戶的所能訪問到的菜單。所有具體的頁面內容都是后來由js處理和生成的。

所有的靜態文件,如果沒有開啟debug=assets的話,都是由ir_qweb.py進行壓縮成單獨的一個js文件,當然其實有兩個,一個是web.assets_common,一個是web.assets_backend(我們主要就是研究這個咯)
在webclient_templates.xml中找到id=’web.assets_backend’的template,和’web.assets_common’的template。 common里面導入一些通用的js文件,backend的js文件主要是登錄成功后渲染/web里面內容所單獨需要的。

 

 

 

odoo面試題---筆試題


1.什么是lambda函數?

Python的匿名函數lambda函數
lambda函數定義

python 使用 lambda 來創建匿名函數。lambda函數的語法只包含一個語句,如下:
參數 表達式
lambda [arg1 [,arg2,.....argn]]:expression

簡單理解起來。匿名函數lambda是指一類無需定義標識符(函數名)的函數或子程序。
lambda 函數可以接收任意多個參數 (包括可選參數) 並且返回單個表達式的值。

lambda匿名函數的格式:冒號前是參數,可以有多個,用逗號隔開,冒號右邊的為表達式。其實lambda返回值是一個函數的地址,也就是函數對象。
lambda函數實例

s=lambda x,y:x+y
h=lambda x,y,z:x+y-z
print(s(10,20),h(10,20,50))


lambda函數的特點和使用場景
特點

    從函數命名的角度:匿名,直接返回可供調用的值
    從輸入輸出的角度:支持多個輸入參數,但只支持一個表達式
    從函數功能的角度:結構簡單,無須定義函數名。但所能實現的功能也極其受限。
    從訪問變量的角度:只支持訪問lambda自己定義的變量。不支持外部變量訪問
    從運行效率的角度:lambda實際上仍開辟了一個內存單元,並沒有提升運行效率

使用場景

    lambda函數復制給變量:減少函數定義的麻煩,同時支持代碼復用。

add=lambda x,y:x+y
print(add(1,2))



    lambda函數賦值給函數:利用lambda函數實現對已有函數的重新定義。

    例如,為了把標准庫time中的函數sleep的功能屏蔽(Mock),我們可以在程序初始化時調用:time.sleep=lambda x:None。這樣,在后續代碼中調用time庫的sleep函數將不會執行原有的功能。例如,執行time.sleep(3)時,程序不會休眠3秒鍾,而是什么都不做。

    利用lambda函數進行函數嵌套:利用lambda構造嵌套的內部和外部函數,實現閉包編程



部分Python內置函數接收函數作為參數。典型的此類內置函數有這些。

- filter函數。此時lambda函數用於指定過濾列表元素的條件。例如filter(lambda x: x % 3 == 0, [1, 2, 3])指定將列表[1,2,3]中能夠被3整除的元素過濾出來,其結果是[3]。
- sorted函數。此時lambda函數用於指定對列表中所有元素進行排序的准則。例如sorted([1, 2, 3, 4, 5, 6, 7, 8, 9], key=lambda x: abs(5-x))將列表[1, 2, 3, 4, 5, 6, 7, 8, 9]按照元素與5距離從小到大進行排序,其結果是[5, 4, 6, 3, 7, 2, 8, 1, 9]。
- map函數。此時lambda函數用於指定對列表中每一個元素的共同操作。例如map(lambda x: x+1, [1, 2,3])將列表[1, 2, 3]中的元素分別加1,其結果[2, 3, 4]。
- reduce函數。此時lambda函數用於指定列表中兩兩相鄰元素的結合條件。例如reduce(lambda a, b: '{}, {}'.format(a, b), [1, 2, 3, 4, 5, 6, 7, 8, 9])將列表 [1, 2, 3, 4, 5, 6, 7, 8, 9]中的元素從左往右兩兩以逗號分隔的字符的形式依次結合起來,其結果是'1, 2, 3, 4, 5, 6, 7, 8, 9'。



原文鏈接:https://blog.csdn.net/zhanshen112/article/details/90603865


2.如何判斷一個對象的類型

a = 10

利用python的type()函數,

print(type(a))   輸出:int

 


3.字典和json數據的轉換

字典與json轉化主要用到以下方法:

   loads():將json數據轉化成dict數據
   dumps():將dict數據轉化成json數據


字典轉json數據:
In [5]: import json                                                                                                                                                                                                                                                           

In [6]: dict = {'name':'mary','age':21}                                                                                                                                                                                                                                       

In [7]: type(dict)                                                                                                                                                                                                                                                            
Out[7]: dict

In [8]: j = json.dumps(dict)                                                                                                                                                                                                                                                  

In [9]: j                                                                                                                                                                                                                                                                     
Out[9]: '{"name": "mary", "age": 21}'


這里需要注意的是,如果字典轉json時包含漢字,就會出現編碼問題,如下:

In [29]: staff = {'name':'權權','age':23,'sex':'女'}                                                                                                                                                                                                                          

In [30]: json.dumps(staff)                                                                                                                                                                                                                                                    
Out[30]: '{"name": "\\u6743\\u6743", "age": 23, "sex": "\\u5973"}'

    1
    2
    3
    4
    5

所以如果有中文,我們需要加參數處理:

In [31]: json.dumps(staff,ensure_ascii=False)                                                                                                                                                                                                                                 
Out[31]: '{"name": "權權", "age": 23, "sex": "女"}'

原因:通常用post方式請求時是json數據,但如果有中文則顯示有問題,因為中文用 unicode 編碼,而默認卻是用ASCII解析的,中文不在ASCII編碼中,所以不能顯示中文。


json數據轉字典:
In [25]: j                                                                                                                                                                                                                                                                    
Out[25]: '{"name": "mary", "age": 21}'

In [26]: result = json.loads(j)                                                                                                                                                                                                                                               

In [27]: result                                                                                                                                                                                                                                                               
Out[27]: {'name': 'mary', 'age': 21}

In [28]: type(result)                                                                                                                                                                                                                                                         
Out[28]: dict



4.列表和元組的轉換

元組 和 列表之間的轉換

使用 list 函數 可以把 元組 轉換成 列表

list(元組)

使用 tuple 函數 可以把 列表 轉換成 元組

tuple(列表)

#列表轉換元組
num_list = [1,2,3,4,5]
print(type(num_list))
print(num_list)
num_tuple = tuple(num_list)
print(type(num_tuple))
print(num_tuple)

#元組轉換列表
num_tuple_01 = (1,2,3,4,5)
print(type(num_tuple_01))
print(num_tuple_01)
num_list_01 = list(num_tuple_01)
print(type(num_list_01))
print(num_list_01)

 


5.使用labana輸出兩數相乘

value=lambda x,y:x×y
print(value(1,2))

 

 


6.一行代碼輸出1-100之和

Python 3.5.2 (default, Nov 12 2018, 13:43:14)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.

range左閉右開區間
>>> total=sum(range(101))
>>> print (total)
5050

 

 


7.[[1, 2], [3, 4], [5, 6]]輸出為[1, 2, 3, 4, 5, 6]

#1)利用推導式運行過程:for i in a ,每個i是【1,2】,【3,4】,【5,6】,for j in i,每個j就是1,2,3,4,5,6,合並后就是結果
 
a=[[1,2],[3,4],[5,6]]
x=[j for i in a for j in i]    #這個的解析過程是 從a中取出每一個值付給i,然后從i中取出每一個 值復制給j 然后輸出j的結果
print(x)       ==>[1, 2, 3, 4, 5, 6]

 

 


8.列表去交集和並集

 求多個list的交集

1 a = [0,1,2,3,4]
2 b = [0,2,6]
3 c = [-1,2,5,8]
4 r = list(set(a).intersection(b,c)) 
5 print('r -->', r)




 求多個list的並集

1 a = [0,1,2,3,4]
2 b = [0,2,6]
3 c = [-1,2,5,8]
4 r = list(set(a).union(b,c)) # 求多個list的並集
5 print('r -->', r)   # 輸出:r --> [0, 1, 2, 3, 4, 5, 6, 8, -1]"""



求多個list的差(補)集 - 即獲取特定1個list中有,其他list都沒有的元素
1 a = [0,1,2,3,4] 2 b = [0,2,6] 3 c = [-1,2,5,8] 4 r = list(set(a).difference(b,c)) # 求特定1個list(a)中有,其他list(b、c)都沒有的元素""" 5 print('r -->', r) # 輸出:r --> [1, 3, 4]"""


list基本操作

 
         
復制代碼
 
         
 1 list = [1, 2, 3]  2 list.append(5)  3 print(list)  4  5 list.extend([6, 7]) # extend是將可迭代對象的元素依次加入列表  6 print(list)  7  8 list.append([6, 7]) # append是把傳入的參數當成一個元素加入列表  9 print(list) 10 11 list.reverse() # 元素翻轉,注意不能將這個操作賦給一個變量,此操作是對list本身操作,即list自身發生變化 12 # l=list.reverse() l為空,沒有得到list翻轉后的值 13 print(list)
 

 

 

 



 

 

 

 


原文鏈接:https://blog.csdn.net/J_z10/article/details/79247533


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM