1
votes

I am working on an Odoo module where instead of directly defining fields in model I have a tuple called schema, which have all the fields. I have define a classmethod in the model that read each field from the tuple and create it on the model through introspection.

@classmethod
def initialze(cls, schema):
    for field in schema:
        add_a_field(cls, field)
        pass
    pass       

As can be seen, this method is iterating on the tuple and pass individual field to another method name 'add_a_field(cls, field)'.

The method 'add_a_field' use python setattr() built-in method.

def add_a_field(cls, field):
    setattr(cls, field.string, field)
    pass

It add a field with same name and label.

In the class where I have this method I call it directly as in the following sample:

from openerp import fields, models
# other imports

def add_a_field(cls, field):
    setattr(cls, field.string, field)
    pass

schema = (
          fields.Char(string='RequestID'),
          fields.Float(string='Discount', default=0.00),
          fields.Float(string='Subtotal', default=0.00),
          )

class Batch(models.Model): 
    _name='batch'
    @classmethod
    def initialze(cls, schema):
        for field in schema:
            add_a_field(cls, field)
            pass
        pass       
    pass

# other model methods
Batch.initialze(schema)

In Odoo v8 it is working fine, but in v9 it gives an error ''RuntimeError: maximum recursion depth exceeded”

In Odoo v9 fields.py __getattr__ is defined as follows (see https://github.com/odoo/odoo/blob/9.0/openerp/fields.py):

def __getattr__(self, name):
    """ Access non-slot field attribute. """
    try:
        return self._attrs[name]
    except KeyError:
        raise AttributeError(name)

While __init__ is as follow:

def __init__(self, string=None, **kwargs):
        kwargs['string'] = string
        args = {key: val for key, val in kwargs.iteritems() if val is not None}
        self.args = args or EMPTY_DICT
        self.setup_full_done = False

In v8 fields.py __init__ is defined as follows:

def __init__(self, string=None, **kwargs):
        kwargs['string'] = string
        attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
        self._attrs = attrs or EMPTY_DICT

Error is:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in initialze
  File "<stdin>", line 2, in add_a_field
  File "openerp/fields.py", line 343, in __getattr__
    return self._attrs[name]
  File "openerp/fields.py", line 343, in __getattr__
    return self._attrs[name]
  File "openerp/fields.py", line 343, in __getattr__
    return self._attrs[name]
  File "openerp/fields.py", line 343, in __getattr__
    return self._attrs[name]
  File "openerp/fields.py", line 343, in __getattr__
    return self._attrs[name]
:
:
:
:
RuntimeError: maximum recursion depth exceeded while calling a Python object

Any idea how this should be resolved?

1

1 Answers

1
votes

After some debugging and looking around in fields.py, it got evident that Odoo no longer want module / application code to access initialization parameters aka init arguments using dot notation, hence using field.string to access the name of field or field.required to find out if this field is required, is not a valid piece of code anymore. Instead all the initialization parameters should now be accessed from a dictionary type field instance variable called args.

As I was accessing field.string in the following code, the runtime error was showing up:

def add_a_field(cls, field):
    setattr(cls, field.string, field)
    pass

I have now modified the code as follows:

def add_a_field(cls, field):
    setattr(cls, field.args['string'], field)
    pass

args and _attrs are define as follows in fields.py:

_slots = {
    'args': EMPTY_DICT,             # the parameters given to __init__()
    '_attrs': EMPTY_DICT,           # the field's non-slot attributes

while earlier in Odoo v8 there was no args, while _attrs was define as follows:

_slots = {
    '_attrs': EMPTY_DICT,           # dictionary of field attributes; it contains:
                                    #  - all attributes after __init__()
                                    #  - free attributes only after set_class_name()

So in conclusion module / application code should now use field.args['string'] or field.args['required'] instead of dot notation directly i.e. field.string or field.required