You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
5.6 KiB
171 lines
5.6 KiB
# Copyright 2017-2018 Onestein (<http://www.onestein.eu>)
|
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
|
|
|
import logging
|
|
|
|
from odoo import SUPERUSER_ID
|
|
from odoo import _, api, models, modules, tools
|
|
from odoo.exceptions import UserError
|
|
|
|
from odoo.tools import (existing_tables, topological_sort)
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
@api.model
|
|
def _bi_view(_name):
|
|
return _name[0:6] == 'x_bve.'
|
|
|
|
|
|
def check_tables_exist(self, cr):
|
|
"""
|
|
Verify that all tables are present and try to initialize
|
|
those that are missing.
|
|
"""
|
|
# This monkey patch is meant to avoid that the _logger writes
|
|
# warning and error messages, while running an update all,
|
|
# in case the model is a bi-view-generated model.
|
|
|
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
|
table2model = {
|
|
model._table: name for name, model in env.items()
|
|
if not model._abstract and not _bi_view(name) # here is the patch
|
|
}
|
|
missing_tables = set(table2model).difference(
|
|
existing_tables(cr, table2model))
|
|
|
|
if missing_tables:
|
|
missing = {table2model[table] for table in missing_tables}
|
|
_logger.warning("Models have no table: %s.", ", ".join(missing))
|
|
# recreate missing tables following model dependencies
|
|
deps = {name: model._depends for name, model in env.items()}
|
|
for name in topological_sort(deps):
|
|
if name in missing:
|
|
_logger.info("Recreate table of model %s.", name)
|
|
env[name].init()
|
|
# check again, and log errors if tables are still missing
|
|
missing_tables = set(table2model).difference(
|
|
existing_tables(cr, table2model))
|
|
for table in missing_tables:
|
|
_logger.error("Model %s has no table.", table2model[table])
|
|
|
|
|
|
modules.registry.Registry.check_tables_exist = check_tables_exist
|
|
|
|
|
|
@api.model_cr_context
|
|
def _auto_init(self):
|
|
""" Initialize the database schema of ``self``:
|
|
- create the corresponding table,
|
|
- create/update the necessary columns/tables for fields,
|
|
- initialize new columns on existing rows,
|
|
- add the SQL constraints given on the model,
|
|
- add the indexes on indexed fields,
|
|
|
|
Also prepare post-init stuff to:
|
|
- add foreign key constraints,
|
|
- reflect models, fields, relations and constraints,
|
|
- mark fields to recompute on existing records.
|
|
|
|
Note: you should not override this method. Instead, you can modify
|
|
the model's database schema by overriding method :meth:`~.init`,
|
|
which is called right after this one.
|
|
"""
|
|
# This monkey patch is meant to fix an error (probably
|
|
# introduced by https://github.com/odoo/odoo/pull/15412), while
|
|
# running an update all. The _auto_init() method invoked during
|
|
# an update all is the one of BaseModel, and not the one of Base.
|
|
|
|
# START OF patch
|
|
if _bi_view(self._name):
|
|
return
|
|
# END of patch
|
|
|
|
models.raise_on_invalid_object_name(self._name)
|
|
|
|
# This prevents anything called by this method (in particular default
|
|
# values) from prefetching a field for which the corresponding column
|
|
# has not been added in database yet!
|
|
self = self.with_context(prefetch_fields=False)
|
|
|
|
self.pool.post_init(self._reflect)
|
|
|
|
cr = self._cr
|
|
parent_store_compute = False
|
|
update_custom_fields = self._context.get('update_custom_fields', False)
|
|
must_create_table = not tools.table_exists(cr, self._table)
|
|
|
|
if self._auto:
|
|
if must_create_table:
|
|
tools.create_model_table(cr, self._table, self._description)
|
|
|
|
if self._parent_store:
|
|
if not tools.column_exists(cr, self._table, 'parent_left'):
|
|
self._create_parent_columns()
|
|
parent_store_compute = True
|
|
|
|
self._check_removed_columns(log=False)
|
|
|
|
# update the database schema for fields
|
|
columns = tools.table_columns(cr, self._table)
|
|
|
|
def recompute(field):
|
|
_logger.info("Storing computed values of %s", field)
|
|
recs = self.with_context(active_test=False).search([])
|
|
recs._recompute_todo(field)
|
|
|
|
for field in self._fields.values():
|
|
if not field.store:
|
|
continue
|
|
|
|
if field.manual and not update_custom_fields:
|
|
continue # don't update custom fields
|
|
|
|
new = field.update_db(self, columns)
|
|
if new and field.compute:
|
|
self.pool.post_init(recompute, field)
|
|
|
|
if self._auto:
|
|
self._add_sql_constraints()
|
|
|
|
if must_create_table:
|
|
self._execute_sql()
|
|
|
|
if parent_store_compute:
|
|
self._parent_store_compute()
|
|
|
|
|
|
models.BaseModel._auto_init = _auto_init
|
|
|
|
|
|
class Base(models.AbstractModel):
|
|
_inherit = 'base'
|
|
|
|
@api.model
|
|
def _auto_init(self):
|
|
if not _bi_view(self._name):
|
|
super(Base, self)._auto_init()
|
|
|
|
@api.model
|
|
def _setup_complete(self):
|
|
if not _bi_view(self._name):
|
|
super(Base, self)._setup_complete()
|
|
else:
|
|
self.pool.models[self._name]._log_access = False
|
|
|
|
@api.model
|
|
def _read_group_process_groupby(self, gb, query):
|
|
if not _bi_view(self._name):
|
|
return super(Base, self)._read_group_process_groupby(gb, query)
|
|
|
|
split = gb.split(':')
|
|
if split[0] not in self._fields:
|
|
raise UserError(
|
|
_('No data to be displayed.'))
|
|
return super(Base, self)._read_group_process_groupby(gb, query)
|
|
|
|
@api.model
|
|
def _add_magic_fields(self):
|
|
if _bi_view(self._name):
|
|
self._log_access = False
|
|
return super(Base, self)._add_magic_fields()
|