Commit Graph
454 Commits
Author SHA1 Message Date
Rémy Voet (ryv) 1cfc896f2c [IMP] core: add Json field
To be able to create easily a jsonb column in database,
we create a Json type Field. Currently, it is quite limited
field:

- We cannot modified the value in-place, we need to always set
the entire jsonify value.
- No domain operator is done to work with jsonb. Now, it works as a
text field.

closes odoo/odoo#103097

X-original-commit: 7eeba9d205d2dace571b5d0895ddba6290a512db
Related: odoo/enterprise#32729
Signed-off-by: Quentin De Paoli <qdp@odoo.com>
2022-10-17 10:11:09 +02:00
Chong Wang (cwg) 7718fe9508 [FIX] core: assert translated fields cannot have size
Translated fields are stored as jsonb columns. Since jsonb columns don't have
size, the size attribute for Char field doesn't work. Setting the size attribute
for a Char fild should raise an error

closes odoo/odoo#103031

X-original-commit: 763fbb1c68385f202f5118fac5bc8b23b8760220
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-10-11 14:10:16 +02:00
std-odoo 499f25759b [IMP] web: allow to add the properties field in a kanban view
Purpose
=======
Allow to add the properties field in the kanban view.

An option has been added in the property definition, "View In Kanban",
to decide which field must be visible in the kanban view.

We need an option because in practice we might have a lot of
properties, and it might break the view.

Task-2980121

X-original-commit: 3af5a59c23183dd43341c1952a04354acbf3a60d
Part-of: odoo/odoo#102243
2022-10-06 00:37:10 +02:00
std-odoo 6823aa5f99 [IMP] base: improve the performance of properties when used in batch
Purpose
=======
Check the existence of the relational properties (many2one / many2many)
in batch and prefetch the values in batch as well to reduce the number
of SQL queries.

Technical
=========
The existence is checked in the read method of the properties field,
because we have the entire recordset. Then, the non-existing ids are
remove from the properties values and the cache is updated.

Task-2965523

X-original-commit: dba9b684d29c0041a32a5508284573851b8dd097
Part-of: odoo/odoo#102243
2022-10-06 00:37:09 +02:00
std-odoo 608bffff59 [FIX] base: fix the default properties value
Bug
===
If we re-write the same definition record, the default values were
applied again (even if the definition record didn't change).

This is because the compute on the properties is called even if the
definition record didn't change.

Task-2965523

X-original-commit: 70d80771f2430f83c91e7ef46eeebde8de70c3fe
Part-of: odoo/odoo#101487
2022-09-28 19:31:53 +02:00
std-odoo 3503dd904e [IMP] base: remove code redundacy in the properties fields
Purpose
=======
Remove the code redundacy between
convert_to_column and convert_to_cache.

Task-2965523

X-original-commit: bbd307d7939319393e3b1782c7200f0010c8c018
Part-of: odoo/odoo#101487
2022-09-28 19:31:52 +02:00
std-odoo 1b4c5e92ff [IMP] base: log a message each time a definition is modified
Purpose
=======
Log a message each time a user change a properties definition
by writing on the record properties values.

Task-2965523

X-original-commit: 6fa6fcd13e248d4bdf1febb0d8399cfe634037f5
Part-of: odoo/odoo#101487
2022-09-28 19:31:52 +02:00
std-odoo a6e7a560ef [IMP] base, web: improve the properties behavior when a record is not accessible
Purpose
=======
Improve the behavior / visual of the properties field when the user does
not have access to some relational properties (many2many, many2one).

When the user does not have access to one many2many value,
show the others record in the list.

Fix a traceback when the model is not set on the definition.

Task-2965523

X-original-commit: cb8b751a335b01192aab185cd0230fa0f036b974
Part-of: odoo/odoo#101487
2022-09-28 19:31:51 +02:00
std-odoo f5c7555d1b [IMP] web, base: shorten generated property names
Purpose
=======
Shorten the property name to take less space in database (while keeping
a very low probability of collision).

E.G.: "aa34746a6851ee4ea1f8d95746e45788" -> "aa34746a6851ee4e"

Automatically generate property name in python if they are missing.

Task-2965523

X-original-commit: d597083ff5e0a4abde67a450a61e04e6bf119c26
Part-of: odoo/odoo#101487
2022-09-28 19:31:51 +02:00
std-odoo 7a135ff585 [IMP] base: prefetch the properties definition
Purpose
=======
Most of the time we will need to read some field on the definition
record when we read the record itself (e.g. fetching some project
information on a task).

Because of this, it makes sense to prefetch by default the properties
definition, and it reduces the number of queries as well.

Task-2965523

X-original-commit: 1295e76f0dc3f3162a1b81a58ed1dc73d0fce3e7
Part-of: odoo/odoo#101487
2022-09-28 19:31:50 +02:00
ef00294e71 [IMP] core: store translated fields as JSONB columns
Translated fields no longer use the model ir.translation.  Instead they store
all their values as JSON, and store them into JSONB columns in the model's
table.  The field's column value is either NULL or a JSON dict mapping language
codes to text (the field's value in the corresponding language), and must
contain an entry for key 'en_US' (as it is used as a fallback for all other
languages).  Empty text is allowed in translation values, but not NULL.

Here are examples for a field with translate=True:

    NULL
    {"en_US": "Foo"}
    {"en_US": "Foo", "fr_FR": "Bar", "nl_NL": "Baz"}
    {"en_US": "Foo", "fr_FR": "", "nl_NL": "Baz"}

Like before, writing False to the field makes it NULL, i.e., False in all
languages.  However, writing "" to the field makes its value empty in the
current language, but does not discard the values in the other languages.

Here are examples for a field with translate=xml_translate:

    NULL
    {"en_US": "<div>Foo<p>Bar</p></div>", "fr_FR": "<div>Fou<p>Barre</p></div>"}

Change for callable(translate) fields: one can now write any value in any
language on such a field.  The new value will be adapted in all languages, based
on the mapping of terms between languages in the old values.  Basically the
structure of the value must remain the same in all languages, like before.

Reading a translated field is now both simpler and faster than the former
implementation.  We fetch the value of the field in the current language by
coalescing its value with the 'en_US' value of the field:

    SELECT id, COALESCE(name->>'fr_FR', name->>'en_US') AS name ...

The raw cache of the field contains either None or a dict which is conceptually
a subset of the JSON value in database (except for missing languages).  For the
sake of simplicity, most cache operations deal with the dict and return the text
value in the current language.

Trigram indexes have been adapted to the new storing strategy, and should enable
to search in any language.  Before this change, only the source value of the
field ('en_US') could be indexed.

Computed stored translated fields are not supported by the framework, because of
the complexity of the computation itself: the field would need to be computed in
all active languages.  We chose to not provide any hook to compute a field in
all languages at once, and the framework always invokes a compute method once to
recompute it.

Code translations are no longer stored into the database.  They become static,
and are extracted from the PO files when needed.  The worker simply uses a cache
with extracted code translations for performance.  This is reasonable, since
fr_FR code translations for all modules takes around 2MB of memory, and the
cache can be shared among all registries in the worker.  Changing code
translations requires to update the corresponding PO file and reloading the
worker(s).

Performance summary:
 (+) reading 'model' translated fields is faster
 (+) reading 'model_terms' translated fields is much faster (no need to inject
     translations into the source value)
 (+) searching translated fields with operator 'ilike' is much faster when the
     field is indexed with 'trigram'
 (+) updating translated fields requires less ORM flushing
 (-) importing translations from PO files is 2x slower

Some extra fixes:
 - make field 'name' of ir.actions.actions translated; because of the PG
   inheritance, this is necessary to make the column definition consistent in
   all models that inherit from ir.actions.actions.
 - add some backend API for the web/website client for editing translations
 - move methods get_field_string() to model ir.model.fields
 - move _load_module_terms to model ir.module.module
 - adapt tests in test_impex, test_new_api
 - because env.lang is injected into SQL queries, its returned value is
   now guaranteed to correspond to a valid active language or None
 - remove wizard to insert missing translations (no longer makes sense)

task-id: 2081307

Co-authored-by: Fabien Pinckaers <fp@openerp.com>
Co-authored-by: Raphael Collet <rco@odoo.com>
2022-09-15 22:37:50 +02:00
Raphael Collet 50767ef90e [IMP] core: simplify code for column conversion in fields 2022-09-15 22:30:56 +02:00
Rémy Voet (ryv) d982c4f319 [IMP] core: save one query when reading one2many on a single record
For reading one2many fields, we make 2 queries (in batch):
- One to search "id" the comodel
- One to read <inverse_field> in the comodel to group lines by record
(sometimes, this one is bypass because the cache already contains the information).

In many cases (> 80% of cases in our tests) we read a one2many field on
a single record.  In that case, we can avoid the second query completely
(grouping all lines on a single record is trivial).

This reduces SQL queries by about 0.8% on all-install runbot builds.

closes odoo/odoo#99415

Signed-off-by: Rémy Voet <ryv@odoo.com>
2022-09-02 23:16:13 +02:00
std-odoo 87307a9010 [IMP] base: add new "Properties" fields
Purpose
=======

Add a new field "Properties" to be able to light customization of workflows
based on a parent model. Those properties acts in some ways like Odoo fields
without requiring specific columns e.g. add new properties on tasks of a
specific project.

Usage
=====

Define properties on a parent model (e.g. project) with

```
    attributes_definition = fields.PropertiesDefinition('Message Properties')
```

It defines properties available on children: types, default, value, model for
relational properties,...

Use it on children records (e.g. task) with

```
    attributes = fields.Properties(
        string='Properties',
        definition='parent_id.attributes_definition',
    )
```

Technical
=========

Parent | Properties definition
------------------------------
The properties definition is stored on the parent, on a JSON field.
This definition contains the type of the properties, the default value,
the model of the many2one,...
```
[
    {
        'name': 'name',
        'string': 'Name',
        'type': 'char',
        'default': 'Default Name',
    }, {
        'name': 'partner_id',
        'string': 'Partner',
        'type': 'many2one',
        'comodel': 'res.partner',
    },
]
```

Child | Properties values
-------------------------
The value is stored on the child, using a Properties field.
```
{
    'name': 'Mitchel',
    'partner_id': 1337,
}
```

When we read this field, we will automatically read the definition on
the parent, and merge both JSON into one, so the web client has the
value of each property, and their definition.

```
[
    {
        'name': 'name',
        'string': 'Name',
        'type': 'char',
        'default': 'Default Name',
        'value': 'Mitchel',
    }, {
        'name': 'partner_id',
        'string': 'Partner',
        'type': 'many2one',
        'comodel': 'res.partner',
        'value': 1337,
    },
]
```

Integrity
---------
If we remove a property on the parent, we won't update the child value.

Instead, when we read the child properties, we will filter them based
on the parent. So the removed properties will be removed the next time
we write on the field.

In the same logic, the many2one existence is checked when we read the
field. There's no foreign key between the integer stored in the JSON
in the SQL row corresponding to the record in database.

Write
-----
We can write on the Properties field with a list of field definition
+ value.

Some types are not JSONifiable (like the date, datetime), they are
stored as string in database and parsed when we read the value.

In order to update the parent definition by writing on the child,
you need to add the dict key `definition_changed` or
`definition_deleted`. This is because we need to be able to know
if the definition has been changed without doing extra SQL queries.

Access rights
-------------
A user can add a many2one / many2many property to a model only if he
has the access rights to it.

Many2one / Many2many
--------------------

The model choice of a many2one / many2many properties was subject to
changes.

First implementation stored models in both parent and children to easily
spot changes and avoid complex queries when fetching records, trying to
synchronize them, ...

As this leads to storing a lot of duplicated content we choose to instead
reset the value on the child if the model has been change. We generate a
new name for the property. So it behaves like if we removed the property
and created a new one.

To be able to restore the old value (e.g. if by mistake we changed the
model, and go back to the old model), we store the initial states.

Task-2852259

Part-of: odoo/odoo#95184
2022-08-29 23:46:06 +02:00
Raphael Collet 6edcad51a5 [IMP] core: add warning when using non-searchable fields in depends
Part-of: odoo/odoo#98562
2022-08-29 22:43:11 +02:00
Romain Derie cf844e34dd [IMP] core: allow some users to bypass the sanitize of HTML field
Add the possibility to flag a HTML field as `sanitize_overridable`.
The sanitizer will then be bypassed if the user doing the operation is
part of the new group `base.group_sanitize_override`.
The "Settings" users are part of that new group.

If such a user wrote some HTML that would have normally been removed by
the sanitizer, then users without the right to bypass the sanitizer
won't be able to write on that field anymore.
Otherwise, it would sanitize the previously written data.
Such cases are detected, and the modification prevented by the system,
which will warn the user about it.

For instance, with a `field.html(sanitize_overridable=True)`: one being
part of `group_sanitize_override` could write `<script></script>`.
Then, someone not part of the group trying to add an element inside that
field like appending a `<p/>` -> `<script></script><p>New Content</p>`
would not be able to because it would go through the sanitizer and
ultimately, removing part of the original value:
`<p>New Content</p>` (`<script></script>` would be removed).

== Real use case ==
In the website builder, there is 2 editor rights:
- group_website_publisher: restricted editor
- group_website_designer: editor & designer

The designer editor can edit pages and views, while the restricted
editor can't do anything unless he is part of other groups.
In edit mode, the restricted editor will only be able to edit fields of
record he has access to.
For instance, being a sales manager allows you to edit a product
description on the website.
Being an event manager -> edit event. Slide manager -> Slides etc.
As those restricted editor are able to edit those fields, they are
(almost) always sanitized to prevent them to introduce malicious code.

Since those fields are sanitized, even admins / designer editor are not
able to fully use the website builder in such fields.

Some clients don't really care about that sanitation, they'd prefer to
avoid it as they trust their manager and would prefer to have the full
builder capability instead.
This is typically the case in small project (butcher, hairdresser,
reseller etc) and in SMEs.

With the new `sanitize_overridable` feature, they will be able to do
that, as the "Designer & Editor" group now also receive the group
`base.group_sanitize_override` (done in next commit).

Part-of: odoo/odoo#97398
2022-08-24 23:03:23 +02:00
Raphael ColletandVincent Schippefilt 384fda2c2a [REF] core: replace towrite by dirty flag in cache
Merging both the memory of field values and suspended updates has
several advantages:
 - avoid inconsistencies between cache and towrite
 - cache updates can be made safer w.r.t. dirty flag

However, the dirty flag in cache does not go well with context-dependent
fields.  When a context-dependent field is dirty in cache, the value to
store in the database is accessible through some context values.  But
when the model is flushed, the context values on the current environment
may be different.  When this happens, the method flush() fails to
retrieve the data to flush.

The proposed solution is to store the "dirty" value in cache under
conventional context values, and to retrieve them under the same
conventional context values to flush them.  For instance, when storing
the value of a binary field, it will be stored once under the context
value `context.get('bin_size')`, and a second time under the context
value `None`.  The flush implementation will then retrieve the value
using the context value `None`.

Translated fields are also problematic when a value is put in cache with
an environment where lang=False, and the value is retrieved with another
environment where lang=None.  This issue is addressed by normalizing the
context key 'lang' to None when the context value is False.

Part-of: odoo/odoo#95325
Co-authored-by: Vincent Schippefilt <vsc@odoo.com>
2022-07-14 22:45:44 +02:00
Raphael Collet a91cb08c5d [IMP] core: avoid flushing fields to read
The idea is to avoid flushing the fields to fetch in method _read().
This delays UPDATE queries, and makes the prefetching mechanism simpler
and more effective.

We do this by not overwriting the cache values by the values fetched
from database.  This simple idea allows to fetch more fields and more
records without having to care about pending computations and updates.
But it requires the cache consistency to be much more strict, because
nothing will "fix" the cache inconsistencies "by chance".  And it also
requires pending updates to be present in cache.

Part-of: odoo/odoo#66938
2022-07-05 11:35:00 +02:00
Raphael Collet 0728b78c68 [IMP] core: improve code of fields
Part-of: odoo/odoo#66938
2022-07-05 11:35:00 +02:00
Raphael Collet c0fea2cec8 [FIX] core: update inverse of many2many in order
Consider two models A and B, a many2many field F from A to B and its
inverse relation G from B to A.  When F is modified, G is updated
accordingly in cache.  The statement

    records.write({F: [Command.link(b.id)]})

is expected to add b.id to records.F, and add records.ids to b.G.  In
order to avoid nondeterminism, the additions should be done at the end
of the recordset, and in order.  In other words, if none of "records"
are in b.G, then after the statement above, b.G should be updated as

    b.G = b.G + records

This patch ensures that "records" are added in order.

closes odoo/odoo#94909

X-original-commit: b8fbc460673827497f6817e1751b5d365d8ae29a
Signed-off-by: Vincent Schippefilt (vsc) <vsc@odoo.com>
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-06-30 09:29:58 +02:00
Raphael Collet 32bc28aa66 [IMP] core: better API for flush() and invalidate()
This provides a new API for those operations, in order to make the
distinction between the use cases more explicit.  The former API was
using obscure parameter combinations to correspond to various cases.

In the summary below, `fnames` is an iterable of field names.  If the
parameter is not given, it means "all fields" in the given context.
Note that method recompute() is now mostly private, as it should not be
used in business code.

    # process pending computations and updates
    records.env.flush_all()                # all fields of all models
    records.flush_model(fnames)            # the fields of all records of the model
    records.flush_recordset(fnames)        # the fields of the given records

    # process pending computations, became non-public methods
    records.env._recompute_all()           # all fields of all models
    records._recompute_model(fnames)       # the fields of all records of the model
    records._recompute_recordset(fnames)   # the fields of the given records

    # invalidate the cache of fields
    records.env.invalidate_all()           # all fields of all models
    records.invalidate_model(fnames)       # the fields of all records of the model
    records.invalidate_recordset(fnames)   # the fields of the given records

Part-of: odoo/odoo#87527
2022-05-25 18:00:46 +02:00
Denis Ledoux b9feebc25c [REF] models, fields: refactor fields_get
- Filter in attributes rather than filter out.
  The goal is to not gather attributes which are
  costly in term of performance if they are not requested
  in the first place.
  e.g., with all modules installed:
  - Before rev:
    ```py
       In [1]: %time for _i in range(1000): self.env["res.partner"].fields_get(attributes=['readonly', 'required', 'states', 'invisible']);self.invalidate_cache()
        CPU times: user 1.99 s, sys: 9.83 ms, total: 2 s
        Wall time: 2.03 s
    ```
  - After rev:
    ```py
        In [2]: %time for _i in range(1000): self.env["res.partner"].fields_get(attributes=['readonly', 'required', 'states', 'invisible']);self.invalidate_cache()
        CPU times: user 345 ms, sys: 0 ns, total: 345 ms
        Wall time: 345 ms
    ```

- Use the `_description_` mechanism for the attributes `name` and `type`,
  so its no longer needed to treat them as exception in
  `field_get` and `get_description` respectively,
  and make the code shorter and cleaner.

- Move out from `fields_get` the block
  ```py
      has_access = functools.partial(self.check_access_rights, raise_exception=False)
      readonly = not (has_access('write') or has_access('create'))
      ...
      if readonly:
         description['readonly'] = True
         description['states'] = {}
  ```
  because:
  - It meant you had a different behavior using `fields_get` or `get_description`
    for the keys `readonly` and `states`, meaning a field could be marked as `readonly`
    by `fields_get` but not by `get_description`, which is confusing.
  - It is actually used in only one place, the post-process of back-end views,
    which mark the fields readonly for the web client if you do not have
    the create or write access to their model.

closes odoo/odoo#87273

Signed-off-by: Denis Ledoux (dle) <dle@odoo.com>
2022-04-29 13:12:49 +02:00
Fabien Pinckaers e045e76e35 [IMP] base: reduce new DB size by ordering columns
Postgres is aligning columns to 4 or 8 bytes, depending on their type.
So, consecutive fixed-length columns of differing size will be padded
with empty bytes due to the alignment requirements.

Before this patch, columns where created in their definition order. Now,
they are ordered based on their size, in order to minimize the padding.

As an example, before each row uses 36 bytes (+24b header):

       attname   |  typname  | typlen
    -------------+-----------+--------
     id          | int4      |      4
     create_uid  | int4      |      4
     create_date | timestamp |      8
     write_uid   | int4      |      4    -> 4 bytes padding
     write_date  | timestamp |      8
     active      | bool      |      1    -> 3 bytes padding

After each row uses 32 bytes (4 bytes saved per row):

       attname   |  typname  | typlen
    -------------+-----------+--------
     id          | int4      |      4
     create_uid  | int4      |      4
     write_uid   | int4      |      4
     active      | bool      |      1    -> 3 bytes padding
     create_date | timestamp |      8
     write_date  | timestamp |      8

This saving scheme applies to all rows in all tables. We save between 4
and 8 bytes per row just on the usual create_uid, create_date,
write_uid, write_date.

closes odoo/odoo#87896

Signed-off-by: Raphael Collet <rco@odoo.com>
2022-04-05 17:17:27 +02:00
Giang Phạm f9e0880c82 [FIX] core: computed stored many2many field not computed at install
closes odoo/odoo#87553

X-original-commit: b54f78de307543efcea934206806f361eaac811a
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-03-30 14:19:52 +02:00
Raphael Collet b08676389c [FIX] fields: ondelete being None on many2many field
The issue is simply creating a related stored many2many field, which is
pretty easy to do with Studio.  When creating the field, Odoo crashes
with a traceback caused by ondelete being None on the field.

The source of the bug is the fact that the attribute field.ondelete is
set to a sensible default ('cascade') on non-related fields only.  The
fix consists in setting the default value on the attribute itself, so
that the setup of the field never falls on a case where field.ondelete
is unset.

closes odoo/odoo#86303

X-original-commit: d0e0d80806a1b63ac986e9bcbaf88f9fc5243319
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-03-11 20:36:12 +00:00
Rémy Voet (ryv) e1785e820a [IMP] base: add prefetching group feature
The field prefetching mechanism was poorly customizable.  Before this,
we could only tell if a field was prefetched with other fields or not at
all.  We have no way to inform the framework, like: "When I need data of
that field, prefetch these other fields, which are likely be used in the
same transaction".

From now on, the `prefetch` attribute is used as a grouping key for
prefetching fields.  When a field is fetched, all the fields with the
same value for `prefetch` are taken for prefetching.

For example, consider a small set of fields that are rarely used, except
for one flow A using them.  You want to prefetch those fields only in
the flow A, and you want to fetch them in a single query.  With the new
feature, simply set `prefetch=A` for some string `A` on those fields,
and they will be grouped for prefetching.

closes odoo/odoo#85220

Signed-off-by: Rémy Voet <ryv@odoo.com>
2022-03-03 11:03:55 +00:00
Pierre-Yves Dufays 354b9e8ed2 [IMP] web, base: export field res_partner.name by default in import-compatible
mode

Purpose:

Include the name of the partners so that:
- it can easily be updated
- it is easier for users to recognize who is who

To generalize this modification, a new attribute has been added to the model
field descriptor (odoo/fields.py): default_export_compatible.
Setting this value to True on a field of a model will force that field to be
included by default in the exportation when "import-compatible export" is
selected.

Specification:

If the user goes:
    Contacts > List view > Select Records > Action Export
	     >  I want to update data
The field name is selected by default

The fields selected by default for export are the columns of the list so that
what is exported by default is what the user sees on the screen.
The display name is one of the column but is not importable.
When the option "I want to update data" is selected, only field that are
compatible for importation are selected and then display name is no longer
selected.
With this modification, the name is selected instead.

Technical:

- a new attribute "default_export_compatible" has been added in odoo/fields
- the controller /web/export/get_fields that lists the fields available
for exportation has been modified to add a default_export attribute on each
returned field. This new attribute is set to True when import_compat is True
and default_export_compatible is True on a given field.
The client uses that information to force by default the field for exportation.

Task 2734222

closes odoo/odoo#83697

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
2022-02-28 16:33:50 +00:00
Raf Geens 731d0d85c2 [FIX] orm: string pattern for Reference wasn't correct
In reality the separator is a comma instead of a dot as can be seen
in the code a bit further below.

closes odoo/odoo#85396

X-original-commit: 2a6e9f166d493aaefc006e949ab45e42311ac886
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2022-02-25 12:35:51 +00:00
Julien Castiaux e655e8da8a Revert "[IMP] base: add prefetching group feature"
This reverts commit 041fe5e21e.

Part-of: odoo/odoo#85199
2022-02-23 12:59:52 +00:00
Rémy Voet (ryv) 041fe5e21e [IMP] base: add prefetching group feature
The field prefetching mechanism was poorly customizable.  Before this,
we could only tell if a field was prefetched with other fields or not at
all.  We have no way to inform the framework, like: "When I need data of
that field, prefetch these other fields, which are likely be used in the
same transaction".

From now on, the `prefetch` attribute is used as a grouping key for
prefetching fields.  When a field is fetched, all the fields with the
same value for `prefetch` are taken for prefetching.

For example, consider a small set of fields that are rarely used, except
for one flow A using them.  You want to prefetch those fields only in
the flow A, and you want to fetch them in a single query.  With the new
feature, simply set `prefetch=A` for some string `A` on those fields,
and they will be grouped for prefetching.

Part-of: odoo/odoo#83818
2022-02-23 10:01:59 +00:00
Raphael Collet 39c1ab249b [FIX] core: discard redundant code
closes odoo/odoo#85028

X-original-commit: b026f17988dfaecac41f0a350cf8cfb52aa3827c
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-02-21 16:07:45 +00:00
Rémy Voet (ryv) 534087fa8d [REF] core: simplify tooling of browse()
Remove str from IdType, because those kinds of ids are no longer
necessary, and lead to unusable recordsets.

Because IdType now only contains int and NewId, simplify (and speed up)
the conversion of the parameter of browse().

Preferably wrap single NewId into a tuple instead of a list, as tuples
are faster.

closes odoo/odoo#83687

Signed-off-by: Raphael Collet <rco@odoo.com>
2022-02-21 10:28:15 +00:00
Rémy Voet (ryv) 6580fe0610 [REM] core: remove attribute 'limit' from X2many fields
Part-of: odoo/odoo#83687
2022-02-21 10:28:14 +00:00
Victor Feyens 65baadeb75 [IMP] core: do not try to translate selection values for nothing
When we are only trying to know accepted raw values, there is no
need to fetch the translations for the description part of the
selection tuples.

This performance problem was first observed on res.config.settings
record creation, where the values are pruned to avoid writes and
invalidation for unmodified related values (cf create override
in base/models/res_config.py & 5a4e7b711e).

After more analysis, the 'problem' also impacts record updates on
any related field (readonly or not).

* Res.config.settings performance gains:

In a database with test_main_flows (& its diverse dependencies),
this reduces by ~50% the number of queries done when triggering
the creation of res.config.settings record through the interface
(aka with all the related updatable fields receiving their current
value as create value).

For the creation of 150 res.config.settings records, with full cache
invalidation between each record creation, we notice a gain of:

* 11853 to 6303 total queries
* 11.2s to 7.5s total creation time

Part-of: odoo/odoo#83104
2022-02-08 11:07:46 +00:00
Victor Feyens 4d4108e3d1 [IMP] base: correctly prefetch currency fields on monetary updates
closes odoo/odoo#81423

Signed-off-by: Raphael Collet <rco@odoo.com>
2022-02-07 14:17:50 +00:00
a6f310fca3 [FIX] base: ignore tags in translations matching
When comparing existing translations to new source value
html tag would invalidate existing translations.
The html tags used for styling (coloring, italic, ...) should not
invalidate the translation as the meaning of the text as not changed.

Changed the logic to compare translations based on textual content only.

+ add some tests.

task-2667950

closes odoo/odoo#83895

X-original-commit: 1fe4b0cc1981382cc3d944ec44276f0aee90945d
Signed-off-by: Antoine Guenet <age@odoo.com>
Signed-off-by: Geelen Sébastien (sge) <sge@odoo.com>
Co-authored-by: rco-odoo <rco@odoo.com>
Co-authored-by: xmo-odoo <xmo@odoo.com>
2022-02-03 11:45:55 +00:00
Rémy Voet (ryv) 5fb6200f46 [REM] base: remove field attribute 'deprecated'
It was only used one time on a unused field.
We don't want to keep useless fields anymore,
the migration is done for that.

task-2735546
odoo/upgrade#3194

closes odoo/odoo#82727

Signed-off-by: Raphael Collet <rco@odoo.com>
2022-02-01 10:54:19 +00:00
Rémy Voet (ryv) ae41be0c5c [REM] base: remove column_format of Field class
The `column_format` of the `Field` class was unused and create useless
noise in the ORM. Then remove it and simplify some flows.

task-2735546

Part-of: odoo/odoo#82727
2022-02-01 10:54:18 +00:00
Raphael Collet a1904aa6f6 [IMP] core: field index names
The possible index names have been renamed "btree", "btree_not_null"
(instead of "not null") and "trigram" (instead of "gin").

Task 2742526

Part-of: odoo/odoo#83274
2022-01-28 14:10:01 +00:00
Rémy Voet (ryv) 5a573c6f18 [IMP] base: don't prefetch translate field by default
Issue
-----
Via the field prefetch mechanism, when we need a value of one field
(not in cache of course), the ORM will prefetch all fields
(which has the attribute to `prefetch=True`, the default value of this
attribute is `True`) for all record ids in `_prefetch_ids`.
Then, for each translate fields (where translate is not a callable)
the ORM need to make a `LEFT JOIN` on the `ir_translation` to fetch the
translated value. For big model, it leads to a simple `SELECT` with
several `LEFT JOIN` on ir_translation but each LEFT JOIN have a cost
in the planner time (a small cost in the execution time) of PostgreSQL.

By example, for `product.template` (stock/sale/purchase installed),
there are 6 LEFT JOIN to get all translated fields (5 of this
fields are rarely used).

Proposed solution
-----------------
Deactivate the prefetch by default for all translate fields expect if
this field is the `_rec_name` of the model (which is more likely to
be used).

In the example on the `product.template`:
Without prefetching the translated fields, there is only one LEFT JOIN
(the name, which is translated but is the `_rec_name` of the model).
With the 6 translated fields to fetch, the
query takes 5 ms to plan and 2 ms to execute VS with 1 translate field,
it 1 ms to plan and 1.5 ms to execute.

Side change note
----------------
- All translate of fields of `website.seo.metadata` should be prefetch
to avoid lot of website errors (it is because, website put in cache data
in sudo before reading it without sudo)
- `description` (`mail.message.subtype`), `subject` (`mail.template`),
`body_html` (`mail.template`) should be prefetch to avoid lot of extra
query from mail module.
- `vat_label` (`res.country`) should be prefetch to avoid a extra query
for each website page.
- Increase some queryCount (when it is legit, due to `subtitle` of
`blog_post` or `description` of `event.type.ticket`, etc)

task-2738029

closes odoo/odoo#82896

Signed-off-by: Raphael Collet <rco@odoo.com>
2022-01-28 14:09:56 +00:00
Fabien Pinckaers 6b87526048 [IMP] Speed Imp: remove unnecessary base64 encode & decode
Avoid to base64 encode, then decode to process assets and images for a ~25% speed improvement.
Change image processing tool to work on images, rather than base64 encoded strings.

Performance is ~25% faster on assets & images:

  /web/assets/...frontend.min.css:    13ms to 7ms,  base64 enc/dec: 2 -> 0
  /web/image/XML_ID:                  10ms to 8ms,  base64 enc/dec: 3 -> 0
  /web/image/res.users/2/avatar_128:  40ms to 20ms, base64 enc/dec: 6 -> 2

closes odoo/odoo#82851

Related: odoo/enterprise#23537
Signed-off-by: Fabien Pinckaers <fp@odoo.com>
2022-01-22 11:51:42 +00:00
Fabien Pinckaers eedf37d6e2 [IMP] Better handling of indexes
Three supported types:
- btree (default for index=True)
- btree not null (when >90% of the data are null)
- gin trigram search (for char fields)

Review of indexes on all objects.

closes odoo/odoo#83015

Signed-off-by: Fabien Pinckaers <fp@odoo.com>
2022-01-19 16:52:23 +00:00
Rémy Voet (ryv)andrco-odoo 504589efb2 [FIX] base: add __reversed__ in BaseModel for efficiency
Before when we do `reserved(records)`, it iterates in a reverse
order of `records`. Unfortunately, the `__reserved__` method don't
exist explicitly in BaseModel then Python fallback on
its own implementation using `__getitem__` and `__len__` (coming from Sequence): https://github.com/python/cpython/blob/3.10/Lib/_collections_abc.py#L1047-L1049
Because it uses __getitem__, it breaks the prefetch of the recordset.

Example:
-------------
```
partners = self.env['res.partner'].browse(1, 2, 3, 4, 5)
for partner in reversed(partners):
    partner.name
```
will generate 5 SQL requests to fetch data (one by record)

Then create our own `__reversed__` and handle the prefetch correctly
(like `__iter__`). Now in the example it will correctly generate only
1 SQL request because of the prefetch.

task-2687953

closes odoo/odoo#79622

Signed-off-by: Raphael Collet <rco@odoo.com>
Co-authored-by: rco-odoo <rco@odoo.com>
2022-01-12 10:17:36 +00:00
Rémy Voet (ryv) 8b7c8d0b71 [REF] core: replace _browse() by __init__()
The creation of recordset was done by class method _browse() instead of
a regular call to the model's class.  As we removed the old usage of
method __init__(), we can now reuse it with a normal usage. After this
commit, we can create a recordset by simply calling its class:

    registry['model_name'](env, ids, prefetch_ids)

closes odoo/odoo#79563

Signed-off-by: Raphael Collet <rco@odoo.com>
2021-12-24 14:56:58 +00:00
Raphael Collet 4347491670 [FIX] core: unexpected display_name "False"
If a form view contains the field 'display_name', when creating a new
record, the initial value of 'display_name' is the string "False", and
that weird value also appears in the breadcrumb instead of "New".  In
order to avoid this unexpected behavior, the conversion of a value to a
display name should be False, like any other field would.

closes odoo/odoo#81788

Signed-off-by: Raphael Collet <rco@odoo.com>
2021-12-22 09:39:08 +00:00
Xavier Morel bdc9d9d369 [FIX] core; base: lots of docstrings
* add configuration for `flake8[flake8-rst-docstring]`
* enable docstring-related checks
* fix invalid docstrings in odoo's core & `base`
* fix a few more bits (mostly missing or incorrect `:param:` info
  fields) are out of scope for the lint but my editor catches

closes odoo/odoo#74604

Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2021-12-09 14:36:58 +00:00
Yannick Tivisse 87224e158c [IMP] core: precompute docstring
Part-of: odoo/odoo#80687
2021-12-01 17:09:00 +00:00
d04a5b5c8c [REF] core: compute fields before database insertion.
Until now, stored compute fields were computed after database insertion.
This meant that required fields should not be computed, for instance,
unless some hackish code was added to make it work.  Another trick was
to provide some default value, but this actually prevents the field for
being computed after insertion.

This commit provides a field parameter to specify that the field should
be precomputed: adding precompute=True on the field definition force the
method create() to compute its value before inserting the new record in
the database.  For the reason explained below, precomputing fields is
not always correct, and therefore the default remains to not precompute
a field.

Some stored fields must be computed after insertion, for instance:
* statistics fields computed with search/read_group/...
* fields referencing the current record (res.partner.commercial_partner_id)
* fields referencing another record that does not exist yet (think about
  records created by one2many fields)
* fields depending on the create_date/write_date/create_uid/write_uid

Those fields shouldn't be defined with precompute=True, which triggers
their computation post record creation.  This is why, by safety, we
consider the default behavior to be precompute=False.

closes odoo/odoo#80449

Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
Co-authored-by: Raphael Collet <rco@odoo.com>
Co-authored-by: Yannick Tivisse <yti@odoo.com>
2021-11-30 14:30:48 +00:00
Raphael Collet aa0cdacced [FIX] core: related field attributes
A related field copies the attributes from its target field, except for
the attributes defined on the related field itself.  The implementation
of this feature was not working properly for attributes with a truthy
default value.

closes odoo/odoo#79025

X-original-commit: dec4a7ec478fa02f19dee8c8426c88d17dc3c7f3
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
2021-10-26 16:06:48 +00:00
Xavier-DoandRaphael Collet d4539c188a [IMP] core: use cache for mro in setup_models
With a lot of module installed setup_models can be quite slow.
One of the reason for that are the call to resolve_mro.

This operation is fast, but called for a lot of models/fields this
become an important part of get_depends and setup_base.

- This is call once for each models in setup_base
- This is call more than once for each computed_field in get_depends.

The use of a cache shows significative performance improvements.

During the install of a database with all modules, the install times
is reduced by ~3 minutes over ~14

Part-of: odoo/odoo#78898
Co-authored-by: Raphael Collet <rco@odoo.com>
2021-10-26 13:22:40 +00:00