视频教程
-
odoo中字段的定义和含义
# _*_ coding:utf-8 _*_
# author: zhy
import uuid
from itertools import groupby
from datetime import datetime, timedelta
from werkzeug.urls import url_encode
from odoo import api, fields, models, _
from odoo.exceptions import UserError, AccessError
from odoo.osv import expression
from odoo.tools import float_is_zero, float_compare, DEFAULT_SERVER_DATETIME_FORMAT
from odoo.tools.misc import formatLang
class zhy_sale(models.Model):
_name = "zhy.sale"
_description = u"销售订单"
# 隐藏创建人创建日期字段
_log_access = False
name = fields.Char(string=u'单据编号', default=lambda self: 'New')
active = fields.Boolean(default=True, string=u'是否归档', track_visibility='onchange')
sequence = fields.Integer(string=u'排序', default=10)
# require=True表示的是必填
price_unit = fields.Float(string=u'单价', require=True, default=0.0, digits=(10,2))
note = fields.Text(string=u'备注', track_visibility='onchange')
content = fields.Html(string=u'正文', strip_style=True)
# track_visibility='onchange'表示的是会在更改信息的时候,message会提示相关的更改信息
# 这个要和下面message社交中的应用联合使用
delivary_date = fields.Date(string=u'发货日期', track_visibility='onchange')
# default=fields.Datetime.now表示的是获取当前日期
date_order = fields.Datetime(string=u'单据日期', required=True, readonly=True, index=True, copy=False, default=fields.Datetime.now)
priority = fields.Selection([
('0', '低'),
('1', '正常'),
('2', '高'),
], default='0', index=True, string=u'优先级')
# res.users表示的是关联的当前用户的表, default表示的是创建的时候默认成当前用户
# lambda self: self.env.user中user表示的是当前的用户, 如果是想关联其他的用户,可以找个这个用户的id,然后再设置中 default=id即可
# xml中(ir.actions.act_window)定义<field name="context">{"default_user_id":7}</field>
user_id = fields.Many2one('res.users', string=u'负责人', index=True, track_visibility='onchange',
default=lambda self: self.env.user)
# 关联字段,一对多的关联 第一个位置表示的是关联的表的名称,第二个字段是表中的字段名称 auto_join表示是否在搜索字段时,生成连接
order_line = fields.One2many('zhy.sale.line', 'order_id', string=u'订单明细行', copy=True, auto_join=True)
# 多对多的关系 a_b_rel a表示的是表名,b表示的是关联字段的表名 a_id b_id可以自己定义 一般是关联表的名称,分别表示的是关联表的名称
# some_ids = fields.Many2many('models', 'a_b_rel', 'a_id', 'b_id')
# res.users表和zhy.sale关联的数据放在zhy_sale_res_users_rel中
users_ids = fields.Many2many('res.users', 'zhy_sale_res_users_rel', 'sale_id', 'user_id', string=u'业务员')
@api.model
def _select_objects(self):
records = self.env['ir.model'].search([])
return [(record.model, record.name) for record in records] + [('','')]
# 可以关联所有的字段
id_object = fields.Reference(string=u'关联', selection='_select_objects')
class zhy_sale_line(models.Model):
_name = "zhy.sale.line"
_description = u"销售订单"
name = fields.Char(string=u'产品名称')
order_id = fields.Many2one('zhy.sale', string=u'销售订单', require=True, ondelete='cascade', index=True, copy=False)
Python2.7中, string中文前要加u
- odoo视图定义xml
<?xml version='1.0' encoding='utf-8'?>
<odoo>
<!--菜单定义-->
<!--sequence表示的是优先级,数值越小,优先级越高-->
<!--销售一级菜单要和内在的对象(models中的zhy.sale)关联之后才能显示到odoo页面中-->
<menuitem id="menu_2" name="销售" sequence="12" />
<menuitem id="menu_2_0" name="CRM" sequence="1" parent="menu_2" />
<menuitem id="menu_2_1" name="销售" sequence="2" parent="menu_2" />
<menuitem id="menu_2_99" name="常用报表" sequence="99" parent="menu_2" />
<menuitem id="menu_2_100" name="设置" sequence="100" parent="menu_2" />
</odoo>
- odoo中基本视图定义(pycharm中快捷键)
<?xml version='1.0' encoding='utf-8'?>
<odoo>
<record id="odoo_form_zhy_sale_views" model="ir.ui.view">
<field name="name">zhy.sale.form</field>
<field name="model">zhy.sale</field>
<field name="arch" type="xml">
<form string="表单">
<sheet>
<div class="oe_button_box" name="button_box">
</div>
<group>
<group>
<field name="name"/>
</group>
<group>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="odoo_tree_zhy_sale_views" model="ir.ui.view" >
<field name="name">zhy.sale.tree</field>
<field name="model">zhy.sale</field>
<field name="priority">1</field>
<field name="arch" type="xml">
<tree string="列表">
<field name="name"/>
</tree>
</field>
</record>
<record id="odoo_action_zhy_sale_views" model="ir.actions.act_window">
<field name="name">销售模块</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">zhy.sale</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Create the first course
<p></p>
</p>
</field>
</record>
<!--odoo中的一级标签, 如果没有自定义name,会使用action中的name的名称-->
<menuitem action="odoo_action_zhy_sale_views" name="zhy_销售" id="menu_zhy_sale_views" sequence="0" />
</odoo>
- 添加字段之后的xml文件解析
<?xml version='1.0' encoding='utf-8'?>
<odoo>
<record id="odoo_form_zhy_sale_views" model="ir.ui.view">
<field name="name">zhy.sale.form</field>
<field name="model">zhy.sale</field>
<field name="arch" type="xml">
<form string="表单">
<sheet>
<div class="oe_button_box" name="button_box">
</div>
<!--一个大的group中包含两个小的group,在视图中显示的是两列数据-->
<group>
<group>
<field name="name"/>
<!--如果想要更改字段, 可以在name后面添加string, xml视图中的优先级是要高于.py文件中的优先级的-->
<!--在.py文件中readonly=True设置为只读状态, 在视图xml中 定义readonly='1'表示的是只读状态-->
<!--当点击active字段的时候, 金额设置为必填字段-->
<field name="price_unit" string="金额" attrs='{"invisible": [("active", "=", True)]}'/>
<!--many2many_tags表示是many2many字段中一个挂件, 具体的效果见项目-->
<field name="users_ids" widget="many2many_tags"/>
</group>
<group>
<field name="date_order"/>
<!--required="1"表示的是这个字段是必填的.相反是0;一般是在视图中定义, 视图中定义的级别是高于.py文件中字段定义的;-->
<field name="delivary_date"/>
<field name="priority"/>
<!--在列表中是关联的res.user表, 是一个many2one字段, 是一个下拉框, 我们可以选择创建用户-->
<field name="user_id"/>
<!--使用的是Reference, 可以关联所有的字段-->
<field name="id_object"/>
<!--是一个boolean, 默认为false-->
<field name="active"/>
<!--integer 整数类型, 只能输入一个整数-->
<!--invisible='1'设置为隐藏状态,后端.py文件中是没有这个属性的-->
<!--attrs='{"invisible": [("active", "=", True)]}', 作用是, 当我们点击一个字段的时候,另外一个字段为显示,隐藏,必填等状态 下面attrs中active表示的是我们点击的字段, invisible表示的字段的隐藏属性.-->
<field name="sequence" attrs='{"invisible": [("active", "=", True)]}'/>
</group>
<!--一个notebook,具体的见视图界面 colspan表示指定的是4行-->
<notebook colspan="4">
<page string="明细" autofocus="autofocus">
<!--多对一和一对多相关联, 这个是一个one2many的字段, 会和另外一张表中的一个many2one的表相关联的-->
<field name="order_line"/>
<!--我们可以通过制定widget="html", 来改变原有设置的视图形状-->
<field name="note"/>
</page>
<page string="正文">
<field name="content"/>
</page>
</notebook>
</group>
</sheet>
</form>
</field>
</record>
<record id="odoo_tree_zhy_sale_views" model="ir.ui.view" >
<field name="name">zhy.sale.tree</field>
<field name="model">zhy.sale</field>
<field name="priority">1</field>
<field name="arch" type="xml">
<tree string="列表">
<field name="name"/>
</tree>
</field>
</record>
<record id="odoo_action_zhy_sale_views" model="ir.actions.act_window">
<field name="name">销售模块</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">zhy.sale</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Create the first course
<p></p>
</p>
</field>
</record>
<menuitem action="odoo_action_zhy_sale_views" name="zhy_销售" id="menu_zhy_sale_views" sequence="0" />
</odoo>
- 实际的项目中xml文件各字段的解析
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<!--制作表单视图,显示课程的名称和描述字段-->
<!--id的定义规则,如果是tree from视图view, 一般是xxx_view, 如果是视图actions, 一般是xxx_action-->
<record model="ir.ui.view" id="course_form_view">
<field name="name">course.form</field> <!--表单名-->
<field name="model">openacademy.course</field> <!-- 数据模型, 模块.模型-->
<field name="arch" type="xml"> <!--重点, 视图类型定义-->
<form string="Course Form"> <!--课程视图-->
<sheet>
<group> <!--表单中第一行-->
<field name="name"/> <!--对应models中的字段名-->
<field name="responsible_id"/>
</group>
<!--notebook结构元素,讲描述字段放在一个选项卡中,然后再添加选项卡放置其他字段 tab 分页效果-->
<notebook>
<page string="描述">
<field name="description"/>
</page>
<page string="授课">
<field name="session_ids">
<tree string="授课注册">
<field name="name"/>
<field name="instructor_id"/>
</tree>
</field>
</page>
<page string="关于">
这是一个关于notebook的案例
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="course_search_view">
<field name="name">course.search</field>
<field name="model">openacademy.course</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="description"/>
</search>
</field>
</record>
<!-- override the automatically generated list view for courses -->
<record model="ir.ui.view" id="course_tree_view">
<field name="name">course.tree</field>
<field name="model">openacademy.course</field>
<field name="arch" type="xml">
<tree string="课程详情">
<field name="name"/>
<!--responsible 责任人-->
<field name="responsible_id"/>
</tree>
</field>
</record>
<!-- window action -->
<!--
The following tag is an action definition for a "window action",
that is an action opening a view or a set of views
-->
<!--动作和菜单中, model为固定属性值, ir.actions.act_window id是唯一的-->
<record model="ir.actions.act_window" id="course_list_action">
<field name="name">Courses</field> <!--视图动作的名称-->
<field name="res_model">openacademy.course</field> <!--模块对象 视图要操作哪个数据模型 模块.数据模型-->
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="context" eval="{'search_default_my_courses': 1}"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">Create the first course
</p>
</field>
</record>
<!-- top level menu: no parent --> <!--顶部菜单-->
<menuitem id="main_openacademy_menu" name="公开教程"/>
<!-- A first level in the left side menu is needed
before using action= attribute -->
<menuitem id="openacademy_menu" name="公开教程"
parent="main_openacademy_menu"/> <!-- 一级菜单 -->
<!-- the following menuitem should appear *after*
its parent openacademy_menu and *after* its
action course_list_action -->
<menuitem id="courses_menu" name="课程" parent="openacademy_menu"
action="course_list_action"/>
<!-- Full id location:
action="openacademy.course_list_action"
It is not required when it is the same module -->
<!-- id不能重复,一般是xxx_menu name表示是视图显示的名称, parent表示是关联的父类 -->
<!-- 二级菜单 action表示 指定菜单的响应动作,依次用什么视图来操作数据模型 就是我点击这个按钮的时候跳转到哪个列表-->
<!--注意: action必须先于menu的连接使用定义, 数据文件在载入时顺序地执行, 所以动作的ID必须首先存在于数据库中才能使用.-->
<!-- session form view -->
<record model="ir.ui.view" id="session_form_view">
<field name="name">session.form</field>
<field name="model">openacademy.session</field>
<field name="arch" type="xml">
<form string="Session Form">
<sheet>
<group>
<group string="综合的">
<field name="course_id"/>
<field name="name"/>
<field name="instructor_id"/>
<field name="active"/>
</group>
<group string="时间表">
<field name="start_date"/>
<field name="duration"/>
<field name="seats"/>
<field name="taken_seats" widget="progressbar"/>
</group>
</group>
<label for="attendee_ids"/>
<field name="attendee_ids"/>
</sheet>
</form>
</field>
</record>
<!-- session tree/list view -->
<record model="ir.ui.view" id="session_tree_view">
<field name="name">session.tree</field>
<field name="model">openacademy.session</field>
<field name="arch" type="xml">
<tree string="Session Tree" >
<field name="name"/>
<field name="course_id"/>
<field name="taken_seats" widget="progressbar"/>
</tree>
</field>
</record>
<record model="ir.actions.act_window" id="session_list_action">
<field name="name">Sessions</field>
<field name="res_model">openacademy.session</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<!--model="ir.actions.act_window" 表示包含链接视图 -->
<menuitem id="session_menu" name="授课"
parent="openacademy_menu"
action="session_list_action"/> <!-- 一级菜单 -->
</data>
</odoo>
表单sequence
- 作用:生成一个带时间的单据编号
- 用法
- 创建一个新的data文件夹, 文件夹中创建一个新的xml文件
<?xml version='1.0' encoding='utf-8'?>
<odoo>
<record id="seq_zhy_sale_sequence" model="ir.sequence">
<field name="name">zhy.sale</field>
<field name="code">zhy.sale</field>
<field name="prefix">A%(year)s%(month)s%(day)s</field>
<field name="padding">5</field>
<field name="company_id" eval="False" />
</record>
</odoo>
# 从 ir.squence中取出self._name字段,然后给他生成一个编号
name = fields.Char(string=u'单据编号', default=lambda self: self.env['ir.sequence'].next_by_code(self._name) or 'New')
- 一般这个单据编号是用在name,char字段中的.
表单日期
from datetime import datetime, timedelta
from odoo.tools import float_is_zero, float_compare, DEFAULT_SERVER_DATETIME_FORMAT
# 获取当前日期
default=fields.Datetime.now
date_order = fields.Datetime(string=u'单据日期', readonly=True, index=True, copy=False, default=fields.Datetime.now)
# 在当前日期上加上多少天(一般是单据日期和发货日期)
# days中的值可以是正值(向前加多少天),也可以是负值(向后减多少天)
default=lambda self: datetime.strptime(fields.Datetime.now(), DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(days=1)
widget="float_time"表示的是可以输入一个时间
hours = fields.Float(string=u’单价’, default=0.0, digits=(10, 2), readonly=False)
domain字段过滤
# 过滤字段, 可以让用户只能选择当前的用户
<field name="user_id" domain="[('id', '=', 1)]"/>
domain="[('id', '=', 1)]"
表单button
- 在xml中添加
<!--在xml中添加header和fields-->
<header>
<button name="button_loading_draft" states="处理中" string="重置草稿" type="object" class="highlight" />
<button name="button_done_draft" states="已审核" string="重置草稿" type="object" class="highlight" />
<button name="button_loading" states="草稿" string="提交草稿" type="object" class="highlight" />
<button name="button_done" states="处理中" string="审核" type="object" class="highlight" />
<field name='state' widget='statusbar' statusbar_visible='草稿,处理中,已审核' />
</header>
state = fields.Selection([
(u'草稿', u'草稿'),
(u'处理中', u'处理中'),
(u'已审核', u'已审核'),
], string='单据状态', readonly=True, copy=False, default=u'草稿', track_visibility='onchange')
@api.multi
def button_loading_draft(self):
return self.write({'state': u'草稿'})
@api.multi
def button_done_draft(self):
return self.write({'state': u'草稿'})
@api.multi
def button_loading(self):
return self.write({'state': u'处理中'})
@api.multi
def button_done(self):
return self.write({'state': u'已审核'})
@api.multi
def action_done(self):
return self.write({'state': u'取消'})
注意: xml文件中button的name值要和.py文件中函数的名称相对应;
几种状态的对应,可以在视图是观察字段所对应的后端值来定
state 关联字段的只读状态
<!--具体的用法-->
<!--attrs="{'readonly': [('state', 'in', (u'处理中', u'已审核'))]}"-->
<!--一般是要结合name=state状态来定的-->
<record id="odoo_form_zhy_sale_views" model="ir.ui.view">
<field name="name">zhy.sale.form</field>
<field name="model">zhy.sale</field>
<field name="arch" type="xml">
<form string="表单">
<header>
<button name="button_loading_draft" states="处理中" string="重置草稿" type="object" class="highlight" />
<button name="button_done_draft" states="已审核" string="重置草稿" type="object" class="highlight" />
<button name="button_loading" states="草稿" string="提交草稿" type="object" class="highlight" />
<button name="button_done" states="处理中" string="审核" type="object" class="highlight" />
<field name='state' widget='statusbar' statusbar_visible='草稿,处理中,已审核' />
</header>
<sheet>
<div class="oe_button_box" name="button_box">
</div>
<group>
<group>
<field name="name"/>
<field name="price_unit" string="金额" attrs="{'readonly': [('state', 'in', (u'处理中', u'已审核'))]}" />
<field name="users_ids" widget="many2many_tags" />
</group>
<group>
<field name="date_order"/>
<field name="delivary_date"/>
<field name="priority"/>
<field name="hours" widget="float_time"/>
<field name="user_id" />
<field name="id_object" />
<field name="active"/>
<field name="sequence" attrs='{"invisible": [("active", "=", True)]}'/>
</group>
<notebook colspan="4">
<page string="明细" autofocus="autofocus">
<field name="order_line"/>
<field name="note" widget="html"/>
</page>
<page string="正文">
<field name="content"/>
</page>
</notebook>
</group>
</sheet>
</form>
</field>
</record>
# 后端中添加只读状态
# 要结合后端中的state这个字段来进行设置的
price_unit = fields.Float(string=u'单价', default=0.0, digits=(10, 2),
readonly=True, states={u'草稿': [('readonly', False)], u'处理中': [('readonly', False)]})
state = fields.Selection([
(u'草稿', u'草稿'),
(u'处理中', u'处理中'),
(u'已审核', u'已审核'),
], string='单据状态', readonly=True, copy=False, default=u'草稿', track_visibility='onchange')
一般的情况下, xml的优先级都要高于.py文件中的优先级
权限管理security
- 在目录下创建一个新的文件夹和文件security/security.xml
<!--在security.xml文件中定义-->
<!--一般id的名称要和关联的模块相对应-->
<!--权限设置之后可以在设置用户中看到-->
<?xml version='1.0' encoding='utf-8'?>
<odoo>
<data>
<record id="sale_groups" model="ir.module.category">
<field name="name">销售z</field>
</record>
<record id="sale_users" model="res.groups">
<field name="name">销售z-用户</field>
<field name="category_id" ref="sale_groups" />
<field name="users" eval="[(4, ref('base.user_root'))]" />
</record>
<record id="sale_manager" model="res.groups">
<field name="name">销售z-经理</field>
<field name="category_id" ref="sale_groups" />
<field name="implied_ids" eval="[(4, ref('sale_users'))]" />
<field name="users" eval="[(4, ref('base.user_root'))]" />
</record>
</data>
</odoo>
- 在实际的项目中怎么设置权限
- 参考教程:http://www.iqiyi.com/w_19s137nyvt.html?list=19rrmefxri
<!--给不用的用户设置不同的权限-->
<!--设置权限组, aa_zhy表示的是文件夹的名称, sale.users表示的是security.xml文件中设置的用户权限-->
<!--groups="aa_zhy.sale_users"-->
<header>
<button name="button_loading_draft" states="处理中" string="重置草稿" type="object" class="highlight" groups="aa_zhy.sale_users" />
<button name="button_done_draft" states="已审核" string="重置草稿" type="object" class="highlight" groups="aa_zhy.sale_manager" />
<button name="button_loading" states="草稿" string="提交草稿" type="object" class="highlight" groups="aa_zhy.sale_users" />
<button name="button_done" states="处理中" string="审核" type="object" class="highlight" groups="aa_zhy.sale_manager"/>
<field name='state' widget='statusbar' statusbar_visible='草稿,处理中,已审核' />
</header>
在实际的项目中, 我们给用户设置了提交审核的权限之后,我们可以用另外的一个用户登录
去查看我们创建的模块,新创建的用户中,我们是没有办法看到我们创建的模块的,这个时候我们需要在管理员账户的设置群组中,找到我们谁知的权限管理模块, 然后进去,给我们的用户设置模块的访问权限即可.
- 给某些字段设置权限
<!--设置权限组, aa_zhy表示的是文件夹的名称, sale_manager表示的是security.xml文件中设置的权限-->
<!--groups='aa_zhy.sale_manager'-->
price_unit = fields.Float(string=u'单价', default=0.0, digits=(10, 2), readonly=True, groups='aa_zhy.sale_manager')
同样,也可以把
groups='aa_zhy.sale_manager'
放到xml中也是可以的.
设置完成之后, 就可以用另外的一个账户登录,这个时候,就会发现, 不是管理员用户,就不能查看金额字段了。
表单message的使用
- 主要是在社交方面的使用
- 使用中我们会用到系统的mail模块,所以要先把mail模块集成到我们的depends继承中.
<!--在.py文件中,继承mail.thread-->
_inherit = ['mail.thread']
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers" />
<field name="message_ids" widget="mail_thread" />
</div>
track_visibility='onchange'
这个是结合message使用的, 是把更改的一些信息显示到下面的message中.track_visibility='always'
用户不更改信息的是时候,message也会记录下这些信息.
<!--批量关注的两种方法-->
<!--users_ids是后端定义的多对多的关联字段; 8表示的是当前的这个用户的id-->
self.message_subscribe_user(users_ids=(self.user_id.id))
self.message_subscribe(partner_ids=[8])
<!--批量取消的两种方法-->
self.message_unsubscribe_user(users_ids=(self.user_id.id))
self.message_unsubscribe(partner_ids=[8])
表单onchange的使用
- 省市区的三级联动
<!--主要是一个many2one字段-->
class res_city1(models.Model):
_name = "res.city1"
_description = u"省"
name = fields.Char(string=u'省')
class res_city2(models.Model):
_name = "res.city2"
_description = u"市"
name = fields.Char(string=u'市')
city1_id = fields.Many2one('res.city1', string=u'明细', )
class res_city3(models.Model):
_name = "res.city3"
_description = u"区"
name = fields.Char(string=u'区')
city2_id = fields.Many2one('res.city2', string=u'明细', )
- 省市区三级联动中提取字段
class zhy_sale_line(models.Model):
_name = "zhy.sale.line"
_description = u"销售订单"
name = fields.Char(string=u'产品名称')
order_id = fields.Many2one('zhy.sale', string=u'销售订单', require=True, ondelete='cascade', index=True, copy=False)
# 这个地方是定义三个多对一的id字段, 关联对应的表单,提取内容
city1_id = fields.Many2one('res.city1', string=u'省')
city2_id = fields.Many2one('res.city2', string=u'市')
city3_id = fields.Many2one('res.city3', string=u'区')
# onchang时间, 通过第三级(或则是最下级)一级一级向上查询
@api.onchange('city3_id')
def onchange_city3_id(self):
values = {}
if self.city3_id:
# city2_id(市)表示的是通过三级的id,找到二级的id.(反向查找)
values['city2_id'] = self.city3_id.city2_id.id
# city1_id(省)表示的是通过三级区的id,找到二级市的id,再找到一级省的id.(反向查找)
values['city1_id'] = self.city3_id.city2_id.city1_id.id
self.update(values)
class res_city1(models.Model):
_name = "res.city1"
_description = u"省"
name = fields.Char(string=u'省')
class res_city2(models.Model):
_name = "res.city2"
_description = u"市"
name = fields.Char(string=u'市')
city1_id = fields.Many2one('res.city1', string=u'明细', )
class res_city3(models.Model):
_name = "res.city3"
_description = u"区"
name = fields.Char(string=u'区')
city2_id = fields.Many2one('res.city2', string=u'明细', )
- xml中视图展示
<!--把后端创建的字段,通过xml文件显示出来-->
<notebook colspan="4">
<page string="明细" autofocus="autofocus">
<field name="order_line">
<!--editable='bottom'表示的是编译方式-->
<tree string="列表" editable="bottom">
<field name="city1_id" />
<!--domain条件过滤 第一个city1_id表示的自己定义的关联字段 第二个city1_id表示的是上一级的id-->
<!--作用是选择上级之后,下级自动做关联筛选-->
<!--特别注意: 第二个city_id是不能够加分号的.-->
<field name="city2_id" domain="[('city1_id', '=', city1_id)]" />
<field name="city3_id" domain="[('city2_id', '=', city2_id)]" />
</tree>
</field>
<field name="note" widget="html"/>
</page>
<page string="正文">
<field name="content"/>
</page>
</notebook>
表单中数量和金额的合计
# 新增数量,单价,和小计等字段.这些字段都是要float类型
# depends中依赖的是自己心定义的两个字段,如果这两个字段发生变化,则执行这个函数
@api.depends('product_uom_qty', 'price_unit')
def _compute_amout(self):
for order in self:
order.price_subtotal = order.product_uom_qty * order.price_unit
product_uom_qty = fields.Float(string=u'数量', required=True, default=1.0)
price_unit = fields.Float(string=u'单价', required=True, default=0.0, digits=(10, 2))
# compute是关联一个函数.我们在这个函数中可以写计算逻辑
price_subtotal = fields.Float(compute='_compute_amout', string=u'小计')
# 在其他的表中做关联和金额合计的时候
# 这个表中依赖的是外键表中的一个字段;通过外键表找到这个字段,然后对这个字段进行操作
@api.depends('order_line.price_subtotal')
def _compute_amount(self):
for order in self:
total = sum(line.price_subtotal for line in order.order_line)
order.amount = total
amount = fields.Float(compute='_compute_amount', string=u'小计')
order_line = fields.One2many('zhy.sale.line', 'order_id', string=u'订单明细行', copy=True, auto_join=True)
表单unlink删除重写事件
- odoo中已经做好了表单的删除功能, 我们只需要继承这个功能就好.
- 我们做的这个案例中, 只有我们的表单处理草稿状态的时候,我们才能删除这个表单. 当我们的表单处理处理中或者审核状态的时候,我们是不能删除的.
# @api.multi表示的是可以选择多个 重写unlink()方法
# 当界面点击删除事件的时候会触发
@api.multi
def unlink(self):
for order in self:
# 判断当前的用户和表单的创建用户是否一致, 如果不一致不允许删除.
if order.user_id:
# user_id(字段名称)
if order.user_id.id != self._uid:
raise UserError(u'只能删除自己的单据')
if order.state != u"草稿":
raise UserError(u'只能删除草稿单据')
# self.env['amos.workflow'].sudo.seach([('res_model', '=', self._name), ('res_id', '=', order.id)]).unlink()
# self.env['if.message'].sudo.seach([('res_model', '=', self._name), ('res_id', '=', order.id)]).unlink()
# 关联的是哪张表
return super(zhy_sale, self).unlink
# 项目中是实际会用到的
@api.multi
def unlink(self):
for order in self:
if order.user_id:
if order.user_id.id != self._uid:
raise UserError(u'只能删除自己的单据aaaa')
# 这个地方有问题, 当不能删除的时候, 注意下判断条件(可以使用debug),打印出判断条件对比下就可以了.
if order.state.encode('utf-8') != "草稿":
print(order.state, type(order.state))
raise UserError(u'只能删除草稿单据bbbb')
return super(zhy_sale, self).unlink()
# 其他的写法
# @api.multi
# def unlink(self):
# if any(order.state in (u'处理中', u'已审核') for order in self):
# raise exceptions.UserError(u'只能删除草稿单据啊啊啊啊啊啊.')
# super(zhy_sale, self).unlink()
表单create创建重写事件
- 可以创建自己的公司的编号等
- 可以修改字段的值
# vals表示的是界面传入进来的所有的值
# self._context表示的传入进来的上下文的值
# 当创建的时候会触发
@api.model
def create(self, vals):
# print(self._context, u'这是上下文')
# print(vals, u'这是界面传入进来的值')
if vals.get('name', 'New') == 'New':
# 这个地方表示是判断传入进来的值中有没有company_id字段, 如果有的话, 设置一个单据编号
if 'company_id' in vals:
# with_context传上下文的 next_by_code下一个字段
vals['name'] = self.env['ir.sequence'].with_context(force_company=vals['company_id']).next_by_code(self._name) or 'New'
else:
vals['name'] = self.env['ir.sequence'].next_by_code(self._name) or 'New'
# 可以指定修改某个值
vals['hours'] = 100.00
line = super(zhy_sale, self).create(vals)
return line
表单widget的使用
# odoo中常用的widget
widget="statusbar" 头部状态条标签
widget="email" 电子邮件地址标签
widget="selection" 下拉选择标签
widget="mail_followers" 关注者标签
widget="mail_thread" 消息标签
widget="progressbar" 进度条,按百分比标签
widget="one2many_list" 一对多列表标签
widget="many2many_tags" 多对多显示标签
widget="url" 网站链接标签
widget='image' 图片标签
widget="many2many_kanban" 看版标签
widget="handler" 触发标签
widget="radio" 单选标签
widget="char_domain" 字符域标签
widget="monetary" 价格(和精度位数相关)标签
widget="float_time" 单精度时间标签
widget="html" html相关标签
widget="pad" pad显示相关标签
widget="date" 日期标签
widget="monetary" 金额标签
widget='text' 文本标签
widget="sparkline_bar" 燃尽标签
widget="checkbox" 复选框标签
widget="reference" 关联标签
<field name="partner_type" nolabel="1" widget="selection" string="" \
attrs="{'required': [('payment_type', 'in', ('inbound', 'outbound'))], \
'invisible': [('payment_type', 'not in', ('inbound', 'outbound'))], \
'readonly': [('state', '!=', 'draft')]}"/>
表单write修改重写事件(时间的改写)
# 当修改表单中数据 的时候会被触发
@api.multi
def write(self, vals):
print(vals, u'我是修改之后的值')
if vals.has_key('price_unit'):
print(vals.has_key('price_unit'), 1111111)
if vals['price_unit'] > 100000:
print(isinstance(vals['price_unit'], str))
print(vals['price_unit'], type(vals['price_unit']))
raise UserError(u'警告: 你没有权限修改')
return super(zhy_sale, self).write(vals)
表单active(归档使用)
- 当一件事情完结的时候, 点击归档,则这件事情就被隐藏掉了;当我们想再去查看的时候, 在筛选条件中是否归档选择为假即可.
<!--toggle_active是odoo底层的方法, 我们可以拦截odoo中的这个方法-->
<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>
<!--重写toggle_active方法来拦截, 之后我们可以自定义自己的方法-->
@api.multi
def toggle_active(self):
for recode in self:
print(recode, "我来了")
recode.active = not recode.active
表单隐藏按钮
隐藏新建,隐藏修改,隐藏删除
create=“false”
edit=“false”
delete=“false”
<form string="表单" create="false" edit="false" delete="false">
</form>
或者
<form string="表单" create="0" edit="0" delete="0">
</form>
- 想让哪部分按钮隐藏, 只需要把相应的代码中的字段设置为false或者0即可.
表单字段related
- 一般是关联某个字段, 做实时同步等
# 两张变中的字段也是可以关联的
# 表zhy.sale中的字段
delivery_date = fields.Date(string=u'发货日期',track_visibility='onchange',)
# 表zhy.sale.line中的字段
# order_id是关联的其他表
order_id = fields.Many2one('zhy.sale', string=u'销售订单', require=True, ondelete='cascade', index=True, copy=False)
# related="表中外键表的字段" 一定要注意字段的类型Date一定要和关联的其他表的字段类型一致
delivery_date = fields.Date(related='order_id.delivery_date', string=u'发货日期', store=True)
api各个装饰器的区别
- api.depends:当用到其他字段的时候需要用到depends(实时计算字段)
- eg:金额的统计的时候, 小计需要关联其他的两个字段
- api.onchange:实时更新用户界面,当用户在表单中更改某个字段的值时,其他相关字段可以在不需保存的情况下实时更新.
- eg:城市的三级联动
- odoo.api.model(method):在记录行方式下装饰一个内容不明确、但模型明确的方法
- odoo.api.multi(method):在记录行方式下装饰一个对记录进行操作的方法
- odoo.api.depends(*args):返回为compute方法指定依赖字段的装饰器,每个参数必须是字符串
- odoo.api.constrains(*args):装饰一个约束检查方法,每个参数必须是需要检查的字段
- 在检验失败时抛出ValidationError错误,且不支持关联字段检验
- odoo.api.onchange(*args):返回一个监控指定字段的onchange方法的装饰器,每个参数必须是字段名称
- 该函数可能会返回以数据字典形式组装的当前更改字段的domain表达式和一个警告消息,不支持关联字段处理