Light function field definition

I had some discussion with @JCavallo where he told me that they’re using a lot of function fields but only to access them in the code but never to present them in the interface.

While this is not exactly a big issue to create the field in the usual way it can become a bit cumbersome.

So I had this idea: why not creating a kind of decorator as the @property decorator of python.
It could result in something like:

class Model:
   __name__ = 'whatever'

  @tryton_property
  @classmethod
  def my_computation(cls, models, name):
    pass

  @my_computation.setter
  @classmethod
  def my_computation(cls, models, name, value):
    pass

  @my_computation.searcher
  @classmethod
  def my_computation(cls, name, clause):
    pass

It would be purely syntactic sugar in order to fill the cls._fields dict. Of course it should work also on function fields where the getter is an instance method. The tryton_property decorator should support all the parameters fields.Function supports. The main problem I see is with the modularity as I am not sure how it could work yet.

We might want to forbid access from the outside to those fields but I am not sure that it would be a problem anyway.

So why having a function field? This can be also achieved with a normal Python property.

I just found that in Tryton it is common to implement a Function field to access fields from a related model. I always tought (and I do not remember if we ever discussed that) that it can be implemented by a generic Function. Something like:

move_state = fields.Related('sale.state', readonly=True, on_change=False, search=False, order=False)

This will translate to:

  • In case readonly is set to False a setter function will be implemented.
  • In case on_change is set to True an on_change_with_function will be created to update the field without the need to save.
  • In case search is set to True, a searcher function will implemented.
  • In case order is set to True, an order Function will implemented.

Probably we can implement a generic function to compute all the getter, setter and searcher of all of the related fields and reduce the decidated code that we have on Tryton.

Does you think this will help also solving your case?

Because tryton function fields comes with caching and queries by batch etc.

Indeed it could reduce some boilerplate code yet I am not a big fan of this.

I don’t know much about the typology of their function fields. I guess it could in some case but my feeling is that they’re usage is a bit more complicated than fields from a remote model.

@JCavallo could you please share the usage of such function fields? It will be great to know what is the usage to be able to know what to optimize.

To be honest, I’m not sure if the property decorator makes it easier to use or it’s just a diferent way to write them.
From the example you posted I expect that the number of lines to write will be moreless the same.

This can be achieved with cached_property.

Will it be a common case?
Maybe in such case a Function fields could be used.

Or we need to implement our own version of cached_property that use the Model._values to store cache (which will be cleared by transaction counter).

I do not like the idea that it has setter and searcher. For me if you need such feature you must use a Function field.

I do not like the idea to fill _fields with properties because it will expose it to the RPC.

I do not like neither because the type of the field could be changed without notice.
Also I would prefer that we could use dotted notation as depends and that client fetch and update such target value automatically.

Another related missing feature would be to define private fields without label that can not be put on a view and maybe not available for RPC.

To add a little more context on the actual use cases…

There are actually two circumstances where we would like to have access to “light” versions of Function fields.

The first is, as @nicoe exposed above, to be able to trigger batch loading (i.e. @classmethod getters), and use Tryton’s cache based on the Transaction’s counter.

The second is for things that must be fields because that’s how Tryton currently works, though they do not represent meaningful data on the records.

Typical cases would be:

  • Expressions that control states of other fields (invisible / readonly / etc…) which cannot be expressed with pure PYSON
  • Possible computed values for a M2O field. We have lots of possible_<field_name>_values M2M function fields for this purpose

Though everything works, we end up with dozens of possible_<field_name>_values, show_<field_name> and is_<field_name>_readonly that make separating “real” fields a little tedious.