diff --git a/addons/board/models/board.py b/addons/board/models/board.py index 738f9850e08..3d7a47f7574 100644 --- a/addons/board/models/board.py +++ b/addons/board/models/board.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import api, models +from odoo import api, fields, models class Board(models.AbstractModel): @@ -9,6 +9,12 @@ class Board(models.AbstractModel): _description = "Board" _auto = False + # This is necessary for when the web client opens a dashboard. Technically + # speaking, the dashboard is a form view, and opening it makes the client + # initialize a dummy record by invoking onchange(). And the latter requires + # an 'id' field to work properly... + id = fields.Id() + @api.model_create_multi def create(self, vals_list): return self diff --git a/odoo/addons/base/models/ir_model.py b/odoo/addons/base/models/ir_model.py index 5777683d53f..ae15a0a9e98 100644 --- a/odoo/addons/base/models/ir_model.py +++ b/odoo/addons/base/models/ir_model.py @@ -1023,6 +1023,8 @@ class IrModelFields(models.Model): model_id = self.env['ir.model']._get_id(model_name) for field in self.env[model_name]._fields.values(): rows.append(self._reflect_field_params(field, model_id)) + if not rows: + return cols = list(unique(['model', 'name'] + list(rows[0]))) expected = [tuple(row[col] for col in cols) for row in rows] diff --git a/odoo/addons/base/models/res_users.py b/odoo/addons/base/models/res_users.py index d53f7d94d1d..832dafdc6ad 100644 --- a/odoo/addons/base/models/res_users.py +++ b/odoo/addons/base/models/res_users.py @@ -1726,4 +1726,6 @@ class APIKeyDescription(models.TransientModel): class APIKeyShow(models.AbstractModel): _name = _description = 'res.users.apikeys.show' + # the field 'id' is necessary for the onchange that returns the value of 'key' + id = fields.Id() key = fields.Char(readonly=True) diff --git a/odoo/fields.py b/odoo/fields.py index fcca4a323fe..6072bafbe26 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -495,10 +495,15 @@ class Field(MetaField('DummyField', (object,), {})): # special cases of inherited fields if self.inherited: + self.inherited_field = field if not self.states: self.states = field.states if field.required: self.required = True + # add modules from delegate and target fields; the first one ensures + # that inherited fields introduced via an abstract model (_inherits + # being on the abstract model) are assigned an XML id + self._modules.update(model._fields[self.related[0]]._modules) self._modules.update(field._modules) if self._depends_context is not None: diff --git a/odoo/models.py b/odoo/models.py index e4a7df6b1b9..be9955de020 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -438,6 +438,9 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): >>> str(datetime.datetime.utcnow()) '2013-06-18 08:31:32.821177' """ + if self._abstract: + return + def add(name, field): """ add ``field`` with the given ``name`` if it does not exist yet """ if name not in self._fields: @@ -2534,6 +2537,8 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): return True def _check_removed_columns(self, log=False): + if self._abstract: + return # iterate on the database columns to drop the NOT NULL constraints of # fields which were required but have been removed (or will be added by # another module) @@ -2714,7 +2719,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): @api.model def _add_inherited_fields(self): """ Determine inherited fields. """ - if not self._inherits: + if self._abstract or not self._inherits: return # determine which fields can be inherited