Odoo中文网|Odoo实施培训

 找回密码
 立即注册
搜索
热搜: Odoo OpenERP 实施
查看: 7387|回复: 3
打印 上一主题 下一主题

openerp Objects, Fields and Methods

  [复制链接]

3

主题

8

帖子

21

积分

新手上路

Rank: 1

积分
21
跳转到指定楼层
楼主
发表于 2015-9-2 20:44:22 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
OpenERP 对象我们可以通过 对象 访问所有ERP的数据。举个例子,可以通过 res.partner 对象访问 合作伙伴 相关的数据,通过 account.invoice 对象访问 发票 相关的数据。
请注意的是,每种类型的资源是一个对象,而不是每个资源是一个对象。我们可以使用res.partner对象来管理所有的partners,而不是每个partner用一个res.partner对象来表示。当我们说“object oriented”术语时,其实说的是每级有个对象(an object per level)。
直接的后果是,对象的所有方法都有一个共同的参数:参数“ids”。这个资源特别指定的方法必须被使用。准确的是,必须使用的这个方法的参数包括着资源ids的列表。
例如,我们有两个标识为1和5的合作伙伴,当我们想要调用res_partner的方法“send_email”时,我们应该这样写::
res_partner.send_email(... , [1, 5], ...)我们在这份文档中将会看到更多具体的对象方法调用的语法。
在下面的章节中,我们将会看到如何定义一个新对象。然后,我们会检验定义新对象所使用的不同方法。
对于开发者来说:
  • OpenERP “object”在面向对象编程中经常被成为类(class)。
  • 一个OpenERP “resource”在面向对象编程中经常被称为一个对象,一个类的实例。
当你试图在OpenERP中编程时会有些困惑,因为使用的是Python语言,而Python语言是一种完全的面向对象语言。它有对象和实例…
我们感到幸运的是,OpenERP“resource”当使用“browse”类方法(OpenERP object method)时,可以神奇的转变成一个Python对象。
The ORM - Object-relational mapping - ModelsORM是Object-Relational Mapping(对象关系映射)的简称,是OpenERP的中心部分。
在OpenERP中,数据模型的描述和操纵都是通过Python的类和对象。ORM的作用是为python文件和底层的关系型数据库(PostgreSQL)消除隔阂(bridge the gap)(尽可能的透明的为开发人员),为对象提供持久性。

OpenERP Object AttributesObjects Introduction想要定义一个新对象,你就要先定义一个新的Python类,然后实例化它。这个类必须继承自osv模块的osv类。

Object definition对象定义的前几行如下::
class name_of_the_object(osv.osv):        _name = 'name.of.the.object'        _columns = { ... }        ...name_of_the_object()一个对象可以通过一些字段来定义,而这些字段在类中已经预定义了名称。其中有两个是必须的(_name和_columns),其他都是可选的。预定义的字段是:

Predefined fields_auto是否自动创建对象对应的Table,如果OpenERP的对象从PostgreSQL views中产生时,将_auto设为False比较好。想要知道更多的细节可以查看“Reporting From PostgreSQL Views”。_columns (required)对象字段,可在field章节了解更多。_constraints定义对象上的约束,可在“constraints”章节了解更多。_sql_constraints定义对象中SQL约束,可在“SQL constraints”章节了解更多。_defaults定义一些字段的缺省值,可在“default value”章节了解更多。_inherit当前对象继承自哪里,可在 :ref:`object inheritance section<inherit-link>`(first form) 了解更多。_inherits
当前对象继承的对象列表,这个列表必须是下面的形式:{‘name_of_the_parent_object’: ‘name_of_the_field’, ...}。可在“object inheritance section”了解更多。缺省值是:{}
_log_access定义对资源的写访问是否应写入日志,如果是true,那将自动在对应的数据表中增加create_uid, create_date, write_uid, write_date四个字段,缺省值为True,即字段增加。这四个字段分布记录record的创建人,创建日期,修改人,修改日期。这四个字段值可以用对象的方法(perm_read)读取。_name (required)定义对象的名称,缺省值为None。_order定义search和read方法的结果记录排序规则。
缺省值:“id”
例如:
_order = "name"_order = "date_order desc"
_rec_name标识每个已被存储的资源的名称字段。缺省值是:“name”,缺省情况(name_get没被重载的话)方法name_get()返回本字段值。_sequence数据库表的id字段的序列采集器,缺省值为: None。_sqlSQL代码执行在对象的创建之上,意思就是说在表格创建后代码执行。_table数据库表名,缺省值是和_name一样,只是将"."替换成"_"。

Object Inheritance - _inheritIntroduction对象可能继承于很多定制的或是特定的模块。这比继承一个对象增加或修改某方面更好。
It is done with:
_inherit='object.name'
Extension of an object这里继承有两种方式:他们都产生一个新的数据类,它将父类的字段和行为做为自己额外的字段和行为,但是他们在heavy programatical consequences上不同。
例子1中产生一个新的子类叫“custom_material”,当操作network.material的视图或是列表时可以看到或是使用它。这和例子2不同。
这是由于这个新类操作的表格(other.material)是不能被之前的对象“network.material”的视图或列表所识别的。
例子1:
class custom_material(osv.osv):    _name = 'network.material'    _inherit = 'network.material'    _columns = {        'manuf_warranty': fields.boolean('Manufacturer warranty?'),    }    _defaults = {        'manuf_warranty': lambda *a: False,    }    custom_material()Tip
Notice
_name == _inherit

这个例子,“custom_material”增加了一个新的字段“manuf_warranty”到对象“network.material”中。这个类的新的实例对运行在父类“network.material”上的视图或列表是可见的。
在面向对象的设计中,这种继承通常称为“类继承(class inheritance)”。子类继承父类的字段和函数。
例子2:
class other_material(osv.osv):    _name = 'other.material'    _inherit = 'network.material'    _columns = {        'manuf_warranty': fields.boolean('Manufacturer warranty?'),    }    _defaults = {        'manuf_warranty': lambda *a: False,    }    other_material()Tip
Notice
_name != _inherit

在这个例子中,“other_material”会继承“network.material”的所有字段,它另外会加入一个新的字段“manuf_warranty”。所有这些字段都在表格“other.material”中。这个类的新的实例对运行在父类“network.material”上的视图或列表是不可见的。
这种类型的继承被称为“原型继承(inheritance by prototyping)”。因为新创建的子类拷贝了父类的所有字段。子类继承父类的字段和方法。


Inheritance by Delegation - _inherits
Syntax ::
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”继承n个对象“tiny.object_a, ...,tiny.object_n”的所有的字段和方法。
为了继承多种表格,每继承一个对象就加一列到表格中。这个列存储继承表格的外键。值‘object_a_id’ ‘object_b_id’ ... ‘object_n_id’ 是字符串类型,定义列头,它里面放着存储对象‘tiny.object_a’, ..., ‘tiny.object_n’的外键。
这种继承称为“对象继承(instance inheritance)”或是“值继承(value inheritance)”。对象有它父类的值(values)。

字段(Fields) 简介对象包括不同类型的字段,分为三类:simple types,relation types,functional fields。simple types是integers, floats, booleans, strings, etc ...;relation types是表示对象间的关系(one2one, one2many, many2one)。Functional fields是特殊的字段,因为它们不是存储在数据库中,而是实时计算视图给定的其他字段。
这是OpenERP继承类方法初始化的头文件。(Here’s the header of the initialization method of the class any field defined in OpenERP inherits)。:
def __init__(self, string='unknown', required=False, readonly=False,             domain=None, context="", states=None, priority=0, change_default=False, size=None,            , translate=False, select=False, **args) :


回复

使用道具 举报

3

主题

8

帖子

21

积分

新手上路

Rank: 1

积分
21
沙发
 楼主| 发表于 2015-9-2 20:45:15 | 只看该作者
这里有一套通用的可选参数,它对大多数的字段类型是可用的:

change_default:别的字段的缺省值是否可依赖于本字段。这些缺省值定义在ir.values表格中。 help:用于描述这个字段如何使用:更长的描述文字。当鼠标滑过该字段时将会显示在一个提示框中。 ondelete:如何处理相关记录的删除。允许的值有:‘restrict’, ‘no action’, ‘cascade’, ‘set null’, and ‘set default’。 priority:Not used? readonly:当值为True时,该字段只读不可修改,缺省值:False required:当值为True时,在对象存储前,该字段必须有个值,缺省值:False size:数据库中该字段的size:number characters or digits. states:让我们为这个对象特定的states重写其他参数,Accepts a dictionary with the state names as keys and a list of name/value tuples as the values. For example: states={‘posted’:[(‘readonly’,True)]} string:The field name as it should appear in a label or column header. Strings containing non-ASCII characters must use python unicode objects. For example: ‘tested’: fields.boolean(u’Testé’) translate:值为True的话应该翻译这个字段的content,为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

    缺省值: [].

示例: domain=[('field','=',value)]) :invisible: 在表单中隐藏该字段的值,例如输入密码区 n_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: 当某个字段是另张表的id reference时就使用它。This is the name of the table to look in. Most commonly used with related and function field types. :select: 视图中select 属性的默认值,1指basic search,2指advanced search.
字段类型
基础类型
boolean:        

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

    语法:

    fields.boolean('字段名称' [, 可选参数]),

integer:        

    整型(integer).

    语法:

    fields.integer('字段名称' [, 可选参数]),

float:        

    浮点型(float).

    语法:

    fields.float('字段名称' [, 可选参数]),

    Note

    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.

    例子:

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

char:        

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

    语法:

    fields.char(
            '字段名称',
            size=n [,
            可选参数]), # 参数 ''n'' 指明了字符串字段的长度.

    Example:

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

text:        

    没有长度限制的text field

    Syntax:

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

date:        

    A date.

    Syntax:

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

datetime:        

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

    Syntax:

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

binary:        

    A binary chain

selection:        

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

    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来代替。

    Syntax:

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

many2one:        

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

    Syntax:

    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

    Syntax:

    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

    Syntax:

    fields.many2many('other.object.name',
                     'relation object',
                     'actual.object.id',
                     'other.object.id',
                     'Field Name')

    Where:

            other.object.name是属于这个关系的其他对象。
            relation object做该关系链接的表格
            actual.object.id和other.object.id是用于关系表格的字段名称。

   
回复 支持 反对

使用道具 举报

3

主题

8

帖子

21

积分

新手上路

Rank: 1

积分
21
板凳
 楼主| 发表于 2015-9-2 20:46:07 | 只看该作者
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)

    Where:

            The first set of parameters are the chain of reference fields to follow, with the desired field at the end.
            :guilabel:type是期望字段的类型。
            Use :guilabel:`relation` if the desired field is still some kind of reference. :guilabel:`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

        :guilabel:`fnct` is the function or method that will compute the field value. It must have been declared before declaring the functional field.
        :guilabel:`fnct_inv` is the function or method that will allow writing values in that field.
        :guilabel:`type` is the field type name returned by the function. It can be any field type name except function.
        :guilabel:`fnct_search` allows you to define the searching behaviour on that field.
        :guilabel:`method` whether the field is computed by a method (of an object) or a global function
        :guilabel:`store` If you want to store field in database or not. Default is False.
        :guilabel:`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):

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

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

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函数的参数字段。

回复 支持 反对

使用道具 举报

3

主题

8

帖子

21

积分

新手上路

Rank: 1

积分
21
地板
 楼主| 发表于 2015-9-2 20:46:29 | 只看该作者
下面是一个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

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>

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

想要在表单中添加属性,就要放<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来配置。

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

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

这个模块继承自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而不是另一个对象的概念
        我们不需要一个字段有个权限管理,不同的延迟被管理所有产品的人管理着。

ORM methods
Keeping the context in ORM methods

在OpenObject中,上下文保留重要数据类似文档必须被写入的语言等,无论function field是否需要更新等。

当调用ORM方法时,你已经拥有一个上下文,例如,框架提供给你一个几乎每个方法的参数。如果你有一个上下文,那么你一直带它通过你调用的每个单独的方法是很重要的。

这个规则适用于写ORM方法。你应该希望接受一个上下文作为参数,让它一直通过你调用的其他方法。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|技术支持|开发手册|Odoo中文网-远鼎旗下odoo培训网站 ( 苏ICP备15039516号 )

GMT+8, 2024-4-20 17:16 , Processed in 0.014190 second(s), 8 queries , Xcache On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表