2
votes

I'm working on a OpenERP environment, but maybe my issue can be answered from a pure python perspective. What I'm trying to do is define a class whose "_columns" variable can be set from a function that returns the respective dictionary. So basically:

class repos_report(osv.osv):
    _name = "repos.report"
    _description = "Reposition"
    _auto = False

    def _get_dyna_cols(self):
        ret = {}
        cr = self.cr
        cr.execute('Select ... From ...')
        pass           #<- Fill dictionary
        return ret

    _columns = _get_dyna_cols()

    def init(self, cr):
        pass    #Other stuff here too, but I need to set my _columns before as per openerp 

repos_report()

I have tried many ways, but these code reflects my basic need. When I execute my module for installation I get the following error.

TypeError: _get_dyna_cols() takes exactly 1 argument (0 given)

When defining the the _get_dyna_cols function I'm required to have self as first parameter (even before executing). Also, I need a reference to openerp's 'cr' cursor in order to query data to fill my _columns dictionary. So, how can I call this function so that it can be assigned to _columns? What parameter could I pass to this function?

From an OpenERP perspective, I guess I made my need quite clear. So any other approach suggested is also welcome.

2
Why don't you put the _columns = line inside of __init__?Mark Ransom
No. Most likely he wants it as a property - long answer bellow.jsbueno

2 Answers

4
votes

From an OpenERP perspective, the right solution depends on what you're actually trying to do, and that's not quite clear from your description. Usually the _columns definition of a model must be static, since it will be introspected by the ORM and (among other things) will result in the creation of corresponding database columns. You could set the _columns in the __init__ method (not init1) of your model, but that would not make much sense because the result must not change over time, (and it will only get called once when the model registry is initialized anyway).

Now there are a few exceptions to the "static columns" rules:

Function Fields

When you simply want to dynamically handle read/write operations on a virtual column, you can simply use a column of the fields.function type. It needs to emulate one of the other field types, but can do anything it wants with the data dynamically. Typical examples will store the data in other (real) columns after some pre-processing. There are hundreds of example in the official OpenERP modules.

Dynamic columns set

When you are developing a wizard model (a subclass of TransientModel, formerly osv_memory), you don't usually care about the database storage, and simply want to obtain some input from the user and take corresponding actions. It is not uncommon in that case to need a completely dynamic set of columns, where the number and types of the columns may change every time the model is used. This can be achieved by overriding a few key API methods to simulate dynamic columns`:

  • fields_view_get is the API method that is called by the clients to obtain the definition of a view (form/tree/...) for the model.
  • fields_get is included in the result of fields_view_get but may be called separately, and returns a dict with the columns definition of the model.
  • search, read, write and create are called by the client in order to access and update record data, and should gracefully accept or return values for the columns that were defined in the result of fields_get

By overriding properly these methods, you can completely implement dynamic columns, but you will need to preserve the API behavior, and handle the persistence of the data (if any) yourself, in real static columns or in other models.

There are a few examples of such dynamic columns sets in the official addons, for example in the survey module that needs to simulate survey forms based on the definition of the survey campaign.

1 The init() method is only called when the model's module is installed or updated, in order to setup/update the database backend for this model. It relies on the _columns to do this.

2
votes

When you write _columns = _get_dyna_cols() in the class body, that function call is made right there, in the class body, as Python is still parsing the class itself. At that point, your _get_dyn_cols method is just a function object in the local (class body) namespace - and it is called.

The error message you get is due to the missing self parameter, which is inserted only when you access your function as a method - but this error message is not what is wrong here: what is wrong is that you are making an imediate function call and expecting an special behavior, like late execution.

The way in Python to achieve what you want - i.e. to have the method called authomatically when the attribute colluns is accessed is to use the "property" built-in. In this case, do just this: _columns = property(_get_dyna_cols) - This will create a class attribute named "columns" which through a mechanism called "descriptor protocol" will call the desired method whenever the attribute is accessed from an instance.

To leran more about the property builtin, check the docs: http://docs.python.org/library/functions.html#property