Add Model.__names__ to retrieve model and field names

issue8159
review72431002
This commit is contained in:
Cédric Krier committed 2019-03-15 12:27:44 +01:00
1 parent 23a1288d32
commit 10e31bc311
8 files changed
+99 -34

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
* Add Model.__names__ to retrieve model and field names
* Add size attribute on image tag
* Allow to use channel to synchronize Cache
* Display ids and rule names on access error
+6
View File
@@ -63,6 +63,12 @@ Class methods:
Return the definition of each field on the model.
.. classmethod:: Model.__names__([field])
Returns a dictionary with the name of the `model` and the `field`.
It is a convenience-method used to format messages which should include
those names.
Instance methods:
.. method:: Model.on_change(fieldnames)
+38 -4
View File
@@ -66,6 +66,7 @@ class Model(ModelSQL, ModelView):
global_search_p = fields.Boolean('Global Search')
fields = fields.One2Many('ir.model.field', 'model', 'Fields',
required=True)
_get_name_cache = Cache('ir.model.get_name')
@classmethod
def __setup__(cls):
@@ -186,6 +187,19 @@ class Model(ModelSQL, ModelView):
record.id, name, icon)
return heapq.nlargest(int(limit), generate())
@classmethod
def get_name(cls, model):
name = cls._get_name_cache.get(model)
if name is None:
models = cls.search([('model', '=', model)], limit=1)
if models:
model, = models
name = model.name
cls._get_name_cache.set(model, name)
else:
name = model
return name
class ModelField(ModelSQL, ModelView):
"Model field"
@@ -224,6 +238,7 @@ class ModelField(ModelSQL, ModelView):
depends=['module'])
module = fields.Char('Module',
help="Module in which this field is defined")
_get_name_cache = Cache('ir.model.field.get_name')
@classmethod
def __setup__(cls):
@@ -333,6 +348,22 @@ class ModelField(ModelSQL, ModelView):
('name',) + tuple(clause[1:]),
]
@classmethod
def get_name(cls, model, field):
name = cls._get_name_cache.get((model, field))
if name is None:
fields = cls.search([
('model.model', '=', model),
('name', '=', field),
], limit=1)
if fields:
field, = fields
name = field.field_description
cls._get_name_cache.set((model, field), name)
else:
name = field
return name
@classmethod
def read(cls, ids, fields_names):
pool = Pool()
@@ -511,6 +542,8 @@ class ModelAccess(DeactivableMixin, ModelSQL, ModelView):
@classmethod
def check(cls, model_name, mode='read', raise_exception=True):
'Check access for model_name and mode'
pool = Pool()
Model = pool.get(model_name)
assert mode in ['read', 'write', 'create', 'delete'], \
'Invalid access mode for security'
if ((Transaction().user == 0)
@@ -522,7 +555,7 @@ class ModelAccess(DeactivableMixin, ModelSQL, ModelView):
if not access and access is not None:
if raise_exception:
raise AccessError(gettext(
'ir.msg_access_rule_error', model=model_name))
'ir.msg_access_rule_error', **Model.__names__()))
else:
return False
return True
@@ -557,7 +590,7 @@ class ModelAccess(DeactivableMixin, ModelSQL, ModelView):
# XXX Can not check access right on instance method
selection = []
for model_name, _ in selection:
if not cls.check(model_name, mode=mode,
if model_name and not cls.check(model_name, mode=mode,
raise_exception=False):
return False
return True
@@ -692,6 +725,8 @@ class ModelFieldAccess(DeactivableMixin, ModelSQL, ModelView):
'''
Check access for fields on model_name.
'''
pool = Pool()
Model = pool.get(model_name)
assert mode in ('read', 'write', 'create', 'delete'), \
'Invalid access mode'
if ((Transaction().user == 0)
@@ -710,8 +745,7 @@ class ModelFieldAccess(DeactivableMixin, ModelSQL, ModelView):
if raise_exception:
raise AccessError(
gettext('ir.msg_access_rule_field_error',
model=model_name,
field=field))
**Model.__names__(field)))
else:
return False
return True
+12
View File
@@ -621,7 +621,11 @@ class Translation(ModelSQL, ModelView):
def delete(cls, translations):
pool = Pool()
Message = pool.get('ir.message')
Model = pool.get('ir.model')
ModelField = pool.get('ir.model.field')
Message._message_cache.clear()
Model._get_name_cache.clear()
ModelField._get_name_cache.clear()
cls._translation_cache.clear()
ModelView._fields_view_get_cache.clear()
return super(Translation, cls).delete(translations)
@@ -630,7 +634,11 @@ class Translation(ModelSQL, ModelView):
def create(cls, vlist):
pool = Pool()
Message = pool.get('ir.message')
Model = pool.get('ir.model')
ModelField = pool.get('ir.model.field')
Message._message_cache.clear()
Model._get_name_cache.clear()
ModelField._get_name_cache.clear()
cls._translation_cache.clear()
ModelView._fields_view_get_cache.clear()
vlist = [x.copy() for x in vlist]
@@ -645,7 +653,11 @@ class Translation(ModelSQL, ModelView):
def write(cls, *args):
pool = Pool()
Message = pool.get('ir.message')
Model = pool.get('ir.model')
ModelField = pool.get('ir.model.field')
Message._message_cache.clear()
Model._get_name_cache.clear()
ModelField._get_name_cache.clear()
cls._translation_cache.clear()
ModelView._fields_view_get_cache.clear()
return super(Translation, cls).write(*args)
+13
View File
@@ -203,6 +203,19 @@ class Model(URLMixin, PoolBase, metaclass=ModelMeta):
def pre_validate(self):
pass
@classmethod
def __names__(cls, field=None):
pool = Pool()
IrModel = pool.get('ir.model')
IrModelField = pool.get('ir.model.field')
names = {
'model': IrModel.get_name(cls.__name__),
}
if field:
names['field'] = IrModelField.get_name(cls.__name__, field)
return names
def __init__(self, id=None, **kwargs):
super(Model, self).__init__()
if id is not None:
+3 -3
View File
@@ -352,7 +352,7 @@ class ModelSQL(ModelStorage):
if values.get(field_name) is None:
raise RequiredValidationError(
gettext('ir.msg_required_validation_record',
**cls._get_error_args(field_name)))
**cls.__names__(field_name)))
for name, _, error in cls._sql_constraints:
if TableHandler.convert_name(name) in str(exception):
raise SQLConstraintError(gettext(error))
@@ -374,7 +374,7 @@ class ModelSQL(ModelStorage):
if not ((target_records
or (values[field_name] in create_records))
and (values[field_name] not in delete_records)):
error_args = cls._get_error_args(field_name)
error_args = cls.__names__(field_name)
error_args['value'] = values[field_name]
raise ForeignKeyError(
gettext('ir.msg_foreign_model_missing',
@@ -1114,7 +1114,7 @@ class ModelSQL(ModelStorage):
if Model.search([
(field_name, 'in', sub_ids),
], order=[]):
error_args = Model._get_error_args(field_name)
error_args = Model.__names__(field_name)
raise ForeignKeyError(
gettext('ir.msg_foreign_model_exist',
**error_args))
+6 -27
View File
@@ -928,27 +928,6 @@ class ModelStorage(Model):
return False
return True
@classmethod
def _get_error_args(cls, field_name):
pool = Pool()
ModelField = pool.get('ir.model.field')
error_args = {
'field': field_name,
'model': cls.__name__
}
if ModelField:
model_fields = ModelField.search([
('name', '=', field_name),
('model.model', '=', cls.__name__),
], limit=1)
if model_fields:
model_field, = model_fields
error_args.update({
'field': model_field.field_description,
'model': model_field.model.name,
})
return error_args
@classmethod
def validate(cls, records):
pass
@@ -1067,7 +1046,7 @@ class ModelStorage(Model):
if sub_relations != set(finds):
raise DomainValidationError(
gettext('ir.msg_domain_validation_record',
**cls._get_error_args(field.name)))
**cls.__names__(field.name)))
field_names = set(field_names or [])
function_fields = {name for name, field in cls._fields.items()
@@ -1094,7 +1073,7 @@ class ModelStorage(Model):
and not isinstance(value, ModelStorage))):
raise RequiredValidationError(
gettext('ir.msg_required_validation_record',
**cls._get_error_args(field_name)))
**cls.__names__(field_name)))
# validate states required
if field.states and 'required' in field.states:
if is_pyson(field.states['required']):
@@ -1137,7 +1116,7 @@ class ModelStorage(Model):
field_size = field.size
size = len(getattr(record, field_name) or '')
if (size > field_size >= 0):
error_args = cls._get_error_args(field_name)
error_args = cls.__names__(field_name)
error_args['size'] = size
error_args['max_size'] = field_size
raise SizeValidationError(
@@ -1146,7 +1125,7 @@ class ModelStorage(Model):
def digits_test(value, digits, field_name):
def raise_error(value):
error_args = cls._get_error_args(field_name)
error_args = cls.__names__(field_name)
error_args['digits'] = digits[1]
error_args['value'] = repr(value)
raise DigitsValidationError(
@@ -1205,7 +1184,7 @@ class ModelStorage(Model):
test.add('')
test.add(None)
if value not in test:
error_args = cls._get_error_args(field_name)
error_args = cls.__names__(field_name)
error_args['value'] = value
raise SelectionValidationError(
gettext('ir.msg_selection_validation_record',
@@ -1218,7 +1197,7 @@ class ModelStorage(Model):
value = value.time()
if value != datetime.datetime.strptime(
value.strftime(format), format).time():
error_args = cls._get_error_args(field_name)
error_args = cls.__names__(field_name)
error_args['value'] = value
raise TimeFormatValidationError(
gettext('ir.msg_time_format_validation_record',
+20
View File
@@ -56,6 +56,26 @@ class ModelTestCase(unittest.TestCase):
self.assertEqual(record.parent.name, "Test 1")
self.assertEqual(record.parent.parent.name, "Test 2")
@with_transaction()
def test_names_model(self):
"Test __names__ for model only"
pool = Pool()
Model = pool.get('test.model')
names = Model.__names__()
self.assertEqual(names, {'model': "Model"})
@with_transaction()
def test_names_field(self):
"Test __names__ with field"
pool = Pool()
Model = pool.get('test.model')
names = Model.__names__('name')
self.assertEqual(names, {'model': "Model", 'field': "Name"})
def suite():
return unittest.TestLoader().loadTestsFromTestCase(ModelTestCase)