[IMP] enable loading custom models/fields and views from module data files
Loading views for custom models from module data files was not possible because custom models and fields were introduced into the registry after all modules were loaded. As a consequence, the view architecture did not pass the checks. This patch takes a different approach: custom models and fields are loaded early on in the registry, so that views can be validated. The trick is to take special care of relational custom fields: we skip them if their comodel does not appear in the registry. This allows to install and upgrade modules that create/modify custom models, fields and views for them.
This commit is contained in:
parent
b0ae5eac57
commit
8f38a7806a
|
@ -446,6 +446,7 @@ class ir_model_fields(osv.osv):
|
|||
|
||||
for item in self.browse(cr, user, ids, context=context):
|
||||
obj = self.pool.get(item.model)
|
||||
field = getattr(obj, '_fields', {}).get(item.name)
|
||||
|
||||
if item.state != 'manual':
|
||||
raise except_orm(_('Error!'),
|
||||
|
@ -481,12 +482,12 @@ class ir_model_fields(osv.osv):
|
|||
|
||||
# We don't check the 'state', because it might come from the context
|
||||
# (thus be set for multiple fields) and will be ignored anyway.
|
||||
if obj is not None:
|
||||
if obj is not None and field is not None:
|
||||
# find out which properties (per model) we need to update
|
||||
for field_name, prop_name, func in model_props:
|
||||
if field_name in vals:
|
||||
prop_value = func(vals[field_name])
|
||||
if getattr(obj._fields[item.name], prop_name) != prop_value:
|
||||
if getattr(field, prop_name) != prop_value:
|
||||
patches[obj][final_name][prop_name] = prop_value
|
||||
|
||||
# These shall never be written (modified)
|
||||
|
|
|
@ -705,7 +705,7 @@ class BaseModel(object):
|
|||
pool._store_function[model].sort(key=lambda x: x[4])
|
||||
|
||||
@classmethod
|
||||
def _init_manual_fields(cls, cr):
|
||||
def _init_manual_fields(cls, cr, partial=False):
|
||||
# Check whether the query is already done
|
||||
if cls.pool.fields_by_model is not None:
|
||||
manual_fields = cls.pool.fields_by_model.get(cls._name, [])
|
||||
|
@ -729,14 +729,20 @@ class BaseModel(object):
|
|||
elif field['ttype'] in ('selection', 'reference'):
|
||||
attrs['selection'] = eval(field['selection'])
|
||||
elif field['ttype'] == 'many2one':
|
||||
if partial and field['relation'] not in cls.pool:
|
||||
continue
|
||||
attrs['comodel_name'] = field['relation']
|
||||
attrs['ondelete'] = field['on_delete']
|
||||
attrs['domain'] = eval(field['domain']) if field['domain'] else None
|
||||
elif field['ttype'] == 'one2many':
|
||||
if partial and field['relation'] not in cls.pool:
|
||||
continue
|
||||
attrs['comodel_name'] = field['relation']
|
||||
attrs['inverse_name'] = field['relation_field']
|
||||
attrs['domain'] = eval(field['domain']) if field['domain'] else None
|
||||
elif field['ttype'] == 'many2many':
|
||||
if partial and field['relation'] not in cls.pool:
|
||||
continue
|
||||
attrs['comodel_name'] = field['relation']
|
||||
_rel1 = field['relation'].replace('.', '_')
|
||||
_rel2 = field['model'].replace('.', '_')
|
||||
|
@ -2939,8 +2945,11 @@ class BaseModel(object):
|
|||
field.reset()
|
||||
|
||||
@api.model
|
||||
def _setup_fields(self):
|
||||
""" Setup the fields (dependency triggers, etc). """
|
||||
def _setup_fields(self, partial=False):
|
||||
""" Setup the fields (dependency triggers, etc).
|
||||
|
||||
:param partial: ``True`` if all models have not been loaded yet.
|
||||
"""
|
||||
cls = type(self)
|
||||
if cls._setup_done:
|
||||
return
|
||||
|
@ -2951,8 +2960,7 @@ class BaseModel(object):
|
|||
self.env[parent]._setup_fields()
|
||||
|
||||
# retrieve custom fields
|
||||
if not self._context.get('_setup_fields_partial'):
|
||||
cls._init_manual_fields(self._cr)
|
||||
cls._init_manual_fields(self._cr, partial=partial)
|
||||
|
||||
# retrieve inherited fields
|
||||
cls._inherits_check()
|
||||
|
|
|
@ -360,10 +360,6 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
|||
['to install'], force, status, report,
|
||||
loaded_modules, update_module)
|
||||
|
||||
# load custom models
|
||||
cr.execute('select model from ir_model where state=%s', ('manual',))
|
||||
for model in cr.dictfetchall():
|
||||
registry['ir.model'].instanciate(cr, SUPERUSER_ID, model['model'], {})
|
||||
registry.setup_models(cr)
|
||||
|
||||
# STEP 4: Finish and cleanup installations
|
||||
|
|
|
@ -158,15 +158,20 @@ class Registry(Mapping):
|
|||
|
||||
:param partial: ``True`` if all models have not been loaded yet.
|
||||
"""
|
||||
# load custom models
|
||||
ir_model = self['ir.model']
|
||||
cr.execute('select model from ir_model where state=%s', ('manual',))
|
||||
for (model_name,) in cr.fetchall():
|
||||
ir_model.instanciate(cr, SUPERUSER_ID, model_name, {})
|
||||
|
||||
# prepare the setup on all models
|
||||
for model in self.models.itervalues():
|
||||
model._prepare_setup_fields(cr, SUPERUSER_ID)
|
||||
|
||||
# do the actual setup from a clean state
|
||||
self._m2m = {}
|
||||
context = {'_setup_fields_partial': partial}
|
||||
for model in self.models.itervalues():
|
||||
model._setup_fields(cr, SUPERUSER_ID, context=context)
|
||||
model._setup_fields(cr, SUPERUSER_ID, partial=partial)
|
||||
|
||||
def clear_caches(self):
|
||||
""" Clear the caches
|
||||
|
|
Loading…
Reference in New Issue