How to browse or search One2many field in Odoo?

ぐ巨炮叔叔 提交于 2019-12-04 07:23:15

Sadly OpenERP/Odoo only supports one level of Relationship in On-change methods, compute methods, and record modification tracking.

So out of the box, Parent/Child setups are allowed (as in FORMVIEW.one2manyLIST) but not Grandparent/Parent/Child (as in FORMVIEW.one2manyLIST.one2manyLIST).

An example of this would be:

  • Model A - Presented as service warranty form (grandparent model)
  • Model B - List of covered locations (parent model, with ref to grandparent)
  • Model C - List of services provided for each covered location (child model, with ref to parent)

Changes made to Model A will not save records of Model C, and on-change/compute methods on Model A cannot use fields on Model C

So, changes are lost and the problem above is experienced if changes are made on the second nested one2many field, or even if you attempt to read the nested one2many field.

I have created a solution that adds another level of tracking to the onchange/compute/write. You will need to modify the core of Odoo. I hope this changes in the future as Odoo SA has this listed on github as "wishlist".

Here is the code for Odoo 8.0. YMMV with other versions.

FIELDS.PY/_RelationalMulti Class. Replace the following method:

def convert_to_write(self, value, target=None, fnames=None):
    # remove/delete former records
    if target is None:
        set_ids = []
        result = [(6, 0, set_ids)]
        add_existing = lambda id: set_ids.append(id)
    else:
        tag = 2 if self.type == 'one2many' else 3
        result = [(tag, record.id) for record in target[self.name] - value]
        add_existing = lambda id: result.append((4, id))

    if fnames is None:
        # take all fields in cache, except the inverses of self
        fnames = set(value._fields) - set(MAGIC_COLUMNS)
        for invf in self.inverse_fields:
            fnames.discard(invf.name)

    # add new and existing records
    for record in value:
        if not record.id or record._dirty:
            values = dict((k, v) for k, v in record._cache.iteritems() if k in fnames)
            tempVal = {}
            for n in values:
                f = record._fields[n] #get field def
                if f.type == 'one2many':
                    subrec = record[n]
                    subfields = subrec._fields                    
                    tempVal[n] = f.convert_to_write(subrec,record)
                else:
                    val = {}
                    val[n] = values.get(n)
                    tempVal[n] = record._convert_to_write(val)[n]

            if tempVal:
                values = tempVal       

            #add to result       
            if not record.id:
                    result.append((0, 0, values))
            else:
                result.append((1, record.id, values))
        else:
            add_existing(record.id)

    return result

In MODELS.py/BaseModel Class, replace the following method:

@api.model
def new(self, values={}):
    """ new([values]) -> record

    Return a new record instance attached to the current environment and
    initialized with the provided ``value``. The record is *not* created
    in database, it only exists in memory.
    """
    record = self.browse([NewId()])
    record._cache.update(record._convert_to_cache(values, update=True))

    if record.env.in_onchange:
        # The cache update does not set inverse fields, so do it manually.
        # This is useful for computing a function field on secondary
        # records, if that field depends on the main record.
        for name in values:
            field = self._fields.get(name)
            if field:
                try:
                    for invf in field.inverse_fields:
                        invf._update(record[name], record)

                        #serarch this field for sub inverse fields
                        for ftmp in self[name]._fields: 

                                f = self[name]._fields.get(ftmp) 
                                if f and f != invf:                  
                                    for invf in f.inverse_fields:
                                        val = record[name]
                                        invf._update(record[name][ftmp], val)

                except:
                    pass

    return record
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!