第7章:模型之间的关系

第 7 章:模型之间的关系

上一章介绍了为包含基本字段的模型创建自定义视图。然而,在任何实际业务场景中,我们都需要不止一个模型。此外,模型之间的链接也是必要的。我们可以轻松想象一个模型包含客户,另一个模型包含用户列表。您可能需要在任何现有业务模型上引用客户或用户。

 

在我们的房地产模块中,我们需要房地产的以下信息:

购买房产的客户

出售房产的房地产经纪人

房产类型:住宅、公寓、顶层公寓、城堡......

描述房产特征的标签列表:舒适、翻新...

收到的报价清单

 

我们将完成以下功能:

新的estate.property.type模型应与相应的菜单、操作和视图一起创建。

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

 

在房地产模块中,我们要定义房产类型的概念。例如,房产类型是指房屋或公寓。根据物业类型对物业进行分类是一种标准的业务需求,尤其是为了细化筛选。

一个属性可以只有一种类型,但同一类型可以分配给多个属性。many2one 概念支持这一点。

many2one 是指向另一个对象的简单链接。例如,为了在测试模型中定义一个指向 res.partner 的链接,我们可以这样写:

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

按照惯例,many2one 字段的后缀是 _id。这样,访问伙伴中的数据就很容易了:

print(my_test_object.partner_id.name)

在实践中,many2one可以看作是表单视图中的下拉列表。

 

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

Field     Type   Attributes

name    Char   required

 

修改我们之前创建的models/estate_property.py

from odoo import models, fields, api

from dateutil.relativedelta import relativedelta

 

class EstateProperty(models.Model):

    _name = "estate_property"

    _description = "Estate Property"

 

    def _get_default_date_available(self):

        # 使用 fields.Date.today() 获取当前日期,并添加三个月

        return fields.Date.today() + relativedelta(months=+3)

    name = fields.Char(required=True, default="Unknown")

    description = fields.Text()

    postcode = fields.Char()

    date_available = fields.Date(copy=False, default=_get_default_date_available)

    expected_price = fields.Float(required=True)

    selling_price = fields.Float(readonly=True, copy=False)

    bedrooms = fields.Integer()

    living_area = fields.Integer()

    facades = fields.Integer()

    garage = fields.Boolean()

    garden = fields.Boolean()

    garden_area = fields.Integer()

    garden_orientation = fields.Selection([

        ("north", "North"),

        ("south", "South"),

        ("east", "East"),

        ("west", "West"),

    ])

    active = fields.Boolean(default=True)

    state = fields.Selection([

        ("new", "New"),

        ("offer_received", "Offer Received"),

        ("offer_accepted", "Offer Accepted"),

        ("sold", "Sold"),

        ("canceled", "Canceled"),

    ], default="new", copy=False)

    property_type_id = fields.Many2one("estate_property_type", string="Property Type")

 

class EstatePropertyType(models.Model):

    _name = "estate_property_type"

    _description = "Estate Property Type"

 

    name = fields.Char(required=True)

 

提示:不要忘记在__init__.py中导入任何新的 Python 文件,在__manifest__.py中添加新的数据文件或添加访问权限 ;-)

再次重启服务器并刷新以查看结果!

 

在房地产模块中,我们仍然缺少关于房产的两项信息:买方partner和销售人员salesperson。买方可以是任何个人,但另一方面,销售人员必须是房地产公司的员工(即Odoo用户)。

在Odoo中,我们有两个模型model我们是经常用到的:

res.partner:合作伙伴是一个物理或法律实体。它可以是公司、个人,甚至是一个联系地址。

res.users:系统用户。用户可以是 "内部 "用户,即他们可以访问Odoo后台。也可以是 "门户 "用户,即他们不能访问后台,只能访问前台(例如,在电子商务中访问他们以前的订单)。

下面给我们的estate_property模型加上:

user_id = fields.Many2one("res.users", string="Salesperson", default=lambda self: self.env.user)

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

注意user_id的默认值必须是当前用户。合作伙伴应该不能被复制。

注意

对象self.env允许访问请求参数和其他有用信息:

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

self.env.uid或self._uid是当前用户的数据库 ID

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

self.env.context或self._context是上下文字典

self.env.ref(xml_id)返回XML id对应的记录

self.env[model_name]返回给定模型的实例

现在让我们看看其他类型的链接。

 

在房地产模块中,我们要定义房产标签的概念。例如,"舒适 "或 "装修"的房产就是房产标签。

一个房产可以有多个标签,一个标签可以分配给多个房产。这可以通过many2many概念来实现。

many2many是一种双向多重关系:一方的任何记录都可以与另一方的任意数量的记录相关联。例如,为了在我们的测试模型上定义一个与 account.tax 模型的链接,我们可以这样写:

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

按照惯例,many2many 字段的后缀是 _ids。这意味着我们可以在测试模型中添加多个税项。它的行为就像一个记录列表,这意味着必须在循环中访问数据:

for tax in my_test_object.tax_ids:

    print(tax.name)

 

记录列表被称为记录集,即记录的有序集合。它支持标准的 Python 集合操作,如len()和 iter(),以及额外的集合操作,如 recs1 | recs2。

 

添加房地产属性标签表

class EstatePropertyTag(models.Model):

    _name = "estate_property_tag"

    _description = "Estate Property Tag"

 

name = fields.Char(required=True)

 

提示:在视图中,使用 widget="many2many_tags" 属性。widget属性将在后面的培训章节中详细讲解。现在,你可以尝试添加或删除它,看看效果如何;-)

 

以下是我们现在的estate_property_views.xml的完整代码:

<?xml version="1.0"?>

<odoo>

    <record id="estate_property_action" model="ir.actions.act_window">

        <field name="name">房产列表</field>

        <field name="res_model">estate_property</field>

        <field name="view_mode">tree,form</field>

    </record>

    <record id="estate_property_type_action" model="ir.actions.act_window">

        <field name="name">房产类型</field>

        <field name="res_model">estate_property_type</field>

        <field name="view_mode">tree,form</field>

    </record>

    <record id="estate_property_tag_action" model="ir.actions.act_window">

        <field name="name">房产标签</field>

        <field name="res_model">estate_property_tag</field>

        <field name="view_mode">tree,form</field>

    </record>

 

    <record id="estate_property_view_tree" model="ir.ui.view">

        <field name="name">房产列表</field>

        <field name="model">estate_property</field>

        <field name="arch" type="xml">

            <tree string="房产列表">

                <field name="name"/>

                <field name="bedrooms"/>

                <field name="living_area"/>

                <field name="expected_price"/>

                <field name="selling_price"/>

                <field name="date_available"/>

            </tree>

        </field>

    </record>

 

    <record id="estate_property_view_form" model="ir.ui.view">

        <field name="name">房产详情</field>

        <field name="model">estate_property</field>

        <field name="arch" type="xml">

            <form string="详情">

                <sheet>

                    <div class="oe_title">

                        <h1>

                            <field name="name"/>

                        </h1>

                    </div>

                    <field name="tag_ids" widget="many2many_tags"/>

                    <group name="estate_property_header">

                        <group name="estate_property_details">

                            <group col="2">

                                <field name="property_type_id"/>

                                <field name="postcode"/>

                                <field name="expected_price"/>

                            </group>

                            <group col="2">

                                <field name="date_available"/>

                                <field name="selling_price"/>

                            </group>

                        </group>

                        <notebook>

                            <page string="Description">

                                <group name="estate_property_details">

                                    <field name="description"/>

                                    <field name="bedrooms"/>

                                    <field name="living_area"/>

                                    <field name="facades"/>

                                    <field name="garage"/>

                                    <field name="garden_area"/>

                                    <field name="garden_orientation"/>

                                    <field name="state"/>

                                </group>

                            </page>

                            <page string="Other info">

                                <group name="estate_property_details">

                                    <field name="user_id"/>

                                    <field name="partner_id"/>

                                </group>

                            </page>

                        </notebook>

                    </group>

                </sheet>

            </form>

        </field>

    </record>

 

    <record id="view_estate_property_search" model="ir.ui.view">

        <field name="name">estate_property</field>

        <field name="model">estate_property</field>

        <field name="arch" type="xml">

            <search string="estate_property">

                <field name="bedrooms" />

                <field name="postcode" />

                <field name="living_area" />

                <field name="property_type_id" />

                <separator/>

                <filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>

                <filter string="New or Offer Accepted" name="new_offer_accepted" domain="[('state', 'in', ['new', 'offer_accepted'])]"/>

                <group expand="1" string="Group By">

                    <filter string="邮编分组" name="postcode" context="{'group_by':'postcode'}"/>

                </group>

            </search>

        </field>

    </record>

</odoo>

 

model模型文件estate_property.py的完整代码如下:

from odoo import models, fields, api

from dateutil.relativedelta import relativedelta

 

class EstateProperty(models.Model):

    _name = "estate_property"

    _description = "Estate Property"

 

    def _get_default_date_available(self):

        # 使用 fields.Date.today() 获取当前日期,并添加三个月

        return fields.Date.today() + relativedelta(months=+3)

    name = fields.Char(required=True, default="Unknown")

    description = fields.Text()

    postcode = fields.Char()

    date_available = fields.Date(copy=False, default=_get_default_date_available)

    expected_price = fields.Float(required=True)

    selling_price = fields.Float(readonly=True, copy=False)

    bedrooms = fields.Integer()

    living_area = fields.Integer()

    facades = fields.Integer()  # 外墙

    garage = fields.Boolean()

    garden = fields.Boolean()

    garden_area = fields.Integer()

    garden_orientation = fields.Selection([

        ("north", "North"),

        ("south", "South"),

        ("east", "East"),

        ("west", "West"),

    ])

    active = fields.Boolean(default=True)

    state = fields.Selection([

        ("new", "New"),

        ("offer_received", "Offer Received"),

        ("offer_accepted", "Offer Accepted"),

        ("sold", "Sold"),

        ("canceled", "Canceled"),

    ], default="new", copy=False)

    property_type_id = fields.Many2one("estate_property_type", string="Property Type")

    user_id = fields.Many2one("res.users", string="Salesperson", default=lambda self: self.env.user)

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

    tag_ids = fields.Many2many("estate_property_tag", string="Tags")

 

 

class EstatePropertyType(models.Model):

    _name = "estate_property_type"

    _description = "Estate Property Type"

 

    name = fields.Char(required=True)

 

class EstatePropertyTag(models.Model):

    _name = "estate_property_tag"

    _description = "Estate Property Tag"

 

name = fields.Char(required=True)

 

在房地产模块中,我们要定义房产出价的概念。房产出价是潜在买家向卖家提出的金额。出价可以低于或高于预期价格。

一个出价适用于一处房产,但同一房产可以有多个出价。many2one的概念再次出现。不过,在这种情况下,我们希望显示给定房产的出价列表,因此我们将使用one2many概念。

one2many是many2one的逆函数。例如,我们在测试模型中通过partner_id字段定义了一个指向res.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中必须定义一个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

 

在estate_property模型中添加以下字段:

offer_ids = fields.One2many("estate_property_offer", "property_id", string="Offers")

新增estate_property_offer表:

class EstatePropertyOffer(models.Model):

    _name = "estate_property_offer"

    _description = "Estate Property Offer"

 

    price = fields.Float()

    status = fields.Selection([

        ("accepted", "Accepted"),

        ("refused", "Refused"),

    ],copy=False)

    partner_id = fields.Many2one("res.partner")

    property_id = fields.Many2one("estate_property")

 

仍然活着?这一章绝对不是最简单的一章。它引入了几个新概念 同时依赖于之前引入的所有内容。下一章会更轻松,别担心 ;-)

第7章:模型之间的关系
谢潮聪 2024年12月12日
分析这篇文章

存档
登录 留下评论
第6章:基本视图