8000 onchange methods · odoo/documentation@d2baef5 · GitHub
[go: up one dir, main page]

Skip to content

Commit d2baef5

Browse files
committed
onchange methods
1 parent 3131b5a commit d2baef5

File tree

4 files changed

+121
-17
lines changed

4 files changed

+121
-17
lines changed

content/developer/tutorials/server_framework_101/02_lay_the_foundations.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,6 @@ same model. It also loads faster, making it the go-to format when performance ma
509509
state_ca_yt,ca,"Yukon","YT"
510510
511511
.. note::
512-
513512
- The file name must match the model name.
514513
- The first line lists the model fields to populate.
515514
- XML IDs are specified via the special `id` field.

content/developer/tutorials/server_framework_101/03_build_user_interface.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,6 @@ field labels and values).
368368
</record>
369369
370370
.. note::
371-
372371
- The XML structure differs between view types.
373372
- The `description` field is omitted from the list view because it wouldn't fit visually.
374373

content/developer/tutorials/server_framework_101/04_relational_fields.rst

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ structure guidelines** that offer several benefits:
5858
└── __manifest__.py
5959
6060
.. note::
61-
6261
- The :file:`models` directory contains its own :file:`__init__.py` file, simplifying Python
6362
imports. The root :file:`__init__.py` file imports the :file:`models` Python package, which
6463
in turns imports individual model files.
@@ -235,7 +234,6 @@ the referenced record's ID.
235234
name = fields.Char(string="Name")
236235
237236
.. note::
238-
239237
- The relationship only needs to be declared on the *many* side to be established.
240238
- The `ondelete` argument on the `Many2one` field defines what happens when the referenced
241239
record is deleted.
@@ -843,7 +841,6 @@ convention, `Many2many` field names end with the `_ids` suffix, like for `One2ma
843841
)
844842
845843
.. note::
846-
847844
- It is not necessary to add a `Many2many` field to both models of the relationship.
848845
- The optional `relation`, `column1`, and `column2` field arguments allow specifying the name
849846
of the junction table and of its columns.

content/developer/tutorials/server_framework_101/05_connect_the_dots.rst

Lines changed: 121 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,14 @@ that computed fields remain consistent.
7272
product.breadcrumb = f"{category.name} / {product.name}"
7373
7474
.. note::
75-
7675
- Compute methods are referenced using their names as strings in the `compute` field argument,
7776
rather than directly linking the method object. This allows placing them after the field
7877
declaration.
7978
- Model methods should be private :dfn:`prefixed with an underscore` to keep them hidden from
8079
the :doc:`external API <../../reference/external_api>`.
8180
- Numeric field values default to `0` when not explicitly set.
8281
- A compute method can depend on another computed field.
83-
- Field values for related models can be accessed via the `Many2one`, `One2many`, or
82+
- Field values for related models can be accessed via their `Many2one`, `One2many`, or
8483
`Many2many` field.
8584
- Variables used for relational field values are typically not suffixed with `_id` or `_ids`.
8685
While the field itself represents the stored ID(s), the variable holds the corresponding
@@ -93,7 +92,7 @@ that computed fields remain consistent.
9392
- :ref:`Coding guidelines on naming and ordering the members of model classes
9493
<contributing/coding_guidelines/model_members>`
9594

96-
Our real estate models can benefit from several computed fields to automate common calculations.
95+
Our real estate models could benefit from several computed fields to automate common calculations.
9796
Let's implement them.
9897

9998
.. exercise::
@@ -576,23 +575,130 @@ the :code:`@api.onchange()` decorator. These methods are triggered when the spec
576575
are altered. They operate on the in-memory representation of a single-record recordset received
577576
through `self`. If field values are modified, the changes are automatically reflected in the UI.
578577

579-
.. todo: explain the env (self.env.uid, self.env.user, self.env.ref(xml_id), self.env[model_name])
578+
.. example::
579+
In the following example, onchange methods are implemented to:
580+
581+
- unpublish products when all sellers are removed;
582+
- warn the user if changing the sales price would result in a negative margin;
583+
- raise a blocking user error if the category is changed after sales have been made.
584+
585+
.. code-block:: python
586+
587+
from odoo import _, api, fields, models
588+
from odoo.exceptions import UserError
589+
590+
591+
class Product(models.Model):
592+
is_published = fields.Boolean(string="Published")
593+
594+
@api.onchange('seller_ids')
595+
def _onchange_seller_ids_unpublish_if_no_sellers(self):
596+
if not self.seller_ids:
597+
self.is_published = False
598+
599+
@api.onchange('price')
600+
def _onchange_price_warn_if_margin_is_negative(self):
601+
if self.margin < 0:
602+
return {
603+
'warning': {
604+
'title': _("Warning"),
605+
'message': _(
606+
"The sales price was changed from %(before_price)s to %(new_price)s, which"
607+
" would result in a negative margin. A sales price of minimum %(min_price)s"
608+
" is recommended.",
609+
before_price=self._origin.price, new_price=self.price, min_price=self.cost,
610+
),
611+
}
612+
}
613+
614+
@api.onchange('category_id')
615+
def _onchange_category_id_block_if_existing_sales(self):
616+
existing_sales = self.env['sales.order'].search([('product_id', '=', self._origin.id)])
617+
if existing_sales:
618+
raise UserError(_(
619+
"You cannot change the category of a product that has already been sold; unpublish"
620+
" it instead."
621+
))
622+
623+
.. note::
624+
- It is recommended to give self-explanatory names to onchange methods as multiple onchange
625+
methods can be defined for a single field.
626+
- Onchange methods don't need to iterate over the records as `self` is always a recordset of
627+
length 1.
628+
- The :code:`_()` function from the `odoo` package marks display strings :dfn:`strings shown
629+
to the user and denoted with double quotes` for translation.
630+
- Regular string interpolation isn't possible withing the translation function. Instead,
631+
values to interpolate are passed as either positional arguments when using the :code:`%s`
632+
format, or as keyword arguments when using the :code:`%(name)s` format.
633+
- The `_origin` model attribute refers to the original record before user modifications.
634+
- The `env` model attribute is an object that allows access to other models and their classes.
635+
- The `search` method can be used to query a model for records matching a given search domain.
636+
- In onchanges methods, the ` 10000 id` attribute cannot be used to directly access the record's ID.
637+
- Blocking user errors are raised as exceptions.
580638

581639
.. seealso::
582640
- Reference documentation for the :meth:`@api.onchange() <odoo.api.onchange>` decorator
641+
- :doc:`How-to guide on translations </developer/howtos/translations>`
583642
- Reference documentation for the :class:`UserError <odoo.exceptions.UserError>` exception
643+
- :ref:`Reference documentation for the environment object <reference/orm/environment>`
644+
- Reference documentation for the :meth:`search <odoo.models.Model.search>` method
584645

585-
.. todo: tip ref translation
586-
587-
.. todo: raise UserError + translation
588-
.. todo: note: mention that the method is public so it can be called directly by the client.
589-
always return something in public methods as they are part of the :ref:external API and can be called through XML-RPC
646+
In our real estate app, data entry could be more intuitive and efficient. Let's use onchange methods
647+
to automate updates and guide users as they edit data.
590648

591649
.. exercise::
592-
tmp
650+
#. Set the garden area to zero if the garden checkbox is unchecked.
651+
#. Set the garden checkbox to checked if the garden area is set.
652+
#. Display a non-blocking warning if the garden area is set to zero and the garden checkbox is
653+
unchecked.
654+
#. Prevent archiving a property that has **pending** offers.
655+
656+
.. spoiler:: Solution
657+
658+
.. code-block:: python
659+
:caption: `real_estate_property.py`
660+
:emphasize-lines: 1-2, 9-40
661+
662+
from odoo import _, api, fields, models
663+
from odoo.exceptions import UserError
664+
from odoo.tools import date_utils
593665
594-
.. todo: if garden unchecked -> set garden area to zero
595-
.. todo: if write in garden area -> set garden checked
666+
667+
class RealEstateProperty(models.Model):
668+
[...]
669+
670+
@api.onchange('active')
671+
def _onchange_active_block_if_existing_offers(self):
672+
if not self.active:
673+
existing_offers = self.env['real.estate.offer'].search(
674+
[('property_id', '=', self._origin.id), ('state', '=', 'waiting')]
675+
)
676+
if existing_offers:
677+
raise UserError(
678+
_("You cannot change the active state of a property that has pending offers.")
679+
)
680+
681+
@api.onchange('has_garden')
682+
def _onchange_has_garden_set_garden_area_to_zero_if_unchecked(self):
683+
if not self.has_garden:
684+
self.garden_area = 0
685+
686+
@api.onchange('garden_area')
687+
def _onchange_garden_area_uncheck_garden_if_zero(self):
688+
if self.garden_area and not self.has_garden:
689+
self.has_garden = True
690+
691+
@api.onchange('garden_area')
692+
def _onchange_garden_area_display_warning_if_zero_and_unchecked(self):
693+
if not self.garden_area and self.has_garden:
694+
return {
695+
'warning': {
696+
'title': _("Warning"),
697+
'message': _(
698+
"The garden area was set to zero, but the garden checkbox is checked."
699+
),
700+
}
701+
}
596702
597703
.. _tutorials/server_framework_101/constraints:
598704

@@ -692,6 +798,9 @@ buttons can be of type **action**, defined in XML, or **object**, implemented in
692798
Together, these types of buttons facilitate the integration of user interactions with business
693799
logic.
694800

801+
.. todo: note: mention that the method is public so it can be called directly by the client.
802+
always return something in public methods as they are part of the :ref:external API and can be called through XML-RPC
803+
695804
.. todo: def create of offer -> write state of the property to offer received
696805
.. todo: def unlink: _unlink_if_state_is_valid (new or cancelled)
697806

0 commit comments

Comments
 (0)
0