Domains, states and safe-eval.

## tl; dr - Formalism used for domains and states is subpar. - They are unnecessary difficult for newcomers and error-prone. - I suggest to use prefix notation to improve the situation and shows than it may also be helpful for safe-eval. --- ## Domains Domains are used to define a subest of records lines on an arbitrary model. warehouses = cls.search([ ('type', '=', 'warehouse'), ['OR', ('storage_location', 'in', ids), ('input_location', 'in', ids), ('output_location', 'in', ids), ]]) --- ## Origin It's one of the few still untouched code from TinyERP --- ## AND and OR ... are boolean operators, but we cannot to use them with boolean fields. For example, active and posted Must be written [('active', '=', True), ('posted', '=', True)] --- ## Test Equality Other boolean operator are only allowed on the field themselves. (first_name = 'john') = (last_name = 'Doe') Must be expressed like ... --- ## Test Equality ['OR', [ ('first_name'', '=', 'John'), ('last_name', '=', Doe'), ], [ ('first_name', '!=', 'John'), ('last_name', '!=', Doe'), ] ] And `not` is only used inside other operators, like `not like`, `not child_of`, etc. --- ## Position matters We can do ('last_name', '!=', Doe') But not ('Doe', '!=', 'last_name') --- ## Fields States We often need to express fields properties wrt to some context. Before 1.6, it was dead simple, we where using strings containing python code. --- ## Example customer = fields.Many2One('party.party', 'Customer', required=True, states={ 'readonly': "state != 'draft' "\ "or bool(outgoing_moves)", }, on_change=['customer']) --- ## Pros & Cons - easy to read But - Bug-prone - "Black-magic" - Python-centric --- ## Enters PYSON customer = fields.Many2One('party.party', 'Customer', required=True, states={ 'readonly': Or( Not( Equal(Eval('state'), 'draft')), Bool( Eval('outgoing_moves')) ), }, on_change=['customer']) --- ## PYSON Pros & Cons - Standardised - "Type checked" - Can be implemented in any langage But - Difficult to read - Verbose --- ## By the way, what is the PYSON definition? *PYSON is the PYthon Statement and Object Notation.* See [http://doc.tryton.org/2.6/trytond/doc/ref/pyson.html](http://doc.tryton.org/2.6/trytond/doc/ref/pyson.html) --- ## And what are domains? Also statements! (More on that later). --- ## Domain on fields field = fields.Many2One('ir.model.field', 'Field', domain=[ ('model', '=', Eval( '_parent_rule_group', {} ).get('model')) ], select=True, required=True) Fields domains can be expressed wrt to a context: - current record and parent_record - date, current user, parent record, etc --- # Whe can do better --- ## S-expressions to the rescue! Simple prefix notation used by the LISP familly. (defun factorial (x) (if (zerop x) 1 (* x (factorial (- x 1))))) --- # S-expressions everywhere! --- ## The first example: This warehouses = cls.search([ ('type', '=', 'warehouse'), ['OR', ('storage_location', 'in', ids), ('input_location', 'in', ids), ('output_location', 'in', ids), ]]) --- ## The first example: Becomes warehouses = cls.search(['and', ['=', 'type', 'warehouse'], ['or', ['in', 'storage_location', ids], ['in', 'input_location', ids], ['in', 'output_location', ids), ]]) --- ## No operators separation active and posted Becomes ['and', 'active', 'posted'] Or even better "[and active posted]" --- ## Also [= [= first_name 'John'] [= 'Doe' last_name] ] --- ## Fields States 'readonly': Or( Not(Equal(Eval('state'), 'draft')), Bool(Eval('outgoing_moves')) ), Becomes 'readonly': "[or [!= state 'draft'] "\ "[bool outgoing_moves]]" --- ## Interlude: Namespace context and record field are mixed in the same namespace. What happend if a model contains a field `time` or `context` ? > Namespaces are one honking great idea, let's do more of those! > > -- Zen of Python --- ## Interlude: Namespace **Several solutions** - Introduce namespace "ctx" and forbid to have a ctx field on model. - Introduce a namespace "this" and consider it as part of the eval context, just like time, active_id, etc. --- ## Prefix Notation: Discussion - More readble than PYSON, but less than Python code. - Safer than Python code, equivalent to PYSON wrt to security. - Easy to implement in virtually all langage, so on par with PYSON. - Like the evaluated Python string it is far cleaner than mixing PYSON and domains. --- ## A complete langage Once we use s-expression, the next logical step is to use several of them. A full programm is mainly a list of expression that shares the same namespace. --- ## Safe-Eval S-expression may provide a way to implement safe evaluation without messing with Python internals But at the cost of a less natural syntax --- # Thanks for watching