`

定义Open-ERP的对象-字段和方法(2)

 
阅读更多

多重继承 : _inherits

class tiny_object(osv.osv)
    _name = 'tiny.object'
    _table = 'tiny_object'
    _inherits = {
        'tiny.object_a': 'object_a_id',
        'tiny.object_b': 'object_b_id',
        ... ,
        'tiny.object_n': 'object_n_id'
    }
    (...)

对象 'tiny.object' 从 'tiny.object_a', ..., 'tiny.object_n' 这n个对象继承了所有的列和方法 。本技术可继承多个对象(表),每个对象加一个列到 tiny_object ,列存储的是一个表的外键,值 'object_a_id' 、 'object_b_id' ... 'object_n_id' 为 String 类型,决定了列的标题 。

 

本基础机制称为 实例继承(instance inheritance)或 值继承( value inheritance)。实例具有父类的属性。

字段(Fields)

对象有不同类型的 字段,分为:简单、关系、函数 等三类。

简单类型有: integer , float, boolean , string ......等等;关系类型用于描述对象之间的关系(one2one,one2many,many2one);函数类型是特殊的字段,他们不储存在数据库,通过对其他字段的实时的计算得到。

下面是 Open-ERP 的 server/bin/osv/fields.py 程序中定义的一个初始化方法,

 

def __init__(self, string='unknown', required=False, readonly=False,
             domain=None, context="", states=None, priority=0, change_default=False, size=None,
             ondelete="set null", translate=False, select=False, **args) :

有一组可选的参数可用于 字段类型的定义:

 

 

               
change_default:

用户是否可定义其他字段的缺省值 由本字段的值决定,这些缺省值需要先在 ir.values 表中定义。

help:字段如何使用的描述,比 String 跟详细,当鼠标经过字段时,它以 tooltip 提示方式显示。

ondelete:定义删除相关记录的时如何处理。

许可的值有:'restrict'、 'no action'、 'cascade'、'set null'、 'set default' 。

priority:Not used?

readonly:只读True,否则 False

required:必须,对象保存前必须提供:True ,否则 false 

size:数据库的字段长度:字符数 或者 数字位数。

states:字典选择,如:states={'posted':[('readonly',True)]}

string:显示在 标签(label) 或者列标题的内容,当内容包含非 ascii 字符时候,请使用 unicode,

如: 'tested': fields.boolean(u'Testé')

translate: True if the content of this field should be translated, otherwise False.

字段类型的可选的参数:

           

context:Define a variable's value visible in the view's context or an on-change function. Used when searching child table of one2many relationship?

domain: Domain restriction on a relational field.

Default value: [].

Example: domain=[('field','=',value)])

invisible: Hide the field's value in forms. For example, a password.

on_change:Default value for the on_change attribute in the view. This will launch a function on the server when the field changes in the client. For example,on_change="onchange_shop_id(shop_id)".

relation:Used when a field is an id reference to another table. This is the name of the table to look in. Most commonly used with related and function field types.

select:Default value for the select attribute in the view. 1 means basic search, and 2 means advanced search.

Type of Fields

Basic Types

boolean: integer: float: char: text: date: datetime: binary: selection:

A boolean (true, false).

Syntax:

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

An integer.

Syntax:

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

A floating point number.

Syntax:

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

Note

The optional parameter digits defines the precision and scale of the number. 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]),

A string of limited length. The required size parameter determines its size.

Syntax:

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

Example:

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

A text field with no limit in length.

Syntax:

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

A date.

Syntax:

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

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

Syntax:

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

A binary chain

A field which allows the user to make a selection between various predefined values.

Syntax:

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

Note

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

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

Note

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: one2many: many2many: related:

A one2one field expresses a one:to:one relation between two objects. It is deprecated. Use many2one instead.

Syntax:

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

Associates this object to a parent object via this Field. For example Department an Employee belongs to would Many to one. i.e Many employees will belong to a Department

Syntax:

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

Optional parameters:

  • ondelete: What should happen when the resource this field points to is deleted.
    • Predefined value: "cascade", "set null", "restrict", "no action", "set default"

    • Default value: "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'),

TODO

Syntax:

fields.one2many(
        'other.object.name',
        'Field relation id',
        'Fieldname',
        optional parameter)
Optional parameters:
  • invisible: True/False

  • states: ?

  • readonly: True/False

Example

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

TODO

Syntax:

fields.many2many('other.object.name',
                 'relation object',
                 'actual.object.id',
                 'other.object.id',
                 'Field Name')
Where:
  • other.object.name is the other object which belongs to the relation

  • relation object is the table that makes the link

  • actual.object.id and other.object.id are the fields' names used in the relation table

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()

Sometimes you need to refer to the relation of a relation. For example, supposing you have objects: City -> State -> Country, and you need to refer to the Country from a City, you can define a field as below in the City object:

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

  • type is the type of that desired field.

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

Functional Fields

A functional field is a field whose value is calculated by a function (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):

Either way, it must return a dictionary of values of the form {id'_1_': value'_1_', id'_2_': value'_2_',...}.

The values of the returned dictionary must be of the type specified by the type argument in the field declaration.

If multi is set, then field_name is replaced by field_names: a list of the field names that should be calculated. Each value in the returned dictionary is also a dictionary from field name to value. For example, if the fields 'name', and 'age' are both based on the vital_statistics function, then the return value ofvital_statistics might look like this when ids is [1, 2, 5]:

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

fnct_inv parameter

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

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

otherwise (if it is a global function), it should be:

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):

otherwise (if it is a global function), it should be:

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

The return value is a list containing 3-part tuples which are used in search function:

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

obj is the same as self, and name receives the field name. args is a list of 3-part tuples containing search criteria for this field, although the search function may be called separately for each tuple.

Example

Suppose we create a contract object which is :

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()

If we want to add a field that retrieves the function of an employee by looking its current contract, we use a functional field. The object hr_employee is inherited this way:

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()

Note

three points

  • 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

The id of the function is retrieved using a SQL query. Note that if the query returns no result, the value of sql_res['func_id'] will be None. We force the False value in this case value because XML:RPC (communication between the server and the client) doesn't allow to transmit this value.

store Parameter

It will calculate the field and store the result in the table. The field will be recalculated when certain fields are changed on other objects. It uses the following syntax:

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

It will call function function_name when any changes are written to fields in the list ['field1','field2'] on object 'object_name'. The function should have the following signature:

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

Where ids will be the ids of records in the other object's table that have changed values in the watched fields. The function should return a list of ids of records in its own table that should have the field recalculated. That list will be sent as a parameter for the main function of the field.

Here's an example from the membership module:

'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

A property is a special field: 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,
                                group_name="Pricelists Properties"),
    }

Then you have to create the default value in a .XML file for this 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>

Tip

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

To add properties in forms, just put the <properties/> tag in your form. This will automatically add all properties fields that are related to this object. The system will add properties depending on your rights. (some people will be able to change a specific property, others won't).

Properties are displayed by section, depending on the group_name attribute. (It is rendered in the client like a separator tag).

How does this work ?

The fields.property class inherits from fields.function and overrides the read and write method. The type of this field is many2one, so in the form a property is represented like a many2one function.

But the value of a property is stored in the ir.property class/table as a complete record. The stored value is a field of type reference (not many2one) because each property may point to a different object. If you edit properties values (from the administration menu), these are represented like a field of type reference.

When you read a property, the program gives you the property attached to the instance of object you are reading. If this object has no value, the system will give you the default property.

The definition of a property is stored in the ir.model.fields class like any other fields. In the definition of the property, you can add groups that are allowed to change to property.

Using properties or normal fields

When you want to add a new feature, you will have to choose to implement it as a property or as normal field. Use a normal field when you inherit from an object and want to extend this object. Use a property when the new feature is not related to the object but to an external concept.

Here are a few tips to help you choose between a normal field or a property:

Normal fields extend the object, adding more features or data.

A property is a concept that is attached to an object and have special features:

  • Different value for the same property depending on the company

  • Rights management per field

  • It's a link between resources (many2one)

Example 1: Account Receivable

The default "Account Receivable" for a specific partner is implemented as a property because:

  • This is a concept related to the account chart and not to the partner, so it is an account property that is visible on a partner form. Rights have to be managed on this fields for accountants, these are not the same rights that are applied to partner objects. So you have specific rights just for this field of the partner form: only accountants may change the account receivable of a partner.

  • This is a multi-company field: the same partner may have different account receivable values depending on the company the user belongs to. In a multi-company system, there is one account chart per company. The account receivable of a partner depends on the company it placed the sale order.

  • The default account receivable is the same for all partners and is configured from the general property menu (in administration).

Note

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

The product expiry module implements all delays related to products: removal date, product usetime, ... This module is very useful for food industries.

This module inherits from the product.product object and adds new fields to it:

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()

This module adds simple fields to the product.product object. We did not use properties because:

  • We extend a product, the life_time field is a concept related to a product, not to another object.

  • We do not need a right management per field, the different delays are managed by the same people that manage all products.

ORM methods

Keeping the context in ORM methods

In OpenObject, the context holds very important data such as the language in which a document must be written, whether function field needs updating or not, etc.

When calling an ORM method, you will probably already have a context - for example the framework will provide you with one as a parameter of almost every method. If you do have a context, it is very important that you always pass it through to every single method you call.

This rule also applies to writing ORM methods. You should expect to receive a context as parameter, and always pass it through to every other method you call..

分享到:
评论

相关推荐

    Open Academy-新建模块及基础视图等代码

    1. **模型定义**:在Python文件(通常以`.py`为扩展名)中定义模型,包括字段、关系和行为。 2. **视图设计**:使用XML文件(通常以`.xml`为扩展名)来定义用户界面,包括树视图、表单视图和搜索视图。 3. **菜单和...

    Open Dynamics Engine_v0.5官方用户指南

    铰链-2关节类似于铰链,但提供了两个旋转轴。 ###### 7.3.6 固定 固定关节将两个刚体永久连接在一起。 ###### 7.3.7 接触 接触关节用于模拟两个刚体之间的接触力。 ###### 7.3.8 角速度电机 角速度电机关节...

    ABAP 开发 sap erp mes 信息化

    它是一种高级语言,能够支持面向对象的编程方法,并且具有丰富的内置功能来处理复杂的业务逻辑。 ABAP不仅用于编写业务逻辑,还用于与SAP系统内的其他组件进行交互,如数据库操作、报表生成、界面定制等。由于ABAP...

    ABAP开发三个月学会

    SAP作为全球领先的企业资源规划(ERP)软件,其核心开发语言就是ABAP,使得ABAP开发者在IT行业内有着较高的需求和薪资水平。 在成为ABAP顾问之前,通常需要具备一定的编程基础和数据库知识,以及对企业业务流程的...

    业务逻辑的存储过程封装方法

    ### 业务逻辑的存储过程封装方法 在数据库管理系统中,存储过程是一种被编译并存储在数据库中的SQL脚本或程序。它能够接受输入参数、返回单个值、结果集或者多个结果集,并且可以调用其他存储过程。通过封装业务...

    python pyyaml==6.0.1

    此外,Odoo的`ir.model.data`模型也使用YAML来定义数据库对象(如视图、字段、记录)的XML ID和初始数据。 综上所述,PyYAML库在Python编程和Odoo开发中起着核心作用,它简化了YAML数据与Python对象之间的交互,...

    layui加载表格,绑定新增,编辑删除,查看按钮事件的例子

    通过上述方法的定义和使用,开发者可以快速构建一个具备基础功能的后台管理界面,完成对数据的增删查改操作。这种方式不仅提高了开发效率,也使得代码更加模块化,易于维护和扩展。通过理解和掌握文档中的知识点,...

    (word完整版)ORACLE-EBS-系统应用基础概述.doc

    值集和查找代码用于定义和控制输入字段的可选值。值集是预定义的一组值,而查找代码提供了快速选择这些值的界面,确保数据的标准化和一致性。 七、配置文件(Profile) 配置文件在Oracle EBS中起到全局设置的作用,...

    SAP屠夫作品汇总

    [2]定义订单类型(Tcode: KOT2|KOT2-OPA|KOT2_FUNCAREA) 324 [3]维护订单编号范围(Tcode:KONK) 326 [4]定义状态主文件(Tcode:OK02|BS02) 326 [5]定义受权码(Tcode:BS52/BS53) 328 [6](Tcode:KOV2) 328 [7]分配状态...

    中国联通SOA平台服务规范

    总的来说,《中国联通SOA平台服务规范》是一份详细的技术文档,它规定了中国联通SOA平台中服务的定义、交互规则和管理策略,确保了不同系统间数据的准确、高效和安全流通,从而支持中国联通业务的灵活扩展和高效运营...

    SAP BW 数据源介绍及增强

    SE12用于修改和扩展提取结构,添加或删除字段,以适应新的报告需求。而SE16则用于管理数据转换,允许用户定制数据处理逻辑,比如数据清洗、计算字段的创建等。 自定义数据源是另一种增强方法,当标准数据源无法满足...

    ABAP学习入门ppt

    5. **ABAP数据类型**: 包括内置数据类型(如CHAR, NUMC, INT, DEC等)和自定义数据类型(如STRUCTURE, TABLE等),用于定义变量和字段。 6. **ABAP语句**: 如DATA声明变量, WRITE用于屏幕输出,SELECT用于数据库...

    ABAP培训标准教材.zip

    学习者将学习如何使用ABAP Query、Report Painter、ALV Grid(ABAP List Viewer)等工具生成报表,以及如何使用Open SQL和Native SQL与数据库交互。此外,还会涉及动态报表和自定义报表的开发方法。 总之,《ABAP...

    abap开发小规则和注意点

    22. SU01:可以定义日期和小数点显示格式。 23. Add-on 表、客制表、外挂表:都是指 Z 开头的表。 24. 批量处理数据(CATT、BDC、BAPI、LSMW(业务人用:没有 TR 请求号))。 25. BAPI(Business Application ...

    ABAP技术总结

    在面向对象编程中,ABAP支持使用类和对象的创建。消息处理是SAP程序中重要的一环,ABAP提供了处理系统消息的机制,其中包含了如何定义、发送、确认消息等。数据库操作主要是通过Open SQL实现,Open SQL是ABAP中用来...

    ORACLE_EBS 系统应用基础概述

    它允许用户定义复杂的规则和步骤,确保每项业务活动都能按照预定流程执行,提高了决策效率和过程控制。 #### 十一、预警(Alert) 预警功能用于实时监控关键业务指标,当某些条件满足时自动通知相关人员。例如,...

    仓库管理系统

    在本项目中,我们采用C++编程语言来构建这个系统,利用Microsoft Foundation Class (MFC)库为用户提供友好的图形用户界面,同时通过Oracle数据库和ODBC(Open Database Connectivity)接口来实现数据的存储和检索。...

Global site tag (gtag.js) - Google Analytics