odoo13学习---10 后端视图
后端视图
在本章中,我们将介绍以下:
- 添加菜单项和窗口操作
- 用一个动作打开一个特定的视图
- 向表单视图添加内容和小部件(widgets)
- 向表单添加按钮
- 将参数传递给表单和动作——上下文(Context)
- 在记录列表上定义过滤器-域(domain)
- 定义列表视图
- 定义搜索视图
- 在表单视图的一侧显示附件
- 更改现有视图-视图继承
- 定义文档样式表单
- 使用attrs的动态表单元素
- 定义嵌入视图
- 定义看板的视图
- 根据看板卡片的状态在列中显示
- 定义日历和甘特视图
- 定义图形和枢轴视图
- 定义队列视图
- 定义仪表板视图
为用户提供新特性最明显的方法是添加菜单项。当单击菜单项时,会发生一些事情。这个章节教你如何定义某个东西。我们将创建一个顶级菜单及其子菜单,它将打开所有客户的列表。
这也可以通过设置菜单使用web用户界面来完成,但是我们更喜欢使用XML数据文件,因为这是我们在创建add-on模块时必须使用的。
怎么做呢?
在我们的加载模块的XML数据文件中,执行以下步骤:
1. 定义一个要执行的动作:
<act_window id="action_all_customers"
name="All customers"
res_model="res.partner"
view_mode="tree,form"
domain="[('customer', '=', True)]"
context="{'default_customer': True}" />
2. 创建顶级菜单,如下所示:
<menuitem id="menu_custom_top_level"
name="My App menu"
web_icon="my_module,static/description/icon.png"/>
3.参考我们在菜单中的动作:
<menuitem id="menu_all_customers"
parent="menu_custom_top_level"
action="action_all_customers"
sequence="10"/>
如果我们现在升级模块,我们将看到一个打开子菜单的顶级菜单。单击该菜单项将打开所有客户的列表。
它是如何工作的…
第一个XML元素act_window声明了一个窗口操作,用于显示包含所有客户的列表视图。我们使用了最重要的属性:
name:作为操作打开的视图的标题。
res_model:这是要使用的模型。我们正在使用res.partner,其中Odoo商店所有的合作伙伴和地址,包括客户。
view_mode:它列出了可用的视图类型。它是视图类型的逗号分隔值。默认值是list、form,它使列表和表单视图可用。如果你只想显示日历和表单视图,那么view_mode的值将是calendar, form。其他可能的视图选择有kanban, graph, pivot, calendar, cohort, and dashboard。
您将在即将发布的菜谱中了解更多关于这些视图的信息。
domain:这是可选的,允许您对视图中可用的记录设置过滤器。在这种情况下,我们希望将合作伙伴仅限于客户。我们将在本章记录lists-Domain章节的过滤器定义中更详细地看到所有这些视图。
context:这可以设置打开的视图可用的值,从而影响它们的行为。在我们的示例中,对于新记录,我们希望客户标志的默认值为True。这将在本章的“向表单和动作-Context传递参数”中更深入地讨论。
limit:设置在列表视图中可以看到的记录的默认数量。默认值是80。
在遗留代码中,您会经常发现tree视图模式。这是包括Odoo 11在内的列表视图的内部名称。版本12仍然接受此值,但将其视为已编写的list。
接下来,我们创建从顶级菜单到可单击的结束菜单项的菜单项层次结构。menuitem元素最重要的属性如下:
name:它用作菜单项显示的文本。如果您的菜单项链接到一个操作,那么您可以忽略它,因为在这种情况下将使用操作的名称。
parent(如果使用record元素,则使用parent_id):这是引用父菜单项的XML ID。没有父目录的项是顶级菜单。
action:这是引用要调用的动作的XML ID。
sequence:用于对同级菜单项进行排序。
groups(带有记录标签的groups_id):这是一个可选的用户组列表,可以访问菜单项。如果为空,则对所有用户可用。
web_icon:这个选项只在顶级菜单上工作。它将在企业版中显示应用程序的图标。
窗口操作通过查找具有预期类型(form、tree等)的目标模型视图并选择序列号最低的视图来自动确定要使用的视图。
act_window和menuitem是隐藏实际操作的快捷XML标记。如果您不想使用快捷的XML标记,那么您可以通过<record>标记创建ir.actions.act_window和ir.ui.menu模型的记录。例如,如果你想用<record>加载act_window,你可以这样做:
<record id='action_all_customers' model='ir.actions.act_window'>
<field name="name">All customers</field>
<field name="res_model">res.partner</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('customer', '=', True)]</field>
<field name="context">{'default_customer': True}</field>
<field name="limit">20</field>
</record>
同样,您可以通过<record>创建menuitem
请注意,与menuitem快捷方式一起使用的名称可能不会映射到使用<record>元素时使用的字段名称——parent应该是parent_id, group应该是groups_id。
要构建menu, web客户端从ir.ui.menu读取所有记录,并从parent_id字段推断出它们的层次结构。Menus还根据用户对模型和分配给菜单和操作的组的权限进行筛选。当用户单击菜单项时,将执行其action。
有更多的…
窗口操作还支持一个target属性来指定如何显示视图。可能的选择如下:
current:这是默认值,在web客户端主内容区域打开视图。
new:这将在弹出窗口中打开视图。
inline:类似current,但在编辑模式下打开表单并禁用操作菜单。
fullscreen:这个动作会覆盖整个浏览器窗口,所以也会覆盖菜单。有时,这被称为平板模式。
main:像cur_rent,但也清除面包屑。
窗口操作的view_type属性现在基本上已经过时了。另一种选择默认形式是tree,它使分组列表呈现层次结构树。不要将此属性与中使用和解释的view_mode属性混淆“如何工作”部分,它实际上决定了视图的类型使用。odoo13已经取消此属性
对于act_window快捷方式标记不支持的窗口操作,还有一些其他属性可用。要使用它们,我们必须使用<record>记录元素和以下字段:
- res_id:如果打开一个表单,您可以通过在这里设置它的ID来让它打开一个特定的记录。这对于多步骤向导,或者在您必须经常查看或编辑特定记录的情况下非常有用。
- search_view_id:指定用于树视图和图形视图的特定搜索视图。
- auto_search:默认情况下为真。如果搜索对象非常耗费时间和/或资源,则将此设置为False。这样,用户可以查看搜索参数,并在满意时按搜索。使用默认值,当操作打开时,搜索将立即触发。
请记住,左上角的菜单(或者企业版中的app图标)和顶部栏中的菜单都是由菜单项组成的。唯一的区别是,左上角菜单中的项目没有任何父菜单,而顶部栏中的项目将顶部栏中的相应菜单项作为父菜单。在左侧栏中,层次结构更加明显。
还要记住,出于设计原因,如果第二级菜单有子菜单,那么第一级菜单将打开下拉菜单。在任何情况下,Odoo将根据子菜单项的顺序打开第一菜单项的动作。
另请参阅
有关菜单和视图,请参阅以下内容:
- 您将在第7章模块数据中找到关于XML ID引用机制的更详细的讨论。现在,请记住您可以用这种方式设置引用,重要的是,顺序很重要。如果前面的标签是反向的,包含此XML代码的附加组件将不会安装,因为menuitem将引用一个未知的action。当您在开发过程中添加新数据文件和新元素时,这可能是一个陷阱,因为您添加这些文件和元素的顺序不一定反映它们将在空数据库中加载的顺序。在部署之前,始终检查您的附加组件是否安装在一个空的数据库中而没有出现错误。
- ir.actions.act_window操作类型是最常见的操作类型,但是菜单可以引用任何类型的操作。从技术上讲,如果链接到客户端操作、服务器操作或ir.actions.*namespace定义的任何其他模型,情况都是一样的。它只是在后端从动作中得到的不同而已。
- 如果在要调用的具体操作中需要稍微多一点灵活性,那么查看返回窗口操作的服务器操作。如果您需要完全的灵活性,请查看一下客户端操作(ir.actions.client),它允许您拥有一个完全定制的用户界面。然而,只有这样做作为最后的手段,因为你失去了很多Odoo的方便助手时,使用他们。
用一个动作打开一个特定的视图
如果没有给定,窗口操作会自动确定要使用的视图,但有时,我们需要一个操作来打开特定的视图。我们将为res.partner模型创建一个基本表单视图,然后我们将创建一个新的窗口操作专门打开那个表单视图。
怎么做呢?
1. 定义伙伴最小树和表单视图:
<record id="view_all_customers_tree" model="ir.ui.view">
<field name="name">All customers</field>
<field name="model">res.partner</field>
<field name="arch" type="xml">
<tree>
<field name="name" />
</tree>
</field>
</record>
<record id="view_all_customers_form" model="ir.ui.view">
<field name="name">All customers</field>
<field name="model">res.partner</field>
<field name="arch" type="xml">
<form>
<group>
<field name="name" />
</group>
</form>
</field>
</record>
2. 更新操作从添加菜单项和窗口操作配制使用一个新的表单视图:
<record id="action_all_customers_tree" model="ir.actions.act_window.view">
<field name="act_window_id" ref="action_all_customers"/>
<field name="view_id" ref="view_all_customers_tree" />
<field name="view_mode">tree</field>
<field name="sequence" eval="2"/>
</record>
<record id="action_all_customers_form" model="ir.actions.act_window.view">
<field name="act_window_id" ref="action_all_customers"/>
<field name="view_id" ref="view_all_customers_form" />
<field name="view_mode">form</field>
<field name="sequence" eval="2"/>
</record>
现在,如果您打开菜单并单击列表中的一个伙伴,您应该会看到我们刚刚定义的非常小的表单和树。
它是如何工作的…
这一次,我们对任何类型的记录使用通用XML代码,即具有所需id和模型属性的记录元素。record元素的id属性是一个任意的字符串,对于add-on必须是唯一的。model属性引用您想要创建的模型的名称。如果我们想要创建一个视图,我们需要创建ir.ui.view模型的一个记录。在这个元素中,您可以通过model属性设置您选择的模型中定义的字段。对于ir.ui.view,关键字段是model和arch。model字段包含您想要为其定义视图的模型,而arch字段包含视图本身的定义。我们一会儿就会讲到它的内容。
name字段虽然不是严格必需的,但在调试视图问题时非常有用,因此将其设置为一个字符串,告诉您该视图打算做什么。该字段的内容不会显示给用户,因此您可以填写任何您认为合理的技术提示。如果您在这里没有设置任何内容,您将获得一个包含模型名称和视图类型的默认名称。
ir.actions.act_window.view
我们定义的第二条记录与前面在添加菜单项和窗口操作配方中定义的act_window一起工作。我们已经知道,通过设置那里的view_id字段,我们可以选择第一个视图模式使用哪个视图。但是,如果我们将view_mode字段设置为tree, form视图,那么view_id将不得不选择一个tree视图,但是我们希望设置form视图,它在这里排在第二位。
如果您发现自己处于这样的情况下,请使用ir.actions.act_window.view模型,它可以让您细粒度地控制为哪个视图类型加载哪个视图。这里定义的前两个字段(act_window_id,view_id)是引用其他对象的通用方法的示例;将元素的主体保持为空,但添加一个名为ref的属性,该属性包含要引用的对象的XML ID。因此,这里所发生的是,我们在act_window_id字段中引用上一个内容中的action,并在view_id字段中引用我们刚刚创建的视图。然后,虽然不是严格必要的,我们添加一个序列号来定位这个视图分配相对于其他视图分配,对于相同的操作。只有当您通过创建多个ir.actions.act_window.view记录来为不同的视图模式分配视图时,这才是相关的。
一旦您定义了ir.actions.act_window.view记录,它们将优先于您在操作的view_mode字段中填充的记录。因此,对于前面的记录,您根本不会看到列表,而只看到表单。您应该添加另一个ir.actions.act_window.view记录,它指向res.partner模型的列表视图
有更多的…
正如我们在添加菜单项和窗口操作配方中看到的,我们可以用<record>替换act_window。如果你想使用自定义视图,你可以遵循给定的语法:
<record id='action_all_customers' model='ir.actions.act_window'>
<field name="name">All customers</field>
<field name="res_model">res.partner</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('customer', '=', True)]</field>
<field name="context">{'default_customer': True,
'tree_view_ref': 'my_module.view_all_customers_tree',
'form_view_ref': 'my_module.view_all_customers_form'
}</field>
<field name="limit">20</field>
</record>
向表单视图添加内容(content)和小部件(widgets)
前面的内容展示了如何为操作选择特定的视图。现在,我们将演示如何使表单视图更有用。在此内容中,我们将使用前面在“让操作打开特定视图内容”中定义的表单视图。
怎么做呢?
1. 定义窗体视图的基本结构:
<record id="form_all_customers" model="ir.ui.view">
<field name="name">All customers</field>
<field name="model">res.partner</field>
<field name="arch" type="xml">
<form>
<!--form content goes here -->
</form>
</field>
</record>
2. 要添加一个通常用于操作按钮和舞台管道的头栏,请在表单中添加如下内容:
3.添加字段到表单,使用组标签组织他们可视化:
<record id="view_all_customers_form" model="ir.ui.view">
<field name="name">All customers</field>
<field name="model">res.partner</field>
<field name="arch" type="xml">
<form>
<header>
<button type="object" name="open_commercial_entity" string="Open commercial partner" class="btn-primary" />
</header>
<group string="Content" name="my_content">
<field name="name" />
<field name="category_id" widget="many2many_tags" />
</group>
</form>
</field>
</record>
现在,表单应该显示一个顶部栏,其中有一个按钮和两个垂直对齐的字段。
它是如何工作的…
我们将首先查看ir.ui.view模型的arch字段。首先,请注意,视图是用XML定义的,因此需要为arch字段传递type=" XML"属性,否则解析器将会混淆。视图定义还必须包含格式良好的XML,否则在加载此代码片段时将遇到麻烦。现在我们将介绍前面使用的标记,并总结其他可用的标记。
Form
在定义form视图时,arch字段中的第一个元素必须是form元素。这个事际在内部用于派生记录的类型字段,这就是为什么不应该设置该字段的原因。不过,您将在遗留代码中经常看到这种情况。form元素本身可以有两个遗留属性,分别是string和version。在Odoo之前的版本中,这些都是用来决定你在breadcrumb中看到的标题,以及区分7.0前版本和之后版本的表单,但现在两者都被认为是过时的。面包屑中的标题现在是从模型的name_get函数推断出来的,而版本假设为7.0或更高版本。
除了以下元素之外,您还可以在表单标记中使用任意的HTML。该算法将Odoo所不知道的每个元素都视为纯HTML,并简单地传递给浏览器。要注意这一点,因为您填充的HTML可能与Odoo元素生成的HTML代码交互,这可能会扭曲呈现。
Header
该元素是一个容器,用于存放应该显示在表单头部的元素,表单头部呈现为白色条。通常,如本例所示,您将操作按钮放置在这里。或者,如果您的模型有一个状态字段,您可以选择一个状态栏。
Button
button元素用于允许用户触发操作。有关详细信息,请参考表单配方的添加按钮。
Group
<group>元素是Odoo的主要元素,用于组织内容。放置在<group>元素中的字段将以它们的标题呈现,并且同一组中的所有字段都将被对齐,以便它们也有一个属于一起的可视化指示符。你也可以嵌套<group>元素;这将导致Odoo渲染相邻列中包含的字段。
通常,您应该使用<group>机制来显示from视图中的所有字段,并且只在必要时恢复到其他元素,如<notebook>、<label>、<newline>等。
如果为一个group分配string属性,则其内容将作为group的标题呈现。
您还应该养成为字段的每个逻辑组分配一个name的习惯。此名称对用户不可见,但在我们覆盖以下内容中的视图时非常有用。在表单定义中保持名称的惟一性,以避免在引用哪个组时产生混淆。不要为此使用string属性,因为字符串的值最终会因为翻译而改变
Field
为了实际显示和操作数据,表单视图应该包含一些field元素。它们有一个强制属性,称为name,它引用模型中的字段名称。在前面,我们为用户提供了编辑合作伙伴的名称和类别的能力。如果我们只想显示其中之一,而用户无法编辑字段,则将readonly属性设置为1或True。这个属性实际上可能包含Python代码的一个小子集,因此readonly="2>1"将使该字段也是只读的。这也适用于invisible属性,该属性用于拥有从数据库读取的值,但不显示给用户。稍后,我们将查看这可以在哪些情况下使用。
您一定注意到了categories字段上的widget属性。这定义了如何将字段中的数据呈现给用户。每种类型的字段都有一个标准的小部件,因此您不必显式地选择一个小部件。但是,有几种类型提供了多种表示方式,在这种情况下,您可以选择缺省值以外的其他表示方式。由于可用小部件的完整列表超出了本菜谱的范围,请参阅Odoo的源代码来试用它们。请参阅第15章,Web客户端开发,了解如何制作自己的客户端。
Notebook和page
如果您的模型有太多的字段,那么您可以使用<notebook>和<page>标记来创建选项卡。<notebook>标签中的每个<page>将创建一个新选项卡,页面内的内容将是选项卡内容。下面的例子中,它将创建2个标签,每个标签中有3个字段:
<notebook>
<page string="Tab 1">
<field name="field1"/>
<field name="field2"/>
<field name="field3"/>
</page>
<page string="Tab 2">
<field name="field4"/>
<field name="field5"/>
<field name="field6"/>
</page>
</notebook>
<page>标记中的string属性将是选项卡的名称。在<notebook>标签,你只能使用<page>标签;但在<page>标记,您可以使用任何其他元素。
通用属性
在大多数元素上(包括group、field和button),您可以设置attrs和groups属性。虽然attrs是使用attrs配制在动态表单元素中讨论的,但groups属性允许您仅向某些组的成员显示某些元素。简单地说,组的完整XML ID(多个group用逗号分隔)是属性,对于不是至少一个上述组成员的所有人,元素将被隐藏。
其他标签
在某些情况下,您可能想要偏离指定的严格布局组。例如,如果您希望将记录的名称字段呈现为标题,则字段的标签将干扰外观。在本例中,不要将字段放在group元素中,而是放在普通的HTML h1元素中。然后,在h1元素之前,放置一个label元素,将for属性设置为field name:
<label for="name" />
<h1><field name="name" /></h1>
这将与字段的内容呈现为一个大标题,但字段的名称用较小的字体书写,位于大标题上方。这基本上就是标准伙伴表单的作用。
如果您需要在group中使用换行符,请使用newline元素。它总是空:
<newline />
另一个有用的元素是footer。当您打开一个弹出式表单时,这是放置操作按钮的好地方。它也将被呈现为一个单独的栏,类似于header元素。
不要使用XML节点的string属性(或任何其他已翻译的属性)来处理它们,因为对于其他语言,视图覆盖将会中断,因为视图是在应用继承之前被翻译的。
有更多的…
由于表单视图基本上是带有一些扩展的HTML, Odoo还大量使用了CSS类。两个非常有用的选项是oe_read_only和oe_edit_only。这将导致应用这些类的元素仅在读/视图模式或编辑模式下可见。要使标签仅在编辑模式下可见,请使用以下方法:
<label for="name" class="oe_edit_only" />
另一个非常有用的类是oe_inline,您可以在字段上使用它使它们作为内联元素呈现,以避免造成不必要的换行。在将字段嵌入文本或其他标记标记时使用此类。
而且,form元素可以拥有create、edit和delete属性。如果将其中一个设置为false,则对应的操作将无法用于此表单。如果不显式地设置此设置,则可以从用户的权限推断操作的可用性。注意,这纯粹是为了拉直UI;为了安全起见,不要使用这个。
另请参阅
小部件和视图已经提供了许多功能,但您迟早会遇到现有的小部件和视图无法满足的需求。参考下面的方法来创建你自己的视图和小部件:
要定义您自己的小部件,请参考第16章“Web客户端开发”中的“创建自定义小部件配置”,参考第16章“Web客户端开发”中的“创建一个新的视图”内容来创建您自己的视图。
向表单添加按钮
我们在前面的表单视图中添加了一个按钮,但是我们可以使用相当多的不同类型的按钮。这个内容将添加另一个按钮。它还将在内容的头部元素中放入以下代码。
怎么做呢?
添加一个指示动作的按钮:
<button type="action" name="%(base.action_partner_category_form)d" string="Open partner categories" />
它是如何工作的…
按钮的type属性决定了其他字段的语义,所以我们首先看看可能的值:
action:这使得按钮调用ir.actions.*名称空间中定义的动作。name属性需要包含动作的数据库ID,您可以方便地使用Python进行Odoo查找包含问题操作的XML ID的格式化字符串。
object:调用当前模型的一个方法。name属性包含函数的名称。该函数应该具有@api_multi签名,并将对当前查看的记录进行操作。
string属性用于分配用户看到的文本。
以前还有第三个值workflow,它向现在已过时和已删除的工作流引擎发送一个信号。如果您在从旧版本迁移代码时遇到这种情况,您可能必须用方法调用替换它。
有更多的…
使用btn-primary CSS类呈现突出显示的按钮,使用btn-default呈现普通按钮。这通常用于向导中的取消按钮,或者以一种视觉上不显眼的方式提供次要操作。设置oe_link类会使按钮看起来像一个链接。
带有对象类型按钮的调用可以返回一个描述操作的字典,该操作随后将在客户端执行。通过这种方式,您可以实现多屏幕向导,或者只是打开另一条记录。请注意,单击按钮总是会导致客户端在运行方法之前发出write或create调用。
通过替换string属性,您还可以在按钮标记中包含内容。这通常在按钮框中使用,如文档样式表单配制中所述。
向表单和动作传递参数- context
在内部,Odoo中的每个方法都可以访问一个称为context的字典,这个字典从每个动作传播到交付该动作的方法。UI也可以访问它,并且可以通过在上下文中设置值以各种方式修改它。在本内容中,我们将通过使用语言、默认值和隐式过滤器来探索这种机制的一些应用程序。
准备
虽然不是必须的,但是如果你安装了法语,如果你还没有这个,这个内容会更有趣。参考第12章国际化,了解如何做到这一点。如果您有一个法语数据库,请将fr_FR更改为其他语言;en_US将用于英语。另外,在您的一个客户上单击Active按钮(当您将其悬浮时更改为Archive),以便对其进行归档并验证该合作伙伴不再出现在列表中。
怎么做呢?
1. 创建一个新的操作,非常类似于添加菜单项和窗口操作配制:
<act_window id="action_all_customers_fr"
name="Tous lesclients"
res_model="res.partner"
domain="[('customer', '=',True)]"
context="{'lang': 'fr_FR', 'default_lang': 'fr_FR','active_test': False}" />
2. 添加一个调用此操作的菜单。这留给读者作为练习。当您打开这个菜单时,视图将以法语显示,并且如果您创建了一个新的合作伙伴,那么它们将以法语作为其预先选择的语言。一个不太明显的区别是,您还将看到停用的合作伙伴记录。
它是如何工作的…
context字典是从几个源中填充的。首先,读取当前用户记录中的一些值(lang和tz,针对用户的语言和时区)。然后,我们有一些add-ons,它们为自己的目的添加键。此外,UI还添加了关于我们目前正在处理哪个模型和哪个记录的键(active_id、active_ids、active_model)。另外,正如在让一个action打开一个特定的视图配制中看到的,我们可以在actions中添加我们自己的键。它们被合并在一起并传递给底层服务器功能,也传递给客户端UI。
因此,通过设置lang context键,我们强制将显示语言为法语。您将注意到,这并没有改变整个UI语言,这是因为只有我们打开的列表视图位于这个context的范围内。UI的其余部分已经用另一个包含用户原始语言的context加载。但是,如果您在这个list视图中打开一条记录,它也将以法语显示,并且如果您在表单上打开一条链接的记录或按下执行某个动作的按钮,该语言也将被传播。
通过设置default_lang,我们为这个上下文范围内创建的每个记录设置了一个默认值。一般模式是default_$fieldname: my_default_value,在本例中,它允许您为新创建的合作伙伴设置默认值。如果我们的菜单是关于客户的,那么还应该设置default_customer: True,以默认地检查客户字段。但是,这是res.partner模型范围内的默认值,所以这不会改变任何东西。对于标量字段,其语法与您在Python代码中编写的语法相同:string字段使用引号,number字段使用引号,Boolean字段使用True或False。对于relational字段,语法稍微复杂一些;参考第7章模块数据,学习如何编写它们。请注意,上下文中设置的默认值会覆盖模型定义中设置的默认值,因此您可以在不同的情况下使用不同的默认值。
最后一个键是active_test,它具有非常特殊的语义。对于每个模型,有一个字段叫active, Odoo自动过滤掉记录,该字段是False。这就是为什么没有选中此字段的合作伙伴从列表中消失了。通过设置这个键,我们可以抑制这种行为。
这对于UI本身很有用,但在Python代码中更有用,因为您需要确保操作应用于所有记录,而不仅仅是active记录。
有更多的…
在定义context时,您可以访问一些变量,最重要的一个是uid,它计算为当前用户的ID。此外,您还可以访问context_today函数和current_date变量,其中第一个是date对象,表示当前日期(从用户的时区来看),后者是UTC中显示的当前日期,格式为YYYYMM-DD。要将日期字段的默认值设置为当前日期,使用current_date,对于默认过滤器,使用context_today()。
此外,还可以使用Python的datetime、time和relativedelta类的一个子集进行一些日期计算。
出于安全原因,domain计算受到了限制。在引入客户端评估时,为了不破坏整个系统,最好的选择是用JavaScript实现Python的一部分。在Odoo中内置了一个小的JavaScript Python解释器,它可以很好地处理简单的表达式,通常这就足够了。
注意在<act_window />快捷方式中使用上下文变量。它们是在安装时评估的,这几乎不是您想要的。如果您的上下文中需要变量,请使用<record />语法。
我们可以像在动作中添加上下文键一样添加按钮。这将导致按钮调用的函数或操作在给定的上下文中运行。
大多数作为Python计算的表单元素属性也可以访问上下文字典。invisible和readonly属性就是这样的例子。因此,如果您希望元素在某些时候出现在表单中,而不是在其他时候,请将invisible属性设置为context.get('my_key')。对于导致字段不可见的情况的操作,将上下文键设置为my_key: True。这种策略使您能够根据不同的情况调整表单,而不必重写它。
还可以在relational字段上设置context,这将影响字段的加载方式。通过将form_view_ref或tree_view_ref键设置为视图的完整XML ID,可以为该字段选择特定的视图。当同一对象有相同类型的多个视图时,这是必要的。如果没有这个键,您将获得具有最低序列号的视图,这可能并不总是理想的。
另请参阅
context还用于设置默认搜索过滤器。您可以在本章的定义搜索视图配制中了解更多关于默认搜索过滤器的信息。
在记录列表上定义过滤器- Domain
我们已经在第一个操作中看到了域的第一个示例,它是[('customer', '=', True)]。通常,您需要显示一个动作中所有可用记录的子集,或者只允许可能记录的子集成为many2one关系的目标。在Odoo中描述这些过滤器的方法是使用Domain域。此内容演示了如何使用域来显示合作伙伴的选择。
怎么做呢?
要从您的操作中显示合作伙伴的子集,您需要执行以下步骤:
1. 为非法语客户添加一个action:
<record id="action_my_customers" model="ir.actions.act_window">
<field name="name">All my customers who don't speak French</field>
<field name="res_model">res.partner</field>
<field name="domain">
[('customer', '=', True), ('user_id', '=', uid),('lang', '!=', 'fr_FR')]
</field>
</record>
2. 为客户或供应商添加一个操作:
<record id="action_customers_or_suppliers" model="ir.actions.act_window">
<field name="name">Customers or suppliers</field>
<field name="res_model">res.partner</field>
<field name="domain">['|', ('customer', '=', True),('supplier', '=',True)]</field>
</record>
3.添加调用这些操作的菜单。这留给读者作为练习。
它是如何工作的…
domain的最简单形式是一个三元组列表,包含一个字段名的模型问题作为第一个元素string,经营者作为string第二元素,和检查的字段值是作为第三个元素。这是我们在第一个条件中所做的,这被解释为,所有这些条件都必须适用于我们感兴趣的记录。这实际上是一种快捷方式,因为域知道两个前缀操作符&和| -,其中&是缺省值。那么,在规范化形式下,第一个域将写成:
['&', '&', ('customer', '=', True), ('user_id', '=', uid), ('lang','!=', 'fr_FR')]
虽然对于较大的表达式来说,前缀操作符可能有点难以阅读,但是前缀操作符的优点是它们的作用域是严格定义的,这使您不必担心操作符的优先级和括号。它总是两个表达式:第一个&应用于'&',('customer', '=', True), ('user_id', '=', uid)作为第一个操作数和('lang', '!=', 'fr_FR')作为第二个。然后,第二个&应用于('customer', '=', True)作为第一个操作数,('user_id', '=',uid)作为第二个操作数。
在第二个action中,我们必须写出完整的形式,因为我们需要"|"操作符。
还有一个"!",但是,如果给定逻辑等价和否定比较运算符(如!=和not in),则实际上没有必要这样做。请注意,这是一个一元前缀操作符,因此它只应用于域中的以下表达式,而不是后面的所有表达式。
请注意,在为窗口操作或其他客户端域编写域时,正确的操作数不需要是固定的值。您可以使用前面在向表单和动作传递参数中描述的相同的最小Python—Context 内容,这样您就可以编写像上周更改的或我的合作伙伴那样的过滤器。
有更多的…
前面的域只对模型本身的字段起作用,而我们经常需要根据链接记录的属性进行筛选。为此,您可以使用也在@api.depends定义或相关字段中使用的符号:创建从当前模型到您想要筛选的模型的虚线路径。要搜索销售人员是以字母G开头的组成员的合作伙伴,可以使用[('user_id.groups_id.name', '=like', 'G%')]域。路径可以很长,因此您只需要确保当前模型和您想要筛选的模型之间有关系字段。
Oper_ators
下表列出了可用的操作符及其语义:
| 操作符 | 语义 |
| =, !=, <> | 第一个表示精确匹配,第二个表示不相等,最后一个表示不相等。 |
| in, not in | 这将检查该值是否为右操作数中列表中指定的值之一。它是作为一个Python列表:[("uid","in",[1,2,3])]或[("uid","not in",[1,2,3])]。 |
| <, <= | 大于,大于或等于 |
| >, >= | 小于,小于或等于 |
| like, not like | 检查值中是否包含正确的操作数(子字符串)。 |
| ilike, not ilike | 与前一个相同,但不区分大小写 |
| =like, =ilike | 你可以在这里搜索模式:%匹配任何字符串,_匹配一个字符。这就相当于PostgreSQL。 |
| child_of | 对于具有parent_id字段的模型,它将搜索右操作数的子操作数。结果中包含右操作数。 |
| =? | 如果正确操作数为假,则计算为真;否则,它的行为类似于“=”。当您以编程方式生成域,并希望在设置了值时通过值进行筛选,但在其他情况下忽略该值时,这是非常有用的。 |
使用域搜索的缺陷
这对于传统字段来说都很好,但是一个臭名昭著的问题是搜索一个未存储的函数字段的值。人们经常忽略搜索功能。通过在自己的代码中提供搜索功能,就可以很简单地解决这个问题,如第5章“应用模型”所述。
另一个可能困扰开发人员的问题是Odoo在使用负运算符搜索one2many或many2many字段时的行为。假设您有一个具有a标记的合作伙伴,您搜索[('category_id.name', '!=','B')]。你的伴侣出现在结果,这正是您所期待的,但是如果你B标记添加到这个伙伴,它仍然在你的结果,因为搜索算法,它是足够的,有一个关联记录(在本例中)不满足标准。现在,如果您删除A标签,使B是唯一的标签,合作伙伴将被过滤掉。如果您还删除了B标记,使合作伙伴没有标记,那么它仍然被过滤掉,因为链接的记录上的条件预先假定了该记录的存在。但在其他情况下,这是您想要的行为,因此不能更改标准行为。如果这里需要不同的行为,那么提供一个搜索函数,以您需要的方式解释否定。
当涉及到域时,人们常常忘记他们正在编写XML文件。您需要转义小于操作符。搜索在当前日期之前创建的记录必须在XML中写成[('create_date', '<', current_date)]。
另请参阅
如果需要操作没有通过编程方式创建的域,请使用odoo.osv.expression中提供的实用函数。is_leaf、normalize_domain和AND OR函数允许您像Odoo那样组合域。不要自己这样做,因为有很多情况需要考虑,而且很可能会忽略一个。有关域的标准应用程序,请参见搜索视图内容。
定义列表视图
在表单视图上花了不少时间之后,现在我们将快速了解如何定义列表视图。在内部,这些视图在某些地方称为树视图,在其他地方称为列表视图,但考虑到在Odoo视图框架中还有另一种结构称为树,这里我们将坚持使用列表的说法。
怎么做呢?
1.定义您的列表视图:
<record id="tree_all_customers" model="ir.ui.view">
<field name="model">res.partner</field>
<field name="arch" type="xml">
<tree decoration-bf="customer_rank"
decoration-danger="supplier_rank"
decoration-warning="customer_rank and supplier_rank">
<field name="name" />
<field name="customer" invisible="1" />
<field name="supplier" invisible="1" />
</tree>
</field>
</record>
2. 在我们在本章添加菜单项和窗口操作内容中创建的动作中注册一个树视图:
<record id="action_all_customers_tree" model="ir.actions.act_window.view">
<field name="act_window_id" ref="action_all_customers" />
<field name="view_id" ref="tree_all_customers" />
<field name="view_mode">tree</field>
<field name="sequence">5</field>
</record>
它是如何工作的…
你已经知道这里发生的大部分事情了。我们定义了一个tree类型的视图,并通过ir.actions.act_window.view元素将其附加到我们的操作。因此,剩下要讨论的就是树元素及其语义了。对于列表,您没有太多设计选择,因此该元素唯一有效的子元素是field和button元素。它们遵循与前面相同的语义,除了关于小部件的选择更少;唯一有趣的选择是progressbar、many2onebutton和handle。前两个类似于它们的表单名称。handle句柄是特定于列表视图的。它用于整型字段,并呈现一个拖拽句柄,用户可以使用该拖拽将一行拖到列表中的不同位置,从而更新字段的值。这对于序列或优先级字段很有用。
这里新增的是树元素中的装饰属性(*)。它包含为行选择哪种字体和/或颜色的规则,以装饰的形式给出decoration-$name="Python code"。所有匹配将转换为相应的CSS类,因此前面的视图将同时是供应商和客户的合作伙伴以棕色显示,客户以粗体显示,而供应商以红色显示。在Python代码中,只能使用在视图定义中命名的字段,这就是为什么我们还必须拉出customer和supplier字段。我们将这些设置为不可见的,因为我们只需要数据,而不想用这两列来打扰用户。可能的类有:decoration-bf(粗体)、decoration-it(斜体),以及语义引导类:decoration-danger, decoration-info, decoration-muted,decoration-primary, decoration-success和decoration-warning。
有更多的…
对于数值字段,可以添加一个sum属性,使该列通过在属性中设置的文本进行汇总,作为工具提示。比较少见的是分别显示平均值、最小值和最大值的avg、min和max属性。请注意,这四个选项只对当前可见的记录起作用,因此您可能需要调整操作的limit(在前面的添加菜单项和窗口操作配方中已经介绍过),以便用户能够立即看到所有记录。
tree元素的一个非常有趣的属性是editable。如果将其设置为top或bottom,则列表的行为完全不同。如果没有它,单击一行将打开该行的表单视图。通过它,单击一行使其可内联编辑,可见字段呈现为表单字段。这在嵌入式列表视图中特别有用,这将在本章后面的定义嵌入式视图配制中讨论。top或bottom的选择与是否将新行添加到列表的top或bottom有关。
默认情况下,记录是根据显示模型的_order属性排序的。用户可以通过单击列标题来更改顺序,但是您也可以通过设置tree元素中的default_order属性来设置不同的初始顺序。语法与_order中相同。
对于新开发人员来说,顺序常常是一个令人沮丧的来源。由于Odoo在这里让PostgreSQL来完成这项工作,所以你只能按照PostgreSQL知道的字段来排序,也只能按照同一数据库表中的字段来排序。因此,如果您希望按函数或相关字段进行排序,请确保设置store=True。如果需要按从另一个模型继承的字段进行排序,那么声明一个已存储的相关字段。
tree元素的create、edit和delete属性与我们在本章的表单视图内容中描述的form元素的工作原理相同。如果设置了editable属性,它们还确定可用的控件。
定义搜索视图
当打开列表视图时,您会注意到右上角的搜索字段。如果你在那里输入一些东西,你会得到搜索的建议,还有一组预定义的过滤器可供选择。本内容将指导您如何定义这些建议和选项。
怎么做呢?
1. 定义您的搜索视图:
<record id="search_all_customers" model="ir.ui.view">
<field name="model">res.partner</field>
<field name="arch" type="xml">
<search>
<field name="name" />
<field name="category_id" filter_domain="[('category_id', 'child_of',self)]" />
<field name="bank_ids" widget="many2one" />
<filter name="suppliers" string="Suppliers" domain="[('supplier', '=', True)]" />
<group expand="0" string="Group By">
<filter string="Country" name="country" context="{'group_by':'country_id'}"/>
</group>
</search>
</field>
</record>
2. 告诉你的action使用它:
<record id="action_all_customers" model="ir.actions.act_window">
<field name="name">All customers</field>
<field name="res_model">res.partner</field>
<field name="domain">[('customer', '=', True)]</field>
<field name="search_view_id" ref="search_all_customers" />
</record>
现在,当您在搜索栏中输入内容时,您将能够在name、categories和bank account字段中搜索该术语。如果你的term在你的系统中恰好是一个银行账号的子串,你甚至可以搜索这个银行账号。
它是如何工作的…
对于name,我们只是列出了要提供给用户进行搜索的字段。我们将语义保留为默认值,即对字符字段进行子字符串搜索。
对于类别,我们做了一些更有趣的事情。默认情况下,您的搜索词应用于many2many字段触发器name_search,在本例中,它将是类别名称中的子字符串搜索。然而,根据您的类别结构,搜索拥有您感兴趣的类别或它的子类别的合作伙伴是非常方便的。考虑一个主要类别,时事通讯订阅者,以及子类别,每周时事通讯,每月时事通讯,以及其他一些类型的时事通讯。搜索时事通讯订阅者与前面的搜索视图定义将给你谁订阅了这些时事通讯的任何一个去,这是比搜索每一个单一类型和组合结果方便得多。
filter_domain属性可以包含任意域,因此您既不局限于搜索在name属性中命名的相同字段,也不局限于只使用一个术语。self变量是用户填写的,也是这里唯一可以使用的变量
这里有一个更详细的例子,从默认搜索视图的合作伙伴:
<field name="name" filter_domain="['|', '|',('display_name', 'ilike', self),('ref', '=', self),('email', 'ilike', self)]"/>
这意味着用户不必考虑要搜索什么。他们所需要做的就是输入一些字母,按回车键,幸运的是,其中一个字段包含了我们正在查找的字符串。
对于bank_ids字段,我们使用了另一个技巧。字段的类型不仅决定了搜索用户输入的默认方式,还定义了Odoo显示建议的方式。另外,由于many2one字段是唯一提供自动完成功能的字段,我们通过设置widget属性强制Odoo这样做,即使bank_ids是one2many字段。没有这个,我们将不得不在这个领域搜索,没有完成建议。这同样适用于许多领域。注意,带有many2one小部件集的每个字段将触发对其模型的每个用户按键的搜索;不要用太多。
您还应该将最常用的字段放在顶部,因为如果用户只是键入一些内容并按回车键,第一个字段就是要搜索的内容。搜索栏也可以与键盘一起使用;按下箭头选择建议,按右箭头打开一个many2one的完成建议。如果您教导您的用户这方面的知识,并注意搜索视图中字段的合理排序,这将比先输入一些东西,抓取鼠标,然后选择一个选项更有效率。
filter元素创建一个按钮,该按钮将过滤器的domain属性的内容添加到搜索域。您应该添加一个逻辑内部name和一个string属性来向用户描述过滤器。
<group>标记用于在Group by按钮下提供分组选项。在这个内容中,我们根据country_id字段添加了对记录分组的选项。
有更多的…
您可以使用group标记对过滤器进行group处理,这将使它们比其他过滤器更接近地呈现在一起,但这也具有语义含义。如果在同一组中放置多个过滤器并激活多个过滤器,它们的域将与|操作符组合,而不在同一组中的过滤器和字段将与&操作符组合。有时,您可能需要筛选器的析取,这是它们筛选互排式集的地方,在这种情况下,同时选择它们将始终导致结果集为空。在同一组中,您可以使用separator元素实现同样的效果.
注意,如果用户为同一个字段填写了多个查询,那么它们也将与|组合在一起,因此不必担心这个问题。
除了field之外,filter元素还可以拥有一个context 属性,它的内容将与当前上下文以及搜索视图中的其他上下文属性合并。这对于支持分组的视图(请参考定义kanban视图和定义graph视图配方)非常重要,因为结果上下文决定使用group_by键分组的字段。我们将研究在适当的内容中分组的细节,但是上下文也有其他用途。例如,您可以编写一个根据上下文返回不同值的函数字段,然后您可以通过激活筛选器来更改这些值。
搜索视图本身也响应上下文键。与创建记录时的默认值非常类似,您可以通过上下文传递搜索视图的默认值。如果我们在前面的操作中设置了{'search_default_suppliers': 1}的上下文,那么supplier过滤器将会在search视图中被预选。但是,这只在过滤器有名称的情况下有效,这就是为什么您应该始终设置它的原因。要设置搜索视图中的字段的默认值,请使用search_default_$fieldname。
此外,field和filter元素可以具有与表单视图中相同的语义的groups属性,以便使元素只对某些组可见。
另请参阅
有关操作上下文的详细信息,请参见向表单和操作传递参数—Context内容。
使用大量变音符号的语言的用户在填写e字符时可能需要使用Odoo搜索e、e、e和e。这是PostgreSQL服务器的一个配置,叫做unaccent, Odoo对它有特殊的支持,但不在本书的讨论范围之内。请参考https://www.postgresql.org/docs/10/ unaccent。有关不重音的更多信息。
更改现有视图-视图继承
到目前为止,我们忽略了现有的观点,宣布了全新的观点。虽然这在教学上是合理的,但您很少会遇到需要为现有模型定义新视图的情况。相反,您需要对现有视图进行轻微的修改,可能只是简单地让它们显示您在插件中添加到模型中的字段,或者根据您或客户的需要对它们进行定制。
在此内容中,我们将更改默认的合作伙伴表单,以显示记录的最后修改日期,并通过修改搜索视图使移动字段可搜索。然后,我们将更改partners的列表视图中的一列的位置。
怎么做呢?
1. 将字段注入到默认的表单视图:
<record id="view_partner_form" model="ir.ui.view">
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form" />
<field name="arch" type="xml">
<field name="website" position="after">
<field name="write_date" />
</field>
</field>
</record>
2. 将该字段添加到默认搜索视图:
<record id="view_res_partner_filter" model="ir.ui.view">
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_res_partner_filter" />
<field name="arch" type="xml">
<xpath expr="." position="inside">
<field name="mobile" />
</xpath>
</field>
</record>
3.将字段添加到默认列表视图:
<record id="view_partner_tree" model="ir.ui.view">
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_tree" />
<field name="arch" type="xml">
<field name="email" position="after">
<field name="phone" position="move"/>
</field>
</field>
</record>
更新模块之后,您应该会在合作伙伴表单的网站字段下面看到最后一次更新的额外字段。当您在搜索框中输入内容时,它应该建议您在移动字段中搜索合作伙伴,并且在合作伙伴的列表视图中,您将看到电话号码和电子邮件的顺序已经改变。
它是如何工作的…
这里的关键字段是,您可能已经猜到了,inherit_id。您需要向它传递您想要修改(继承)的视图的XML ID。arch字段包含关于如何修改所继承的视图中的现有XML节点的说明。实际上,您应该将整个过程看作是简单的XML处理,因为所有的语义部分都是在很久之后才出现的
继承视图的arch字段中最规范的指令是field元素,它具有必需的属性name和position。由于每个字段在表单上只能使用一次,因此名称已经惟一地标识了字段。通过position属性,我们可以放置放置在field元素中的任何内容,before、inside或after我们命名的字段。默认位置是inside,但为了可读性,您应该始终指定所需的位置。记住,我们这里讲的不是语义学;这是关于XML树中相对于我们命名的字段的位置。之后的渲染方式就完全不同了的事。
第二步演示一种不同的方法。xpath元素选择与expr属性中命名的xpath表达式匹配的第一个元素。这里,position属性告诉处理器将xpath元素的内容放在何处。
第3步展示了如何更改元素的位置。这个选项是在版本12中引入的,很少使用。在我们的示例中,我们使用position=move选项将phone字段移动到email字段之后。
XPath看起来可能有点可怕,但它是选择需要处理的节点的一种非常有效的方法。花点时间看看一些简单的表达;它是值得的。您可能会遇到术语上下文节点,有些表达式是相对的。在Odoo的视图继承中,这总是你继承的视图的根元素。
对于在继承视图的arch字段中找到的所有其他元素,处理器查找具有相同节点名称和匹配属性的第一个元素(不包括属性位置,因为这是指令的一部分)。仅在此组合不太可能不是唯一的情况下使用,例如group元素与name属性组合。
注意,您可以在arch字段中根据需要设置任意数量的指令元素。我们只对每个继承视图使用了一个视图,因为目前没有其他需要更改的内容。
有更多的…
position属性还有另外两个可能的值:replace和attributes。使用replace将导致选择的元素被替换为指令元素的内容。因此,如果您没有任何内容,则可以简单地删除选中的元素。上述列表或表单视图将导致电子邮件字段被删除:
<field name="email" position="replace" />
警告:
删除字段可能会导致其他继承视图中断和其他一些不希望看到的副作用,因此要尽可能避免这种情况。如果您确实需要删除字段,那么在计算顺序较晚的视图中这样做(有关更多信息,请参阅下一节“视图继承中的计算顺序”)。
attributes的语义与前面的示例非常不同。处理器期望元素包含带有name属性的attribute元素。然后,这些元素将用于设置所选元素的属性。如果您想注意前面的警告,您应该将em_ail字段的invisible属性设置为1:
<field name="email" position="attributes">
<attribute name="invisible">1</attribute>
</field>
一个attribute节点可以有add和remove属性,这些属性应该包含要从以空格分隔的列表中删除或添加的值。这对于class属性非常有用,你可以通过以下方法添加class(而不是覆盖整个属性):
<field name="email" position="attributes">
<attribute name="class" add="oe_inline" />
</field>
视图继承中求值的顺序
由于我们目前只有一个父视图和一个继承视图,所以我们不会遇到任何视图覆盖冲突的问题。当您安装了几个模块后,您将发现合作伙伴表单的许多覆盖。只要它们在视图中更改了不同的内容,这是可以的,但是在某些情况下,理解重写如何工作以避免冲突是很重要的。
视图的直接后代将按照其priority字段的升序进行计算,因此优先级较低的视图将首先应用。继承的每一步都应用于第一步的结果,所以如果一个优先级为3的视图更改了一个字段,而另一个优先级为5的视图删除了它,这是可以的。然而,如果优先级颠倒过来,这是行不通的。
您还可以从一个视图继承,这个视图本身就是一个继承视图。在本例中,第二级inherited视图应用于它所继承的视图的结果。所以,如果你有四个视图,A B C和D,其中A是独立的形式,B和C继承于A, D继承于B,计算的顺序是A B D C,使用这个来执行顺序而不需要依赖于优先级;一般来说,这样更安全。如果继承视图添加了一个字段,并且您需要对该字段应用更改,则从继承视图而不是从独立视图继承。
这种继承总是在原始视图的完整XML树上工作,并应用了以前继承视图的修改。
另请参阅
以下几点提供了一些高级技巧,用于调整视图继承的行为:
对于继承视图,一个非常有用但不太知名的字段是groups_id。只有当请求父视图的用户是其中一个组的成员时,该字段才会导致继承发生。在为不同级别的访问调整用户界面时,这可以为您节省大量工作,因为使用继承,您可以进行更复杂的操作,而不仅仅是显示或不显示基于的元素在组成员关系上,正如表单元素上的groups属性一样。
例如,如果用户是一个组的成员,您可以删除元素(这与groups属性的作用相反)。您还可以执行一些复杂的技巧,比如根据组添加属性,成员资格;可以考虑一些简单的事情,比如让字段对某些组只读,或者更有趣的概念,比如为不同的组使用不同的小部件。
在此内容中所描述的将原始视图的mode字段设置为primary,而继承视图具有模式扩展,这是默认的。稍后我们将研究继承视图的mode被设置为primary的情况,其中规则略有不同。
定义文档样式表单
在本内容中,我们将回顾一些设计指南,以呈现统一的用户体验。
怎么做呢?
1. 用header元素开始表单:
<header>
<button type="object" name="open_commercial_entity" string="Open commercial partner" class="btn-primary" />
</header>
2. 为内容添加sheet元素:
<sheet>
3.放入stat按钮,该按钮将用于存档记录:
<div class="oe_button_box" name="button_box">
<button name="toggle_active" type="object" class="oe_stat_button" icon="fa-archive">
<field name="active" widget="boolean_button" options="{'terminology': 'archive'}"/>
</button>
</div>
4. 添加一些突出的字段:
<div class="oe_left oe_title">
<label for="name" />
<h1>
<field name="name" />
</h1>
</div>
5. 添加你的内容,如果有很多字段,你可以使用笔记本:
<group>
<field name="category_id" widget="many2many_tags" />
<field name="email"/>
<field name="mobile"/>
</group>
6. 在工作表之后,添加chatter小部件(如果适用):
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="activity_ids" widget="mail_activity"/>
<field name="message_ids" widget="mail_thread"/>
</div>
它是如何工作的…
header应该包含在用户当前看到的对象上执行操作的按钮。使用btn-primary类使按钮在视觉上引人注目(在编写本文时是紫色的),这是指导用户当前执行最符合逻辑的操作的好方法。尝试将所有高亮显示的按钮放在未高亮显示的按钮的左边,并隐藏在当前状态中不相关的按钮(如果适用)。如果模型有状态,则使用statusbar小部件在标题中显示它。这将在header中以右对齐的方式呈现。
表单元素被呈现为一个样式化的表单,最重要的字段应该是用户在查看它时首先看到的内容。使用oe_title和oe_left类将它们呈现在一个突出的位置(浮动的左边,在编写时稍微调整一下字体大小)。
如果用户当前看到的记录中还有其他相关的记录(比如合作伙伴表单上的合作伙伴发票),那么将它们放在带有oe_right和oe_button_box类的元素中;这将使其中的按钮向右对齐。在按钮本身上,使用oe_stat_button类强制实现按钮的统一呈现。从字体awesome icons中为icon属性指定一个icon类也是一种习惯。你可以通过https://fontawesome.com/v4.7.0/icons/了解更多关于fontawesome的信息。
您可以使用oe_chatter类和Chatter小部件在表单视图的底部获得默认的Chatter。为此,需要使用mail.thread mixin。我们将在第23章Odoo管理电子邮件中看到详细内容。
即使您不喜欢这种布局,也要坚持使用这里描述的元素和类名,并调整CSS和JavaScript所需要的内容。这将使用户界面与现有插件更兼容,并允许你更好地与核心插件集成。
到目前为止,我们只研究了如何根据用户的组(元素上的groups属性和继承视图上的groups_id字段)更改表单,但没有其他内容。此内容将向您展示如何根据表单中字段的值修改表单视图。
怎么做呢?
1. 在表单元素上定义一个叫做attrs的属性:
<field name="parent_id"
attrs="{
'invisible': [('is_company', '=', True)],
'required': [('is_company', '=', False)]
}" />
2. 确保您所引用的所有字段都在您的表单中可用:
<field name="is_company" invisible="True" />
如果合作伙伴是公司,这将使parent_id字段不可见,如果不是公司,则是必需的。
它是如何工作的…
attrs属性包含一个具有invisible、required和readonly键的字典(所有键都是可选的)。域可以引用的值字段的形式存在(真的只有这些,所以没有点路径),和整个字典是评估根据客户端Python规则,如前所述传递参数的形式和行为——本章Context配制。例如,您可以访问右手操作数中的上下文。
有更多的…
虽然这种机制对于标量字段非常简单,但是如何处理one2many和many2many字段就不那么明显了。实际上,在标准Odoo中,您不能在attrs属性中使用这些字段。但是,如果您只需要检查这样的字段是否为空,可以使用[[6,False,[]]]作为右手操作数。
定义嵌入视图(embedded views)
当您在表单上显示one2many或many2many字段时,如果没有使用专门的小部件,您无法控制它的呈现方式。此外,对于many2one字段,有时希望能够影响打开链接记录的方式。在本内容中,我们将了解如何为这些字段定义私有视图。
怎么做呢?
1. 像往常一样定义你的字段,但是不要关闭标签:
<field name="child_ids">
2. 将视图定义写入标签:
<tree>
<field name="name" />
<field name="email" />
<field name="phone" />
</tree>
<form>
<group>
<field name="name" />
<field name="function" />
</group>
</form>
3.关闭标签:
</field>
它是如何工作的…
当Odoo加载一个form视图时,它首先检查相对类型字段是否在字段中嵌入视图,如前面概述的那样。这些嵌入视图可以具有与我们之前定义的视图完全相同的元素。只有当Odoo没有找到某个类型的嵌入式视图时,它才会使用该类型的模型默认视图。
有更多的…
虽然嵌入式视图看起来是一个很棒的特性,但它们会使视图继承变得非常复杂。例如,一旦涉及到嵌入式视图,字段名就不能保证是唯一的,通常你必须使用一些精心设计的xpath来选择嵌入视图中的元素。
因此,一般来说,您最好定义独立视图,并使用form_view_ref和tree_view_ref键,正如前面在本章的操作打开特定视图内容中所描述的那样。
在表单视图的一侧显示附件
在某些应用程序中,例如货品计价,您需要根据文档填充数据。为了简化数据填充过程,Odoo版本12增加了一个新功能,可以在表单视图的侧面显示文档。在这个内容中,我们将学习如何并排显示表单视图和文档:
这个特性只适用于大屏幕(>1534px),所以如果你有一个小的视口,这个特性将被隐藏。在内部,该特性使用了一些响应性实用程序,因此该特性只能在企业版中工作。然而,您仍然可以在模块中使用此代码。Odoo会自动处理这个,所以如果这个模块安装在企业版,它会显示文档,而在社区版,它会隐藏所有内容没有任何副作用。
怎么做呢?
我们将启用这个特性来修改res.partner模型的表单视图,如下所示:
<record id="view_all_customers_form" model="ir.ui.view">
<field name="name">All customers</field>
<field name="model">res.partner</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name" />
<field name="email"/>
</group>
</sheet>
<div class="o_attachment_preview" options="{types: ['image', 'pdf'], 'order': 'desc'}"/>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="activity_ids" widget="mail_activity"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
</record>
它是如何工作的…
只有在您的模型继承了mail.thread模型时,该特性才能工作。要在任何表单视图的一侧显示文档,您需要在chatter元素之前添加一个带有o_attachment_preview类的空<div>。就是这样,在聊天中附加的文档将显示在表单视图的一侧。
默认情况下,pdf和image类型的文档将按日期升序显示。您可以通过提供额外选项来更改此行为,其中包括以下选项:
type:你需要传递你想要允许的文档类型列表。只有两个值是可能的:pdf和image。例如,如果只想显示pdf类型的图像,可以传递{'type': ['pdf']}。
order:可能的值是asc和desc。它们允许您按文档创建日期的升序或降序显示文档。
有更多的…
在大多数情况下,您希望在任何记录的初始状态一侧显示文档。如果你想隐藏基于域的附件预览,你可以使用attrs在<div>标签隐藏预览。
看一下下面的例子;如果state字段的值不是draft,它会隐藏PDF预览:
<div class="o_attachment_preview"
attrs="{'invisible': [('state', '!=', 'draft')]/>
定义kanban视图
到目前为止,我们已经向您提供了一个可以打开以显示表单的记录列表。虽然这些列表在呈现大量信息时是有效的,但由于缺乏设计可能性,它们往往有点乏味。在这个内容中,我们将研究看板视图,它允许我们以一种更吸引人的方式显示记录列表。
怎么做呢?
1. 定义kanban类型的视图:
<record id="view_all_customers_kanban" model="ir.ui.view">
<field name="model">res.partner</field>
<field name="arch" type="xml">
<kanban>
2. 列出你将在你的视图中使用的字段:
<field name="name" />
<field name="supplier" />
<field name="customer" />
3.实现一个设计:
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_card">
<a type="open">
<field name="name" />
</a>
<t t-if="record.supplier.raw_value or record.customer.raw_value">
is
<t t-if="record.customer.raw_value">
a customer
</t>
<t t-if="record.customer.raw_value and record.supplier.raw_value">
and
</t>
<t t-if="record.supplier.raw_value">
a supplier
</t>
</t>
</div>
</t>
</templates>
4. 关闭所有标签:
</kanban>
</field>
</record>
5. 将此视图添加到一个操作中。这留给读者作为练习。您将在GitHub示例文件中找到完整的工作示例:https://github.com/PacktPublishing/Odoo-12-Development-Cookbook-Third-Edition。
它是如何工作的…
我们需要给出要在步骤2中加载的字段列表,以便以后能够访问它们。templates元素的内容必须是t-name属性设置为kanban-box的单个t元素。
您在这个元素中编写的内容将对每个记录重复,对于t元素和t-*attributes有特殊的语义。要了解更多细节,请参考第15章“Web客户端开发”中的“使用客户端QWeb模板”内容,因为从技术上讲看板视图只是QWeb模板的一个应用。
有一些特别针对看板视图的修改。在计算期间,您可以访问read_only_mode、record和widget变量。可以使用record.fieldname访问字段,它是一个具有value和raw_value属性的对象,其中value是字段的值,它被格式化为用户可以看到的格式,raw_value是字段的值,因为它来自数据库。
这里是许多领域的例外。您只能通过record变量获得一个ID列表。对于用户可读的表示,您必须使用field元素。
注意模板顶部链接的type属性。这个属性使Odoo生成一个链接,在查看模式(open)或编辑模式(edit)下打开记录,或者删除记录(delete)。type属性也可以是object或action,它们将呈现调用模型函数或action的链接。在这两种情况下,都需要为表单视图中的按钮添加属性,如本章的“向表单添加按钮”内容中所述。除了a元素,你还可以使用button元素;这里的type属性具有相同的语义。
有更多的…
还有一些辅助函数值得一提。如果需要为元素生成伪随机颜色,请使用kanban_color(some_variable)函数,该函数将返回一个设置背景和颜色属性的CSS类。这通常在t-tt-class元素中使用。
如果你想显示一个图像存储在一个二进制字段,使用kanban_image (modelname, fieldname, record.id.raw_value),它返回一个数据URI如果你包含在字段列表,字段设置,一个占位符,如果没有设置字段,或URL使Odoo流场的内容如果不包括在你的字段列表。如果需要同时显示大量记录或希望显示非常大的图像,则不要在字段列表中包含该字段。通常,您可以在img元素的t-att-src属性中使用它。
在看板视图中进行设计可能有点烦人。通常工作得更好的是使用HTML类型的函数字段生成HTML,并从Qweb view生成此HTML。通过这种方式,您仍然在使用QWeb,但是是在服务器端,当您需要处理大量数据时,这样会方便得多。
根据看板卡片的状态在列中显示
这个内容向您展示了如何建立看板视图,在这个视图中,用户可以将一条记录从一列拖放到另一列,从而将有问题的记录推到另一种状态。
准备
从现在开始,我们将在这里使用project模块,因为它定义的模型比在基模块中定义的视图更适合基于日期和状态的视图。因此,在继续之前,将project添加到add-on的依赖项列表中。
怎么做呢?
1. 为任务定义kanban视图:
<record id="kanban_tasks" model="ir.ui.view">
<field name="name">project.task.kanban</field>
<field name="model">project.task</field>
<field name="sequence">20</field>
<field name="arch" type="xml">
<kanban default_group_by="stage_id">
<field name="stage_id" />
<field name="name" />
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_card oe_kanban_global_click">
<field name="name" />
</div>
</t>
</templates>
</kanban>
</field>
</record>
2. 使用此视图添加menu和action。这留给读者作为练习。
它是如何工作的…
Kanban视图支持分组,它允许您在同一列中显示具有相同组字段的记录。这通常用于state或stage_id字段,因为它允许用户通过简单地将记录拖放到另一列中来更改该记录的字段值。将kanban元素上的default_group_by属性设置为您想要分组的字段的name,以便使用此功能。
为了控制看板分组的行为,在Odoo中有几个可用的选项:
group_create:这个选项用于隐藏或显示分组看板中的添加新列选项。默认值为true。
group_delete:此选项启用或禁用看板组上下文菜单中的列删除选项。默认值为true。
group_edit:此选项启用或禁用看板组上下文菜单中的列编辑选项。默认值为true。
archivable:此选项启用或禁用从看板组上下文菜单中归档和恢复记录的选项。只有在模型中存在active布尔值时,这才有效。
quick_create:使用这个选项,您可以直接从kanban视图创建记录。
quick_create_view:默认情况下,quick_create选项只在看板中显示name字段。但是使用quick_create_view选项,您可以提供最小表单视图的引用,以便在看板中显示它。
on_create:如果在创建新记录时不想使用quick_create,也不想将用户重定向到表单视图,可以提供向导的引用,这样单击时向导就会打开创建按钮。
有更多的…
如果在专用属性中没有定义,任何搜索筛选器都可以通过将字段名的上下文键group_by设置为group by来添加分组。
定义日历和甘特视图
此内容指导您如何以可视化的方式在记录中显示和编辑有关日期和持续时间的信息。
怎么做呢?
按照以下步骤为project.task模型添加日历视图:
1. 定义日历视图:
<record id="view_project_task_calendar" model="ir.ui.view">
<field name="model">project.task</field>
<field name="arch" type="xml">
<calendar date_start="date_start" date_stop="date_end" color="project_id">
<field name="name" />
<field name="user_id" />
</calendar>
</field>
</record>
2. 使用此视图添加菜单和操作。这留给读者作为练习。
它是如何工作的…
需要向calendar视图传递date_start和date_stop属性中的字段名,以指示在构建可视化表示时要查看哪些字段。只使用Datetime或Date类型的字段,其他类型的字段将不能工作,相反会生成错误。虽然date_start是必需的,但是您可以忽略date_stop,而设置date_delay属性,它应该是一个Float字段,表示持续时间(以小时为单位)。
calendar视图允许您在具有相同(任意分配的)颜色的字段中提供具有相同值的记录。要使用此功能,请将color属性设置为所需字段的name。在我们的示例中,我们可以一目了然地看到哪些任务属于同一个项目,因为我们分配了project_id作为字段来确定颜色组。
日历元素主体中命名的字段显示在表示所覆盖的时间间隔的块中,用逗号分隔。
有更多的…
日历视图还有其他一些有用的属性。如果希望在弹出窗口而不是标准表单视图中打开日历条目,请将event_open_popup设置为1。默认情况下,您通过填充一些文本来创建一个新条目,这将在内部调用模型的name_create函数来实际创建记录。如果您想禁用此行为,请将quick_add设置为0。
如果您的模型覆盖了一整天,那么将all_day设置为字段的名称,如果记录覆盖了一整天,则设置为true,否则设置为false。
甘特的观点
甘特视图是企业版的一部分,所以如果您使用的是Odoo社区版,它将无法工作。如果您想在community edition中使用它,您可以从OCA的web存储库中使用一个类似的模块web_timeline。这个模块引入了一个视图,如果您想要一个gantt样式的表示,就可以使用这个视图。
要在模块中使用甘特视图,需要在清单文件中添加web_gantt依赖项。web_gantt在企业版中提供。如果您使用的是企业版,您可以添加以下甘特视图:
<record id="view_project_task_calendar" model="ir.ui.view">
<field name="model">project.task.gantt</field>
<field name="model">project.task</field>
<field name="arch" type="xml">
<gantt date_start="date_assign" date_stop="date_deadline" default_group_by="user_id">
</gantt>
</field>
</record>
在企业甘特视图中,您可以使用以下选项:
date_start:这个选项的值将是日期的名称或保存甘特记录的开始日期的datetime字段。
date_stop:这个选项的值将是日期的名称或保存甘特记录结束日期的datetime字段。
date_delay:如果你不想使用date_stop,你可以使用这个选项。这将用于计算甘特记录的持续时间。
duration_unit:该选项的值将是以下选项之一:minute、hour (default)、day、week、month或year。
default_group_by:这是对甘特记录进行分组的字段的name。
定义图形和枢轴视图
在这个内容中,我们将看看Odoo的商业智能视图。这些是表示数据的只读视图。
准备
我们仍然在使用项目模块。您可以配置图形和枢轴视图以获得不同的统计信息。对于我们的示例,我们将重点关注分配的用户。我们将生成一个图形和透视视图来查看每个用户任务的用户。顺便说一下,最终用户可以通过修改视图选项来生成他们所选择的统计信息。
怎么做呢?
1. 使用条形图定义图形视图:
<record id="view_project_tasks_graph" model="ir.ui.view">
<field name="model">project.task</field>
<field name="arch" type="xml">
<graph type="bar">
<field name="user_id"/>
<field name="stage_id"/>
</graph>
</field>
</record>
2. 定义枢轴视图:
<record id="view_project_tasks_pivot" model="ir.ui.view">
<field name="model">project.task</field>
<field name="arch" type="xml">
<pivot> <field name="user_id" type="row"/>
<field name="project_id" type="col"/>
<field name="stage_id" type="col"/>
</pivot>
</field>
</record>
3.使用此视图添加菜单和操作。这留给读者作为练习。
如果一切顺利,您应该会看到显示分配给哪个用户多少任务以及这些任务的状态的图表。
它是如何工作的…
graph元素上的type属性决定了图形视图的初始模式。可能的值是bar、line和chart,但bar是默认值。图形视图具有高度的交互性,因此用户可以在不同模式之间切换,还可以添加和删除字段。Pivot视图有它们自己的根元素Pivot。
field元素告诉Odoo在哪个轴上显示什么。对于所有图形模式,至少需要一个带有type="row "的字段和一个带有 type="col"的字段才能看到有用的内容。measure类型的字段决定分组,而measure类型的字段代表要显示的值。线图只支持每种类型的一个字段,而图表和柱状图可以很好地处理两个组字段。pivot视图支持任意数量的group和measure字段。如果你切换到不支持你定义的组和度量数量的模式,什么也不会中断;有些字段会被忽略,结果可能不像它可能的那样有趣。
有更多的…
对于所有的graph类型,对Datetime字段进行分组是需要技巧的,因为您很少会在这里遇到相同的字段值。因此,如果您有一个row类型的Datetime字段,也要指定interval属性,该属性具有以下值之一:day、week、month、quarter或year。这将导致分组在给定的时间间隔内进行。
数据透视表还支持对列进行分组。对你想要的字段使用col类型。
像排序一样,分组在很大程度上依赖于PostgreSQL。因此,这里还有一条规则,即字段必须存在于数据库和当前表中,才能使用。通常的做法是定义收集所需所有数据的数据库视图,并在该视图上定义一个模型,以便拥有所有必要的字段。
根据视图和分组的复杂性,构建图可能是一项非常昂贵的工作。在这些情况下,考虑将auto_search属性设置为False,这样用户可以首先调整所有参数,然后才触发搜索。
定义队列视图
为了对记录进行队列分析,在Odoo版本12中增加了新的队列视图。队列视图用于查找记录在特定时间段内的生命周期。通过队列视图,您可以看到任何对象在特定时间内的流失率和保留率。
准备
队列视图是Odoo企业版的一部分,所以你不能只与社区版一起使用。如果您使用的是企业版,则需要在模块的清单文件中添加web_cohort。对于我们的示例,我们将创建一个视图来查看任务的队列分析。
怎么做呢?
按照以下步骤为项目添加队列视图。任务模型:
1. 定义队列视图:
<record id="view_project_tasks_graph" model="ir.ui.view">
<field name="name">project task cohort</field>
<field name="model">project.task</field>
<field name="arch" type="xml">
<cohort date_start="date_start" date_stop="date_deadline" interval="month" string="Task Cohort" />
</field>
</record>
2. 使用此视图添加菜单和操作。这留给读者作为练习。
它是如何工作的…
要创建队列视图,需要提供date_start和date_stop。这些将在视图中用于确定任何记录的时间跨度。例如,如果您正在管理服务的订阅,订阅的开始日期将为date_start,订阅的过期日期将为date_stop。
默认情况下,队列视图将以保留模式显示,间隔为一个月。你可以使用给定的选项在队列视图中获得不同的行为:
mode:你可以在两种模式下使用队列:retention(默认)或churn。保留率模式从100%开始,随时间减少,而流失模式从0%开始,随时间增加。
timeline:该选项接受两个值:forward(默认)或backward。在大多数情况下,您需要使用forward时间线。但是如果date_start是将来的,则需要使用向后的时间轴。我们使用向后时间轴的一个示例是注册事件参与者,其中事件日期在未来而注册日期在过去。
interval:默认情况下,队列是按月分组的,但是你可以在interval选项中更改。除了month,队列还支持day、week和year间隔。
measure:就像graph和pivot一样,measure用于显示给定字段的聚合值。如果没有选项,队列将显示记录的计数。
定义仪表板视图
Odoo版本12引入了一个名为dashboard的新视图。这用于在一个屏幕中显示多个视图和各种业务kpi。
准备
仪表盘视图是Odoo企业版的一部分,所以你不能在社区版中使用它。如果您使用的是企业版,则需要在模块的清单文件中添加web_dashboard的依赖项。在我们的示例中,我们将显示一些kpi和一些现有视图。我们将在同一个屏幕中显示图形和pivot视图,因此如果您还没有创建pivot和图形视图,请遵循定义图形和pivot视图的配制。我们将在仪表板视图中使用这些视图的ID。
怎么做呢?
1. 定义仪表板视图:
<record id="view_project_tasks_dashboard" model="ir.ui.view">
<field name="name">project task dashbaord</field>
<field name="model">project.task</field>
<field name="arch" type="xml">
<dashboard>
<view ref="my_project.view_project_tasks_graph" type="graph" />
<group>
<aggregate name="all_task" string="Total Tasks"
group_operator="count" field="id" measure="__count__"/>
<aggregate name="progress_task" string="In Progress Tasks" domain="[('stage_id.name', 'ilike', 'In Progress')]"
group_operator="count" field="id" measure="__count__"/>
<aggregate name="done_task" string="Completed Tasks" domain="[('stage_id.name', 'ilike','Done')]"
group_operator="count" field="id" measure="__count__"/>
<formula name="price_average" string="Overall Progress"
value="record.done_task / record.all_task" widget="percentage"/>
</group>
<view ref="my_project.view_project_tasks_pivot" type="pivot"/>
</dashboard>
</field>
</record>
2. 使用此视图添加菜单和操作。这留给读者作为练习。
它是如何工作的…
通过仪表盘视图,您可以使用aggregate和formula显示kpi。您可以在同一个屏幕上显示多个视图。如果您查看视图的定义,您会看到我们添加了两个视图:开始是图形,结束是枢轴。要显示视图,您只需要使用带有XML引用和视图类型的<view>标记。
我们使用<aggregate>标记显示了各种kip,包括总任务、正在进行的任务和已完成的任务。此标记将在search视图中显示当前域记录的聚合结果。在<aggregate>标记中,可以使用可选的域属性来显示特定记录集的聚合。默认情况下,聚合函数显示记录的计数,但是您可以使用group_operator属性(例如avg或max)提供SQL聚合函数。
有时,用<aggregate>不可能显示KPI;它需要一些额外的计算。在<formula>的帮助下,您可以为任何KPI定义公式。在我们的示例中,我们已经显示了所有任务的进度,并且使用可选属性小部件以百分比的形式显示值。
有更多的…
另一个有用的元素是<widget>标记。这样,您就可以使用自己选择的UI显示数据。在第16章,Web客户端开发中,我们将了解如何创建一个定制的小部件。

浙公网安备 33010602011771号