[IMP] models: refactor _add_manual_fields to make it extendable

This commit is contained in:
Raphael Collet
2016-09-28 16:55:51 +02:00
committed by Géry Debongnie
parent 979c553918
commit 1c4661ee9d
2 changed files with 74 additions and 65 deletions
+68
View File
@@ -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):
"""
+6 -65
View File
@@ -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):