|
| 1 | +=========================== |
| 2 | +Chapter 5: Connect the dots |
| 3 | +=========================== |
| 4 | + |
| 5 | +In this chapter, we'll add business logic to the models to automate the processes of our application |
| 6 | +and turn it into a dynamic and useful tool. This will involve defining actions, constraints, |
| 7 | +automatic computations, and other model methods. |
| 8 | + |
| 9 | +.. todo: explain the env (self.env.uid, self.env.user, self.env.ref(xml_id), self.env[model_name]) |
| 10 | +.. todo: explain magic commands |
| 11 | +.. todo: 6,0,0 to associate tags to properties in data |
| 12 | +.. todo: create (create offer -> offer received state) and write methods |
| 13 | +.. todo: auto-update property state based on received offers state (write) |
| 14 | +.. todo: best offer stat button |
| 15 | +.. todo: accepting offer refuses others |
| 16 | +
|
| 17 | +.. _tutorials/server_framework_101/computed_fields: |
| 18 | + |
| 19 | +Computed fields |
| 20 | +=============== |
| 21 | + |
| 22 | +.. todo: change section title |
| 23 | +
|
| 24 | +**Computed fields** are a special type of field that derive their value through programmatic |
| 25 | +computation rather than direct storage. The computation is performed on-the-fly by the server |
| 26 | +whenever the field is accessed. This makes computed fields highly convenient for tasks like |
| 27 | +displaying the results of calculations directly in views, automating repetitive processes, or |
| 28 | +assisting users with data entry. |
| 29 | + |
| 30 | +In Odoo, computed fields are implemented by defining a specific Python method and linking it to a |
| 31 | +field declaration using the `compute` argument. This method operates on a **recordset** :dfn:`a |
| 32 | +collection of records from the same model` represented by the `self` argument of the compute method. |
| 33 | +The method must iterate over the records to compute and set the field's value for each record. |
| 34 | + |
| 35 | +.. todo: ref on recordsets |
| 36 | +.. todo: compute (offer deadline) |
| 37 | +.. todo: For relational fields, it’s possible to use paths through a field as a dependency: @api.depends('partner_id.name') |
| 38 | +.. todo: methods are private by default, meaning that they can't be called from the presentation |
| 39 | + tier, only from the business tier. See chap 1 |
| 40 | +.. todo: Although relational field names end with the `_id` or `_ids` suffix, variables holding a recordset of such fields |
| 41 | + are typically not suffixed. That is because, while the field represents the referenced record's id that is stored in the |
| 42 | + database, the variable is holding the full record in memory. |
| 43 | +
|
| 44 | +.. _tutorials/server_framework_101/inverse_computed_fields: |
| 45 | + |
| 46 | +Inverse computed fields |
| 47 | +----------------------- |
| 48 | + |
| 49 | +You might have noticed that computed fields are read-only by default. This is expected since the |
| 50 | +user is not supposed to set a value by themselves. |
| 51 | + |
| 52 | +.. todo: change section title |
| 53 | +
|
| 54 | +tmp |
| 55 | + |
| 56 | +.. todo: inverse (offer deadline) |
| 57 | +
|
| 58 | +.. _tutorials/server_framework_101/related_fields: |
| 59 | + |
| 60 | +Related fields |
| 61 | +-------------- |
| 62 | + |
| 63 | +.. todo: change section title |
| 64 | +
|
| 65 | +tmp |
| 66 | + |
| 67 | +.. todo: related fields (buyer's phone) |
| 68 | +
|
| 69 | +.. _tutorials/server_framework_101/stored_computed_fields: |
| 70 | + |
| 71 | +Stored computed fields |
| 72 | +---------------------- |
| 73 | + |
| 74 | +.. todo: change section title |
| 75 | +
|
| 76 | +When the computation depends on other fields, the method should be decorated with |
| 77 | +:code:`@api.depends()`, which specifies the fields that trigger an automatic recomputation whenever |
| 78 | +their value changes. |
| 79 | + |
| 80 | +.. todo: implement search method |
| 81 | +
|
| 82 | +.. _tutorials/server_framework_101/onchanges: |
| 83 | + |
| 84 | +Onchange methods |
| 85 | +================ |
| 86 | + |
| 87 | +.. todo: change section title |
| 88 | +
|
| 89 | +**Onchange methods** are a feature of the server framework designed to respond to changes in field |
| 90 | +values directly within the user interface. They are executed when a user modifies a field in a form |
| 91 | +view, even before saving the record to the database. This allows for real-time updates of other |
| 92 | +fields and provides immediate user feedback, such as blocking user errors, non-blocking warnings, or |
| 93 | +suggestions. However, because onchange methods are only triggered by changes made in the UI, |
| 94 | +specifically from a form view, they are best suited for assisting with data entry and providing |
| 95 | +feedback, rather than implementing core business logic in a module. |
| 96 | + |
| 97 | +In Odoo, onchange methods are implemented as Python methods and linked to one or more fields using |
| 98 | +the :code:`@api.onchange()` decorator. These methods are triggered when the specified fields' values |
| 99 | +are altered. They operate on the in-memory representation of a single-record recordset received |
| 100 | +through `self`. If field values are modified, the changes are automatically reflected in the UI. |
| 101 | + |
| 102 | +.. todo: raise UserError + translation |
| 103 | +.. todo: if garden checked -> show and compute total area |
| 104 | +
|
| 105 | +.. _tutorials/server_framework_101/constraints: |
| 106 | + |
| 107 | +Constraints |
| 108 | +=========== |
| 109 | + |
| 110 | +.. todo: change section title |
| 111 | +
|
| 112 | +- Constraints enforce rules on model data to maintain integrity and validity. |
| 113 | +- Two Types: |
| 114 | + - SQL constraints: Defined directly in the database schema and enforce data integrity at the database level. |
| 115 | + - Python constraints: Defined in the model logic and enforce dynamic or conditional rules based on business logic. |
| 116 | +- They ensure that data follows the expected structure and rules, preventing errors like duplicate |
| 117 | + entries, invalid relationships, or inconsistent values. |
| 118 | + |
| 119 | + |
| 120 | + |
| 121 | +.. _tutorials/server_framework_101/sql_constraints: |
| 122 | + |
| 123 | +SQL constraints |
| 124 | +--------------- |
| 125 | + |
| 126 | +tmp |
| 127 | + |
| 128 | +.. todo: price more than zero |
| 129 | +.. todo: unique tag constraint |
| 130 | +
|
| 131 | +.. _tutorials/server_framework_101/python_constraints: |
| 132 | + |
| 133 | +Python constraints |
| 134 | +------------------ |
| 135 | + |
| 136 | +tmp |
| 137 | + |
| 138 | +.. todo: accept only one offer |
| 139 | +
|
| 140 | +.. _tutorials/server_framework_101/defaults: |
| 141 | + |
| 142 | +Defaults |
| 143 | +======== |
| 144 | + |
| 145 | +.. todo: change section title |
| 146 | +.. todo: introduce lambda functions and fields.Date.today for defaults :point_down: |
| 147 | + also mention that `self` is evaluated as the current recordset in lambda functions |
| 148 | +
|
| 149 | +There is a problem with the way we defined our `Date` fields in the previous chapters: their default value relies on |
| 150 | +:code:`fields.Date.today()` or some other static method. When the code is loaded into memory, the date is |
| 151 | +computed once and reused for all newly created records until the server is shut down. You probably didn't |
| 152 | +notice it, unless you kept your server running for several days, but it would be much more visible with |
| 153 | +`Datetime` fields, as all newly created records would share the same timestamp. |
| 154 | + |
| 155 | +That's where lambda functions come in handy. As they generate an anonymous function each time they're evaluated |
| 156 | +at runtime, they can be used in the computation of default field values to return an updated value for each new record. |
| 157 | + |
| 158 | +.. todo: salesperson_id = fields.Many2one(default=lambda self: self.env.user) |
| 159 | +.. todo: real.estate.offer.amount::default -> property.selling_price (add related?) |
| 160 | +.. todo: real.estate.tag.color -> default=_default_color ; def _default_color(self): return random.randint(1, 11) (check if lambda works) |
| 161 | +.. todo: copy=False on some fields |
| 162 | +
|
| 163 | +.. _tutorials/server_framework_101/actions: |
| 164 | + |
| 165 | +Actions |
| 166 | +======= |
| 167 | + |
| 168 | +.. todo: change section title |
| 169 | +.. todo: "assign myself as salesperson" action |
| 170 | +.. todo: "view best offer" statbutton |
| 171 | +.. todo: accept/refuse offer buttons |
| 172 | +.. todo: action name=... |
| 173 | +
|
| 174 | +tmp |
| 175 | + |
| 176 | +.. _tutorials/server_framework_101/action_object: |
| 177 | + |
| 178 | +Object |
| 179 | +------ |
| 180 | + |
| 181 | +tmp |
| 182 | + |
| 183 | +.. _tutorials/server_framework_101/action_name: |
| 184 | + |
| 185 | +Name |
| 186 | +---- |
| 187 | + |
| 188 | +tmp |
| 189 | + |
| 190 | +.. _tutorials/server_framework_101/shell: |
| 191 | + |
| 192 | +Shell |
| 193 | +===== |
| 194 | + |
| 195 | +.. todo: change section title |
| 196 | +
|
| 197 | +tmp |
| 198 | + |
| 199 | +---- |
| 200 | + |
| 201 | +.. todo: add incentive for chapter 6 |
0 commit comments