Allow to edit default values for env-computed fields
When they don't have any key in the environment configuration files. In the UI, when a field is set in a configuration file, the field is readonly, if not the field is editable. Which means you can selectively choose which fields depend on the environment and which can use a "default" value stored in database.
This commit is contained in:
parent
ff91cc4a0f
commit
04bbfce412
|
|
@ -3,6 +3,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from functools import partialmethod
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
from ..serv_config import serv_config
|
from ..serv_config import serv_config
|
||||||
|
|
||||||
|
|
@ -26,7 +30,7 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
environment configuration for the field ``directory_path``.
|
environment configuration for the field ``directory_path``.
|
||||||
|
|
||||||
Under the hood, this mixin automatically replaces the original field
|
Under the hood, this mixin automatically replaces the original field
|
||||||
by a computed field that reads from the configuration files.
|
by an env-computed field that reads from the configuration files.
|
||||||
|
|
||||||
By default, it looks for the configuration in a section named
|
By default, it looks for the configuration in a section named
|
||||||
``[model_name.Record Name]`` where ``model_name`` is the ``_name`` of the
|
``[model_name.Record Name]`` where ``model_name`` is the ``_name`` of the
|
||||||
|
|
@ -38,6 +42,10 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
and the configuration files do not contain a key, the env-computed field
|
and the configuration files do not contain a key, the env-computed field
|
||||||
uses the default value stored in database. If a key is empty, the
|
uses the default value stored in database. If a key is empty, the
|
||||||
env-computed field has an empty value.
|
env-computed field has an empty value.
|
||||||
|
|
||||||
|
Env-computed fields are conditionally editable, based on the absence
|
||||||
|
of their key in environment configuration files. When edited, their
|
||||||
|
value is stored in the database.
|
||||||
"""
|
"""
|
||||||
_name = 'server.env.mixin'
|
_name = 'server.env.mixin'
|
||||||
|
|
||||||
|
|
@ -97,6 +105,11 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _compute_server_env(self):
|
def _compute_server_env(self):
|
||||||
|
"""Read values from environment configuration files
|
||||||
|
|
||||||
|
If an env-computed field has no key in configuration files,
|
||||||
|
read from the ``<field>_env_default`` field from database.
|
||||||
|
"""
|
||||||
for record in self:
|
for record in self:
|
||||||
for field_name, getter_name in self._server_env_fields.items():
|
for field_name, getter_name in self._server_env_fields.items():
|
||||||
section_name = self._server_env_section_name()
|
section_name = self._server_env_section_name()
|
||||||
|
|
@ -115,17 +128,114 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
|
|
||||||
record[field_name] = value
|
record[field_name] = value
|
||||||
|
|
||||||
|
def _inverse_server_env(self, field_name):
|
||||||
|
default_field = self._server_env_default_fieldname(field_name)
|
||||||
|
is_editable_field = self._server_env_is_editable_fieldname(field_name)
|
||||||
|
for record in self:
|
||||||
|
# when we write in an env-computed field, if it is editable
|
||||||
|
# we update the default value in database
|
||||||
|
if record[is_editable_field]:
|
||||||
|
record[default_field] = record[field_name]
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _compute_server_env_is_editable(self):
|
||||||
|
"""Compute <field>_is_editable values
|
||||||
|
|
||||||
|
We can edit an env-computed filed only if there is no key
|
||||||
|
in any environment configuration file. If there is an empty
|
||||||
|
key, it's an empty value so we can't edit the env-computed field.
|
||||||
|
"""
|
||||||
|
# we can't group it with _compute_server_env otherwise when called
|
||||||
|
# in ``_inverse_server_env`` it would reset the value of the field
|
||||||
|
for record in self:
|
||||||
|
for field_name in self._server_env_fields:
|
||||||
|
is_editable_field = self._server_env_is_editable_fieldname(
|
||||||
|
field_name
|
||||||
|
)
|
||||||
|
section_name = self._server_env_section_name()
|
||||||
|
is_editable = not (section_name in serv_config
|
||||||
|
and field_name in serv_config[section_name])
|
||||||
|
record[is_editable_field] = is_editable
|
||||||
|
|
||||||
|
def _server_env_view_set_readonly(self, view_arch):
|
||||||
|
field_xpath = './/field[@name="%s"]'
|
||||||
|
for field in self._server_env_fields:
|
||||||
|
is_editable_field = self._server_env_is_editable_fieldname(field)
|
||||||
|
for elem in view_arch.findall(field_xpath % field):
|
||||||
|
# set env-computed fields to readonly if the configuration
|
||||||
|
# files have a key
|
||||||
|
elem.set('attrs',
|
||||||
|
str({'readonly': [(is_editable_field, '=', False)]}))
|
||||||
|
if not view_arch.findall(field_xpath % is_editable_field):
|
||||||
|
# add the _is_editable fields in the view for the 'attrs'
|
||||||
|
# domain
|
||||||
|
view_arch.append(
|
||||||
|
etree.Element(
|
||||||
|
'field',
|
||||||
|
name=is_editable_field,
|
||||||
|
invisible="1"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return view_arch
|
||||||
|
|
||||||
|
def _fields_view_get(self, view_id=None, view_type='form', toolbar=False,
|
||||||
|
submenu=False):
|
||||||
|
view_data = super()._fields_view_get(
|
||||||
|
view_id=view_id, view_type=view_type,
|
||||||
|
toolbar=toolbar, submenu=submenu
|
||||||
|
)
|
||||||
|
view_arch = etree.fromstring(view_data['arch'].encode('utf-8'))
|
||||||
|
view_arch = self._server_env_view_set_readonly(view_arch)
|
||||||
|
view_data['arch'] = etree.tostring(view_arch, encoding='unicode')
|
||||||
|
return view_data
|
||||||
|
|
||||||
def _server_env_default_fieldname(self, base_field_name):
|
def _server_env_default_fieldname(self, base_field_name):
|
||||||
|
"""Return the name of the field with default value"""
|
||||||
return '%s_env_default' % (base_field_name,)
|
return '%s_env_default' % (base_field_name,)
|
||||||
|
|
||||||
|
def _server_env_is_editable_fieldname(self, base_field_name):
|
||||||
|
"""Return the name of the field for "is editable
|
||||||
|
|
||||||
|
This is the field used to tell if the env-computed field can
|
||||||
|
be edited.
|
||||||
|
"""
|
||||||
|
return '%s_env_is_editable' % (base_field_name,)
|
||||||
|
|
||||||
def _server_env_transform_field_to_read_from_env(self, field):
|
def _server_env_transform_field_to_read_from_env(self, field):
|
||||||
"""Transform the original field in a computed field"""
|
"""Transform the original field in a computed field"""
|
||||||
field.compute = '_compute_server_env'
|
field.compute = '_compute_server_env'
|
||||||
|
inverse_method_name = '_inverse_server_env_%s' % field.name
|
||||||
|
inverse_method = partialmethod(
|
||||||
|
ServerEnvMixin._inverse_server_env, field.name
|
||||||
|
)
|
||||||
|
setattr(ServerEnvMixin, inverse_method_name, inverse_method)
|
||||||
|
field.inverse = inverse_method_name
|
||||||
field.store = False
|
field.store = False
|
||||||
field.copy = False
|
field.copy = False
|
||||||
field.sparse = None
|
field.sparse = None
|
||||||
|
|
||||||
|
def _server_env_add_is_editable_field(self, base_field):
|
||||||
|
"""Add a field indicating if we can edit the env-computed fields
|
||||||
|
|
||||||
|
It is used in the inverse function of the env-computed field
|
||||||
|
and in the views to add 'readonly' on the fields.
|
||||||
|
"""
|
||||||
|
fieldname = self._server_env_is_editable_fieldname(base_field.name)
|
||||||
|
if fieldname not in self._fields:
|
||||||
|
field = fields.Boolean(
|
||||||
|
compute='_compute_server_env_is_editable',
|
||||||
|
automatic=True,
|
||||||
|
)
|
||||||
|
self._add_field(fieldname, field)
|
||||||
|
|
||||||
def _server_env_add_default_field(self, base_field):
|
def _server_env_add_default_field(self, base_field):
|
||||||
|
"""Add a field storing the default value
|
||||||
|
|
||||||
|
The default value is used when there is no key for an env-computed
|
||||||
|
field in the configuration files.
|
||||||
|
|
||||||
|
The field is stored in the serialized field ``server_env_defaults``.
|
||||||
|
"""
|
||||||
fieldname = self._server_env_default_fieldname(base_field.name)
|
fieldname = self._server_env_default_fieldname(base_field.name)
|
||||||
if fieldname not in self._fields:
|
if fieldname not in self._fields:
|
||||||
base_field_cls = base_field.__class__
|
base_field_cls = base_field.__class__
|
||||||
|
|
@ -144,4 +254,5 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
for fieldname in self._server_env_fields:
|
for fieldname in self._server_env_fields:
|
||||||
field = self._fields[fieldname]
|
field = self._fields[fieldname]
|
||||||
self._server_env_transform_field_to_read_from_env(field)
|
self._server_env_transform_field_to_read_from_env(field)
|
||||||
|
self._server_env_add_is_editable_field(field)
|
||||||
self._server_env_add_default_field(field)
|
self._server_env_add_default_field(field)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue