2

Could someone please explain to me how self.env.cr.commit() works, when to use it and some good practices?
From Odoo documentation seems the use of cr.commit is very dangerous. This is my first time using it and I am not sure how to use it properly for my use case.

Edit:
More information for my use case: I am creating shipments through shipping provider API. Let's say my API call is successful and I have created shipment but during handling of the response, I have to raise UserError for some reason and my changes are rollbacked. So now the state of the shipment is different in Odoo and on the shipping provider server which is unacceptable.
So if I am calling the method create_dhl_shipment() and the flag variable is True (an error occurred during last API call) then I would like to delete the original shipment and create a new one.
And my problem is: How do I make a change in the database and keep it from rollbacking.
During search on the internet, I came across cr.commit() but Odoo in documentation really discourages using it.

very simplified example:

Class StockPickingInherited(models.Model)
    _inherit = 'stock.picking'

    remnant_shipment = fields.Boolean("Possible remnant shipment")
    packages = fields.One2many("stock.shipment.package", "picking_id")
    
    def create_dhl_shipment(self):
        response_from_shipping_provider = requests.get("API URL")
        if response_from_shipping_provider != 200:
            if not remnant_shipment:
                raise UserError("Shipment creation failed")
            else:
                self.write({"packages" : [(5, 0, 0)]})
                self.env.cr.commit() # write data into the db and keep the change from rollbacking due to raising UserError
                raise UserError("Shipment creation failed")

Am I doing it right? Are there some potential dangers?

xixo222
  • 157
  • 2
  • 9
  • It will help us to understand your question better if you show the `class ...` declaration that this method is defined inside. Also show how `self.env` is assigned. For more tips on created a good code example, see [mcve]. – Code-Apprentice Jan 28 '22 at 18:51

2 Answers2

0

It is true that self.env.cr.commit() should be used sparingly. However, there are some legitimate use cases for it. For example:

@api.model
def some_cron_job(self):
    for record in self.env[...].search(...):
        record.do_some_process()
        self.env.cr.commit()

The above is fine because you are doing some process on a batch of records and to avoid that the cron job does it over and over because of an error on one of the records, you can commit after each record has been processed. (PS: the above could be made safer with try...except... or marking a record as "failed" for example. This can be done in conjunction with commit())

In your use case you want to display a message to the user, but your problem is that once you raise the transaction will rollback, so to counter this you call a commit().

I have had similar situations and I sometimes find that using an @api.onchange works well for showing a message to the user.

@api.onchange('your_field')
def onchange_your_field(self):
    if self.your_field > 100:
        raise UserError("Thanks for entering the field")

However, I am no longer a fan of that approach since Odoo has gotten rid of most @api.onchange in favour of computed fields. (There are good reasons for the move, so try and avoid it too.)

Luckily there is a new way to do this, introduced in Odoo Version 13.0.

def some_method(self):
    # Do something
    self.write({"something" : False})
    # Display message
    return {
        'type': 'ir.actions.client',
        'tag': 'display_notification',
        'params': {
            'title': title,
            'message': message,
            'sticky': False,
        }
    }

The above is taken from here in the Odoo repo where a message is displayed to the user if the mail server credentials are correct.

adekock11
  • 544
  • 2
  • 11
0

The following rule is taken from the Odoo guidelines (never commit the transaction):

You should NEVER call cr.commit() yourself, UNLESS you have created your own
database cursor explicitly! And the situations where you need to do that are
exceptional!

And by the way if you did create your own cursor, then you need to handle error
cases and proper rollback, as well as properly close the cursor when you’re done
with it.

In the following example, we create a new cursor to avoid rollback that could be caused by an upper method:

registry = odoo.registry(self.env.cr.dbname)
with registry.cursor() as cr:
    env = api.Environment(cr, SUPERUSER_ID, {})
    

For more details check the well-documented Odoo guidelines

You can find an example in auth_ldap module

Kenly
  • 24,317
  • 7
  • 44
  • 60