yogakumboro yogakumboro - 3 months ago 25
Python Question

Odoo: _get_state() takes at least 4 arguments (4 given) in xml view

Here is mycode. I want to get id_employee of current record before define its model class:

def _get_state(self, cr, uid, context=None):
idemployee = ""
for adv in self.browse(cr, uid, ids, context):
id_employee = adv.id_employee
if id_employee is None:
idemployee = _default_employee(self, cr, uid, context=None)
else:
idemployee = id_employee

sql = " SELECT C.id AS id, C.sequence, C.name \
FROM wf_group_member A \
LEFT JOIN wf_group B ON B.id = A.group_id \
LEFT JOIN wf_process BB ON BB.id = B.process_id \
LEFT JOIN wf_state C ON C.group_id = B.id \
LEFT JOIN hr_employee D ON D.id = A.member_id \
WHERE LOWER(code) = 'ca' AND member_id = %s ORDER BY sequence "
res = []
cr.execute(sql, [(idemployee)])
ardata = cr.fetchall()
for data in ardata:
res.append((data[1], data[2]))
return res


and this is my model class that I put it after _get_state function

class cashadvance(osv.osv):
_name = 'ga.cashadvance'
_columns = {
'id_user' : fields.many2one('res.users', string='User', required=True, readonly=True),
'state' : fields.selection(_get_state, 'Status', readonly=True),
'id_employee' : fields.many2one('hr.employee', string='Employee', required=True, readonly=True),
}


When I call _get_state function, it raised error :

Error details:
global name 'ids' is not defined
None" while parsing /opt/custom-addons/comben/views/cashadvance_view.xml:4, near
<record id="cashadvance_list" model="ir.ui.view">
<field name="name">cashadvance_list</field>
<field name="model">ga.cashadvance</field>
<field name="arch" type="xml">
<tree string="Cashadvance List">
<field name="id_employee"/>
<field name="category_id"/>
<field name="est_date_from" string="Est Date From"/>
<field name="est_date_to" string="Est Date To"/>
<field name="description"/>
<field name="state"/>
</tree>
</field>
</record>


can somebady help me please, thanks

Answer

_get_state has to be part of the class for you to use it not outside the class, the self you're passing in there is not seen as the python self i.e the current instance, it's seen as a normal argument of the function.

Python isn't passing in self for you, let me deviate a little and reproduce the error you're getting. look at this code

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

class func_test:
    def __init__(self):
        func(1, 1, 1, context=1)

a = func_test()

This is the Error raised (make sure you run this with python2)

Traceback (most recent call last):
  File "script.py", line 8, in <module>
    a = func_test()
  File "script.py", line 6, in __init__
    func(1, 1, 1, context=1)
TypeError: func() takes at least 4 arguments (4 given)

python is actually correct even though it's misleading, the function expects at least four arguments because context is a keyword argument and has been filled in at the time of the function definition, but it's still missing one parameter, because the function is outside a class so the first argument which is usually called self is taken as a normal parameter (since it's a function) not a method (function in a class). so starting from left to right self, cr, uid would be filled in as 1 1 1 leaving ids and at this point python would cry out for help because no value was found for that argument, if we move that function into the class and call it with self.func, the current instance automatically gets passed for us.

class func_test:
    def func(self,cr, uid, ids, context=None):
        pass

    def __init__(self):
        self.func(1, 1, 1, context=1)

a = func_test()

Of course you could still have func(self, 1, 1, 1, context=1) but that would be defeating the purpose of methods

But note that python3 is smarter and handles this scenario better than python2, this is a python3 traceback

Traceback (most recent call last):
  File "script.py", line 8, in <module>
    a = func_test()
  File "script.py", line 6, in __init__
    func(1, 1, 1, context=1)
TypeError: func() missing 1 required positional argument: 'ids'

It plainly tells us that no value was provided for ids in the function call

so going back to Odoo your code should look like this

from openerp.osv import osv, fields

class cashadvance(osv.osv):
    _name='comben.cashadvance'

    def _get_state(self, cr, uid, context=None):
        idemployee = ""

        ids = self.search(cr, uid, [], context=context) # you probably want to search for all the records
        for adv in self.browse(cr, uid, ids, context=context):
            id_employee = adv.id_employee
            if id_employee is None:
                idemployee = self._default_employee(cr, uid, ids, context=None) # might raise singleton error if more than one record is returned
            else:
                idemployee = id_employee.id

        sql = " SELECT C.id AS id, C.sequence, C.name \
                   FROM wf_group_member A \
           LEFT JOIN wf_group B ON B.id = A.group_id \
           LEFT JOIN wf_process BB ON BB.id = B.process_id \
           LEFT JOIN wf_state C ON C.group_id = B.id \
           LEFT JOIN hr_employee D ON D.id = A.member_id \
           WHERE LOWER(code) = 'ca' AND member_id = %s ORDER BY sequence "
        res = []
        cr.execute(sql, [(idemployee)])
        ardata = cr.fetchall()
        for data in ardata:
            res.append((data[1], data[2]))

       return res 

    _columns = {
        'id_user': fields.many2one('res.users', string='User', required=True, readonly=True),
        'state': fields.selection(_get_state, string='Status', readonly=True),
        'id_employee': fields.many2one('hr.employee', string='Employee', required=True, readonly=True),
    }

and also move _default_employee into the class body, so you can call it like this

self._default_employee(cr, uid, context=None) # python implicitly passes self for you

xml view

<record id="cashadvance_list" model="ir.ui.view">
  <field name="name">cashadvance list</field>
  <field name="model">comben.cashadvance</field>
  <field name="arch" type="xml">
    <tree string="Cashadvance List">
      <field name="id_employee"/>
      <field name="id_user"/>
      <field name="state"/>
    </tree>
  </field>
</record>
Comments