This is the documentation for older versions of Odoo (formerly OpenERP).

See the new Odoo user documentation.

See the new Odoo technical documentation.

字段类型

基础类型

boolean:

布尔型(boolean) (true, false).

语法:

fields.boolean('Field Name' [, Optional Parameters]),
integer:

整型(integer).

语法:

fields.integer('Field Name' [, Optional Parameters]),
float:

浮点型(float).

语法:

fields.float('Field Name' [, Optional Parameters]),

注解

digits定义整数部分和小数部分的位数。 The scale being the number of digits after the decimal point whereas the precision is the total number of significant digits in the number (before and after the decimal point). If the parameter digits is not present, the number will be a double precision floating point number. Warning: these floating-point numbers are inexact (not any value can be converted to its binary representation) and this can lead to rounding errors. You should always use the digits parameter for monetary amounts.

Example:

'rate': fields.float(
    'Relative Change rate',
    digits=(12,6) [,
    Optional Parameters]),
char:

字符串(char): 限定长度的字符串,size属性定义字符串长度。

语法:

fields.char(
        'Field Name',
        size=n [,
        Optional Parameters]), # where ''n'' is an integer.

Example:

'city' : fields.char('City Name', size=30, required=True),
text:

没有长度限制的text field

语法:

fields.text('Field Name' [, Optional Parameters]),
date:

A date.

语法:

fields.date('Field Name' [, Optional Parameters]),
datetime:

Allows to store a date and the time of day in the same field.

语法:

fields.datetime('Field Name' [, Optional Parameters]),
binary:

A binary chain

selection:

这个字段让用户对之前定义的值进行选择

语法:

fields.selection((('n','Unconfirmed'), ('c','Confirmed')),
                   'Field Name' [, Optional Parameters]),

注解

Format of the selection parameter: tuple of tuples of strings of the form:

(('key_or_value', 'string_to_display'), ... )

注解

You can specify a function that will return the tuple. Example

def _get_selection(self, cursor, user_id, context=None):
    return (
       ('choice1', 'This is the choice 1'),
       ('choice2', 'This is the choice 2'))

_columns = {
   'sel' : fields.selection(
       _get_selection,
       'What do you want ?')
}

Example

Using relation fields many2one with selection. In fields definitions add:

...,
'my_field': fields.many2one(
        'mymodule.relation.model',
        'Title',
        selection=_sel_func),
...,

And then define the _sel_func like this (but before the fields definitions):

def _sel_func(self, cr, uid, context=None):
    obj = self.pool.get('mymodule.relation.model')
    ids = obj.search(cr, uid, [])
    res = obj.read(cr, uid, ids, ['name', 'id'], context)
    res = [(r['id'], r['name']) for r in res]
    return res

Relational Types

one2one:

表示有两个对象是一对一的关系。现在用many2one来代替。

语法:

fields.one2one('other.object.name', 'Field Name')
many2one:

通过这个字段一个对象与它的父对象关联。例如,员工和部门的关系就是多对一的关系。

语法:

fields.many2one(
        'other.object.name',
        'Field Name',
        optional parameters)

可选的参数:

  • ondelete: 当该字段指示的资源被删除时会发生些什么
    • 预先定义的值: "cascade", "set null", "restrict", "no action", "set default"

    • 缺省值: "set null"

  • required: True

  • readonly: True

  • select: True - (creates an index on the Foreign Key field)

Example

'commercial': fields.many2one(
        'res.users',
        'Commercial',
        ondelete='cascade'),
one2many:

TODO

语法:

fields.one2many(
        'other.object.name',
        'Field relation id',
        'Fieldname',
        optional parameter)
可选的参数:
  • invisible: True/False

  • states: ?

  • readonly: True/False

Example

'address': fields.one2many(
        'res.partner.address',
        'partner_id',
        'Contacts'),
many2many:

TODO

语法:

fields.many2many('other.object.name',
                 'relation object',
                 'actual.object.id',
                 'other.object.id',
                 'Field Name')
其中:
  • other.object.name是属于这个关系的其他对象。

  • relation object做该关系链接的表格

  • actual.object.id和other.object.id是用于关系表格的字段名称。

Example:

'category_ids':
   fields.many2many(
    'res.partner.category',
    'res_partner_category_rel',
    'partner_id',
    'category_id',
    'Categories'),

To make it bidirectional (= create a field in the other object):

class other_object_name2(osv.osv):
    _inherit = 'other.object.name'
    _columns = {
        'other_fields': fields.many2many(
            'actual.object.name',
            'relation object',
            'actual.object.id',
            'other.object.id',
            'Other Field Name'),
    }
other_object_name2()

Example:

class res_partner_category2(osv.osv):
    _inherit = 'res.partner.category'
    _columns = {
        'partner_ids': fields.many2many(
            'res.partner',
            'res_partner_category_rel',
            'category_id',
            'partner_id',
            'Partners'),
    }
res_partner_category2()
related:

有时候你需要考虑关联中的关联。例如,假设你有这样的对象:City -> State -> Country,你需要从一个城市名得到一个国家名,你可以在City对象中定义:

'country_id': fields.related(
    'state_id',
    'country_id',
    type="many2one",
    relation="res.country",
    string="Country",
    store=False)
其中:
  • The first set of parameters are the chain of reference fields to follow, with the desired field at the end.

  • :guilabel:type是期望字段的类型。

  • Use relation if the desired field is still some kind of reference. relation is the table to look up that reference in.

Functional Fields

功能字段是通过函数计算了值的字段。 (rather than being stored in the database).

Parameters:

fnct, arg=None, fnct_inv=None, fnct_inv_arg=None, type="float",
    fnct_search=None, obj=None, method=False, store=False, multi=False

where

  • fnct is the function or method that will compute the field value. It must have been declared before declaring the functional field.

  • fnct_inv is the function or method that will allow writing values in that field.

  • type is the field type name returned by the function. It can be any field type name except function.

  • fnct_search allows you to define the searching behaviour on that field.

  • method whether the field is computed by a method (of an object) or a global function

  • store If you want to store field in database or not. Default is False.

  • multi is a group name. All fields with the same multi parameter will be calculated in a single function call.

fnct parameter

If method is True, the signature of the method must be:

def fnct(self, cr, uid, ids, field_name, arg, context):

otherwise (if it is a global function), its signature must be:

def fnct(cr, table, ids, field_name, arg, context):

不管哪种形式,它的返回值形式是: {id'_1_': value'_1_', id'_2_': value'_2_',...}.

返回值必须是之前定义的类型。

如果multi是set,则field_name将会被field_names代替:a list of the field names会被计算。Each value in the returned dictionary is also a dictionary from field name to value.例如,如果字段’name’和’age’都基于函数vital_statistics,那么该函数的返回值应该是这样(当ids是[1,2,5])::

{
    1: {'name': 'Bob', 'age': 23},
    2: {'name': 'Sally', 'age', 19},
    5: {'name': 'Ed', 'age': 62}
}

fnct_inv parameter

如果method是True,那么method声明是::

def fnct(self, cr, uid, ids, field_name, field_value, arg, context):

不然(如果是全局函数),声明是:

def fnct(cr, table, ids, field_name, field_value, arg, context):

fnct_search parameter

If method is true, the signature of the method must be:

def fnct(self, cr, uid, obj, name, args, context):

不然(如果是全局函数),声明是:

def fnct(cr, uid, obj, name, args, context):

在查找函数中,返回值是三元祖的列表:

return [('id','in',[1,3,5])]

obj和self相同,name接受字段名。args是三元祖的列表,包含这个字段的查询规范,即使每个元祖分别调用该查询函数。

例子

我们创建这样一个contract对象:

class hr_contract(osv.osv):
    _name = 'hr.contract'
    _description = 'Contract'
    _columns = {
        'name' : fields.char('Contract Name', size=30, required=True),
        'employee_id' : fields.many2one('hr.employee', 'Employee', required=True),
        'function' : fields.many2one('res.partner.function', 'Function'),
    }
hr_contract()

如果添加一个字段要通过看它的current contract来检索员工,我们使用functional field。对象hr_employee这样继承:

class hr_employee(osv.osv):
    _name = "hr.employee"
    _description = "Employee"
    _inherit = "hr.employee"
    _columns = {
        'contract_ids' : fields.one2many('hr.contract', 'employee_id', 'Contracts'),
        'function' : fields.function(
            _get_cur_function_id,
            type='many2one',
            obj="res.partner.function",
            method=True,
            string='Contract Function'),
    }
hr_employee()

注解

三点

  • type ='many2one' is because the function field must create a many2one field; function is declared as a many2one in hr_contract also.

  • obj ="res.partner.function" is used to specify that the object to use for the many2one field is res.partner.function.

  • We called our method _get_cur_function_id because its role is to return a dictionary whose keys are ids of employees, and whose corresponding values are ids of the function of those employees. The code of this method is:

def _get_cur_function_id(self, cr, uid, ids, field_name, arg, context):
    for i in ids:
        #get the id of the current function of the employee of identifier "i"
        sql_req= """
        SELECT f.id AS func_id
        FROM hr_contract c
          LEFT JOIN res_partner_function f ON (f.id = c.function)
        WHERE
          (c.employee_id = %d)
        """ % (i,)

        cr.execute(sql_req)
        sql_res = cr.dictfetchone()

        if sql_res: #The employee has one associated contract
            res[i] = sql_res['func_id']
        else:
            #res[i] must be set to False and not to None because of XML:RPC
            # "cannot marshal None unless allow_none is enabled"
            res[i] = False
    return res

在SQL query中使用函数的id来检索。如果这个查询没有结果返回,那么sql_res[‘func_id’]的值为None。我们必须将值设为False,因为XML:RPC不允许传送该值。

store Parameter

它会计算该字段的值,并且将结果存储在表中。当其他对象中相应该字段的值发生变化时会重新计算它。它使用一下句法:

store = {
    'object_name': (
            function_name,
            ['field_name1', 'field_name2'],
            priority)
}

当对象object_name中列表['field1','field2']中的字段发生变化时,会调用函数function_name。函数声明如下:

def function_name(self, cr, uid, ids, context=None):

ids是其他对象表中记录的ids,这些对象有些字段值已被更改。这个函数在自己表中返回a list of ids of records 并且有些值已被更改。这个list作为main函数的参数字段。

下面是一个membership模块的例子:

'membership_state':
    fields.function(
        _membership_state,
        method=True,
        string='Current membership state',
        type='selection',
        selection=STATE,
        store={
            'account.invoice': (_get_invoice_partner, ['state'], 10),
            'membership.membership_line': (_get_partner_id,['state'], 10),
            'res.partner': (
                lambda self, cr, uid, ids, c={}: ids,
                ['free_member'],
                10)
        }),

Property Fields

Declaring a property

property是一个特定的字段:fields.property

class res_partner(osv.osv):
    _name = "res.partner"
    _inherit = "res.partner"
    _columns = {
                'property_product_pricelist':
                                            fields.property(
                                'product.pricelist',
                        type='many2one',
                        relation='product.pricelist',
                        string="Sale Pricelist",
                                method=True,
                                view_load=True,
                                group_name="Pricelists Properties"),
    }

你要在.xml文件中为这个property创建一个默认值:

<record model="ir.property" id="property_product_pricelist">
    <field name="name">property_product_pricelist</field>
    <field name="fields_id" search="[('model','=','res.partner'),
      ('name','=','property_product_pricelist')]"/>
    <field name="value" eval="'product.pricelist,'+str(list0)"/>
</record>

小技巧

if the default value points to a resource from another module, you can use the ref function like this:

<field name="value" eval="'product.pricelist,'+str(ref('module.data_id'))"/>

Putting properties in forms

想要在表单中添加属性,就要放<properties/>标签在表单中。这会自动添加与该对象相关的所有属性字段。你有权添加想要的属性。

属性值以部分的形式显示,依赖于group_name属性(这个在客户端像分隔符标签的样子渲染)。

How does this work ?

fields.property类继承自fields.function,重写了读写方法。这个字段的type是many2one,所以在表单中,property像many2one方法那样显示。

但是property的值作为一个完全的记录存储在ir.property class/table中。已存储的值是一个字段类型参考(不是many2one),因为每个property指向一个不同的对象。如果你要编辑properties values(从administration menu),这些代表着字段类型参考。

当你读取一个property时,程序会给你链接该property附属的对象实例。如果该对象没有值,系统会给你缺省值。

property的定义像其他字段一样存储在ir.model.fields类中。在property的定义中,你可以添加groups来更改property。

Using properties or normal fields

当你想要添加一个新feature,你会选择是作为property还是normal field来实现它。当你继承自对象并想要扩展它时,应该选择作为normal field。当新feature和对象不相关,只是一个外部概念时,应该选择作为property。

下面有些技巧来帮助你在normal field和property之间做选择:

Normal field扩展对象,增加很多features或是数据。

A property是附属于对象的概念,有着特定的属性:

  • 由公司决定相同的property有不同的值。

  • 每个字段权限管理

  • 资源之间的链接 (many2one)

Example 1: Account Receivable

对于一个特定partner的默认“Account Receivable”作为property执行是因为:

  • 这是个关系到account chart而不是partner的概念,所以account property在合作伙伴表单上是可见。会计人员有权管理这个字段。这不是相同的权限应用于partner对象。所以你对于partner表单的这个字段有特定的权限:只有会计人员可以更改the account receivable of a partner。

  • 1.这是个多公司字段:相同的partner有不同的account receivable values取决于这个用户属于哪个公司。在多公司系统中,每个公司有一个account chart。一个合作伙伴的account receivable取决于该公司的销售订单。

  • 1.对于所有合作伙伴的默认account receivable是相同的,由(administration中)主要property menu来配置。

注解

One interesting thing is that properties avoid "spaghetti" code. The account module depends on the partner (base) module. But you can install the partner (base) module without the accounting module. If you add a field that points to an account in the partner object, both objects will depend on each other. It's much more difficult to maintain and code (for instance, try to remove a table when both tables are pointing to each others.)

Example 2: Product Times

生产到期模块实现所有相关产品的逾期:搬运日期,生产所用时间…这个模块对于食品生产非常有用。

这个模块继承自product.product,并且添加新字段到其中:

class product_product(osv.osv):

    _inherit = 'product.product'
    _name = 'product.product'
    _columns = {

        'life_time': fields.integer('Product lifetime'),
        'use_time': fields.integer('Product usetime'),
        'removal_time': fields.integer('Product removal time'),
        'alert_time': fields.integer('Product alert time'),
        }

product_product()

这个模块添加简单的字段到product.product对象中。我们不使用properties的原因是:

  • 我们扩展一个product,life_time字段是相关到product而不是另一个对象的概念

  • 我们不需要一个字段有个权限管理,不同的延迟被管理所有产品的人管理着。