# 配置文件

# 创建

在 odoo 的根目录创建文件 odoo.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[options]
; 进入密码
admin_passwd=admin
; 主机名
db_host=localhost
; 数据库端口号
db_port=5432
; 数据库用户名
db_user=odoo
; 数据库密码
db_password=123456
; 指定要使用的数据库名
db_name=odoo
; 启动端口
http_port=8069
; addons模块的查找路径
addons_path=addons/,enterprise/

# 基础模块

房地产广告模块

首先看 odoo 是否提供了一种方法来回答特定的业务

  1. 创建模块
    在根目录创建
1
2
3
4
5
custom
|__estate
|__ __init__.py
|__ __manifest__.py

一个模块至少包含两个文件 manifest.py 和 init.py
manifest.py 是描述模块的不能为空,他唯一需要的字段是 “名称”

1
2
3
4
5
{
'name': 'Estate',
# 依赖项、框架模块
'depends': ['base'],
}

重启 odoo 服务器前将 custom 文件夹加入到配置文件 addons-path

1
addons_path=addons/,enterprise/,custom/

然后进入应用中搜索,如果没有请在设置中找到开发者工具 - 激活开发者模式 (使用资源测试)
开发者

在点击 应用 ——> 更新应用程序列表 —— 更新
开发者

把我们的模块启用

  1. 开发者模式

正常地址为
http:/localhost:8069/web#action=38&model=ir.module.module&view_type=kanban&cids=1&menu_id=54

开发者模式地址为
http:/localhost:8069/web?debug#action=38&model=ir.module.module&view_type=kanban&cids=1&menu_id=54

页面会多出一个调试
调试

# 模型和字段

# 对象关系映射

Odoo 的一个关键组件是 ORM 层此层避免了手动编写大多数 SQL,并提供可扩展性和安全服务
ORM:自动转换为 sql 语言

在 estate 中创建子目录 model 存放模型文件

1
2
3
4
5
6
7
custom
|__estate
|__ __init__.py
|__ __manifest__.py
|__ models
|__ __init__.py
|__ estate_model.py

init.py

1
from . import estate_model

estate_model.py

1
2
3
4
5
from odoo import api, fields, models

class EstateModel(models.Model):
# 数据表名 会吧'.'转换为'_' 最后的表名为estate_model
_name = 'estate.model'

对 python 的任何更改都要重启 odoo 服务器
然后找到模块升级,升级才能生效
升级

或者在启动配置里加 - u [模块]

img_4.png

# 模型字段

estate_model.py

1
2
3
4
5
6
7
8
9
from odoo import api, fields, models


class EstateModel(models.Model):
# 数据表名
_name = 'estate.model'
_description = '房地产'
# 字段
name=fields.Char(string='名称')

fields 里包含了数据类型
string 字段描述

在 estate 包下的__init__.py 引入 models

1
from . import models

重启后安装就可以看到数据库中多了一张叫 estate_model 的表

就成功把类映射成数据表

img_5.png

而除了 name 以外的字段为自动添加的字段
id (id)
模型记录的唯一标识符
create_date (Datetime)
记录的创建日期
create_uid (Many2one)
创建记录的用户
write_date (Datetime)
记录的上次修改日期
write_uid (Many2one)
上次修改记录的用户

添加完整字段
estate_model.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from odoo import api, fields, models


class EstateModel(models.Model):
# 数据表名
_name = 'estate.model'
_description = '房地产'

# 字段
name=fields.Char(string='名称', required=True)
description=fields.Text(string='描述')
postcode=fields.Char(string='邮政编码')
date_availability=fields.Date(string='可用日期')
expected_price=fields.Float(string='预期价格',required=True)
selling_price=fields.Float(string='销售价格')
bedrooms=fields.Integer(string='卧室')
living_area=fields.Integer(string='生活面积')
facades=fields.Integer(string='外观')
garage=fields.Boolean(string='车库')
garden=fields.Boolean(string='花园')
garden_area=fields.Integer(string='花园面积')
garden_orientation=fields.Selection(string='花园朝向', selection=[('north', '北'), ('south', '南'), ('east', '东'), ('west', '西')])

selection=[(‘north’, ‘北’), (‘south’, ‘南’), (‘east’, ‘东’), (‘west’, ‘西’)]
即选择时为’北’ 数据库保存为 ‘north’

常见属性

string (‘str’, 默认值字段名称)
ui 中字段的标签 (用户可见)

required (‘bool’, 默认值: false)
如果为 true, 则该字段不能为空,他必须有一个默认值,或者创建时给个值

help (‘str’, 默认值: ‘’)
在 ui 中为用户提供长格式的帮助工具

:attr:~odoo.fields.Field.index (‘bool’ , 默认值: False)
请求 Odoo 在列上创建一一个 "数据库索引"

# 安全性

Odoo 提供了 - - 种安全机制,允许特定用户组访问数据
即是否给特定的一类用户 增删改查 的权限

在 estate 下新建
security/ir.model.access.csv

1
2
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_estate_model,access_estate_model,model_estate_model,base.group_user,1,1,1,1
  • “id” 是外部标识符
  • “name” 是访问权限的名称
  • “model_id/id” 通过 “model_<model_name>” 的方式引用模型,其中 “<model_name>” 是模型的 “name”
  • “group_id/id” 引用访问权限适用的组
  • “perm_read,perm_write,perm_create,perm_unlink” 分别对应读、写、创建和取消链接权限

写完之后在 estate/manifest.py 中引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
'name': 'Estate',
'version': '1.0', # 版本号
'summary': 'estate 概要', # 概要
'description': 'estate 描述', # 描述
'author': 'douk', # 作者
'category': 'App', # 分类
'sequence': 1, # 排序
'auto_install': True, # 是否自动安装
'external_dependencies': {}, # 外部依赖项
'license': 'LGPL-3', # 许可证
'depends': ['base'], # 依赖项、框架模块
'installable': True, # 是否可安装
'application': True, # 是否为应用程序
'data': [
'security/ir.model.access.csv', # 权限
], # 数据文件
}

数据文件按照它们在 manifest.py 文件中的顺序顺序加载这意味着如果数据’A’ 引用数据 "B"', 您必须确保’B" 在’A’之前加载

# UI 交互

菜单
odoo 有 3 级菜单

  1. 根菜单
    img_6.png
  2. 一级菜单
    img_7.png
  3. 二级菜单
    img_8.png

首先在 estate 下新建
views/estate_model_view.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<record id="estate_model_action" model="ir.actions.act_window">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model</field>
<!-- 模型名称-->
<field name="res_model">estate.model</field>
<!-- 视图模式-->
<field name="view_mode">tree,form</field>
</record>
</data>
</odoo>



views/estate_model_menus.xml

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<menuitem id="estate_model_root" name="Estate">
<menuitem id="estate_model_level_menu" name="Estate_Property">
<menuitem id="estate_model_menu_action" action="estate_model_action"/>
</menuitem>
</menuitem>
</data>
</odoo>

action=“estate_model_action”
跳转到视图 estate_model_action

引入到__manifest__.py 的 data

1
2
3
4
5
'data': [
'security/ir.model.access.csv', # 权限
'views/estate_model_views.xml', # 视图
'views/estate_model_menus.xml', # 菜单视图
], # 数据文件

最后重启就能看见

ab

# 软删除

  1. 在模型中添加字段

estate_model.py

1
active=fields.Boolean(string='激活', default=True)

重新启动

  1. 软删除操作
    img_10.png
    我们新建一条数据 选中 操作 存档
    最后就会发现数据被软删除了
  2. 恢复
    img_11.png

img_12.png

img_13.png

# 状态

estate_model.py 中添加字段

1
state=fields.Selection(string='状态', selection=[('new', '新建'), ('offer', '报价'), ('sold', '已售'), ('cancel', '取消')], default='new')

# 基本视图

# 列表视图

列表视图,也称为树视图,以表格形式显示记录。

把要加的字段加上

views/estate_model_view.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>

<record id="estate_model_view_list" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model</field>
<!-- 模型名称-->
<field name="model">estate.model</field>
<field name="arch" type="xml">
<tree string="Estate Model" editable="top">
<field name="name"/>
<field name="postcode"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="expected_price"/>
<field name="selling_price"/>
<field name="date_availability"/>
</tree>
</field>
</record>

<record id="estate_model_action" model="ir.actions.act_window">
<!-- 显示在菜单中的名称-->
<field name="name">房屋信息</field>
<!-- 模型名称-->
<field name="res_model">estate.model</field>
<!-- 视图模式-->
<field name="view_mode">tree,form</field>
</record>
</data>
</odoo>

editable=“top” 点击数据可以直接修改不用进到修改页面修改

# 表单视图

它们根元素是

,它们由结构元素和交互元素组成
也可以使用常规的 html 标签

首先去掉
editable=“top”
这样点击才能进入 form 视图

views/estate_model_view.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>

<record id="estate_model_view_list" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model</field>
<!-- 模型名称-->
<field name="model">estate.model</field>
<field name="arch" type="xml">
<tree string="Estate Model">
<field name="name"/>
<field name="postcode"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="expected_price"/>
<field name="selling_price"/>
<field name="date_availability"/>
</tree>
</field>
</record>

<record id="estate_model_view_form" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model form view</field>
<!-- 模型名称-->
<field name="model">estate.model</field>
<field name="arch" type="xml">
<form>
<sheet>
<h1>
<field name="name"/>
</h1>
<group>
<group>
<field name="postcode"/>
<field name="date_availability"/>
</group>
<group>
<field name="expected_price"/>
<field name="selling_price"/>
</group>

</group>
<notebook>
<page string="描述">
<group>
<group>
<field name="name"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="facades"/>
<field name="garden"/>
<field name="garage"/>
<field name="garden_area"/>
<field name="garden_orientation"/>
</group>
</group>
</page>
</notebook>
</sheet>

</form>
</field>
</record>

<record id="estate_model_action" model="ir.actions.act_window">
<!-- 显示在菜单中的名称-->
<field name="name">房屋信息</field>
<!-- 模型名称-->
<field name="res_model">estate.model</field>
<!-- 视图模式-->
<field name="view_mode">tree,form</field>
</record>
</data>
</odoo>

# 搜索视图

它们的根元素为,odoo 提供了默认按名称的过滤方式
img_14.png

views/estate_model_view.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<!-- 列表视图-->
<record id="estate_model_view_list" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model</field>
<!-- 模型名称-->
<field name="model">estate.model</field>
<field name="arch" type="xml">
<tree string="Estate Model">
<field name="name"/>
<field name="postcode"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="expected_price"/>
<field name="selling_price"/>
<field name="date_availability"/>
</tree>
</field>
</record>
<!-- 表单视图-->
<record id="estate_model_view_form" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model form view</field>
<!-- 模型名称-->
<field name="model">estate.model</field>
<field name="arch" type="xml">
<form>
<sheet>
<h1>
<field name="name"/>
</h1>
<group>
<group>
<field name="postcode"/>
<field name="date_availability"/>
</group>
<group>
<field name="expected_price"/>
<field name="selling_price"/>
</group>

</group>
<notebook>
<page string="描述">
<group>
<group>
<field name="name"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="facades"/>
<field name="garden"/>
<field name="garage"/>
<field name="garden_area"/>
<field name="garden_orientation"/>
</group>
</group>
</page>
</notebook>
</sheet>

</form>
</field>
</record>
<!-- 搜索视图-->
<record id="estate_model_view_search" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model search view</field>
<!-- 模型名称-->
<field name="model">estate.model</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="postcode"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="expected_price"/>
<field name="selling_price"/>
<field name="date_availability"/>
</search>
</field>
</record>

<record id="estate_model_action" model="ir.actions.act_window">
<!-- 显示在菜单中的名称-->
<field name="name">房屋信息</field>
<!-- 模型名称-->
<field name="res_model">estate.model</field>
<!-- 视图模式-->
<field name="view_mode">tree,form</field>
</record>
</data>
</odoo>

img_15.png

odoo 自带的过滤功能,如果搜索框输入的汉字就不会显示搜索关于浮点型等其他类型的搜索
img_16.png

#

在 odoo 中,域对记录条件进行编码:域是用于选择模型记录子集的标准列表。每个标准都是一个三元组,具有 “字段名称 *、运肄符 * 和值。
如果指定的字段满足应用于该值的运算符的条件,则记录满足条件。
在 odoo 中两个表表达式默认为 and
例如

1
[('product_type', '=', 'service'), ('unit_price', '>', 1000)]

odoo 的与或非为前缀表达式
例如,要选择 “哪些是服务 或 单价不在 1000 到 2000 之间” 的产品:

1
2
3
4
5
['|',
('product_type', '=', 'service'),
'!', '&',
('unit_price', '>=', 1000),
('unit_price', '<', 2000)]

现在我们需要一个三居室的搜索条件和以邮编的分组条件
views/estate_model_view.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

<!-- 搜索视图-->
<record id="estate_model_view_search" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model search view</field>
<!-- 模型名称-->
<field name="model">estate.model</field>
<!-- 列表视图-->
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="postcode"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="expected_price"/>
<field name="selling_price"/>
<field name="date_availability"/>
<filter name="three_bedrooms" string="三居室" domain="[('bedrooms','=','3')]"/>

<group expand="1" string="邮编">
<filter name="postcode" string="邮编" context="{'group_by':'postcode'}"/>
</group>
</search>
</field>
</record>

img_18.png

# 模型之间的关系 (表与表之间的关系)

# 多对一 (Many2one)

# 准备

  1. 新建 model
    estate_model_type.py
1
2
3
4
5
6
7
8
9
10
from odoo import api, fields, models


class EstateModelType(models.Model):
# 数据表名
_name = 'estate.model.type'
_description = '房屋类型'

# 字段
name=fields.Char(string='类型名称', required=True)

在__init__.py 中导入

1
2
3
from . import estate_model
from . import estate_model_type

  1. 加权限
1
estate.access_estate_model_type,access_estate_model_type,estate.model_estate_model_type,base.group_user,1,1,1,1
  1. 加视图
    estate_model_type_views.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<!-- 列表视图-->
<record id="estate_model_type_view_list" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model type</field>
<!-- 模型名称-->
<field name="model">estate.model.type</field>
<!-- 列表视图-->
<field name="arch" type="xml">
<tree>
<field name="name"/>

</tree>
</field>
</record>
<!-- 表单视图-->
<record id="estate_model_type_view_form" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model type form view</field>
<!-- 模型名称-->
<field name="model">estate.model.type</field>
<!-- 列表视图-->
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="name"/>
</group>
</group>
</sheet>
</form>
</field>
</record>

<record id="estate_model_type_action" model="ir.actions.act_window">
<!-- 显示在菜单中的名称-->
<field name="name">房屋类型</field>
<!-- 模型名称-->
<field name="res_model">estate.model.type</field>
<!-- 视图模式-->
<field name="view_mode">tree,form</field>
</record>
</data>
</odoo>

在__manifest__.py 中导入数据文件

1
2
3
4
5
6
'data': [
'security/ir.model.access.csv', # 权限
'views/estate_model_views.xml', # 视图
'views/estate_model_menus.xml', # 菜单视图
'views/estate_model_type_views.xml', # 房产类型视图
], # 数据文件
  1. 加菜单
    estate_model_menus.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<menuitem id="estate_model_root" name="房屋销售">
<menuitem id="estate_model_level_menu" name="房地产">
<menuitem id="estate_model_menu_action" action="estate_model_action"/>
</menuitem>
<menuitem id="estate_model_menu_setting" name="设置">
<menuitem id="estate_model_type" action="estate_model_type_action"/>
</menuitem>
</menuitem>
</data>
</odoo>

# 关联字段

在 estate_model.py 中加入字段

1
2
3
4
5
# 关联字段
model_type_id=fields.Many2one('estate.model.type', string='类型')
sales_person_id=fields.Many2one('res.users', string='销售人员')
buyer_id=fields.Many2one('res.partner', string='买家')

res,partner : 合作伙伴是物理实体或法人实体。它可以是公司、个人甚至是联系地址。
res.users : 系统的用户。用户可以是 “内部” 用户,即他们可以访问 Odoo 后端。或者他们可以是 “门户”,即他们不能访问后端,只能访问
前端 (例如访问他们以前在电子商务中的订单)。

在 estate_model_views.xml 中加入显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<!-- 列表视图-->
<record id="estate_model_view_list" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model</field>
<!-- 模型名称-->
<field name="model">estate.model</field>
<!-- 列表视图-->
<field name="arch" type="xml">
<tree string="Estate Model">
<field name="name"/>
<field name="postcode"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="expected_price"/>
<field name="selling_price"/>
<field name="date_availability"/>
</tree>
</field>
</record>
<!-- 表单视图-->
<record id="estate_model_view_form" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model form view</field>
<!-- 模型名称-->
<field name="model">estate.model</field>
<!-- 列表视图-->
<field name="arch" type="xml">
<form>
<sheet>
<h1>
<field name="name"/>
</h1>
<group>
<group>
<field name="model_type_id"/>
<field name="postcode"/>
<field name="date_availability"/>
</group>
<group>
<field name="expected_price"/>
<field name="selling_price"/>
</group>

</group>
<notebook>
<page string="描述">
<group>
<group>
<field name="name"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="facades"/>
<field name="garden"/>
<field name="garage"/>
<field name="garden_area"/>
<field name="garden_orientation"/>
</group>
</group>
</page>
<page string="其他信息">
<group>
<group>
<field name="sales_person_id"/>
<field name="buyer_id"/>

</group>
</group>
</page>
</notebook>
</sheet>

</form>
</field>
</record>
<!-- 搜索视图-->
<record id="estate_model_view_search" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model search view</field>
<!-- 模型名称-->
<field name="model">estate.model</field>
<!-- 列表视图-->
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="postcode"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="expected_price"/>
<field name="selling_price"/>
<field name="date_availability"/>
<filter name="three_bedrooms" string="三居室" domain="[('bedrooms','=','3')]"/>

<group expand="1" string="邮编">
<filter name="postcode" string="邮编" context="{'group_by':'postcode'}"/>
</group>
</search>
</field>
</record>

<record id="estate_model_action" model="ir.actions.act_window">
<!-- 显示在菜单中的名称-->
<field name="name">房屋信息</field>
<!-- 模型名称-->
<field name="res_model">estate.model</field>
<!-- 视图模式-->
<field name="view_mode">tree,form</field>
</record>
</data>
</odoo>

# 多对多 (Many2many)

# 准备

  1. 新建 model
    estate_model_tag.py
1
2
3
4
5
6
7
8
9
10
from odoo import api, fields, models


class EstateModelTag(models.Model):
# 数据表名
_name = 'estate.model.tag'
_description = '房屋标签'

# 字段
name=fields.Char(string='房屋标签', required=True)

在__init__.py 中导入

  1. 新建视图
    estate_model_tag_views.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<!-- 列表视图-->
<record id="estate_model_tag_view_list" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model tag</field>
<!-- 模型名称-->
<field name="model">estate.model.tag</field>
<!-- 列表视图-->
<field name="arch" type="xml">
<tree>
<field name="name"/>

</tree>
</field>
</record>
<!-- 表单视图-->
<record id="estate_model_tag_view_form" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model tag form view</field>
<!-- 模型名称-->
<field name="model">estate.model.tag</field>
<!-- 列表视图-->
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="name"/>
</group>
</group>
</sheet>
</form>
</field>
</record>

<record id="estate_model_tag_action" model="ir.actions.act_window">
<!-- 显示在菜单中的名称-->
<field name="name">房屋标签</field>
<!-- 模型名称-->
<field name="res_model">estate.model.tag</field>
<!-- 视图模式-->
<field name="view_mode">tree,form</field>
</record>
</data>
</odoo>

在__manifest__.py 中引入

  1. 增加权限

ir.model.access.csv

1
estate.access_estate_model_tag,access_estate_model_tag,estate.model_estate_model_tag,base.group_user,1,1,1,1

# 关联

estate_model.py
增加关联字段

1
tag_ids=fields.Many2many('estate.model.tag', string='房屋标签')

修改房屋信息的列表视图和表单视图
estate_model_views.xml

1
2

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

# 一对多 (one2Many)

one2Many 和 Many2one 是对应的

# 准备

  1. 新建 model
    estate_model_offer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
from odoo import api, fields, models


class EstateModelOffer(models.Model):
# 数据表名
_name = 'estate.model.offer'
_description = '房屋报价单'

# 字段
price = fields.Float(string='报价')
status = fields.Selection([('已接受', '已接受'), ('已拒绝', '已拒绝'), ('已取消', '已取消')], string='报价状态',copy=False)
partner_id = fields.Many2one('res.partner', string='客户', required=True)
property_id = fields.Many2one('estate.model', string='房屋', required=True)

init.py 导入

  1. 新建视图
    estate_model_offer_views.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<!-- 列表视图-->
<record id="estate_model_offer_view_list" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model offer</field>
<!-- 模型名称-->
<field name="model">estate.model.offer</field>
<field name="arch" type="xml">
<tree>
<field name="price"/>
<field name="partner_id"/>
<field name="status"/>
</tree>
</field>
</record>
<!-- 表单视图-->
<record id="estate_model_offer_view_form" model="ir.ui.view">
<!-- 显示在菜单中的名称-->
<field name="name">Estate Model offer form view</field>
<!-- 模型名称-->
<field name="model">estate.model.offer</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="price"/>
<field name="partner_id"/>
<field name="status"/>
</group>
</sheet>
</form>
</field>
</record>

<record id="estate_model_offer_action" model="ir.actions.act_window">
<!-- 显示在菜单中的名称-->
<field name="name">房屋报价</field>
<!-- 模型名称-->
<field name="res_model">estate.model.offer</field>
<!-- 视图模式-->
<field name="view_mode">tree,form</field>
</record>
</data>
</odoo>

在__manifest__.py 中引入

  1. 权限
    ir.model.access.csv
1
estate.access_estate_model_offer,access_estate_model_offer,estate.model_estate_model_offer,base.group_user,1,1,1,1

# 关系

estate_model.py

1
offer_ids=fields.One2many('estate.model.offer', 'property_id', string='报价')

修改房屋信息的列表视图和表单视图
estate_model_views.xml

1
2

<field name="offer_ids" widget="one2many_list"/>

# 计算字段和 Onchanges

# 计算字段

要求:计算总面积和最佳报价

总面积等于生活面积加花园面积
增加字段
estate_model.py

1
2
3
4
5
6
7
8
9
# compute下划线开头
total_area=fields.Float(string='总面积', compute='_compute_total_area', store=True)
# 当living_area或garden_area发生改变
@api.depends('living_area', 'garden_area') # 计算字段
def _compute_total_area(self):
# 对数据的每一行计算
for rec in self:
rec.total_area = rec.living_area + rec.garden_area

在视图显示
estate_model_views.xml

1
2

<field name="total_area"/>

# 计算关系字段

计算最佳报价

增加字段
estate_model.py

1
2
3
4
5
6
best_price=fields.Float(string='最佳价格', compute='_compute_best_price', store=True)
@api.depends('offer_ids.price') # 计算字段
def _compute_best_price(self): # 对数据的每一行计算
for rec in self:
rec.best_price = max(rec.offer_ids.mapped('price'))

在视图显示
estate_model_views.xml

1
2

<field name="best_price"/>

# 反函数

计算报价的有效日期

estate_model_offer.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
validity = fields.Integer(string='有效天数')
date_deadline = fields.Date(string='截止日期', compute='_compute_date_deadline', inverse='_inverse_date_deadline')

# 截止日期=创建日期+有效天数
@api.depends('validity', 'create_date')
def _compute_date_deadline(self):
for rec in self:
if rec.create_date:
rec.date_deadline = rec.create_date + timedelta(days=rec.validity)
else:
rec.date_deadline = fields.Datetime.today() + timedelta(days=rec.validity)

def _inverse_date_deadline(self):
self.validity = (self.date_deadline - self.create_date.date()).days

compute 每次数据发生变化更新

inverse 提交时更新

添加到视图

1
2
3

<field name="validity"/>
<field name="date_deadline"/>

默认情况下,计算字段不存储在数据库中。因此,除非定义了 “搜索” 方法,否则不可能在计算字段上进行搜索
另一种解决方案是使用 store=True 属性存储该字段。虽然这通常很方便,但请注意添加到模型中的潜在计算负载。

# Onchanges

当花园启用,花园面积默认为 10,花园朝向默认为北
当取消花园,花园面积为 0,花园朝向为空

estate_model.py

1
2
3
4
5
6
7
8
@api.onchange('garden')
def _onchange_garden(self): # garden
if self.garden:
self.garden_area = 10
self.garden_orientation = 'north'
else:
self.garden_area = 0
self.garden_orientation = ''

警告用法

estate_model.py

1
2
3
4
5
6
7
8
9
10
# 警告
@api.onchange('bedrooms')
def _onchange_bedrooms(self):
if self.bedrooms < 2:
return {
'warning': {
'title': '警告',
'message': '卧室数量不能少于2个',
}
}

# 按钮

# 动作类型

# 添加售出和取消

estate_model.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 按钮
def do_sold(self):
# 设置房屋状态已售
if self.state == '已取消':
# 报个异常
raise exceptions.UserError('已取消的房屋不能售出')
else:
self.state = '已售'

def do_cancel(self):
# 设置房屋状态已取消
if self.state == '已售':
raise exceptions.UserError('已售的房屋不能取消')
else:
self.state = '已取消'

estate_model_views.xml

1
2
3

<button string="售出" class="btn btn-primary" type="object" name="do_sold"/>
<button string="取消" class="btn btn-danger" type="object" name="do_cancel"/>

通过 name 绑定

img_19.png

# 添加售出和取消

estate_model_offer.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def do_accept(self):
# 接受报价
if self.status == "新建" and self.search_count(
domain=([('status', '=', '已接受'), ('property_id', '=', self.property_id.id)])) == 0:
self.status = '已接受'
# 修改销售价格
self.property_id.selling_price = self.price
# 修改购买人员
self.property_id.buyer_id = self.partner_id
else:
raise exceptions.ValidationError("该状态下不允许此操作")

def do_cancel(self):
# 拒绝报价
if self.status == "新建":
self.status = '已拒绝'
else:
raise exceptions.ValidationError("该状态下不允许此操作")

estate_model_offer_views.xml

1
2
3

<button name="do_accept" string="接受" type="object" icon="fa-check"/>
<button name="do_cancel" string="拒绝" type="object" icon="fa-close"/>

img_20.png

# 对象类型

阅读次数

请我喝[茶]~( ̄▽ ̄)~*

douk 微信支付

微信支付

douk 支付宝

支付宝