diff --git a/odoo/addons/base/ir/ir_model.py b/odoo/addons/base/ir/ir_model.py index b57c8de4fd09889bd16a541911b1a560c1c740c8..79d191dce423b93bed4801cf212e9d38eddf74c0 100644 --- a/odoo/addons/base/ir/ir_model.py +++ b/odoo/addons/base/ir/ir_model.py @@ -1,6 +1,9 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import datetime +import dateutil import logging +import time from collections import defaultdict from odoo import api, fields, models, SUPERUSER_ID, tools, _ @@ -18,6 +21,20 @@ def encode(s): return s.encode('utf8') if isinstance(s, unicode) else s +# base environment for doing a safe_eval +SAFE_EVAL_BASE = { + 'datetime': datetime, + 'dateutil': dateutil, + 'time': time, +} + +def make_compute(text, deps): + """ Return a compute function from its code body and dependencies. """ + func = lambda self: safe_eval(text, SAFE_EVAL_BASE, {'self': self}, mode="exec") + deps = [arg.strip() for arg in (deps or "").split(",")] + return api.depends(*deps)(func) + + # # IMPORTANT: this must be the first model declared in the module # @@ -557,6 +574,57 @@ class IrModelFields(models.Model): res.append((field.id, '%s (%s)' % (field.field_description, field.model))) return res + @api.model + def _instanciate(self, field_data, partial): + """ Return a field instance corresponding to parameters ``field_data``. """ + attrs = { + 'manual': True, + 'string': field_data['field_description'], + 'help': field_data['help'], + 'index': bool(field_data['index']), + 'copy': bool(field_data['copy']), + 'related': field_data['related'], + 'required': bool(field_data['required']), + 'readonly': bool(field_data['readonly']), + 'store': bool(field_data['store']), + } + # FIXME: ignore field_data['serialization_field_id'] + if field_data['ttype'] in ('char', 'text', 'html'): + attrs['translate'] = bool(field_data['translate']) + attrs['size'] = field_data['size'] or None + elif field_data['ttype'] in ('selection', 'reference'): + attrs['selection'] = safe_eval(field_data['selection']) + elif field_data['ttype'] == 'many2one': + if partial and field_data['relation'] not in self.env: + return + attrs['comodel_name'] = field_data['relation'] + attrs['ondelete'] = field_data['on_delete'] + attrs['domain'] = safe_eval(field_data['domain']) if field_data['domain'] else None + elif field_data['ttype'] == 'one2many': + if partial and not ( + field_data['relation'] in self.env and ( + field_data['relation_field'] in self.env[field_data['relation']]._fields or + field_data['relation_field'] in self.pool.get_manual_fields(self._cr, field_data['relation']) + )): + return + attrs['comodel_name'] = field_data['relation'] + attrs['inverse_name'] = field_data['relation_field'] + attrs['domain'] = safe_eval(field_data['domain']) if field_data['domain'] else None + elif field_data['ttype'] == 'many2many': + if partial and field_data['relation'] not in self.env: + return + attrs['comodel_name'] = field_data['relation'] + rel, col1, col2 = self._custom_many2many_names(field_data['model'], field_data['relation']) + attrs['relation'] = field_data['relation_table'] or rel + attrs['column1'] = field_data['column1'] or col1 + attrs['column2'] = field_data['column2'] or col2 + attrs['domain'] = safe_eval(field_data['domain']) if field_data['domain'] else None + # add compute function if given + if field_data['compute']: + attrs['compute'] = make_compute(field_data['compute'], field_data['depends']) + + return fields.Field.by_type[field_data['ttype']](**attrs) + class IrModelConstraint(models.Model): """ diff --git a/odoo/models.py b/odoo/models.py index dff252d0d3e0a38e1581faee7f9a62fc8b1494f4..12c9becc6ac20f5621704858ff6c827f62ffe938 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -29,7 +29,6 @@ import logging import operator import pytz import re -import time from collections import defaultdict, MutableMapping, OrderedDict from inspect import getmembers, currentframe from operator import attrgetter, itemgetter @@ -65,20 +64,6 @@ onchange_v7 = re.compile(r"^(\w+)\((.*)\)$") AUTOINIT_RECALCULATE_STORED_FIELDS = 1000 -# base environment for doing a safe_eval -SAFE_EVAL_BASE = { - 'datetime': datetime, - 'dateutil': dateutil, - 'time': time, -} - -def make_compute(text, deps): - """ Return a compute function from its code body and dependencies. """ - func = lambda self: safe_eval(text, SAFE_EVAL_BASE, {'self': self}, mode="exec") - deps = [arg.strip() for arg in (deps or "").split(",")] - return api.depends(*deps)(func) - - def check_object_name(name): """ Check if the given name is a valid model name. @@ -646,57 +631,13 @@ class BaseModel(object): @api.model def _add_manual_fields(self, partial): + IrModelFields = self.env['ir.model.fields'] manual_fields = self.pool.get_manual_fields(self._cr, self._name) - - for name, field in manual_fields.iteritems(): - if name in self._fields: - continue - attrs = { - 'manual': True, - 'string': field['field_description'], - 'help': field['help'], - 'index': bool(field['index']), - 'copy': bool(field['copy']), - 'related': field['related'], - 'required': bool(field['required']), - 'readonly': bool(field['readonly']), - 'store': bool(field['store']), - } - # FIXME: ignore field['serialization_field_id'] - if field['ttype'] in ('char', 'text', 'html'): - attrs['translate'] = bool(field['translate']) - attrs['size'] = field['size'] or None - elif field['ttype'] in ('selection', 'reference'): - attrs['selection'] = safe_eval(field['selection']) - elif field['ttype'] == 'many2one': - if partial and field['relation'] not in self.env: - continue - attrs['comodel_name'] = field['relation'] - attrs['ondelete'] = field['on_delete'] - attrs['domain'] = safe_eval(field['domain']) if field['domain'] else None - elif field['ttype'] == 'one2many': - if partial and not ( - field['relation'] in self.env and ( - field['relation_field'] in self.env[field['relation']]._fields or - field['relation_field'] in self.pool.get_manual_fields(self._cr, field['relation']) - )): - continue - attrs['comodel_name'] = field['relation'] - attrs['inverse_name'] = field['relation_field'] - attrs['domain'] = safe_eval(field['domain']) if field['domain'] else None - elif field['ttype'] == 'many2many': - if partial and field['relation'] not in self.env: - continue - attrs['comodel_name'] = field['relation'] - rel, col1, col2 = self.env['ir.model.fields']._custom_many2many_names(field['model'], field['relation']) - attrs['relation'] = field['relation_table'] or rel - attrs['column1'] = field['column1'] or col1 - attrs['column2'] = field['column2'] or col2 - attrs['domain'] = safe_eval(field['domain']) if field['domain'] else None - # add compute function if given - if field['compute']: - attrs['compute'] = make_compute(field['compute'], field['depends']) - self._add_field(name, Field.by_type[field['ttype']](**attrs)) + for name, field_data in manual_fields.iteritems(): + if name not in self._fields: + field = IrModelFields._instanciate(field_data, partial) + if field: + self._add_field(name, field) @classmethod def _init_constraints_onchanges(cls):