模型约束
Odoo提供了两种设置自动验证不变量的方法: Python constraints
和SQL constraints
.
Python约束定义为用constrains()
修饰的方法,并在记录集上调用。修饰符指定约束中涉及哪些字段,以便在修改其中一个约束时自动评估约束。如果不满足该不变量,则期望该方法引发异常:
from odoo.exceptions import ValidationError
@api.constrains('age')
def _check_something(self):
for record in self:
if record.age > 20:
raise ValidationError("Your record is too old: %s" % record.age)
# all records passed the test, don't return anything
练习
添加Python约束
添加一个约束,该约束检查instructor在他/她自己的session的attendees中不存在。
openacademy/models.py
# -*- coding: utf-8 -*-
from odoo import models, fields, api, exceptions
class Course(models.Model):
_name = 'openacademy.course'
'message': "Increase seats or remove excess attendees",
},
}
@api.constrains('instructor_id', 'attendee_ids')
def _check_instructor_not_in_attendees(self):
for r in self:
if r.instructor_id and r.instructor_id in r.attendee_ids:
raise exceptions.ValidationError("A session's instructor can't be an attendee")
SQL约束是通过模型属性_sql_constraints
定义的。后者被分配给字符串的三元组列表(name, sql_definition, message),其中name
是有效的SQL约束名称,sql_definition
是table_constraint表达式,message
是错误消息。
练习
添加SQL约束
在PostgreSQL's documentation的帮助下,添加以下约束:
openacademy/models.py
session_ids = fields.One2many(
'openacademy.session', 'course_id', string="Sessions")
_sql_constraints = [
('name_description_check',
'CHECK(name != description)',
"The title of the course should not be the description"),
('name_unique',
'UNIQUE(name)',
"The course title must be unique"),
]
class Session(models.Model):
_name = 'openacademy.session'
练习
练习6 - 添加重复选项
因为我们为课程名称的唯一性添加了一个约束,所以不再可能使用“复制”函数 (Form ‣ Duplicate)。
重新实现你自己的“复制”方法,允许复制课程对象,把原来的名字改成“[原始名字]的拷贝”。
openacademy/models.py
session_ids = fields.One2many(
'openacademy.session', 'course_id', string="Sessions")
@api.multi
def copy(self, default=None):
default = dict(default or {})
copied_count = self.search_count(
[('name', '=like', u"Copy of {}%".format(self.name))])
if not copied_count:
new_name = u"Copy of {}".format(self.name)
else:
new_name = u"Copy of {} ({})".format(self.name, copied_count)
default['name'] = new_name
return super(Course, self).copy(default)
_sql_constraints = [
('name_description_check',
'CHECK(name != description)',
树视图可以采取补充属性来进一步定制他们的行为:
decoration-{$name}
允许根据相应的记录属性更改行文本的样式。
值是Python表达式。对于每个记录,表达式以记录的属性作为上下文值来评估,如果为true,则将相应的样式应用于行。 其他上下文值是uid
(当前用户的id) 和current_date
(当前日期为yyyy-MM-dd形式的字符串)。
{$name}
可以是 bf
(font-weight: bold
), it
(font-style: italic
),或任何的bootstrap contextual color (danger
,info
, muted
, primary
, success
or warning
).
editable
要么是 "top"
要么是 "bottom"
。使树视图可就地编辑(而不是必须通过表单视图),该值是新行出现的位置
练习
列表颜色
修改Session树视图的方式是持续少于5天的会话是蓝色的,而持续超过15天的则是红色的。
修改Session树视图:
openacademy/views/openacademy.xml
session.tree
openacademy.session
将记录显示为日历事件。它们的根元素是
color
用于颜色分割的字段的名称。颜色被自动分配给事件,但是相同颜色段中的事件(具有与它们的@color字段相同的值的记录)将被赋予相同的颜色
date_start
记录的字段保存事件的开始日期/时间
date_stop
(可选的)
记录字段保存事件的结束日期/时间
字段(定义每个日历事件的标签 )
练习
日历视图
向Session模型添加日历视图,使用户能够查看与Open Academy相关的事件。
添加从start_date和duration计算的AA字段end_date
提示
逆函数使字段可写,并允许在日历视图中移动会话(通过拖放)
openacademy/models.py
# -*- coding: utf-8 -*-
from datetime import timedelta
from odoo import models, fields, api, exceptions
class Course(models.Model):
attendee_ids = fields.Many2many('res.partner', string="Attendees")
taken_seats = fields.Float(string="Taken seats", compute='_taken_seats')
end_date = fields.Date(string="End Date", store=True,
compute='_get_end_date', inverse='_set_end_date')
@api.depends('seats', 'attendee_ids')
def _taken_seats(self):
},
}
@api.depends('start_date', 'duration')
def _get_end_date(self):
for r in self:
if not (r.start_date and r.duration):
r.end_date = r.start_date
continue
# Add duration to start_date, but: Monday + 5 days = Saturday, so
# subtract one second to get on Friday instead
start = fields.Datetime.from_string(r.start_date)
duration = timedelta(days=r.duration, seconds=-1)
r.end_date = start + duration
def _set_end_date(self):
for r in self:
if not (r.start_date and r.end_date):
continue
# Compute the difference between dates, but: Friday - Monday = 4 days,
# so add one day to get 5 days instead
start_date = fields.Datetime.from_string(r.start_date)
end_date = fields.Datetime.from_string(r.end_date)
r.duration = (end_date - start_date).days + 1
@api.constrains('instructor_id', 'attendee_ids')
def _check_instructor_not_in_attendees(self):
for r in self:
openacademy/views/openacademy.xml
session.calendar
openacademy.session
Sessions
openacademy.session
form
tree,form,calendar
搜索视图self
表示用户输入的值。在下面的示例中,它用于搜索字段name
和description
搜索视图还可以包含
domain
将给定域添加到当前搜索
context
在当前搜索中添加一些上下文;使用键group_by在给定字段名上分组结果
若要在操作中使用非默认搜索视图,则应使用动作记录的search_view_id字段链接。
该操作还可以通过context
字段设置搜索字段的默认值:表单search_default_field_name的上下文键将初始化所提供的值的field_name。搜索过滤器必须有一个可选的@name,具有默认值,并作为布尔值(默认情况下只能启用)。
练习
搜索视图
openacademy/views/openacademy.xml
openacademy.course
form
tree,form
Create the first course
警告
甘特图视图需要在企业版版本中存在的web_gantt模块。
水平柱状图通常用于显示项目规划和进度,它们的根元素是
。
练习
甘特图
添加甘特图,使用户能够查看与Open Academy模块相关联的session调度。session应由instructor分组。
openacademy/models.py
end_date = fields.Date(string="End Date", store=True,
compute='_get_end_date', inverse='_set_end_date')
hours = fields.Float(string="Duration in hours",
compute='_get_hours', inverse='_set_hours')
@api.depends('seats', 'attendee_ids')
def _taken_seats(self):
for r in self:
end_date = fields.Datetime.from_string(r.end_date)
r.duration = (end_date - start_date).days + 1
@api.depends('duration')
def _get_hours(self):
for r in self:
r.hours = r.duration * 24
def _set_hours(self):
for r in self:
r.duration = r.hours / 24
@api.constrains('instructor_id', 'attendee_ids')
def _check_instructor_not_in_attendees(self):
for r in self:
openacademy/views/openacademy.xml
session.gantt
openacademy.session
Sessions
openacademy.session
form
tree,form,calendar,gantt
图形视图允许聚合的概述和模型的分析,它们的根元素是
。
注
数据透视图(元素
图形视图有4种显示模式,默认模式是使用@typ属性来选择的。
柱状图(默认的)
柱状图,第一维用于定义水平轴上的组,其他维度定义每个组内的聚合条。
默认情况下,柱状图是并排的,它们可以通过使用@stacked="True"在
曲线图
二维线图
饼图
二维的饼图
图形视图包含具有强制@type属性的
row
(默认的)
默认情况下应聚合字段
measure
字段应该聚合而不是分组
警告
图形视图对数据库值执行聚合,它们不与非存储的计算字段一起使用
练习
图形视图
在Session对象中添加一个图形视图,它显示每个course中在柱形图表单下attendees的数量。
openacademy/models.py
hours = fields.Float(string="Duration in hours",
compute='_get_hours', inverse='_set_hours')
attendees_count = fields.Integer(
string="Attendees count", compute='_get_attendees_count', store=True)
@api.depends('seats', 'attendee_ids')
def _taken_seats(self):
for r in self:
for r in self:
r.duration = r.hours / 24
@api.depends('attendee_ids')
def _get_attendees_count(self):
for r in self:
r.attendees_count = len(r.attendee_ids)
@api.constrains('instructor_id', 'attendee_ids')
def _check_instructor_not_in_attendees(self):
for r in self:
openacademy/views/openacademy.xml
openacademy.session.graph
openacademy.session
Sessions
openacademy.session
form
tree,form,calendar,gantt,graph
用于组织任务、生产过程等。它们的根元素是
。
看板视图显示了一组可能列在列中的卡。每个卡代表一个记录,每个列代表聚合字段的值。
例如,项目任务可以按阶段(每个列是一个阶段),或由负责的(每个列是用户)来组织,等等。
看板视图将每个卡的结构定义为表单元素(包括基本HTML)和QWEB的混合。
练习
看板视图
添加一个看板视图,显示按course分组的sessions(列是course)。
openacademy/models.py
duration = fields.Float(digits=(6, 2), help="Duration in days")
seats = fields.Integer(string="Number of seats")
active = fields.Boolean(default=True)
color = fields.Integer()
instructor_id = fields.Many2one('res.partner', string="Instructor",
domain=['|', ('instructor', '=', True),
openacademy/views/openacademy.xml
openacad.session.kanban
openacademy.session
Session name:
Start date:
duration:
Sessions
openacademy.session
form
tree,form,calendar,gantt,graph,kanban
工作流是与描述其动态的业务对象相关联的模型。工作流也用于跟踪随着时间推移而演变的进程。
练习
工作流
添加一个state
字段到Session模型。它将被用来定义一个workflow-ish。
sesion可以有三种可能的状态:Draft (默认的), Confirmed 和Done。
在session表单中,添加一个(只读)字段来可视化状态,以及按钮来更改状态。有效的转换是:
state
字段openacademy/models.py
attendees_count = fields.Integer(
string="Attendees count", compute='_get_attendees_count', store=True)
state = fields.Selection([
('draft', "Draft"),
('confirmed', "Confirmed"),
('done', "Done"),
], default='draft')
@api.multi
def action_draft(self):
self.state = 'draft'
@api.multi
def action_confirm(self):
self.state = 'confirmed'
@api.multi
def action_done(self):
self.state = 'done'
@api.depends('seats', 'attendee_ids')
def _taken_seats(self):
for r in self:
openacademy/views/openacademy.xml
openacademy.session
工作流可以与Odoo中的任何对象相关联,并且是完全可定制的。工作流用于构造和管理业务对象和文档的生命周期,并用图形工具定义转换、触发器等。像往常一样,工作流、活动(节点或动作)和转换(条件)被声明为XML记录。在工作流中导航的令牌被称为工作项。
警告
与模型相关联的工作流仅在创建模型的记录时创建。因此,在工作流定义之前没有与工作流实例相关联的工作流实例
练习
工作流
通过实际工作流替换特设Session工作流。转换Session 表单视图,因此它的按钮调用工作流而不是模型的方法。
openacademy/__manifest__.py
'templates.xml',
'views/openacademy.xml',
'views/partner.xml',
'views/session_workflow.xml',
],
# only loaded in demonstration mode
'demo': [
openacademy/models.py
('draft', "Draft"),
('confirmed', "Confirmed"),
('done', "Done"),
])
@api.multi
def action_draft(self):
openacademy/views/openacademy.xml
openacademy/views/session_workflow.xml
OpenAcademy sessions workflow
openacademy.session
True
Draft
function
action_draft()
Confirmed
function
action_confirm()
Done
function
action_done()
confirm
draft
draft
done
提示
为了检查工作流的实例是否与session一起正确创建,可以到设置‣ 技术‣ 工作流‣ 实例中查看
练习
自动转换
当超过一半的session席位被使用时,自动将session从Draft 转换为Confirmed 。
openacademy/views/session_workflow.xml
done
taken_seats > 50
练习
服务器动作
替换Python方法,通过服务器操作同步session状态。
工作流和服务器操作都可以完全由UI创建。
openacademy/views/session_workflow.xml
True
Set session to Draft
model.search([('id', 'in', context['active_ids'])]).action_draft()
Draft
dummy
Set session to Confirmed
model.search([('id', 'in', context['active_ids'])]).action_confirm()
Confirmed
dummy
Set session to Done
model.search([('id', 'in', context['active_ids'])]).action_done()
Done
dummy
访问控制机制必须被配置来实现一致的安全策略。
在模型res.groups上创建组作为正常记录,并通过菜单定义授予菜单访问权限。然而,即使没有菜单,对象仍然可以被间接访问,因此必须为组定义实际的对象级权限(读、写、创建、断开)。它们通常通过CSV文件插入模块内部。还可以使用字段的组属性限制对视图或对象的特定字段的访问。
访问权限被定义为ir.model.access模型的记录。每个访问权限与一个模型、一个组(或者没有全局访问组)以及一组权限相关:读、写、创建、解锁。这样的访问权限通常由以其模型ir.model.access.csv命名的CSV文件创建:
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_idea_idea,idea.idea,model_idea_idea,base.group_user,1,1,1,0
access_idea_vote,idea.vote,model_idea_vote,base.group_user,1,1,1,0
练习
通过Odoo 接口添加访问控制
创建新用户"John Smith",然后创建一个组"OpenAcademy / Session Read",其具有对Session 模型的读访问权限。
练习
通过模块中的数据文件添加访问控制
使用数据文件,
openacademy/security/ir.model.access.csv
openacademy/__manifest__.py
# always loaded
'data': [
'security/security.xml',
'security/ir.model.access.csv',
'templates.xml',
'views/openacademy.xml',
'views/partner.xml',
openacademy/security/ir.model.access.csv
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
course_manager,course manager,model_openacademy_course,group_manager,1,1,1,1
session_manager,session manager,model_openacademy_session,group_manager,1,1,1,1
course_read_all,course all,model_openacademy_course,,1,0,0,0
session_read_all,session all,model_openacademy_session,,1,0,0,0
openacademy/security/security.xml
OpenAcademy / Manager
记录规则限制对给定模型的记录子集的访问权限。规则是模型ir.rule的记录,与模型、多个组(many2many 字段)、限制应用的权限以及域相关联。指定访问权限受限的记录的域。
下面是一个防止删除不处于状态cancel的引线的规则的示例。请注意,字段groups
的值必须遵循与ORM方法write()
相同的约定。
Only cancelled leads may be deleted
[('state','=','cancel')]
练习
记录规则
为模型Course和"OpenAcademy / Manager"组添加记录规则,该规则限制write
和unlink
访问course的responsible。如果course没有responsible,小组的所有用户必须能够修改它。
在openacademy/security/security.xml中创建新规则:
openacademy/security/security.xml
OpenAcademy / Manager
Only Responsible can modify Course
['|', ('responsible_id','=',False),
('responsible_id','=',user.id)]
向导通过动态表单描述与用户(或对话框)的交互会话。向导只是一个扩展类TransientModel而不是Model
的模型。类TransientModel
扩展了Model
并重用其现有的机制,具有以下特殊性:
我们希望创建一个向导,允许用户为特定sessio创建attendees,或者同时为session列表创建向导。
练习
定义向导
创建一个与Session模型具有many2one关系的向导模型,以及与Partner模型具有many2many 的关系。
添加新文件 openacademy/wizard.py
:
openacademy/__init__.py
from . import controllers
from . import models
from . import partner
from . import wizard
openacademy/wizard.py
# -*- coding: utf-8 -*-
from odoo import models, fields, api
class Wizard(models.TransientModel):
_name = 'openacademy.wizard'
session_id = fields.Many2one('openacademy.session',
string="Session", required=True)
attendee_ids = fields.Many2many('res.partner', string="Attendees")
向导是由ir.actions.act_window记录发起的,其中字段target
设置为值new。后者将向导视图打开到弹出窗口中。动作可以由菜单项触发。
还有另一种方法来启动向导:使用像上面那样的ir.actions.act_window记录,但是有一个额外的字段src_model,它指定了在哪个模型的上下文中该动作是可用的。向导将出现在模型的上下文动作中,位于主视图的上方。由于ORM中的一些内部钩子,这样的动作用标签act_window声明为XML。
向导使用常规视图,它们的按钮可以使用属性special="cancel"
关闭向导窗口而不保存。
练习
启动向导
openacademy/wizard.py
class Wizard(models.TransientModel):
_name = 'openacademy.wizard'
def _default_session(self):
return self.env['openacademy.session'].browse(self._context.get('active_id'))
session_id = fields.Many2one('openacademy.session',
string="Session", required=True, default=_default_session)
attendee_ids = fields.Many2many('res.partner', string="Attendees")
openacademy/views/openacademy.xml
wizard.form
openacademy.wizard
练习
注册attendees
向向导添加按钮,并实现将attendees 添加到给定session的相应方法
openacademy/views/openacademy.xml
openacademy/wizard.py
session_id = fields.Many2one('openacademy.session',
string="Session", required=True, default=_default_session)
attendee_ids = fields.Many2many('res.partner', string="Attendees")
@api.multi
def subscribe(self):
self.session_id.attendee_ids |= self.attendee_ids
return {}
练习
注册参会者多个会话
修改向导模型,以便参会者可以注册到多个会话
openacademy/views/openacademy.xml
openacademy/wizard.py
class Wizard(models.TransientModel):
_name = 'openacademy.wizard'
def _default_sessions(self):
return self.env['openacademy.session'].browse(self._context.get('active_ids'))
session_ids = fields.Many2many('openacademy.session',
string="Sessions", required=True, default=_default_sessions)
attendee_ids = fields.Many2many('res.partner', string="Attendees")
@api.multi
def subscribe(self):
for session in self.session_ids:
session.attendee_ids |= self.attendee_ids
return {}
每个模块都可以在i18n目录内提供自己的翻译,通过将文件命名为LANG.po,其中LANG是语言的区域代码,或者当它们不同时,语言和国家组合(例如pt.po或者pt_BR.po)。对于所有启用的语言,翻译将由Odoo自动加载。开发人员在创建模块时总是使用英语,然后使用Odoo的获取文本POT导出功能(设置‣ 翻译‣ 导入/导出‣ 导出翻译 不指定语言)导出模块术语,以创建模块模板POT文件,然后导出翻译的PO文件。许多IDE有用于编辑和合并PO/POT文件的插件或模型。
提示
由Odoo生成的轻便对象文件在Transifex上发布,使其易于翻译软件
|- idea/ # The module directory
|- i18n/ # Translation files
| - idea.pot # Translation Template (exported from Odoo)
| - fr.po # French translation
| - pt_BR.po # Brazilian Portuguese translation
| (...)
提示
默认情况下,Odoo的POT导出只在Python代码中提取XML文件内的标签或字段定义,但是任何Python字符串可以通过将其与函数odoo._()(例如 _("Label")
)包围起来来进行翻译
练习
翻译模块
为您的Odoo安装选择第二种语言。使用Odoo提供的工具翻译您的模块。(以下步骤需激活开发者模式)
openacademy/i18n/
openacademy/i18n/
openacademy/i18n/
openacademy/models.py
# -*- coding: utf-8 -*-
from datetime import timedelta
from odoo import models, fields, api, exceptions, _
class Course(models.Model):
_name = 'openacademy.course'
default = dict(default or {})
copied_count = self.search_count(
[('name', '=like', _(u"Copy of {}%").format(self.name))])
if not copied_count:
new_name = _(u"Copy of {}").format(self.name)
else:
new_name = _(u"Copy of {} ({})").format(self.name, copied_count)
default['name'] = new_name
return super(Course, self).copy(default)
if self.seats < 0:
return {
'warning': {
'title': _("Incorrect 'seats' value"),
'message': _("The number of available seats may not be negative"),
},
}
if self.seats < len(self.attendee_ids):
return {
'warning': {
'title': _("Too many attendees"),
'message': _("Increase seats or remove excess attendees"),
},
}
def _check_instructor_not_in_attendees(self):
for r in self:
if r.instructor_id and r.instructor_id in r.attendee_ids:
raise exceptions.ValidationError(_("A session's instructor can't be an attendee"))
Odoo 8附带了一个基于QWeb、Twitter Bootstrap和Wkhtmltopdf的新报表引擎。
报表是两个要素的组合:
一个ir.actions.report.xml,它提供了一个
实际报表的标准QWeb view:
Report title
the standard rendering context provides a number of elements, the most
important being:
``docs``
the records for which the report is printed
``user``
the user printing the report
因为报表是标准网页,它们可以通过URL获得,并且可以通过该URL操作输出参数,例如,通过http://localhost:8069/report/html/account.report_invoice/1(如果安装了account)和通过http://localhost:8069/report/pdf/account.report_invoice/1的PDF版本,可以提供发票报表的HTML版本。
危险
如果您的PDF报表缺少样式(即文本出现,但样式/布局与HTML版本不同),可能您的wkhtmltopdf进程无法到达Web服务器下载。
如果在生成PDF报表时检查服务器日志并发现CSS样式没有被下载,则这肯定是问题所在。
wkhtmltopdf 进程将使用web.base.url系统参数作为所有链接文件的根路径,但是每次管理员登录时,该参数都会自动更新。如果您的服务器驻留在某种代理后面,则无法到达。您可以通过添加这些系统参数中的一个来修复:
report.url
, 指向一个可从服务器访问的URL (可能是http://localhost:8069或类似的东西)。它只用于这个特殊目的web.base.url.freeze
, 设置为True
, 将停止自动更新 web.base.url
练习
为session模型创建报表
对于每个会话,它应该显示会话的名称、它的开始和结束,并列出会话的参与者。
openacademy/__manifest__.py
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base', 'report'],
# always loaded
'data': [
openacademy/__manifest__.py
'views/openacademy.xml',
'views/partner.xml',
'views/session_workflow.xml',
'reports.xml',
],
# only loaded in demonstration mode
'demo': [
openacademy/reports.xml
From to
Attendees:
练习
定义仪表板
定义一个仪表板,其中包含您创建的图形视图、会话日历视图和课程列表视图(可切换到表单视图)。此仪表板应通过菜单中的菜单项提供,并在选择OpenAcademy主菜单时自动显示在Web客户端中
注:此模块需要您odoo已经安装了系统的仪表板模块,此练习是菜单附加在系统仪表板中。
新建文件 openacademy/views/session_board.xml
。它应该包含板视图、在该视图中引用的动作、打开仪表板的动作以及重新定义主菜单项以添加仪表板动作
注
可用的仪表板样式是 1
, 1-1
, 1-2
, 2-1
和1-1-1
openacademy/__manifest__.py
以引用新的数据文件openacademy/__manifest__.py
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base', 'report', 'board'],
# always loaded
'data': [
'views/openacademy.xml',
'views/partner.xml',
'views/session_workflow.xml',
'views/session_board.xml',
'reports.xml',
],
# only loaded in demonstration mode
openacademy/views/session_board.xml
Attendees by course
openacademy.session
form
graph
Sessions
openacademy.session
form
calendar
Courses
openacademy.course
form
tree,form
Session Dashboard Form
board.board
form
Session Dashboard
board.board
form
form
menu
Web服务模块为所有Web服务提供一个公共接口:
业务对象也可以通过分布式对象机制访问。它们都可以通过与上下文视图的客户端接口进行修改。
Odoo 可通过XML-RPC/JSON-RPC接口访问,其中库以多种语言存在。
下面的例子是一个Python程序,它与Odoo服务器与xmlrpclib库交互:
import xmlrpclib
root = 'http://%s:%d/xmlrpc/' % (HOST, PORT)
uid = xmlrpclib.ServerProxy(root + 'common').login(DB, USER, PASS)
print "Logged in as %s (uid: %d)" % (USER, uid)
# Create a new note
sock = xmlrpclib.ServerProxy(root + 'object')
args = {
'color' : 8,
'memo' : 'This is a note',
'create_uid': uid,
}
note_id = sock.execute(DB, uid, PASS, 'note.note', 'create', args)
练习
向客户端添加新服务
编写一个Python程序,可以发送XML-RPC/请求给运行Odoo的PC (你的,或者你的指导者的)。这个程序应该显示所有的sessions,以及它们对应的seats数目。它还应该为一个course创建一个新的session
import functools
import xmlrpclib
HOST = 'localhost'
PORT = 8069
DB = 'openacademy'
USER = 'admin'
PASS = 'admin'
ROOT = 'http://%s:%d/xmlrpc/' % (HOST,PORT)
# 1. Login
uid = xmlrpclib.ServerProxy(ROOT + 'common').login(DB,USER,PASS)
print "Logged in as %s (uid:%d)" % (USER,uid)
call = functools.partial(
xmlrpclib.ServerProxy(ROOT + 'object').execute,
DB, uid, PASS)
# 2. Read the sessions
sessions = call('openacademy.session','search_read', [], ['name','seats'])
for session in sessions:
print "Session %s (%s seats)" % (session['name'], session['seats'])
# 3.create a new session
session_id = call('openacademy.session', 'create', {
'name' : 'My session',
'course_id' : 2,
})
代替使用硬编码的课程ID,代码可以按名称查找课程:
# 3.create a new session for the "Functional" course
course_id = call('openacademy.course', 'search', [('name','ilike','Functional')])[0]
session_id = call('openacademy.session', 'create', {
'name' : 'My session',
'course_id' : course_id,
})
下面的示例是一个Python程序,它与标准的Python库urllib2和json交互,与Odoo服务器交互:
import json
import random
import urllib2
def json_rpc(url, method, params):
data = {
"jsonrpc": "2.0",
"method": method,
"params": params,
"id": random.randint(0, 1000000000),
}
req = urllib2.Request(url=url, data=json.dumps(data), headers={
"Content-Type":"application/json",
})
reply = json.load(urllib2.urlopen(req))
if reply.get("error"):
raise Exception(reply["error"])
return reply["result"]
def call(url, service, method, *args):
return json_rpc(url, "call", {"service": service, "method": method, "args": args})
# log in the given database
url = "http://%s:%s/jsonrpc" % (HOST, PORT)
uid = call(url, "common", "login", DB, USER, PASS)
# create a new note
args = {
'color' : 8,
'memo' : 'This is another note',
'create_uid': uid,
}
note_id = call(url, "object", "execute", DB, uid, PASS, 'note.note', 'create', args)
这里是同一个程序,使用jsonrpclib库:
import jsonrpclib
# server proxy object
url = "http://%s:%s/jsonrpc" % (HOST, PORT)
server = jsonrpclib.Server(url)
# log in the given database
uid = server.call(service="common", method="login", args=[DB, USER, PASS])
# helper function for invoking model methods
def invoke(model, method, *args):
args = [DB, uid, PASS, model, method] + list(args)
return server.call(service="object", method="execute", args=args)
# create a new note
args = {
'color' : 8,
'memo' : 'This is another note',
'create_uid': uid,
}
note_id = invoke('note.note', 'create', args)
实例可以很容易地从XML- RPC适应JSON-RPC。
注
在不显式地通过XML- RPC或JSON-RPC的情况下,在各种语言中有许多高级API来访问Odoo系统,例如:
上一篇:Odoo10教程---模块化二:模型间关系,继承,计算字段等
代码下载地址:https://download.csdn.net/download/mzl87/10405778
ps:有翻译不当之处,欢迎留言指正。
原文地址:https://www.odoo.com/documentation/10.0/howtos/backend.html