為了簡單敘述,暫時不考慮多個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