Chapter 11: Add The Sprinkles
第十一章:添加最后的点缀
原文来自:
https://www.odoo.com/documentation/17.0/zh_CN/developer/tutorials/server_framework_101/11_sprinkles.html
使用通义千问翻译。
Our real estate module now makes sense from a business perspective. We created specific views, added several action buttons and constraints. However our user interface is still a bit rough. We would like to add some colors to the list views and make some fields and buttons conditionally disappear. For example, the ‘Sold’ and ‘Cancel’ buttons should disappear when the property is sold or canceled since it is no longer allowed to change the state at this point.
我们的房地产模块现在从商业角度来看已经合情合理。我们创建了特定的视图,添加了多个操作按钮以及约束条件。然而,我们的用户界面仍然有点粗糙。我们想要给列表视图添加一些颜色,并且根据条件让某些字段和按钮消失。例如,“出售”和“取消”按钮在房产已经售出或取消时应该消失,因为在这一点上不再允许改变状态。
This chapter covers a very small subset of what can be done in the views. Do not hesitate to read the reference documentation for a more complete overview.
本章覆盖了在视图中可以做到的非常小的一部分功能。不要犹豫,阅读参考文档以获得更全面的概述。
Reference: the documentation related to this chapter can be found in View records and View architectures.
参考:与本章相关的文档可以在“查看记录”和“查看架构”中找到。
Inline Views 内联视图
注解
Goal: at the end of this section, a specific list of properties should be added to the property type view:
目标:在本节结束时,应在属性类型视图中添加一个特定的属性列表:
In the real estate module we added a list of offers for a property. We simply added the field offer_ids
with:
在房地产模块中,我们为一处房产添加了一个报价列表。我们仅仅通过添加字段offer_ids
做到了这一点:
<field name="offer_ids"/>
The field uses the specific view for estate.property.offer
. In some cases we want to define a specific list view which is only used in the context of a form view. For example, we would like to display the list of properties linked to a property type. However, we only want to display 3 fields for clarity: name, expected price and state.
该字段使用了专门针对estate.property.offer
的视图。在某些情况下,我们希望定义一个特定的列表视图,它只在表单视图的上下文中使用。例如,我们想要显示与房产类型关联的房产列表。但是,为了清晰起见,我们只希望显示3个字段:名称、预期价格和状态。
To do this, we can define inline list views. An inline list view is defined directly inside a form view. For example:
要做到这一点,我们可以定义内联列表视图。内联列表视图直接定义在表单视图内部。例如:
from odoo import fields, models class TestModel(models.Model): _name = "test_model" _description = "Test Model" description = fields.Char() line_ids = fields.One2many("test_model_line", "model_id") class TestModelLine(models.Model): _name = "test_model_line" _description = "Test Model Line" model_id = fields.Many2one("test_model") field_1 = fields.Char() field_2 = fields.Char() field_3 = fields.Char()
<form> <field name="description"/> <field name="line_ids"> <tree> <field name="field_1"/> <field name="field_2"/> </tree> </field> </form>
In the form view of the test_model
, we define a specific list view for test_model_line
with fields field_1
and field_2
.
在test_model
的表单视图中,我们为test_model_line
定义了一个包含字段field_1
和field_2
的特定列表视图。
An example can be found here. 一个例子可以在这里找到。
Exercise
Add an inline list view. 添加一个内联列表视图。
- Add the
One2many
fieldproperty_ids
to theestate.property.type
model.
向estate.property.type
模型中添加多对一字段property_ids
。 - Add the field in the
estate.property.type
form view as depicted in the Goal of this section.
按照本节目标中描述的方式,在estate.property.type
的表单视图中添加该字段。
Widgets 控件
Reference: the documentation related to this section can be found in Fields.
参考:与本节相关的内容可以在“字段”文档中找到。
注解
Goal: at the end of this section, the state of the property should be displayed using a specific widget:
目标:在本节结束时,应使用特定的控件来显示房产的状态:
Four states are displayed: New, Offer Received, Offer Accepted and Sold.
显示四种状态:新、收到报价、报价已接受和已售。
Whenever we’ve added fields to our models, we’ve (almost) never had to worry about how these fields would look like in the user interface. For example, a date picker is provided for a Date
field and a One2many
field is automatically displayed as a list. Odoo chooses the right ‘widget’ depending on the field type.
每当我们向模型添加字段时,我们(几乎)从未担心过这些字段在用户界面中的表现形式。例如,为日期字段提供了日期选择器,多对一字段会自动显示为列表。Odoo会根据字段类型选择正确的“控件”。
However, in some cases, we want a specific representation of a field which can be done thanks to the widget
attribute. We already used it for the tag_ids
field when we used the widget="many2many_tags"
attribute. If we hadn’t used it, then the field would have displayed as a list.
然而,在某些情况下,我们想要对字段的表示方式进行特定定制,这可以通过使用widget
属性实现。我们在tag_ids
字段上使用widget="many2many_tags"
属性时就已经用到了它。如果我们没有使用它,那么该字段将以列表的形式显示。
Each field type has a set of widgets which can be used to fine tune its display. Some widgets also take extra options. An exhaustive list can be found in Fields.
每种字段类型都有一套可用的控件,可用于微调其显示方式。某些控件还接受额外的选项。完整的列表可以在“字段”文档中找到。
Exercise
Use the status bar widget. 使用状态栏控件。
Use the statusbar
widget in order to display the state
of the estate.property
as depicted in the Goal of this section.
使用状态栏控件来显示如本节目标中所述的estate.property
的状态。
Tip: a simple example can be found here. 提示:一个简单的示例可以在这里找到。
警告
Same field multiple times in a view 同一视图中多次添加相同字段
Add a field only once to a list or a form view. Adding it multiple times is not supported.
仅将字段添加到列表或表单视图一次。多次添加同一字段不受支持。
List Order 列表排序
Reference: the documentation related to this section can be found in Models.
参考:与本节相关的内容可以在“模型”文档中找到。
注解
Goal: at the end of this section, all lists should display by default in a deterministic order. Property types can be ordered manually.
目标:在本节结束时,所有列表应默认以确定的顺序显示。房产类型可手动排序。
During the previous exercises, we created several list views. However, at no point did we specify which order the records had to be listed in by default. This is a very important thing for many business cases. For example, in our real estate module we would want to display the highest offers on top of the list.
在之前的练习中,我们创建了多个列表视图。然而,我们从未指定记录默认应按何种顺序列出。这对于许多业务案例来说非常重要。例如,在我们的房地产模块中,我们希望将最高的报价列在列表的顶部。
Model
Odoo provides several ways to set a default order. The most common way is to define the _order
attribute directly in the model. This way, the retrieved records will follow a deterministic order which will be consistent in all views including when records are searched programmatically. By default there is no order specified, therefore the records will be retrieved in a non-deterministic order depending on PostgreSQL.
Odoo提供了几种设置默认排序的方法。最常见的方式是在模型中直接定义_order
属性。这样,检索到的记录将遵循确定的顺序,在所有视图中都将保持一致,即使是在程序化地搜索记录时也是如此。默认情况下没有指定排序,因此记录将以非确定的顺序检索,这取决于PostgreSQL。
The _order
attribute takes a string containing a list of fields which will be used for sorting. It will be converted to an order_by clause in SQL. For example:_order
属性接收一个包含用于排序的字段列表的字符串。它将转换为SQL中的ORDER BY
子句。例如:
from odoo import fields, models class TestModel(models.Model): _name = "test_model" _description = "Test Model" _order = "id desc" description = fields.Char()
Our records are ordered by descending id
, meaning the highest comes first.
我们的记录按递减的ID排序,意味着最高的ID排在首位。
Exercise
Add model ordering. 添加模型排序。
Define the following orders in their corresponding models:
在相应的模型中定义以下排序:
Model | Order |
---|---|
estate.property | Descending ID |
estate.property.offer | Descending Price |
estate.property.tag | Name |
estate.property.type | Name |
View 视图
Ordering is possible at the model level. This has the advantage of a consistent order everywhere a list of records is retrieved. However, it is also possible to define a specific order directly in a view thanks to the default_order
attribute (example).
排序可在模型级别实现。这具有在检索记录列表的所有地方都保持一致顺序的优势。然而,也可以通过default_order
属性在视图中直接定义特定的顺序(示例)。
Manual 手动
Both model and view ordering allow flexibility when sorting records, but there is still one case we need to cover: the manual ordering. A user may want to sort records depending on the business logic. For example, in our real estate module we would like to sort the property types manually. It is indeed useful to have the most used types appear at the top of the list. If our real estate agency mainly sells houses, it is more convenient to have ‘House’ appear before ‘Apartment’.
模型和视图排序在排序记录方面提供了灵活性,但仍有一个情况需要涵盖:手动排序。用户可能希望根据业务逻辑对记录进行排序。例如,在我们的房地产模块中,我们希望手动排序房产类型。确实,让最常用的类型出现在列表的顶部是有帮助的。如果我们主要销售房屋,那么“House”出现在“Apartment”之前更为方便。
To do so, a sequence
field is used in combination with the handle
widget. Obviously the sequence
field must be the first field in the _order
attribute.
为此,需要使用序列字段结合handle
控件。显然,序列字段必须是_order
属性中的第一个字段。
Exercise
Add manual ordering. 添加手动排序。
- Add the following field: 添加以下字段:
Model | Field | Type |
---|---|---|
estate.property.type | Sequence | Integer |
- Add the sequence to the
estate.property.type
list view with the correct widget.
将序列字段添加到estate.property.type
的列表视图中,并使用正确的控件。
Tip: you can find an example here: model and view. 提示:您可以在此处找到示例:模型和视图。
Attributes and options 属性和选项
It would be prohibitive to detail all the available features which allow fine tuning of the look of a view. Therefore, we’ll stick to the most common ones.
详尽介绍所有可用的特性来微调视图的外观将是成本过高的。因此,我们将只坚持介绍最常用的一些。
Form 表单
注解
Goal: at the end of this section, the property form view will have:
目标: 在本节结束时,属性表单视图将具备以下特点:
- Conditional display of buttons and fields
条件性显示按钮和字段 - Tag colors
标签颜色
In our real estate module, we want to modify the behavior of some fields. For example, we don’t want to be able to create or edit a property type from the form view. Instead we expect the types to be handled in their appropriate menu. We also want to give tags a color. In order to add these behavior customizations, we can add the options
attribute to several field widgets.
在我们的房地产模块中,我们想要修改一些字段的行为。例如,我们不想能够在表单视图中创建或编辑物业类型。相反,我们期望类型在它们适当的菜单中被处理。我们还想要给标签上色。为了添加这些行为定制,我们可以给多个字段控件添加选项属性。
Exercise
Add widget options. 添加控件选项。
- Add the appropriate option to the
property_type_id
field to prevent the creation and the editing of a property type from the property form view. Have a look at the Many2one widget documentation for more info.
给property_type_id
字段添加适当的选项,以防止在物业表单视图中创建和编辑物业类型。更多信息请查看Many2one控件文档。 - Add the following field:
添加以下字段:
Model | Field | Type |
---|---|---|
estate.property.tag | Color | Integer |
Then add the appropriate option to the tag_ids
field to add a color picker on the tags. Have a look at the FieldMany2ManyTags widget documentation for more info.
然后给tag_ids
字段添加适当的选项,以在标签上添加颜色选择器。更多信息请查看FieldMany2ManyTags控件文档。
In Chapter 5: Finally, Some UI To Play With, we saw that reserved fields were used for specific behaviors. For example, the active
field is used to automatically filter out inactive records. We added the state
as a reserved field as well. It’s now time to use it! A state
field can be used in combination with an invisible
attribute in the view to display buttons conditionally.
在第5章:最后,一些UI来玩,我们看到保留字段用于特定行为。例如,active字段用于自动过滤非活动记录。我们也把state作为保留字段添加了。现在使用它的时候到了!一个state字段可以与视图中的invisible属性结合使用,以条件性地显示按钮。
Exercise
Add conditional display of buttons. 添加条件性显示按钮。
Use the invisible
attribute to display the header buttons conditionally as depicted in this section’s Goal (notice how the ‘Sold’ and ‘Cancel’ buttons change when the state is modified).
使用invisible属性条件性地显示标题按钮,如本节目标所示(注意当状态被修改时‘Sold’和‘Cancel’按钮是如何变化的)。
Tip: do not hesitate to search for invisible=
in the Odoo XML files for some examples.
小贴士:不要犹豫在Odoo XML文件中搜索invisible=以获取一些示例。
More generally, it is possible to make a field invisible
, readonly
or required
based on the value of other fields. Note that invisible
can also be applied to other elements of the view such as button
or group
.
更一般地说,可以根据其他字段的值使字段不可见、只读或必填。请注意,invisible也可以应用于视图的其他元素,如按钮或组。
invisible
, readonly
and required
can have any Python expression as value. The expression gives the condition in which the property applies. For example:
invisible、readonly和required可以有任意Python表达式作为值。表达式给出了属性适用的条件。例如:
<form> <field name="description" invisible="not is_partner"/> <field name="is_partner" invisible="True"/> </form>
This means that the description
field is invisible when is_partner
is False
. It is important to note that a field used in invisible
must be present in the view. If it should not be displayed to the user, we can use the invisible
attribute to hide it.
这意味着当is_partner为False时,description字段是不可见的。重要的是要注意,invisible中使用的字段必须在视图中存在。如果它不应该显示给用户,我们可以使用invisible属性来隐藏它。
Exercise
Use invisible
. 使用invisible。
- Make the garden area and orientation invisible in the
estate.property
form view when there is no garden.
当没有花园时,在estate.property表单视图中使花园区域和方向不可见。 - Make the ‘Accept’ and ‘Refuse’ buttons invisible once the offer state is set.
一旦报价状态被设置,使‘Accept’和‘Refuse’按钮不可见。 - Do not allow adding an offer when the property state is ‘Offer Accepted’, ‘Sold’ or ‘Canceled’. To do this use the
readonly
attribute.
当物业状态为‘Offer Accepted’、‘Sold’或‘Canceled’时,不允许添加报价。为此使用readonly属性。
警告
Using a (conditional) readonly
attribute in the view can be useful to prevent data entry errors, but keep in mind that it doesn’t provide any level of security! There is no check done server-side, therefore it’s always possible to write on the field through a RPC call.
在视图中使用(条件性)readonly属性可以防止数据输入错误,但请记住它不提供任何安全级别!服务器端没有进行检查,因此总是可以通过RPC调用来写入字段。
List 列表
注解
Goal: at the end of this section, the property and offer list views should have color decorations. Additionally, offers and tags will be editable directly in the list, and the availability date will be hidden by default.
目标: 在本节结束时,物业和报价的列表视图应该有颜色装饰。此外,报价和标签将可以直接在列表中编辑,且可用性日期默认隐藏。
When the model only has a few fields, it can be useful to edit records directly through the list view and not have to open the form view. In the real estate example, there is no need to open a form view to add an offer or create a new tag. This can be achieved thanks to the editable
attribute.
当模型只有少数字段时,通过列表视图直接编辑记录而不必打开表单视图可能是有用的。在房地产示例中,没有必要打开表单视图来添加报价或创建新标签。这可以通过editable属性实现。
Exercise
Make list views editable. 使列表视图可编辑
Make the estate.property.offer
and estate.property.tag
list views editable.
使estate.property.offer和estate.property.tag列表视图可编辑。
On the other hand, when a model has a lot of fields it can be tempting to add too many fields in the list view and make it unclear. An alternative method is to add the fields, but make them optionally hidden. This can be achieved thanks to the optional
attribute.
另一方面,当模型有很多字段时,可能会想在列表视图中添加太多字段,使其变得不清晰。一种替代方法是添加字段,但使它们可选隐藏。这可以通过optional属性实现。
Exercise
Make a field optional. 使字段成为可选。
Make the field date_availability
on the estate.property
list view optional and hidden by default.
使estate.property列表视图中的date_availability字段成为可选并默认隐藏。
Finally, color codes are useful to visually emphasize records. For example, in the real estate module we would like to display refused offers in red and accepted offers in green. This can be achieved thanks to the decoration-{$name}
attribute (see Fields for a complete list):
最后,颜色代码有助于视觉上强调记录。例如,在房地产模块中,我们希望将被拒绝的报价显示为红色,接受的报价显示为绿色。这可以通过decoration-{$name}属性实现(参阅字段以获取完整列表):
<tree decoration-success="is_partner==True"> <field name="name"/> <field name="is_partner" invisible="1"/> </tree>
The records where is_partner
is True
will be displayed in green.
is_partner为True的记录将以绿色显示。
Exercise
Add some decorations. 添加一些装饰。
On the estate.property
list view: 在estate.property列表视图中:
- Properties with an offer received are green
收到报价的物业是绿色的 - Properties with an offer accepted are green and bold
接受报价的物业是绿色且加粗的 - Properties sold are muted
已售出的物业是柔和的
On the estate.property.offer
list view: 在estate.property.offer列表视图中:
- Refused offers are red 被拒绝的报价是红色的
- Accepted offers are green 被接受的报价是绿色的
- The state should not be visible anymore 状态不再可见
Tips: 提示:
- Keep in mind that all fields used in attributes must be in the view!
请记住,所有在属性中使用的字段都必须在视图中! - If you want to test the color of the “Offer Received” and “Offer Accepted” states, add the field in the form view and change it manually (we’ll implement the business logic for this later).
如果你想测试“收到报价”和“报价被接受”的颜色,请在表单视图中添加字段并手动更改它(我们将在稍后实现此业务逻辑)。
Search 搜索
Reference: the documentation related to this section can be found in Search and Search defaults.
参考:本节相关的文档可以在搜索和搜索默认值中找到。
注解
Goal: at the end of this section, the available properties will be filtered by default, and searching on the living area returns results where the area is larger than the given number.
目标: 在本节结束时,默认情况下可用的属性将被过滤,并且搜索生活区域将返回面积大于给定数值的结果。
Last but not least, there are some tweaks we would like to apply when searching. First of all, we want to have our ‘Available’ filter applied by default when we access the properties. To make this happen, we need to use the search_default_{$name}
action context, where {$name}
is the filter name. This means that we can define which filter(s) will be activated by default at the action level.
最后但同样重要的是,我们在搜索时想要应用一些微调。首先,我们希望在访问属性时默认应用“可用”过滤器。为了实现这一点,我们需要使用search_default_{$name}
动作上下文,其中{$name}
是过滤器名称。这意味着我们可以定义在动作级别默认激活哪些过滤器。
Here is an example of an action with its corresponding filter.
以下是一个带有对应过滤器的动作示例。
Exercise
Add a default filter. 添加一个默认过滤器。
Make the ‘Available’ filter selected by default in the estate.property
action.
在房地产属性动作中,默认选择“可用”过滤器。
Another useful improvement in our module would be the ability to search efficiently by living area. In practice, a user will want to search for properties of ‘at least’ the given area. It is unrealistic to expect users would want to find a property of an exact living area. It is always possible to make a custom search, but that’s inconvenient.
在我们模块中另一个有用的改进是能够通过生活区域高效搜索。实际上,用户将想要搜索“至少”给定面积的属性。期望用户寻找完全精确的生活区域是不现实的。虽然总是可以进行自定义搜索,但这很不方便。
Search view <field>
elements can have a filter_domain
that overrides the domain generated for searching on the given field. In the given domain, self
represents the value entered by the user. In the example below, it is used to search on both name
and description
fields.
搜索视图<field>
元素可以有一个filter_domain
,它覆盖了为搜索给定字段生成的域。在给定域中,self
表示用户输入的值。以下示例中,它用于在名称和描述字段上进行搜索。
xml
<search string="Test"> <field name="description" string="Name and description" filter_domain="['|', ('name', 'ilike', self), ('description', 'ilike', self)]"/> </search>
Exercise
Change the living area search. 更改生活区域搜索。
Add a filter_domain
to the living area to include properties with an area equal to or greater than the given value.
向生活区域添加一个filter_domain
,以包括面积等于或大于给定值的属性。
Stat Buttons 统计按钮
注解
Goal: at the end of this section, there will be a stat button on the property type form view which shows the list of all offers related to properties of the given type when it is clicked on.
目标: 在本节结束时,物业类型表单视图上将有一个统计按钮,点击时会显示与给定类型相关的所有报价列表。
If you’ve already used some functional modules in Odoo, you’ve probably already encountered a ‘stat button’. These buttons are displayed on the top right of a form view and give a quick access to linked documents. In our real estate module, we would like to have a quick link to the offers related to a given property type as depicted in the Goal of this section.
如果您已经使用过Odoo中的一些功能模块,您可能已经遇到了“统计按钮”。这些按钮显示在表单视图的右上角,可以快速访问相关文档。在我们的房地产模块中,我们希望快速链接到与给定物业类型相关的报价,如本节目标所示。
At this point of the tutorial we have already seen most of the concepts to do this. However, there is not a single solution and it can still be confusing if you don’t know where to start from. We’ll describe a step-by-step solution in the exercise. It can always be useful to find some examples in the Odoo codebase by looking for oe_stat_button
.
在本教程的这一点上,我们已经看到了实现这一点的大多数概念。然而,并没有单一的解决方案,如果您不知道从哪里开始,仍然可能会感到困惑。我们将在练习中描述一个分步解决方案。在Odoo代码库中寻找例子,使用oe_stat_button进行搜索,这总是很有用。如果您卡住了,可能附近有人可以帮助您;)
The following exercise might be a bit more difficult than the previous ones since it assumes you are able to search for examples in the source code on your own. If you are stuck there is probably someone nearby who can help you 😉
这项练习可能比前几项练习更困难,因为它假定您能够自己搜索源代码中的例子。如果您遇到困难,不要担心;)
The exercise introduces the concept of Related fields. The easiest way to understand it is to consider it as a specific case of a computed field. The following definition of the description
field:
这项练习引入了相关字段的概念。最简单的理解方式是将其视为计算字段的特例。以下描述字段的定义:
... partner_id = fields.Many2one("res.partner", string="Partner") description = fields.Char(related="partner_id.name")
is equivalent to: 等同于:
... partner_id = fields.Many2one("res.partner", string="Partner") description = fields.Char(compute="_compute_description") @api.depends("partner_id.name") def _compute_description(self): for record in self: record.description = record.partner_id.name
Every time the partner name is changed, the description is modified.
每次合作伙伴名称更改时,描述也会被修改。
Exercise
Add a stat button to property type. 向物业类型添加一个统计按钮。
- Add the field
property_type_id
toestate.property.offer
. We can define it as a related field onproperty_id.property_type_id
and set it as stored.
向estate.property.offer添加property_type_id字段。我们可以将其定义为property_id.property_type_id的相关字段,并将其设置为存储字段。
Thanks to this field, an offer will be linked to a property type when it’s created. You can add the field to the list view of offers to make sure it works.
得益于这个字段,创建报价时会将其链接到物业类型。您可以将字段添加到报价的列表视图中,以确保它有效。
- Add the field
offer_ids
toestate.property.type
which is the One2many inverse of the field defined in the previous step.
向estate.property.type添加offer_ids字段,它是前一步中定义字段的One2many反向字段。 - Add the field
offer_count
toestate.property.type
. It is a computed field that counts the number of offers for a given property type (useoffer_ids
to do so).
向estate.property.type添加offer_count字段。它是一个计算字段,用于计算给定物业类型有多少报价(使用offer_ids进行计算)。
At this point, you have all the information necessary to know how many offers are linked to a property type. When in doubt, add offer_ids
and offer_count
directly to the view. The next step is to display the list when clicking on the stat button.
到此为止,您已经拥有所有必要的信息,知道有多少报价与物业类型相关联。如果有疑问,直接将offer_ids和offer_count添加到视图中。下一步是点击统计按钮时显示列表。
- Create a stat button on
estate.property.type
pointing to theestate.property.offer
action. This means you should use thetype="action"
attribute (go back to the end of Chapter 9: Ready For Some Action? if you need a refresher).
在estate.property.type上创建一个指向estate.property.offer动作的统计按钮。这意味着您应该使用type=”action”属性(如果您需要复习,请回到第9章:准备好采取行动了吗?)。
At this point, clicking on the stat button should display all offers. We still need to filter out the offers.
到此为止,点击统计按钮应该显示所有报价。我们仍然需要过滤报价。
- On the
estate.property.offer
action, add a domain that definesproperty_type_id
as equal to theactive_id
(= the current record, here is an example)
在estate.property.offer动作上,添加一个域,定义property_type_id等于active_id(即当前记录,这里是一个例子)。
Looking good? If not, don’t worry, the next chapter doesn’t require stat buttons 😉
看起来不错吗?如果不好,不要担心,下一章不要求统计按钮;)
发表回复