Browse Source

Introducing the notion of immutable fieldtypes for classtypes fields definition

Yann Weber 8 years ago
parent
commit
5ad9c27a07

+ 1
- 0
EditorialModel/classes.py View File

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

+ 13
- 0
EditorialModel/classtypes.py View File

@@ -14,33 +14,39 @@ common_fields = {
14 14
     object_uid: {
15 15
         'fieldtype': 'pk',
16 16
         'internal': 'autosql',
17
+        'immutable' : True,
17 18
     },
18 19
     object_em_class_id : {
19 20
         'fieldtype': 'emuid',
20 21
         'is_id_class': True,
21 22
         'internal': 'automatic',
23
+        'immutable' : True,
22 24
     },
23 25
     object_em_type_id : {
24 26
         'fieldtype': 'emuid',
25 27
         'is_id_class': False,
26 28
         'internal': 'automatic',
29
+        'immutable' : True,
27 30
     },
28 31
     'string': {
29 32
         'fieldtype': 'char',
30 33
         'max_length': 128,
31 34
         'internal': 'automatic',
32 35
         'nullable': True,
36
+        'immutable' : False,
33 37
     },
34 38
     'creation_date': {
35 39
         'fieldtype': 'datetime',
36 40
         'now_on_create': True,
37 41
         'internal': 'autosql',
42
+        'immutable' : True,
38 43
     },
39 44
     'modification_date': {
40 45
         'fieldtype': 'datetime',
41 46
         'now_on_create': True,
42 47
         'now_on_update': True,
43 48
         'internal': 'autosql',
49
+        'immutable' : True,
44 50
     }
45 51
 }
46 52
 
@@ -48,29 +54,36 @@ relations_common_fields = {
48 54
     relation_uid: {
49 55
         'fieldtype': 'pk',
50 56
         'internal': 'autosql',
57
+        'immutable' : True,
51 58
     },
52 59
     'nature': {
53 60
         'fieldtype': 'naturerelation',
61
+        'immutable' : True,
54 62
     },
55 63
     'depth': {
56 64
         'fieldtype': 'integer',
57 65
         'internal': 'automatic',
66
+        'immutable' : True,
58 67
     },
59 68
     'rank': {
60 69
         'fieldtype': 'rank',
61 70
         'internal': 'automatic',
71
+        'immutable' : True,
62 72
     },
63 73
     relation_superior : {
64 74
         'fieldtype': 'leo',
65 75
         'superior': True,
76
+        'immutable' : True,
66 77
     },
67 78
     relation_subordinate: {
68 79
         'fieldtype': 'leo',
69 80
         'superior': False,
81
+        'immutable' : True,
70 82
     },
71 83
     relation_name: {
72 84
         'fieldtype': 'namerelation',
73 85
         'max_length': 128,
86
+        'immutable' : True,
74 87
     }
75 88
 }
76 89
 

+ 23
- 11
EditorialModel/fields.py View File

@@ -32,7 +32,6 @@ class EmField(EmComponent):
32 32
     # @param uniq bool : if True the value should be uniq in the db table
33 33
     # @param **kwargs : more keywords arguments for the fieldtype
34 34
     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):
35
-
36 35
         self.class_id = class_id
37 36
         self.check_type('class_id', int)
38 37
         self.optional = bool(optional)
@@ -57,21 +56,12 @@ class EmField(EmComponent):
57 56
         self.fieldtype = fieldtype
58 57
         self._fieldtype_args = kwargs
59 58
         self._fieldtype_args.update({'nullable': nullable, 'uniq': uniq, 'internal': self.internal})
60
-        try:
61
-            fieldtype_instance = self._fieldtype_cls(**self._fieldtype_args)
62
-        except AttributeError as e:
63
-            raise AttributeError("Error will instanciating fieldtype : %s" % e)
59
+        self.set_fieldtype_options(**self._fieldtype_args)
64 60
 
65 61
         if 'default' in kwargs:
66 62
             if not fieldtype_instance.check(default):
67 63
                 raise TypeError("Default value ('%s') is not valid given the fieldtype '%s'" % (default, fieldtype))
68 64
 
69
-        self.nullable = nullable
70
-        self.uniq = uniq
71
-
72
-        for kname, kval in kwargs.items():
73
-            setattr(self, kname, kval)
74
-
75 65
         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)
76 66
 
77 67
     @staticmethod
@@ -89,6 +79,28 @@ class EmField(EmComponent):
89 79
     def em_class(self):
90 80
         return self.model.component(self.class_id)
91 81
     
82
+    ## @brief Update the fieldtype of a field
83
+    def set_fieldtype(self, fieldtype_name):
84
+        self.fieldtype = fieldtype_name
85
+        self._fieldtype_cls = GenericFieldType.from_name(self.fieldtype)
86
+        self.set_fieldtype_options(**self._fieldtype_args)
87
+
88
+    ## @brief Set fieldtype options
89
+    def set_fieldtype_options(self, nullable = False, uniq = False, internal=False, **kwargs):
90
+        # Cleaning old options
91
+        if hasattr(self, 'name'): # If not called by __init__
92
+            for opt_name in self._fieldtype_args:
93
+                if hasattr(self, opt_name):
94
+                    delattr(self,opt_name)
95
+        self._fieldtype_args = kwargs
96
+        self._fieldtype_args.update({'nullable': nullable, 'uniq': uniq, 'internal': internal})
97
+        try:
98
+            fieldtype_instance = self._fieldtype_cls(**self._fieldtype_args)
99
+        except AttributeError as e:
100
+            raise AttributeError("Error will instanciating fieldtype : %s" % e)
101
+        for opt_name, opt_val in self._fieldtype_args.items():
102
+            setattr(self, opt_name, opt_val)
103
+
92 104
     ## @brief Getter for private property EmField._fieldtype_args
93 105
     # @return A copy of private dict _fieldtype_args
94 106
     def fieldtype_options(self):

+ 22
- 6
Lodel/utils/mlstring.py View File

@@ -1,27 +1,40 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 
3 3
 import json
4
+import warnings
4 5
 
5 6
 
6 7
 ## Handle string with translations
7 8
 # @todo define a default language that will be used in case the wanted language is not available for this string (gettext-like behavior)
8 9
 class MlString(object):
9 10
 
11
+    default_lang = '__default__'
10 12
     ## Instanciate a new string with translation
11 13
     #
12 14
     # @param translations dict: With key = lang and value the translation
13
-    def __init__(self, translations=None):
14
-        self.translations = dict() if translations is None else translations
15
+    def __init__(self, translations=None, default_value = None):
16
+        if translations is None:
17
+            translations = dict()
18
+        elif isinstance(translations, str):
19
+            translations = json.loads(translations)
20
+
21
+        if not isinstance(translations, dict):
22
+            raise ValueError('Bad value given for translations argument on MlString instanciation')
23
+        else:
24
+            self.translations = translations
25
+
26
+        self.translations[self.default_lang] = '' if default_value is None else default_value
27
+        if default_value is None:
28
+            warnings.warn('No default value when isntanciating an MlString')
15 29
     
16 30
     ## Return a translation
17 31
     # @param lang str: The lang
18 32
     # @return An empty string if the wanted lang don't exist
19 33
     # @warning Returns an empty string if the wanted translation didn't exists
20 34
     # @todo if the asked language is not available, use the default one, defined as a class property
21
-    def get(self, lang):
22
-        if not lang in self.translations:
23
-            return ''
24
-
35
+    def get(self, lang = None):
36
+        if lang is None or lang not in self.translations:
37
+            lang = self.default_lang
25 38
         return self.translations[lang]
26 39
     
27 40
     ## Set a translation for this MlString
@@ -35,6 +48,9 @@ class MlString(object):
35 48
         else:
36 49
             self.translations[lang] = text
37 50
 
51
+    def set_default(self, text):
52
+        self.set(self.default_lang, text)
53
+
38 54
     def __repr__(self):
39 55
         return self.__str__()
40 56
 

+ 1
- 1
leapi/leclass.py View File

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

+ 49
- 24
leapi/lefactory.py View File

@@ -49,23 +49,26 @@ class LeFactory(object):
49 49
         res_ft_l = list()
50 50
         res_uid_ft = None
51 51
         for fname, ftargs in ft_dict.items():
52
-            ftargs = copy.copy(ftargs)
53
-            fieldtype = ftargs['fieldtype']
54
-            self.needed_fieldtypes |= set([fieldtype])
55
-            del(ftargs['fieldtype'])
56
-
57
-            constructor = '{ftname}.EmFieldType(**{ftargs})'.format(
58
-                ftname = GenericFieldType.module_name(fieldtype),
59
-                ftargs = ftargs,
60
-            )
61
-
62
-            if fieldtype == 'pk':
63
-                #
64
-                #       WARNING multiple PK not supported
65
-                #
66
-                res_uid_ft = "{ %s: %s }"%(repr(fname),constructor)
52
+            if ftargs is None:
53
+                res_ft_l.append('%s: None' % repr(fname))
67 54
             else:
68
-                res_ft_l.append( '%s: %s'%(repr(fname), constructor) )
55
+                ftargs = copy.copy(ftargs)
56
+                fieldtype = ftargs['fieldtype']
57
+                self.needed_fieldtypes |= set([fieldtype])
58
+                del(ftargs['fieldtype'])
59
+
60
+                constructor = '{ftname}.EmFieldType(**{ftargs})'.format(
61
+                    ftname = GenericFieldType.module_name(fieldtype),
62
+                    ftargs = ftargs,
63
+                )
64
+
65
+                if fieldtype == 'pk':
66
+                    #
67
+                    #       WARNING multiple PK not supported
68
+                    #
69
+                    res_uid_ft = "{ %s: %s }"%(repr(fname),constructor)
70
+                else:
71
+                    res_ft_l.append( '%s: %s'%(repr(fname), constructor) )
69 72
         return (res_uid_ft, res_ft_l)
70 73
 
71 74
     ## @brief Given a Model generate concrete instances of LeRel2Type classes to represent relations
@@ -112,23 +115,30 @@ class {classname}(LeRel2Type):
112 115
         for rfield in [ f for f in emclass.fields() if f.fieldtype == 'rel2type']:
113 116
             fti = rfield.fieldtype_instance()
114 117
             cls_linked_types[rfield.name] = _LeCrud.name2classname(model.component(fti.rel_to_type_id).name)
118
+        ml_fieldnames = dict()
115 119
         # Populating fieldtype attr
116 120
         for field in emclass.fields(relational = False):
117
-            self.needed_fieldtypes |= set([field.fieldtype])
118
-            cls_fields[field.name] = LeFactory.fieldtype_construct_from_field(field)
119
-            fti = field.fieldtype_instance()
121
+            if field.name not in EditorialModel.classtypes.common_fields.keys() or not ( hasattr(field, 'immutable') and field.immutable):
122
+                self.needed_fieldtypes |= set([field.fieldtype])
123
+                cls_fields[field.name] = LeFactory.fieldtype_construct_from_field(field)
124
+                fti = field.fieldtype_instance()
125
+                if field.string.get() == '':
126
+                    field.string.set_default(field.name)
127
+                ml_fieldnames[field.name] = field.string.__str__()
120 128
 
121 129
         return """
122 130
 #Initialisation of {name} class attributes
123 131
 {name}._fieldtypes = {ftypes}
132
+{name}.ml_fields_strings = {fieldnames}
124 133
 {name}._linked_types = {ltypes}
125 134
 {name}._classtype = {classtype}
126 135
 """.format(
127 136
             name = _LeCrud.name2classname(emclass.name),
128 137
             ftypes = "{" + (','.join(['\n    %s: %s' % (repr(f), v) for f, v in cls_fields.items()])) + "\n}",
138
+            fieldnames = '{' + (','.join(['\n   %s: MlString(%s)' % (repr(f), v) for f,v in ml_fieldnames.items()])) + '\n}',
129 139
             ltypes = "{" + (','.join(['\n    %s: %s' % (repr(f), v) for f, v in cls_linked_types.items()])) + "\n}",
130 140
 
131
-            classtype = repr(emclass.classtype)
141
+            classtype = repr(emclass.classtype),
132 142
         )
133 143
 
134 144
     ## @brief Given a Model and an EmType instances generate python code for corresponding LeType
@@ -177,6 +187,7 @@ class {classname}(LeRel2Type):
177 187
 import EditorialModel
178 188
 from EditorialModel import fieldtypes
179 189
 from EditorialModel.fieldtypes import {needed_fieldtypes_list}
190
+from Lodel.utils.mlstring import MlString
180 191
 
181 192
 import leapi
182 193
 import leapi.lecrud
@@ -197,9 +208,15 @@ import %s
197 208
             leobj_me_uid[comp.uid] = _LeCrud.name2classname(comp.name)
198 209
         
199 210
         #Building the fieldtypes dict of LeObject
200
-        (leobj_uid_fieldtype, leobj_fieldtypes) = self.concret_fieldtypes(EditorialModel.classtypes.common_fields)
211
+        common_fieldtypes = dict()
212
+        for ftname, ftdef in EditorialModel.classtypes.common_fields.items():
213
+            common_fieldtypes[ftname] = ftdef if 'immutable' in ftdef and ftdef['immutable'] else None
214
+        (leobj_uid_fieldtype, leobj_fieldtypes) = self.concret_fieldtypes(common_fieldtypes)
201 215
         #Building the fieldtypes dict for LeRelation
202
-        (lerel_uid_fieldtype, lerel_fieldtypes) = self.concret_fieldtypes(EditorialModel.classtypes.relations_common_fields)
216
+        common_fieldtypes = dict()
217
+        for ftname, ftdef in EditorialModel.classtypes.relations_common_fields.items():
218
+            common_fieldtypes[ftname] = ftdef if 'immutable' in ftdef and ftdef['immutable'] else None
219
+        (lerel_uid_fieldtype, lerel_fieldtypes) = self.concret_fieldtypes(common_fieldtypes)
203 220
 
204 221
         result += """
205 222
 ## @brief _LeCrud concret class
@@ -256,28 +273,36 @@ class LeType(LeClass, _LeType):
256 273
 
257 274
         #LeClass child classes definition
258 275
         for emclass in emclass_l:
276
+            if emclass.string.get() == '':
277
+                emclass.string.set_default(emclass.name)
259 278
             result += """
260 279
 ## @brief EmClass {name} LeClass child class
261 280
 # @see leapi.leclass.LeClass
262 281
 class {name}(LeClass, LeObject):
263 282
     _class_id = {uid}
283
+    ml_string = MlString({name_translations})
264 284
 
265 285
 """.format(
266 286
                 name=_LeCrud.name2classname(emclass.name),
267
-                uid=emclass.uid
287
+                uid=emclass.uid,
288
+                name_translations = repr(emclass.string.__str__()),
268 289
             )
269 290
         #LeType child classes definition
270 291
         for emtype in emtype_l:
292
+            if emtype.string.get() == '':
293
+                emtype.string.set_default(emtype.name)
271 294
             result += """
272 295
 ## @brief EmType {name} LeType child class
273 296
 # @see leobject::letype::LeType
274 297
 class {name}(LeType, {leclass}):
275 298
     _type_id = {uid}
299
+    ml_string = MlString({name_translations})
276 300
 
277 301
 """.format(
278 302
                 name=_LeCrud.name2classname(emtype.name),
279 303
                 leclass=_LeCrud.name2classname(emtype.em_class.name),
280
-                uid=emtype.uid
304
+                uid=emtype.uid,
305
+                name_translations = repr(emtype.string.__str__()),
281 306
             )
282 307
 
283 308
         #Generating concret class of LeRel2Type

+ 2
- 1
leapi/letype.py View File

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

+ 3
- 2
leapi/test/test_lefactory.py View File

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

Loading…
Cancel
Save