模型之間的關係(Relations Between Models) 上一章介紹了為包含基本欄位的模型創建自定義視圖。然而,在任何真實的業務場景中,我們都需要不止一個模型。此外,模型之間的鏈接是必要的。人們可以很容易地想象一個模型包含客戶,另一個模型則包含用戶列表。你可能需要參考任何現有業務模型上的客 ...
模型之間的關係(Relations Between Models)
上一章介紹了為包含基本欄位的模型創建自定義視圖。然而,在任何真實的業務場景中,我們都需要不止一個模型。此外,模型之間的鏈接是必要的。人們可以很容易地想象一個模型包含客戶,另一個模型則包含用戶列表。你可能需要參考任何現有業務模型上的客戶或用戶。
在我們的estate模塊中,我們需要有關房產的以下信息:
- 購買房產的客戶
- 出售房產的真實重述代理人
- 房產類型:房子、公寓、頂層公寓、城堡…
- 顯示了該酒店特色的一系列標簽:舒適、翻新…
- 收到的報價清單
Many2one
參考: 本主題相關文檔可查閱 Many2one
在我們的房地產模塊中,我們想定義房地產類型的概念,例如,房屋或公寓。根據的類型對房地產進行分類是一種標準的業務需求,尤其是為了優化過濾。
一個房產可以有一個類型,但同一類型可以分配給多個房產。這得到了many2one
概念的支持。
many2one
是指向另一個對象的簡單鏈接。例如,為了在我們的測試模型中定義到 res.partner
的連接,我們可以這樣寫:
partner_id = fields.Many2one("res.partner", string="Partner")
按約定,many2one
欄位以_id
尾碼。可通過以下方式輕鬆的訪問partner
中的數據:
print(my_test_object.partner_id.name)
參見
練習1
添加房地產類型表
- 創建
estate.property.type
模型,並添加以下欄位:
Field | Type | Attributes |
---|---|---|
name | Char | required |
-
添加菜單
-
添加
property_type_id
到estate.property
模型和表單,樹,搜索視圖
該練習是對前幾章很好的扼要重述:你需要創建一個 model,設置 model,添加 動作和菜單,並且創建視圖.
提示: 別忘記在 __init__.py
導入新的Python模塊文件,併在__manifest.py__
中添加數據或者訪問許可權。
新增odoo14/custom/estate/models/estate_property_type.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from odoo import models, fields
class EstatePropertyType(models.Model):
_name = 'estate.property.type'
_description = 'estate property type'
name = fields.Char(string='type', required=True)
修改odoo14/custom/estate/models/__init__.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from . import estate_property
from . import estate_property_type # 新增內容
修改odoo14/custom/estate/security/ir.model.access.csv
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_estate_model,access_estate_model,model_estate_property,base.group_user,1,1,1,1
access_estate_property_type_model,access_estate_property_type_model,model_estate_property_type,base.group_user,1,1,1,1
修改odoo14/custom/estate/views/estate_menus.view
<?xml version="1.0"?>
<odoo>
<menuitem id="test_menu_root" name="Real Estate">
<menuitem id="test_first_level_menu" name="Advertisements">
<menuitem id="estate_property_menu_action" action="link_estate_property_action"/>
</menuitem>
<menuitem id="property_type_first_level_menu" name="Settings">
<menuitem id="property_type_action" action="estate_property_type_action"/>
</menuitem>
</menuitem>
</odoo>
新增odoo14/custom/estate/views/estate_property_type_views.xml
<?xml version="1.0"?>
<odoo>
<record id="estate_property_type_action" model="ir.actions.act_window">
<field name="name">Property Types</field>
<field name="res_model">estate.property.type</field>
<field name="view_mode">tree,form</field>
</record>
<record id="estate_property_type_view_tree" model="ir.ui.view">
<field name="name">estate.property.type.tree</field>
<field name="model">estate.property.type</field>
<field name="arch" type="xml">
<tree string="PropertyTypes">
<field name="name" string="Title"/>
</tree>
</field>
</record>
</odoo>
修改odoo14/custom/__manifest__.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
{
'name': 'estate',
'depends': ['base'],
'data':['security/ir.model.access.csv',
'views/estate_property_views.xml',
'views/estate_property_type_views.xml', # 新增內容
'views/estate_menus.xml',
]
}
再次重啟服務,並刷新查看結果。
新增一條記錄
修改odoo14/custom/estate/models/estate_property.py
,添加property_type_id
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from odoo import models,fields
class EstateProperty(models.Model):
_name = 'estate.property'
_description = 'estate property table'
name = fields.Char(size=15, required=True)
description = fields.Text()
postcode = fields.Char(size=15)
date_availability = fields.Datetime('Availability Date', copy=False, default= lambda self: fields.Datetime.today())
expected_price = fields.Float('expected price', digits=(8, 2), required=True) # 最大8位,小數占2位
selling_price = fields.Float('selling price', digits=(8, 2), readonly=True, copy=False)
bedrooms = fields.Integer(default=2)
living_area = fields.Integer()
facades = fields.Integer()
garage = fields.Boolean('garage')
garden = fields.Boolean('garden')
garden_area = fields.Integer()
garden_orientation = fields.Selection(
string='Orientation',
selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('West','West')],
help="garden orientation"
)
active = fields.Boolean('Active', default=True, invisible=True)
state = fields.Selection(
string='State',
selection=[('New','New'),
('Offer Received','Offer Received'),
('Offer Accepted', 'Offer Accepted'),
('Sold','Sold'),
('Canceled', 'Canceled')],
copy=False
)
property_type_id = fields.Many2one("estate.property.type", "PropertyType")
修改odoo14/custom/estate/views/estate_property_views.xml
tree
,form
視圖
<?xml version="1.0"?>
<odoo>
<record id="link_estate_property_action" model="ir.actions.act_window">
<field name="name">Properties</field>
<field name="res_model">estate.property</field>
<field name="view_mode">tree,form</field>
</record>
<record id="estate_property_view_tree" model="ir.ui.view">
<field name="name">estate.property.tree</field>
<field name="model">estate.property</field>
<field name="arch" type="xml">
<tree string="Tests">
<field name="name" string="Title"/>
<field name="postcode" string="Postcode"/>
<field name="bedrooms" string="Bedrooms"/>
<field name="living_area" string="Living Area"/>
<field name="expected_price" string="Expected Price"/>
<field name="selling_price" string="Selling Price"/>
<field name="date_availability" string="Avalilable Form"/>
<field name="property_type_id" string="Property Type"/>
</tree>
</field>
</record>
<record id="estate_property_view_form" model="ir.ui.view">
<field name="name">estate.property.form</field>
<field name="model">estate.property</field>
<field name="arch" type="xml">
<form string="estate property form">
<sheet>
<h1>
<field name="name"/>
</h1>
<group>
<group>
<field name="property_type_id" string="Property Type"/>
<field name="postcode" string="Postcode" ></field>
<field name="date_availability" string="Available From"></field>
</group>
<group>
<field name="expected_price" string="Expected Price"></field>
<field name="selling_price" string="Selling Price"></field>
</group>
</group>
<notebook>
<page string="Description">
<group>
<field name="description"></field>
<field name="bedrooms"></field>
<field name="living_area"></field>
<field name="facades"></field>
<field name="garage"></field>
<field name="garden"></field>
<field name="garden_area"></field>
<field name="garden_orientation"></field>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="estate_property_search_view" model="ir.ui.view">
<field name="name">estate.property.search</field>
<field name="model">estate.property</field>
<field name="arch" type="xml">
<search string="Estate Property">
<!-- 搜索 -->
<field name="name" string="Title" />
<field name="postcode" string="Postcode"></field>
<separator/>
<!-- 篩選 -->
<filter string="Available" name="state" domain="['|',('state', '=', 'New'),('state', '=', 'Offer Received')]"></filter>
<filter name="bedrooms" domain="[('bedrooms', '>', 3)]"></filter>
<filter name="bedrooms and selling_price" domain="[('bedrooms', '>', 2),('selling_price', '>=', 1000)]"></filter>
<!-- 分組 -->
<group expand="1" string="Group By">
<filter string="朝向" name="garden_orientation" context="{'group_by':'garden_orientation'}"/>
</group>
</search>
</field>
</record>
</odoo>
重啟服務,刷新瀏覽器驗證
在房地產模塊中,我們仍然缺失兩條關於房產的信息:買家和銷售人員。買家可以是任何個人,然而,銷售人員必須是房產機構的員工(即odoo用戶)。
在odoo中,有兩種我們經常引用的兩種模型:
res.partner
: 一個partner
為一個物理實體或者法人實體。可以是一個公司,個人,甚至是一個聯繫地址。res.users
: 系統用戶。可以是內部(internal
)用戶,也就是說有odoo後端的訪問許可權,可以是門戶(portal
)用戶,僅可以訪問前端(比如訪問他們之前的電子商務訂單) ,不可以訪問後端。
練習2
會用上述提到的兩種常用model添加買家和銷售人員到estate.property
模塊。將它們添加到form視圖中新tab頁面。
銷售人員的預設值必須是當前用戶。買家不能被覆制。
提示:要獲取預設值,請查看下麵的註解或查看示例
user_id = fields.Many2one('res.users', string='Salesperson', index=True, tracking=True, default=lambda self: self.env.user)
註解
self.env
對象為其它請求參數和其它有用的東西提供了訪問許可權:
self.env.cr
或者self._cr
為資料庫游標(cursor)對象。用於查詢資料庫self.env.uid
或者self._uid
當前用戶資料庫IDself.env.user
當前用戶記錄self.env.context
或者self._context
上下文字典self.env.ref(xml_id)
返回和XML id對應的記錄self.env[model_name]
返回給定模型的實例
修改odoo14\custom\estate\models\estate_property.py
,內容如下
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from odoo import models,fields
class EstateProperty(models.Model):
_name = 'estate.property'
_description = 'estate property table'
name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char(size=15)
date_availability = fields.Datetime('Availability Date', copy=False, default= lambda self: fields.Datetime.today())
expected_price = fields.Float('expected price', digits=(8, 2), required=True) # 最大8位,小數占2位
selling_price = fields.Float('selling price', digits=(8, 2), readonly=True, copy=False)
bedrooms = fields.Integer(default=2)
living_area = fields.Integer()
facades = fields.Integer()
garage = fields.Boolean('garage')
garden = fields.Boolean('garden')
garden_area = fields.Integer()
garden_orientation = fields.Selection(
string='Orientation',
selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('West','West')],
help="garden orientation"
)
active = fields.Boolean('Active', default=True, invisible=True)
state = fields.Selection(
string='State',
selection=[('New','New'),
('Offer Received','Offer Received'),
('Offer Accepted', 'Offer Accepted'),
('Sold','Sold'),
('Canceled', 'Canceled')],
copy=False
)
property_type_id = fields.Many2one("estate.property.type", string="PropertyType")
# 以下為本次新增內容
salesman_id = fields.Many2one("res.users", string="Salesman")
buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False, default=lambda self: self.env.user)
修改odoo14\custom\estate\views\estate_property_views.xml
中notebook
內容為如下:
<notebook>
<page string="Description">
<group>
<field name="description"></field>
<field name="bedrooms"></field>
<field name="living_area"></field>
<field name="facades"></field>
<field name="garage"></field>
<field name="garden"></field>
<field name="garden_area"></field>
<field name="garden_orientation"></field>
</group>
</page>
<page string="Other info">
<group>
<field name="salesman_id" string="Salesman"></field>
<field name="buyer_id" string="Buyer"></field>
</group>
</page>
</notebook>
重啟服務,瀏覽器中驗證
Many2many
參考:和本主題關聯的文檔可參考Many2many
.
在我們的房地產模塊中,我們想定義房產標簽的概念。例如,房地產是“舒適”或是“翻新”的
一個地產可以有多個標簽,一個標記可以分配給多個房產。這得到了many2many
概念的支持。
many2many
是一種雙向多重關係:一側的任何記錄都可以與另一側的任何數量的記錄相關。例如,為了在我們的測試模型中定義到 account.tax
的鏈接,我們可以這樣寫:
tax_ids = fields.Many2many("account.tax", string="Taxes")
按約定,many2many
欄位擁有_ids
尾碼。這意味著可以將多個"tax"添加到我們的測試模型。它表現為一個記錄列表,意味著必須通過迴圈訪問數據:
for tax in my_test_object.tax_ids:
print(tax.name)
記錄列表即為眾所周知的recordset
,即記錄的有序集合。它支持標準Python的集合操作,如len()
和iter()
,以及recs1 | recs2
等額外的集合操作。
練習
添加房產標簽表
- 創建
estate.property.tag
模型和並添加以下欄位:
Field | Type | Attributes |
---|---|---|
name | Char | required |
- 添加菜單
- 添加
tag_ids
到estate.property
模型,表單和列表視圖
提示: 視圖中,使用 widget="many2many_tags"
屬性正如這裡展示的一樣。
<group string="Website Traffic Conditions">
<field name="country_ids" widget="many2many_tags"/>
<field name="website_id" options="{'no_open': True, 'no_create_edit': True}" groups="website.group_multi_website"/>
...
</group>
添加odoo14\custom\estate\models\estate_property_tag.py
,內容如下:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from odoo import models, fields
class EstatePropertyTag(models.Model):
_name = 'estate.property.tag'
_description = 'estate property tag'
name = fields.Char(string='tag', required=True)
修改odoo14\custom\estate\models\__init__.py
,內容如下:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from . import estate_property
from . import estate_property_type
from . import estate_property_tag # 本次新增內容
修改odoo14\custom\estate\security\ir.model.access.csv
,內容如下:
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_estate_model,access_estate_model,model_estate_property,base.group_user,1,1,1,1
access_estate_property_type_model,access_estate_property_type_model,model_estate_property_type,base.group_user,1,1,1,1
access_estate_property_tag_model,access_estate_property_tag_model,model_estate_property_tag,base.group_user,1,1,1,1
修改odoo14/custom/estate/views/estate_menus.view
<?xml version="1.0"?>
<odoo>
<menuitem id="test_menu_root" name="Real Estate">
<menuitem id="test_first_level_menu" name="Advertisements">
<menuitem id="estate_property_menu_action" action="link_estate_property_action"/>
</menuitem>
<menuitem id="settings_menu" name="Settings">
<menuitem id="property_type_action" action="estate_property_type_action"/>
<!-- 本次新增內容 -->
<menuitem id="property_tag_action" action="estate_property_tag_action"/>
</menuitem>
</menuitem>
</odoo>
新增odoo14/custom/estate/views/estate_property_tag_views.xml
<?xml version="1.0"?>
<odoo>
<record id="estate_property_tag_action" model="ir.actions.act_window">
<field name="name">Property Tags</field>
<field name="res_model">estate.property.tag</field>
<field name="view_mode">tree,form</field>
</record>
<record id="estate_property_tag_view_tree" model="ir.ui.view">
<field name="name">estate.property.tag.tree</field>
<field name="model">estate.property.tag</field>
<field name="arch" type="xml">
<tree string="PropertyTags">
<field name="name" string="tag"/>
</tree>
</field>
</record>
</odoo>
修改odoo14/custom/estate/__manifest__.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
{
'name': 'estate',
'depends': ['base'],
'data':['security/ir.model.access.csv',
'views/estate_property_views.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag_views.xml', # 本次新增內容
'views/estate_menus.xml',
]
}
重啟服務,刷新瀏覽器驗證,效果如下
添加2條記錄,供下文使用
修改odoo14/custom/estate/models/estate_property.py
,末尾添加property_tag_id
tag_ids = fields.Many2many("estate.property.tag")
修改odoo14\custom\estate\views\estate_property_views.xml
中estate_property_view_form
視圖
<record id="estate_property_view_form" model="ir.ui.view">
<field name="name">estate.property.form</field>
<field name="model">estate.property</field>
<field name="arch" type="xml">
<form string="estate property form">
<sheet>
<h1>
<field name="name"/>
</h1>
<!--<p>元素為本次添加內容-->
<p>
<field name="tag_ids" widget="many2many_tags"/>
</p>
<group>
<group>
<field name="property_type_id" string="Property Type"/>
<field name="postcode" string="Postcode" ></field>
<field name="date_availability" string="Available From"></field>
</group>
<group>
<field name="expected_price" string="Expected Price"></field>
<field name="selling_price" string="Selling Price"></field>
</group>
</group>
<notebook>
<page string="Description">
<group>
<field name="description"></field>
<field name="bedrooms"></field>
<field name="living_area"></field>
<field name="facades"></field>
<field name="garage"></field>
<field name="garden"></field>
<field name="garden_area"></field>
<field name="garden_orientation"></field>
</group>
</page>
<page string="Other info">
<group>
<field name="salesman_id" string="Salesman"></field>
<field name="buyer_id" string="Buyer"></field>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
重啟服務,驗證效果
One2many
參考:主題關聯文檔可以參考One2many
在我們的房地產模塊中,我們想定義房產報價的概念。房地產報價是潛在買家向賣家提供的金額。報價可能低於或高於預期價格。
報價適用於一個房產,但同一個房產可以有多個報價。many2one
的概念再次出現。然而,在本例中,我們希望顯示給定地產的報價列表,因此我們將使用one2many
概念。
one2many
是many2one
的反向實現。例如,我們通過partner_id
欄位,在測試模型上定義了到res.partner
模型的鏈接。我們可以定義反向關係,即與partner
鏈接的測試模型列表:
test_ids = fields.One2many("test.model", "partner_id", string="Tests")
第一個參數叫做comodel
,第二個參數是我們用於反向查詢的欄位。
按照慣例,one2many
欄位都有_ids
尾碼。它們表現為記錄列表,這意味著訪問數據必須在迴圈中完成:
for test in partner.test_ids:
print(test.name)
註意
One2many
是一種虛擬的關係,必須在comodel
,必須在comodel
中定義Many2one
欄位
練習
添加房地產報價表
- 創建
estate.property.offer
模型,並添加以下欄位:
Field | Type | Attributes | Values |
---|---|---|---|
price | Float | ||
status | Selection | no copy | Accepted, Refused |
partner_id | Many2one (res.partner ) |
required | |
property_id | Many2one (estate.property ) |
required |
-
使用
price
,partner_id
,status
欄位創建列表和表單視圖 ,不必創建動作和菜單 -
添加
offer_ids
欄位到estate.property
模型極其表單視圖
這裡有幾件重要的事情需要註意。首先,我們不需要所有模型的操作或菜單。某些模型只能通過另一個模型訪問。在我們的練習中就是這樣的:報價總是通過房產獲得的。
其次,儘管property_id
欄位是必需的,但我們沒有將其包含在視圖中。odoo如何知道我們的報價與哪個房產相關?這就是使用odoo框架的一部分魔力:有時候事情是隱式定義的。當我們通過one2many
欄位創建記錄時,為了方便,會自動填充相應的many2one
添加odoo14\custom\estate\models\estate_property_offer.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from odoo import models, fields
class EstatePropertyOffer(models.Model):
_name = 'estate.property.offer'
_description = 'estate property offer'
price = fields.Float(string='Price')
status = fields.Selection(string='Status',
selection=[('Accepted', 'Accepted'), ('Refused', 'Refused')],
copy=False
)
partner_id = fields.Many2one('res.partner', required=True)
property_id = fields.Many2one('estate.property', required=True)
添加odoo14\custom\estate\views\estate_property_offer_views.xml
<?xml version="1.0"?>
<odoo>
<record id="estate_property_offer_view_tree" model="ir.ui.view">
<field name="name">estate.property.offer.tree</field>
<field name="model">estate.property.offer</field>
<field name="arch" type="xml">
<tree string="PropertyOffers">
<field name="price" string="Price"/>
<field name="partner_id" string="partner ID"/>
<field name="validity" string="Validity(days)"/>
<field name="deadline" string="Deadline"/>
<field name="status" string="Status"/>
</tree>
</field>
</record>
<record id="estate_property_offer_view_form" model="ir.ui.view">
<field name="name">estate.property.offer.form</field>
<field name="model">estate.property.offer</field>
<field name="arch" type="xml">
<form string="estate property offer form">
<sheet>
<group>
<field name="price" string="Price"/>
<field name="validity" string="Validity(days)"/>
<field name="deadline" string="Deadline"/>
<field name="partner_id" string="partner ID"/>
<field name="status" string="Status"/>
</group>
</sheet>
</form>
</field>
</record>
</odoo>
修改odoo14\custom\estate\__manifest__.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
{
'name': 'estate',
'depends': ['base'],
'data':['security/ir.model.access.csv',
'views/estate_property_views.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag_views.xml',
'views/estate_property_offer_views.xml', # 本次新增內容
'views/estate_menus.xml',
]
}
修改odoo14\custom\estate\models\__init__.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from . import estate_property_type
from . import estate_property_tag
from . import estate_property_offer
from . import estate_property
修改odoo14\custom\estate\security\ir.model.access.csv
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_estate_model,access_estate_model,model_estate_property,base.group_user,1,1,1,1
access_estate_property_type_model,access_estate_property_type_model,model_estate_property_type,base.group_user,1,1,1,1
access_estate_property_tag_model,access_estate_property_tag_model,model_estate_property_tag,base.group_user,1,1,1,1
access_estate_property_offer_model,access_estate_property_offer_model,model_estate_property_offer,base.group_user,1,1,1,1
修改odoo14\custom\estate\models\estate_property.py
,最末尾添加offer_ids
欄位,如下
offer_ids = fields.One2many("estate.property.offer", "property_id", string="PropertyOffer")
修改odoo14\custom\estate\views\estate_property_views.xml
中estate_property_view_form
表單視圖
<record id="estate_property_view_form" model="ir.ui.view">
<field name="name">estate.property.form</field>
<field name="model">estate.property</field>
<field name="arch" type="xml">
<form string="estate property form">
<sheet>
<h1>
<field name="name"/>
</h1>
<p>
<field name="tag_ids" widget="many2many_tags"/>
</p>
<group>
<group>
<field name="property_type_id" string="Property Type"/>
<field name="postcode" string="Postcode" ></field>
<field name="date_availability" string="Available From"></field>
</group>
<group>
<field name="expected_price" string="Expected Price"></field>
<field name="selling_price" string="Selling Price"></field>
</group>
</group>
<notebook>
<page string="Description">
<group>
<field name="description"></field>
<field name="bedrooms"></field>
<field name="living_area"></field>
<field name="facades"></field>
<field name="garage"></field>
<field name="garden"></field>
<field name="garden_area"></field>
<field name="garden_orientation"></field>
</group>
</page>
<page>
<field name="offer_ids" />
</page>
<page string="Other info">
<group>
<field name="salesman_id" string="Salesman"></field>
<field name="buyer_id" string="Buyer"></field>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
重啟服務,瀏覽器中驗證
點擊"Add a line" 添加記錄
作者:授客
微信/QQ:1033553122
全國軟體測試QQ交流群:7156436
Git地址:https://gitee.com/ishouke
友情提示:限於時間倉促,文中可能存在錯誤,歡迎指正、評論!
作者五行缺錢,如果覺得文章對您有幫助,請掃描下邊的二維碼打賞作者,金額隨意,您的支持將是我繼續創作的源動力,打賞後如有任何疑問,請聯繫我!!!
微信打賞
支付寶打賞 全國軟體測試交流QQ群