1
0
Fork 0
mirror of https://github.com/yweber/lodel2.git synced 2026-07-15 11:41:58 +02:00

Introducing the notion of immutable fieldtypes for classtypes fields definition

This commit is contained in:
Yann 2016-01-21 14:03:58 +01:00
commit 5ad9c27a07
8 changed files with 114 additions and 45 deletions

View file

@ -58,6 +58,7 @@ class EmClass(EmComponent):
ctype_opts[opt_name] = opt_val
if ftype_opts != ctype_opts:
field.set_fieldtype_options(**ctype_opts)
# If options mismatch produce a diff and display a warning
ctype_opts = [ "%s: %s\n"%(repr(k), repr(ctype_opts[k])) for k in sorted(ctype_opts.keys())]
ftype_opts = [ "%s: %s\n"%(repr(k), repr(ftype_opts[k])) for k in sorted(ftype_opts.keys())]

View file

@ -14,33 +14,39 @@ common_fields = {
object_uid: {
'fieldtype': 'pk',
'internal': 'autosql',
'immutable' : True,
},
object_em_class_id : {
'fieldtype': 'emuid',
'is_id_class': True,
'internal': 'automatic',
'immutable' : True,
},
object_em_type_id : {
'fieldtype': 'emuid',
'is_id_class': False,
'internal': 'automatic',
'immutable' : True,
},
'string': {
'fieldtype': 'char',
'max_length': 128,
'internal': 'automatic',
'nullable': True,
'immutable' : False,
},
'creation_date': {
'fieldtype': 'datetime',
'now_on_create': True,
'internal': 'autosql',
'immutable' : True,
},
'modification_date': {
'fieldtype': 'datetime',
'now_on_create': True,
'now_on_update': True,
'internal': 'autosql',
'immutable' : True,
}
}
@ -48,29 +54,36 @@ relations_common_fields = {
relation_uid: {
'fieldtype': 'pk',
'internal': 'autosql',
'immutable' : True,
},
'nature': {
'fieldtype': 'naturerelation',
'immutable' : True,
},
'depth': {
'fieldtype': 'integer',
'internal': 'automatic',
'immutable' : True,
},
'rank': {
'fieldtype': 'rank',
'internal': 'automatic',
'immutable' : True,
},
relation_superior : {
'fieldtype': 'leo',
'superior': True,
'immutable' : True,
},
relation_subordinate: {
'fieldtype': 'leo',
'superior': False,
'immutable' : True,
},
relation_name: {
'fieldtype': 'namerelation',
'max_length': 128,
'immutable' : True,
}
}

View file

@ -32,7 +32,6 @@ class EmField(EmComponent):
# @param uniq bool : if True the value should be uniq in the db table
# @param **kwargs : more keywords arguments for the fieldtype
def __init__(self, model, uid, name, class_id, fieldtype, optional=False, internal=False, rel_field_id=None, icon='0', string=None, help_text=None, date_update=None, date_create=None, rank=None, nullable=False, uniq=False, **kwargs):
self.class_id = class_id
self.check_type('class_id', int)
self.optional = bool(optional)
@ -57,21 +56,12 @@ class EmField(EmComponent):
self.fieldtype = fieldtype
self._fieldtype_args = kwargs
self._fieldtype_args.update({'nullable': nullable, 'uniq': uniq, 'internal': self.internal})
try:
fieldtype_instance = self._fieldtype_cls(**self._fieldtype_args)
except AttributeError as e:
raise AttributeError("Error will instanciating fieldtype : %s" % e)
self.set_fieldtype_options(**self._fieldtype_args)
if 'default' in kwargs:
if not fieldtype_instance.check(default):
raise TypeError("Default value ('%s') is not valid given the fieldtype '%s'" % (default, fieldtype))
self.nullable = nullable
self.uniq = uniq
for kname, kval in kwargs.items():
setattr(self, kname, kval)
super(EmField, self).__init__(model=model, uid=uid, name=name, string=string, help_text=help_text, date_update=date_update, date_create=date_create, rank=rank)
@staticmethod
@ -89,6 +79,28 @@ class EmField(EmComponent):
def em_class(self):
return self.model.component(self.class_id)
## @brief Update the fieldtype of a field
def set_fieldtype(self, fieldtype_name):
self.fieldtype = fieldtype_name
self._fieldtype_cls = GenericFieldType.from_name(self.fieldtype)
self.set_fieldtype_options(**self._fieldtype_args)
## @brief Set fieldtype options
def set_fieldtype_options(self, nullable = False, uniq = False, internal=False, **kwargs):
# Cleaning old options
if hasattr(self, 'name'): # If not called by __init__
for opt_name in self._fieldtype_args:
if hasattr(self, opt_name):
delattr(self,opt_name)
self._fieldtype_args = kwargs
self._fieldtype_args.update({'nullable': nullable, 'uniq': uniq, 'internal': internal})
try:
fieldtype_instance = self._fieldtype_cls(**self._fieldtype_args)
except AttributeError as e:
raise AttributeError("Error will instanciating fieldtype : %s" % e)
for opt_name, opt_val in self._fieldtype_args.items():
setattr(self, opt_name, opt_val)
## @brief Getter for private property EmField._fieldtype_args
# @return A copy of private dict _fieldtype_args
def fieldtype_options(self):

View file

@ -1,27 +1,40 @@
# -*- coding: utf-8 -*-
import json
import warnings
## Handle string with translations
# @todo define a default language that will be used in case the wanted language is not available for this string (gettext-like behavior)
class MlString(object):
default_lang = '__default__'
## Instanciate a new string with translation
#
# @param translations dict: With key = lang and value the translation
def __init__(self, translations=None):
self.translations = dict() if translations is None else translations
def __init__(self, translations=None, default_value = None):
if translations is None:
translations = dict()
elif isinstance(translations, str):
translations = json.loads(translations)
if not isinstance(translations, dict):
raise ValueError('Bad value given for translations argument on MlString instanciation')
else:
self.translations = translations
self.translations[self.default_lang] = '' if default_value is None else default_value
if default_value is None:
warnings.warn('No default value when isntanciating an MlString')
## Return a translation
# @param lang str: The lang
# @return An empty string if the wanted lang don't exist
# @warning Returns an empty string if the wanted translation didn't exists
# @todo if the asked language is not available, use the default one, defined as a class property
def get(self, lang):
if not lang in self.translations:
return ''
def get(self, lang = None):
if lang is None or lang not in self.translations:
lang = self.default_lang
return self.translations[lang]
## Set a translation for this MlString
@ -35,6 +48,9 @@ class MlString(object):
else:
self.translations[lang] = text
def set_default(self, text):
self.set(self.default_lang, text)
def __repr__(self):
return self.__str__()

View file

@ -22,7 +22,7 @@ class _LeClass(_LeObject):
@classmethod
def fieldtypes(cls):
ret = dict()
ret.update(super(_LeClass,cls).fieldtypes())
ret.update(super().fieldtypes())
ret.update(cls._fieldtypes)
return ret

View file

@ -49,23 +49,26 @@ class LeFactory(object):
res_ft_l = list()
res_uid_ft = None
for fname, ftargs in ft_dict.items():
ftargs = copy.copy(ftargs)
fieldtype = ftargs['fieldtype']
self.needed_fieldtypes |= set([fieldtype])
del(ftargs['fieldtype'])
constructor = '{ftname}.EmFieldType(**{ftargs})'.format(
ftname = GenericFieldType.module_name(fieldtype),
ftargs = ftargs,
)
if fieldtype == 'pk':
#
# WARNING multiple PK not supported
#
res_uid_ft = "{ %s: %s }"%(repr(fname),constructor)
if ftargs is None:
res_ft_l.append('%s: None' % repr(fname))
else:
res_ft_l.append( '%s: %s'%(repr(fname), constructor) )
ftargs = copy.copy(ftargs)
fieldtype = ftargs['fieldtype']
self.needed_fieldtypes |= set([fieldtype])
del(ftargs['fieldtype'])
constructor = '{ftname}.EmFieldType(**{ftargs})'.format(
ftname = GenericFieldType.module_name(fieldtype),
ftargs = ftargs,
)
if fieldtype == 'pk':
#
# WARNING multiple PK not supported
#
res_uid_ft = "{ %s: %s }"%(repr(fname),constructor)
else:
res_ft_l.append( '%s: %s'%(repr(fname), constructor) )
return (res_uid_ft, res_ft_l)
## @brief Given a Model generate concrete instances of LeRel2Type classes to represent relations
@ -112,23 +115,30 @@ class {classname}(LeRel2Type):
for rfield in [ f for f in emclass.fields() if f.fieldtype == 'rel2type']:
fti = rfield.fieldtype_instance()
cls_linked_types[rfield.name] = _LeCrud.name2classname(model.component(fti.rel_to_type_id).name)
ml_fieldnames = dict()
# Populating fieldtype attr
for field in emclass.fields(relational = False):
self.needed_fieldtypes |= set([field.fieldtype])
cls_fields[field.name] = LeFactory.fieldtype_construct_from_field(field)
fti = field.fieldtype_instance()
if field.name not in EditorialModel.classtypes.common_fields.keys() or not ( hasattr(field, 'immutable') and field.immutable):
self.needed_fieldtypes |= set([field.fieldtype])
cls_fields[field.name] = LeFactory.fieldtype_construct_from_field(field)
fti = field.fieldtype_instance()
if field.string.get() == '':
field.string.set_default(field.name)
ml_fieldnames[field.name] = field.string.__str__()
return """
#Initialisation of {name} class attributes
{name}._fieldtypes = {ftypes}
{name}.ml_fields_strings = {fieldnames}
{name}._linked_types = {ltypes}
{name}._classtype = {classtype}
""".format(
name = _LeCrud.name2classname(emclass.name),
ftypes = "{" + (','.join(['\n %s: %s' % (repr(f), v) for f, v in cls_fields.items()])) + "\n}",
fieldnames = '{' + (','.join(['\n %s: MlString(%s)' % (repr(f), v) for f,v in ml_fieldnames.items()])) + '\n}',
ltypes = "{" + (','.join(['\n %s: %s' % (repr(f), v) for f, v in cls_linked_types.items()])) + "\n}",
classtype = repr(emclass.classtype)
classtype = repr(emclass.classtype),
)
## @brief Given a Model and an EmType instances generate python code for corresponding LeType
@ -177,6 +187,7 @@ class {classname}(LeRel2Type):
import EditorialModel
from EditorialModel import fieldtypes
from EditorialModel.fieldtypes import {needed_fieldtypes_list}
from Lodel.utils.mlstring import MlString
import leapi
import leapi.lecrud
@ -197,9 +208,15 @@ import %s
leobj_me_uid[comp.uid] = _LeCrud.name2classname(comp.name)
#Building the fieldtypes dict of LeObject
(leobj_uid_fieldtype, leobj_fieldtypes) = self.concret_fieldtypes(EditorialModel.classtypes.common_fields)
common_fieldtypes = dict()
for ftname, ftdef in EditorialModel.classtypes.common_fields.items():
common_fieldtypes[ftname] = ftdef if 'immutable' in ftdef and ftdef['immutable'] else None
(leobj_uid_fieldtype, leobj_fieldtypes) = self.concret_fieldtypes(common_fieldtypes)
#Building the fieldtypes dict for LeRelation
(lerel_uid_fieldtype, lerel_fieldtypes) = self.concret_fieldtypes(EditorialModel.classtypes.relations_common_fields)
common_fieldtypes = dict()
for ftname, ftdef in EditorialModel.classtypes.relations_common_fields.items():
common_fieldtypes[ftname] = ftdef if 'immutable' in ftdef and ftdef['immutable'] else None
(lerel_uid_fieldtype, lerel_fieldtypes) = self.concret_fieldtypes(common_fieldtypes)
result += """
## @brief _LeCrud concret class
@ -256,28 +273,36 @@ class LeType(LeClass, _LeType):
#LeClass child classes definition
for emclass in emclass_l:
if emclass.string.get() == '':
emclass.string.set_default(emclass.name)
result += """
## @brief EmClass {name} LeClass child class
# @see leapi.leclass.LeClass
class {name}(LeClass, LeObject):
_class_id = {uid}
ml_string = MlString({name_translations})
""".format(
name=_LeCrud.name2classname(emclass.name),
uid=emclass.uid
uid=emclass.uid,
name_translations = repr(emclass.string.__str__()),
)
#LeType child classes definition
for emtype in emtype_l:
if emtype.string.get() == '':
emtype.string.set_default(emtype.name)
result += """
## @brief EmType {name} LeType child class
# @see leobject::letype::LeType
class {name}(LeType, {leclass}):
_type_id = {uid}
ml_string = MlString({name_translations})
""".format(
name=_LeCrud.name2classname(emtype.name),
leclass=_LeCrud.name2classname(emtype.em_class.name),
uid=emtype.uid
uid=emtype.uid,
name_translations = repr(emtype.string.__str__()),
)
#Generating concret class of LeRel2Type

View file

@ -55,7 +55,8 @@ class _LeType(_LeClass):
@classmethod
def fieldtypes(cls):
return { fname: cls._fieldtypes[fname] for fname in cls._fieldtypes if fname in cls._fields }
super_fieldtypes = super().fieldtypes()
return { fname: super_fieldtypes[fname] for fname in super_fieldtypes if fname in cls._fields }
## @brief Get all the datas for this LeType
# @return a dict with fieldname as key and field value as value

View file

@ -72,11 +72,12 @@ class TestLeFactory(TestCase):
)
#Testing fieldtypes
expected_fieldtypes = [ f for f in emclass.fields(relational=False) if not(hasattr(f, 'immutable') and f.immutable)]
self.assertEqual(
set([ f.name for f in emclass.fields(relational=False)]),
set([ f.name for f in expected_fieldtypes]),
set(leclass._fieldtypes.keys())
)
for field in emclass.fields(relational=False):
for field in expected_fieldtypes:
self.assertEqual(
hash(field.fieldtype_instance()),
hash(leclass._fieldtypes[field.name])