Browse Source

Not fully tested. Enables separated datasources for read only or read&write access to the DB

Yann Weber 8 years ago
parent
commit
77e7c36de9

+ 19
- 9
lodel/editorial_model/components.py View File

@@ -47,18 +47,28 @@ class EmComponent(object):
47 47
 class EmClass(EmComponent):
48 48
     
49 49
     ##@brief Instanciate a new EmClass
50
-    # @param uid str : uniq identifier
51
-    # @param display_name MlString|str|dict : component display_name
52
-    # @param abstract bool : set the class as asbtract if True
53
-    # @param pure_abstract bool : if True the EmClass will not be represented in leapi dyncode
54
-    # @param parents list: parent EmClass list or uid list
55
-    # @param help_text MlString|str|dict : help_text
56
-    # @param datasource str : The datasource name ( see @ref lodel2_datasources )
57
-    def __init__(self, uid, display_name = None, help_text = None, abstract = False, parents = None, group = None, pure_abstract = False, datasource = 'default'):
50
+    #@param uid str : uniq identifier
51
+    #@param display_name MlString|str|dict : component display_name
52
+    #@param abstract bool : set the class as asbtract if True
53
+    #@param pure_abstract bool : if True the EmClass will not be represented in
54
+    #leapi dyncode
55
+    #@param parents list: parent EmClass list or uid list
56
+    #@param help_text MlString|str|dict : help_text
57
+    #@param datasources str|tuple|list : The datasource name ( see 
58
+    #@ref lodel2_datasources ) or two names (first is read_only datasource the
59
+    #second is read write)
60
+    def __init__(
61
+        self, uid, display_name = None, help_text = None, abstract = False,
62
+        parents = None, group = None, pure_abstract = False,
63
+        datasources = 'default'):
64
+
58 65
         super().__init__(uid, display_name, help_text, group)
59 66
         self.abstract = bool(abstract)
60 67
         self.pure_abstract = bool(pure_abstract)
61
-        self.__datasource = datasource
68
+        self.__datasource = datasources
69
+        if not isinstance(datasources, str) and len(datasources) != 2:
70
+            raise ValueError("datasources arguement can be a single datasource\
71
+ name or two names in a tuple or a list")
62 72
         if self.pure_abstract:
63 73
             self.abtract = True
64 74
         if parents is not None:

+ 3
- 2
lodel/leapi/lefactory.py View File

@@ -115,7 +115,8 @@ class {clsname}({parents}):
115 115
     _abstract = {abstract}
116 116
     _fields = None
117 117
     _uid = {uid_list}
118
-    _datasource = None
118
+    _ro_datasource = None
119
+    _rw_datasource = None
119 120
     _datasource_name = {datasource_name}
120 121
 
121 122
 """.format(
@@ -135,7 +136,7 @@ class {clsname}({parents}):
135 136
     bootstrap += "\n"
136 137
     for em_class in get_classes(model):
137 138
         # Dyncode datasource bootstrap instructions
138
-        bootstrap += """{classname}._init_datasource()
139
+        bootstrap += """{classname}._init_datasources()
139 140
 """.format(
140 141
             classname = LeObject.name2objname(em_class.uid))
141 142
     return res, set(imports), bootstrap

+ 47
- 15
lodel/leapi/leobject.py View File

@@ -76,8 +76,10 @@ class LeObject(object):
76 76
     _fields = None
77 77
     ##@brief A tuple of fieldname (or a uniq fieldname) representing uid
78 78
     _uid = None 
79
-    ##@brief The datasource name ( see @ref lodel2_datasources )
80
-    _datasource = None
79
+    ##@brief Read only datasource ( see @ref lodel2_datasources )
80
+    _ro_datasource = None
81
+    ##@breif Read & write datasource ( see @ref lodel2_datasources )
82
+    _rw_datasource = None
81 83
 
82 84
     ##@brief Construct an object representing an Editorial component
83 85
     # @note Can be considered as EmClass instance
@@ -185,25 +187,57 @@ class LeObject(object):
185 187
             raise NameError("No field named '%s' in %s" % ( fieldname,
186 188
                                                             cls.__name__))
187 189
     
190
+    ##@brief Initialise both datasources (ro and rw)
191
+    #
192
+    #This method is used once at dyncode load to replace the datasource string
193
+    #by a datasource instance to avoid doing this operation for each query
194
+    #@see LeObject::_init_datasource()
195
+    @classmethod
196
+    def _init_datasources(cls):
197
+        if isinstance(cls._datasource_name, str):
198
+            rw_ds = ro_ds = cls._datasource_name
199
+        else:
200
+            ro_ds, rw_ds = cls._datasource_name
201
+        #Read only datasource initialisation
202
+        cls._ro_datasource = cls._init_datasource(ro_ds, True)
203
+        log_msg = "Read only datasource %s initialized for LeObject %s"
204
+        log_msg %= (ro_ds, cls.__name__)
205
+        logger.debug(log_msg)
206
+        #Read write datasource initialisation
207
+        cls._rw_datasource = cls._init_datasource(rw_ds, False)
208
+        log_msg = "Read&write only datasource %s initialized for LeObject %s"
209
+        log_msg %= (rw_ds, cls.__name__)
210
+        logger.debug(log_msg)
211
+        
212
+
188 213
     ##@brief Replace the _datasource attribute value by a datasource instance
189 214
     #
190
-    # This method is used once at dyncode load to replace the datasource string
191
-    # by a datasource instance to avoid doing this operation for each query
215
+    #This method is used once at dyncode load to replace the datasource string
216
+    #by a datasource instance to avoid doing this operation for each query
217
+    #@param ds_name str : The name of the datasource to instanciate
218
+    #@param ro bool : if true initialise the _ro_datasource attribute else
219
+    #initialise _rw_datasource attribute
192 220
     @classmethod
193
-    def _init_datasource(cls):
221
+    def _init_datasource(cls, ds_name, ro):
194 222
         expt_msg = "In LeAPI class '%s' " % cls.__name__
195
-        datasource_orig_name = cls._datasource_name
196
-        if cls._datasource_name not in Settings.datasources._fields:
223
+        datasource_orig_name = ds_name
224
+        if ds_name not in Settings.datasources._fields:
197 225
             expt_msg += "Unknow or unconfigured datasource %s"
198
-            expt_msg %= (cls._datasource_name, cls.__name__)
226
+            expt_msg %= (ds_name, cls.__name__)
199 227
             raise SettingsError(expt_msg)
200 228
         
201
-        ds_identifier = getattr(Settings.datasources, cls._datasource_name)
229
+        ds_identifier = getattr(Settings.datasources, ds_name)
230
+        read_only = getattr(ds_identifier, 'read_only')
202 231
         try:
203 232
             ds_identifier = getattr(ds_identifier, 'identifier')
204 233
         except NameError:
205 234
             expt_msg += "Datasource %s is missconfigured, missing identifier."
206
-            expt_msg %= cls._datasource_name
235
+            expt_msg %= ds_name
236
+            raise SettingsError(expt_msg)
237
+        if read_only and not ro:
238
+            expt_msg += "Error in datasource %s configuration. Trying to use \
239
+a read only as a read&write datasource"
240
+            expt_msg %= ds_name
207 241
             raise SettingsError(expt_msg)
208 242
 
209 243
         ds_plugin, ds_name = ds_identifier.split('.')
@@ -226,7 +260,8 @@ class LeObject(object):
226 260
             datasource_class = getattr(ds_plugin_module, "Datasource")
227 261
         except AttributeError as e:
228 262
             raise e
229
-            expt_msg += "The datasource plugin %s seems to be invalid. Error raised when trying to import Datasource"
263
+            expt_msg += "The datasource plugin %s seems to be invalid. Error \
264
+raised when trying to import Datasource"
230 265
             expt_msg %= ds_identifier
231 266
             raise SettingsError(expt_msg)
232 267
         ds_conf_old = ds_conf
@@ -234,10 +269,7 @@ class LeObject(object):
234 269
         for k in ds_conf_old._fields:
235 270
             ds_conf[k] = getattr(ds_conf_old, k)
236 271
 
237
-        cls._datasource = datasource_class(**ds_conf)
238
-        log_msg = "Datasource %s initialized for LeObject %s"
239
-        log_msg %= (datasource_orig_name, cls.__name__)
240
-        logger.debug(log_msg)
272
+        return datasource_class(**ds_conf)
241 273
 
242 274
     ##@brief Read only access to all datas
243 275
     # @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance

+ 8
- 7
lodel/leapi/query.py View File

@@ -49,7 +49,8 @@ class LeQuery(object):
49 49
         if not issubclass(target_class, LeObject):
50 50
             raise TypeError("target class has to be a child class of LeObject")
51 51
         self._target_class = target_class
52
-        self._datasource = target_class._datasource
52
+        self._ro_datasource = target_class._ro_datasource
53
+        self._rw_datasource = target_class._rw_datasource
53 54
     
54 55
     ##@brief Execute a query and return the result
55 56
     # @param **datas
@@ -67,7 +68,7 @@ class LeQuery(object):
67 68
         LodelHook.call_hook(    self._hook_prefix+'_pre',
68 69
                                 self._target_class,
69 70
                                 datas)
70
-        ret = self.__query(target = self._target_class._datasource, **datas)
71
+        ret = self.__query(target = self._target_class, **datas)
71 72
         ret = LodelHook.call_hook(  self._hook_prefix+'_post',
72 73
                                     self._target_class,
73 74
                                     ret)
@@ -487,7 +488,7 @@ class LeInsertQuery(LeQuery):
487 488
     ## @brief Implements an insert query operation, with only one insertion
488 489
     # @param new_datas : datas to be inserted
489 490
     def __query(self, new_datas):
490
-        nb_inserted = self._datasource.insert(self._target_class,new_datas)
491
+        nb_inserted = self._rw_datasource.insert(self._target_class,new_datas)
491 492
         if nb_inserted < 0:
492 493
             raise LeQueryError("Insertion error")
493 494
         return nb_inserted
@@ -521,7 +522,7 @@ class LeUpdateQuery(LeFilteredQuery):
521 522
     #@param updated_datas dict : datas to update
522 523
     #@returns the number of updated items
523 524
     def __query(self, filters, rel_filters, updated_datas):
524
-        nb_updated = self._datasource.update(
525
+        nb_updated = self._rw_datasource.update(
525 526
             self._target_class, filters, rel_filters, update_datas)
526 527
         return nb_updated
527 528
     
@@ -546,7 +547,7 @@ class LeDeleteQuery(LeFilteredQuery):
546 547
     #@param rel_filters list : see @ref LeFilteredQuery
547 548
     #@returns the number of deleted items
548 549
     def __query(self, filters, rel_filters):
549
-        nb_deleted = datasource.delete(
550
+        nb_deleted = self._rw_datasource.delete(
550 551
             self._target_class, filters, rel_filters)
551 552
         return nb_deleted
552 553
 
@@ -636,9 +637,9 @@ class LeGetQuery(LeFilteredQuery):
636 637
 
637 638
     ##@brief Implements select query operations
638 639
     # @returns a list containing the item(s)
639
-    def __query(self, datasource):
640
+    def __query(self):
640 641
         # select datas corresponding to query_filter
641
-        l_datas=datasource.select(  self._target_class,
642
+        l_datas=self._ro_datasource.select(  self._target_class,
642 643
                                     list(self.field_list),
643 644
                                     self.query_filter,
644 645
                                     None,

+ 1
- 1
lodel/settings/validator.py View File

@@ -329,6 +329,6 @@ LODEL2_CONF_SPECS = {
329 329
         'editormode': ( False, SettingValidator('bool')),
330 330
     },
331 331
     'lodel2.datasources.*': {
332
-        'read_only': (True, SettingValidator('bool')),
332
+        'read_only': (False, SettingValidator('bool')),
333 333
         'identifier': ( None, SettingValidator('string'))}
334 334
 }

Loading…
Cancel
Save