Fault: cannot execute nextval() in a read-only transaction

Hi there and thank you in advance.

I try to implement a simple template system for analyses in my laboratory module. The goal is to define a function that creates ‘parameter’ records that are linked to my ‘analysis’ record via One2Many when a template is chosen.

The ‘Analysis’ model looks like this.

class Analysis(ModelSQL, ModelView):
    'Analysis'
    __name__ = 'toxicology.analysis'

    name = fields.Char('Analysis Name')

    analysis_template = fields.Many2One('toxicology.analysis_template', 'Analysis Template')

    parameter = fields.One2Many('toxicology.parameter', 'analysis', 'Parameter')

The function that is supposed to work looks like this:

    @fields.depends('analysis_template')
    def on_change_with_parameter(self):
        if self.analysis_type:
            pool = Pool()
            Parameter = pool.get('toxicology.parameter')

            parameters = []

            for parameter in self.analysis_template.parameter:
                parameters.append({
                    'analysis': self.id,
                    'analyte': parameter.analyte,
                    'result': parameter.result,
                    'uom': parameter.uom and parameter.uom.id,
                    })

            Parameter.create(parameters)

I do however end up with the following error message “Fault: cannot execute nextval() in a read-only transaction” which indicates that this is approach is not meant to write to the database.

Is there any way I can achieve this?

on_change methods are execute in read-only transaction because we decided that it was a bad idea to allow them to have side-effect (because there are no guarantee that the client will call them neither in which order).

So instead of creating parameters, you should assign new parameter instances (unsaved) to the One2Many.
Also you must not use a on_change_with because this form can only return a value for the corresponding field but instead probably on_change_analysis_type.

Thank you a lot! The following code works like a charm:

    @fields.depends('analysis_template', 'parameter')
    def on_change_analysis_template(self):
        if self.analysis_template:
            self.parameter = self.analysis_template.parameter

However, when I create an ‘analysis’ record from another form it gives me the following error “Fault: (Pool().get(‘toxicology.analysis’)(**{}), ‘parameter’)”.

I guess it’s due to the fact, that the record wasn’t yet created. Would it make sense to implement an “On Creation”-Trigger for that case?

I do not know if parameter is a One2Many or a Many2Many but if it is a One2Many you can not assign existing instance to it but you must assign new unsaved instances.

It is a One2Many. How do I create an unsaved instance? I’m a bit puzzled.

Maybe you already have a tryton example. That would suffice.

You just need to instantiate the class. From your example it could be something like:

    @fields.depends('analysis_template', 'parameter')
    def on_change_analysis_template(self):
        pool = Pool()
        Parameter = pool.get('toxicology.parameter')
        if self.analysis_template:
            parameter = []
            for param in self.analysis_template.parameter:
                 parameter.append(Parameter(
                     'analyte': param.analyte,
                     'result': param.result,
                     'uom': param.uom))
            self.parameter = parameter

PS: I would strongly suggest to use plural form for xxx2Many fields, the code would be much more easier to read and reason.

Thank you for your code suggestion, instantiating the class makes of course sense. I adjusted the syntax of the instantiating and included your suggestion of making xxx2Many field names plural. I ended up with this method:

    @fields.depends('analysis_template', 'parameters')
    def on_change_analysis_template(self):
        pool = Pool()
        Parameter = pool.get('toxicology.parameter')

        if self.analysis_template:
            parameters = []

            for param in self.analysis_template.parameters:
                parameters.append(Parameter(
                    analysis = self.id,
                    analyte = param.analyte,
                    result = param.result,
                    uom = param.uom and param.uom.id,
                    ))

            self.parameters = parameters

Again it works within a created record, it works when creating a new “Analysis” record from the “Analysis” Tree View, but not if I try to create a new “Analysis” record from within another form (e. g. a “Sample” form that is linked to “Analysis” using another One2Many.
Then it gives me this error:

Traceback (most recent call last):
  File "/trytond/wsgi.py", line 110, in dispatch_request
    return endpoint(request, **request.view_args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/trytond/protocols/dispatcher.py", line 44, in rpc
    return methods.get(request.rpc_method, _dispatch)(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/trytond/wsgi.py", line 76, in wrapper
    return func(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/trytond/protocols/wrappers.py", line 208, in wrapper
    result = func(request, pool, *args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/trytond/protocols/dispatcher.py", line 221, in _dispatch
    result = rpc.result(meth(inst, *c_args, **c_kwargs))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/trytond/model/fields/field.py", line 195, in on_change_result
    return record._changed_values
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/trytond/model/modelview.py", line 891, in _changed_values
    assert hasattr(init_record, fname), (init_record, fname)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: (Pool().get('toxicology.analysis')(**{}), 'parameters')

Fault: (Pool().get('toxicology.analysis')(**{}), 'parameters')

I don’t understand why one unsaved record works and the other one does not. I hope you have any idea.

You must not set the reverse Many2One value especially when it is a negative id.

Thank you for your answer. I deleted the line. But the error persists.

It looks like the client does not have the parameters field in the view.
So it is not possible to calculate accurate modification.

That was the last piece of the puzzle. Thank you very much for your patience!

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.