Skip to content

10. 模型关系

上一章介绍了如何为包含基本字段的模型创建自定义视图。在任何真实的业务场景中,一般需要多个模型组合起来才能开发出来功能丰富贴合实际业务的应用。此外,模型之间的关联也是必不可少的。可以很容易地想象,一个模型包含客户信息,另一个模型包含用户列表。在任何现有的业务模型中,可能都需要引用某个客户或用户。

在房地产模块中,用户希望获取房产的以下信息:

  • 购买该房产的客户

  • 出售该房产的房地产经纪人

  • 房产类型:独栋住宅、公寓、顶层公寓、城堡……

  • 描述该房产的标签列表:温馨、翻新……

  • 收到客户的报价列表

1、Many2one

在本节结束时:

应创建一个新的 estate.property.type 模型,并配有相应的菜单、操作和视图。

应在 estate.property 模型中添加三个 Many2one 字段:房产类型、买方和卖方。

estate模块中,我们希望定义“房产类型”这一概念。房产类型可以是房屋或公寓等。根据房产类型进行分类是标准的业务需求,尤其是为了优化筛选功能。

一个房产可以属于一种类型,但同一种类型可以被分配给多个房产。这通过“多对一”的概念来实现。

“多对一”是一种指向另一个对象的简单关联。例如,为了测试模型中定义与 res.partner 的关联,可以这样写:

python
partner_id = fields.Many2one("res.partner", string="Partner")

按照惯例,many2one 字段通常带有 _id 后缀。因此,可以通过以下方式轻松访问伙伴中的数据:

python
print(my_test_object.partner_id.name)

实际上,多对一关系可以看作表单视图中的下拉列表。

添加“房地产类型”表

创建 estate.property.type 模型,并添加以下字段:

字段类型属性
nameCharrequired

estate.property 模型及其表单、列表和搜索视图中添加字段 property_type_id

本练习是对前几章内容的良好复习:需要创建一个模型,配置该模型,添加一个操作和一个菜单,并创建一个视图。

提示:需在__init__.py 中导入新的 Python 文件,在 __manifest.py 中添加新的数据文件,或者设置访问权限 。

estate模块中,关于房产信息仍有两项缺失:买家和销售人员。买家可以是任何个人,但销售人员必须是房地产中介公司的员工(即 Odoo 用户)。

在 Odoo 中,有两个常提到(用到)的模型:

  • res.partner:合作伙伴是指自然人或法人实体。它可以是公司、个人,甚至是联系地址。

  • res.users:系统的用户。用户可以是“内部”用户,即他们有权访问 Odoo 后台。也可以是“门户”用户,即无法访问后台,仅能访问前端(例如:在电子商务中查看其历史订单)。

实践:添加买家和销售员

请利用上述提到的两个通用模型,将买家和销售员添加到 estate.property 模型中。在表单视图的新选项卡中添加这些字段。销售员的默认值必须为当前用户。买家字段不应被复制。

对象 self.env 提供了对请求参数及其他有用信息的访问:

  • self.env.crself._cr 是数据库游标对象;用于查询数据库;

  • self.env.uidself._uid 是当前用户的数据库 ID;

  • self.env.user 是当前用户的记录;

  • self.env.contextself._context 是上下文字典;

  • self.env.ref(xml_id) 返回与 XML ID 对应的记录;

  • self.env[model_name] 返回指定模型的实例;

2、Many2many

参考:有关此主题的文档可在 Many2many 中找到。

创建一个新的 estate.property.tag 模型,并配以相应的菜单和操作。

应在 estate.property 模型中添加标签:

我们有时希望根据房产的实际情况自由定义“房产标签”这一概念。例如,“温馨”或“翻新”就是房产标签。一个房产可以拥有多个标签,一个标签也可以被分配给多个房产。这通过“多对多”关系的概念来实现。

多对多关系是一种双向的多重关系:一侧的任何记录都可以与另一侧的任意数量的记录建立关联。例如,为了在测试模型中定义与 account.tax 模型的关联,可以这样写:

python
tax_ids = fields.Many2many("account.tax", string="Taxes")

按照惯例,多对多字段通常带有 _ids 后缀。这意味着可以在测试模型中添加多个税目。它表现为一个记录列表,因此需要通过循环来访问数据:

python
for tax in my_test_object.tax_ids:
    print(tax.name)

记录的集合称为记录集,即按顺序排列的记录集合。它支持 Python 集合的标准操作,例如 len()iter(),还支持诸如 recs1 | recs2 之类的额外集合运算。

实践练习:添加房地产属性标签(estate.property.tag)表

创建 estate.property.tag 模型,并添加以下字段:

字段类型属性
nameCharrequired

estate.property 模型及其表单和列表视图中添加字段 tag_ids,在视图中,使用 widget="many2many_tags" 属性。

property 模型中定义的tag_ids 字段:

py
tag_ids = fields.Many2many(
    'estate.property.tag',
    string='Tags',
    # 自定义中间表名、当前模型外键、关联模型外键
    relation='estate_property_tag_rel',
    column1='property_id',
    column2='tag_id'
)

3、One2many

创建一个新的 estate.property.offer 模型,并配以相应的表单和列表视图。向 estate.property 模型添加 offers字段:

在我们的房地产模块中,希望定义“房产报价”这一概念。房产报价是指潜在买家向卖家提出的金额。该报价可能低于或高于预期价格。

一个报价对应一处房产,但同一处房产可能存在多个报价。这里再次出现了“多对一”的关系。不过,在这种情况下,我们希望显示某处房产的报价列表,因此将使用“一对多”的关系。

“一对多”是“多对一”的逆关系。例如,在test模型中通过 partner_id 字段定义了与 res.partner 模型的关联。可以定义其逆关系,即与partner关联的test模型列表:

python
test_ids = fields.One2many("test_model", "partner_id", string="Tests")

第一个参数称为“comodel”,第二个参数则是要进行逆向计算的字段。

按照惯例,一对多字段的名称后缀为 _ids。它们的行为类似于记录列表,可通过循环来访问数据:

python
for test in partner.test_ids:
    print(test.name)

注意(重要):由于 One2many 是一种虚拟关系,因此必须在共模中定义一个 Many2one 字段。

实践:添加“房地产报价”表

创建 estate.property.offer 模型,并添加以下字段:

字段类型属性价值
priceFloatno copyAccepted, Refused
statusSelectionrequired
partner_idMany2one (res.partner)required
property_idMany2one (estate.property)required

创建一个列表视图和一个表单视图,包含 pricepartner_idstatus 字段。无需创建操作或菜单。按照本节目标中的说明,将 offer_ids 字段添加到 estate.property 模型及其表单视图中。

这里有几点重要事项需要注意。首先,并非所有模型都需要操作或菜单。有些模型的设计初衷就是仅通过另一个模型进行访问。本练习中的情况正是如此:报价总是通过房产进行访问。

其次,尽管 property_id 字段是必填的,但并未将其包含在视图中。Odoo 是如何知道报价与哪处房产相关联的?这正是使用 Odoo 框架的妙处之一:有时定义是隐式的。当通过一个 one2many 字段创建记录时,相应的 many2one 字段会为了方便而自动填充。

最近更新