Browse Source

Implements add_related set_relation_rank in MySQL ledatasource and updated get_relation

Yann Weber 9 years ago
parent
commit
668d97e2fb
3 changed files with 114 additions and 18 deletions
  1. 11
    5
      leobject/datasources/dummy.py
  2. 101
    11
      leobject/datasources/ledatasourcesql.py
  3. 2
    2
      leobject/leobject.py

+ 11
- 5
leobject/datasources/dummy.py View File

90
     # @note rel2type relations. Superior is the LeType from the EmClass and subordinate the LeType for the EmType
90
     # @note rel2type relations. Superior is the LeType from the EmClass and subordinate the LeType for the EmType
91
     # @param lesup LeType : LeType child class instance that is from the EmClass containing the rel2type field
91
     # @param lesup LeType : LeType child class instance that is from the EmClass containing the rel2type field
92
     # @param lesub LeType : LeType child class instance that is from the EmType linked by the rel2type field ( @ref EditorialModel.fieldtypes.rel2type.EmFieldType.rel_to_type_id )
92
     # @param lesub LeType : LeType child class instance that is from the EmType linked by the rel2type field ( @ref EditorialModel.fieldtypes.rel2type.EmFieldType.rel_to_type_id )
93
+    # @param rank int : Begin at 0 ?
93
     # @return The relation_id if success else return False
94
     # @return The relation_id if success else return False
94
-    def add_related(self, lesup, lesub, **rel_attr):
95
+    def add_related(self, lesup, lesub, rank = 'last', **rel_attr):
95
         pass
96
         pass
96
     
97
     
97
     ## @brief Returns related LeType
98
     ## @brief Returns related LeType
98
     # @param leo LeType : The from LeType child class instance
99
     # @param leo LeType : The from LeType child class instance
99
     # @param letype LeType : The wanted LeType child class (not instance !)
100
     # @param letype LeType : The wanted LeType child class (not instance !)
100
     # @param get_sub bool : If True, leo will be the superior and we wants all subordinates of Type letype, else its the oposite, leo is the subordinates and we want superiors with Type letype
101
     # @param get_sub bool : If True, leo will be the superior and we wants all subordinates of Type letype, else its the oposite, leo is the subordinates and we want superiors with Type letype
101
-    # @return A dict with LeType instance as key and dict('id_relation':id, attr_name:attr_val, ...) as value
102
+    # @return A list of tuple( LeType instance, dict('id_relation':id,'rank':rank,  attr_name:attr_val, ...) ) ordered by rank
102
     def get_related(self, leo, letype, get_sub=True):
103
     def get_related(self, leo, letype, get_sub=True):
103
         pass
104
         pass
104
 
105
 
105
     ## @brief Fetch a relation
106
     ## @brief Fetch a relation
106
     # @param id_relation int : The relation identifier
107
     # @param id_relation int : The relation identifier
107
-    # @return a tuple(lesup, lesub, dict_attr) or False if no relation exists with this id
108
-    # @throw Exception if the nature is not NULL
109
-    def get_relation(self, id_relation):
108
+    # @return a dict{'id_relation':.., 'lesup':.., 'lesub':.., < if exists 'dict_attr':..>}
109
+    def get_relation(self, id_relation, no_attr = False):
110
         pass
110
         pass
111
 
111
 
112
     ## @brief Fetch all relations concerning an object (rel2type relations)
112
     ## @brief Fetch all relations concerning an object (rel2type relations)
115
     def get_relations(self, leo):
115
     def get_relations(self, leo):
116
         pass
116
         pass
117
 
117
 
118
+    ## @brief Set the rank of a relation identified by its ID
119
+    # @param id_relation int : relation ID
120
+    # @param rank int|str : 'first', 'last', or an integer value
121
+    def set_relation_rank(self, id_relation, rank):
122
+        pass
123
+
118
     ## @brief Delete a relation between two LeType
124
     ## @brief Delete a relation between two LeType
119
     # @note It will deleted a relation in a rel2type between lesup.Class and lesub.Type
125
     # @note It will deleted a relation in a rel2type between lesup.Class and lesub.Type
120
     # @param id_relation int : The relation identifier
126
     # @param id_relation int : The relation identifier

+ 101
- 11
leobject/datasources/ledatasourcesql.py View File

198
 
198
 
199
         return prepared_filters
199
         return prepared_filters
200
 
200
 
201
+
202
+    ## @brief Make a relation between 2 LeType
203
+    # @note rel2type relations. Superior is the LeType from the EmClass and subordinate the LeType for the EmType
204
+    # @param lesup LeType : LeType child class instance that is from the EmClass containing the rel2type field
205
+    # @param lesub LeType : LeType child class instance that is from the EmType linked by the rel2type field ( @ref EditorialModel.fieldtypes.rel2type.EmFieldType.rel_to_type_id )
206
+    # @return The relation_id if success else return False
207
+    def add_related(self, lesup, lesub, rank, **rel_attr):
208
+        with self.connection as cur:
209
+            #First step : relation table insert
210
+            sql = insert(MySQL.relations_table_name,{
211
+                        'id_sup': lesup.lodel_id, 
212
+                        'id_sub': lesub.lodel_id,
213
+                        'rank': 0, #default value that will be set latter
214
+                    })
215
+            cur.execute(sql)
216
+            relation_id = cur.lastrowid
217
+
218
+
219
+            if len(rel_attr) > 0:
220
+                #There is some relation attribute to add in another table
221
+                attr_table = get_r2t2table_name(lesup._leclass.__name__, lesub.__class__.__name__)
222
+                rel_attr['id_relation'] = relation_id
223
+                sql = insert(attr_table, rel_attr)
224
+                cur.execute(sql)
225
+        self._set_relation_rank(id_relation, rank)
226
+        return relation_id
227
+    
228
+    ## @brief Set the rank of a relation identified by its ID
229
+    # @param id_relation int : relation ID
230
+    # @param rank int|str : 'first', 'last', or an integer value
231
+    # @throw ValueError if rank is not valid
232
+    # @throw leobject.leobject.LeObjectQueryError if id_relation don't exists
233
+    def set_relation_rank(self, id_relation, rank):
234
+        self._check_rank(rank)
235
+        self._set_relation_rank(id_relation, rank)
236
+
237
+        
238
+
239
+    ## @brief Set the rank of a relation identified by its ID
240
+    #
241
+    # @note this solution is not the more efficient solution but it
242
+    # garantee that ranks are continuous and starts at 1
243
+    # @warning there is no way to fail on rank parameters even giving very bad parameters, if you want a method that may fail on rank use set_relation_rank() instead
244
+    # @param id_relation int : relation ID
245
+    # @param rank int|str : 'first', 'last', or an integer value
246
+    # @throw leobject.leobject.LeObjectQueryError if id_relation don't exists
247
+    def _set_relation_rank(self, id_relation, rank):
248
+        ret = self.get_relation(id_relation, no_attr = True)
249
+        if not ret:
250
+            raise leobject.leobject.LeObjectQueryError("No relation with id_relation = %d"%id_relation)
251
+        lesup = ret['lesup']
252
+        lesub = ret['lesup']
253
+        cur_rank = ret['rank']
254
+        rank = 1 if rank == 'first' or rank < 1 else rank
255
+        if cur_rank == rank:
256
+            return True
257
+
258
+        relations = self.get_related(lesup, lesub.__class__, get_sub=True)
259
+
260
+        if not isinstance(rank, int) or rank > len(relations):
261
+            rank = len(relations)
262
+            if cur_rank == rank:
263
+                return True
264
+        
265
+        #insert the relation at the good position
266
+        our_relation = relations.pop(cur_rank)
267
+        relations.insert(our_relation, rank)
268
+
269
+        #gathering (relation_id, new_rank)
270
+        rdatas = [ (attrs['relation_id'], new_rank+1) for new_rank,(sup, sub, attrs) in enumerate(relations) ]
271
+        sql = insert(MySQL.relations_table_name, columns=(MySQL.relations_pkname, 'rank'), values = rdatas, on_duplicate_key_update={'rank',mosql.util.raw('VALUES(`rank`)')})
272
+
273
+    
274
+    ## @brief Check a rank value
275
+    # @param rank int | str : Can be an integer >= 1 , 'first' or 'last'
276
+    # @throw ValueError if the rank is not valid
277
+    def _check_rank(self, rank):
278
+        if isinstance(rank, str) and rank != 'first' and rank != 'last':
279
+            raise ValueError("Invalid rank value : %s"%rank)
280
+        elif isinstance(rank, int) and rank < 1:
281
+            raise ValueError("Invalid rank value : %d"%rank)
282
+        else:
283
+            raise ValueError("Invalid rank type : %s"%type(rank))
284
+    
201
     ## @brief Link two object given a relation nature, depth and rank
285
     ## @brief Link two object given a relation nature, depth and rank
202
     # @param lesup LeObject : a LeObject
286
     # @param lesup LeObject : a LeObject
203
     # @param lesub LeObject : a LeObject
287
     # @param lesub LeObject : a LeObject
230
             pk_where = {MySQL.relations_pkname:id_relation}
314
             pk_where = {MySQL.relations_pkname:id_relation}
231
             if not MySQL.fk_on_delete_cascade and len(lesup._linked_types[lesub.__class__]) > 0:
315
             if not MySQL.fk_on_delete_cascade and len(lesup._linked_types[lesub.__class__]) > 0:
232
                 #Delete the row in the relation attribute table
316
                 #Delete the row in the relation attribute table
233
-                (lesup, lesub, _) = self.get_relation(id_relation, no_attr = False)
317
+                ret = self.get_relation(id_relation, no_attr = False)
318
+                lesup = ret['lesup']
319
+                lesub = ret['lesub']
234
                 sql = delete(MySQL.relations_table_name, pk_where)
320
                 sql = delete(MySQL.relations_table_name, pk_where)
235
                 if cur.execute(sql) != 1:
321
                 if cur.execute(sql) != 1:
236
                     raise RuntimeError("Unknown SQL Error")
322
                     raise RuntimeError("Unknown SQL Error")
242
     
328
     
243
     ## @brief Fetch a relation
329
     ## @brief Fetch a relation
244
     # @param id_relation int : The relation identifier
330
     # @param id_relation int : The relation identifier
245
-    # @param no_attr bool : If true put None in place of relations attributes
246
-    # @return a tuple(lesup, lesub, dict_attr) or False if no relation exists with this id
247
-    # @throw Exception if the nature is not NULL
331
+    # @param no_attr bool : If true dont fetch rel_attr
332
+    # @return a dict{'id_relation':.., 'lesup':.., 'lesub':..,'rank':.., 'depth':.., #if not none#'nature':.., #if exists#'dict_attr':..>}
248
     #
333
     #
249
     # @todo TESTS
334
     # @todo TESTS
250
     def get_relation(self, id_relation, no_attr = False):
335
     def get_relation(self, id_relation, no_attr = False):
336
+        relation = dict()
251
         with self.connection as cur:
337
         with self.connection as cur:
252
             sql = select(MySQL.relation_table_name, {MySQL.relations_pkname: id_relation})
338
             sql = select(MySQL.relation_table_name, {MySQL.relations_pkname: id_relation})
253
             if cur.execute(sql) != 1:
339
             if cur.execute(sql) != 1:
264
             leobj = leobject.lefactory.LeFactory.leobj_from_name('LeObject')
350
             leobj = leobject.lefactory.LeFactory.leobj_from_name('LeObject')
265
             lesup = leobj.uid2leobj(res['id_sup'])
351
             lesup = leobj.uid2leobj(res['id_sup'])
266
             lesub = leobj.uid2leobj(res['id_sub'])
352
             lesub = leobj.uid2leobj(res['id_sub'])
353
+
354
+            relation['id_relation'] = res['id_relation']
355
+            relation['lesup'] = lesup
356
+            relation['lesub'] = lesub
357
+            relation['rank'] = rank
358
+            relation['depth'] = depth
359
+            if not (res['nature'] is None):
360
+                relation['nature'] = res['nature']
267
             
361
             
268
-            if no_attr:
269
-                attrs = None
270
-            elif len(lesup._linked_types[lesub.__class__]) == 0:
271
-                #No relation attributes
272
-                attrs = dict()
273
-            else:
362
+            if not no_attr and res['nature'] is None and len(lesup._linked_types[lesub.__class__]) != 0:
274
                 #Fetch relation attributes
363
                 #Fetch relation attributes
275
                 rel_attr_table = MySQL.get_r2t2table_name(lesup.__class__.__name__, lesub.__class__.__name__)
364
                 rel_attr_table = MySQL.get_r2t2table_name(lesup.__class__.__name__, lesub.__class__.__name__)
276
                 sql = select(MySQL.rel_attr_table, {MySQL.relations_pkname: id_relation})
365
                 sql = select(MySQL.rel_attr_table, {MySQL.relations_pkname: id_relation})
284
                 if len(res) > 1:
373
                 if len(res) > 1:
285
                     raise RuntimeError("When selecting on primary key, get more than one result. Bailout")
374
                     raise RuntimeError("When selecting on primary key, get more than one result. Bailout")
286
                 attrs = res[0]
375
                 attrs = res[0]
376
+            relation['rel_attr'] = attrs
287
 
377
 
288
-            return (lesup, lesub, attrs)
378
+            return relation
289
 
379
 
290
     ## @brief Fetch all relations concerning an object (rel2type relations)
380
     ## @brief Fetch all relations concerning an object (rel2type relations)
291
     # @param leo LeType : LeType child instance
381
     # @param leo LeType : LeType child instance

+ 2
- 2
leobject/leobject.py View File

163
     # @todo Code factorisation on relation check
163
     # @todo Code factorisation on relation check
164
     # @todo unit tests
164
     # @todo unit tests
165
     @classmethod
165
     @classmethod
166
-    def link_together(cls, lesup, lesub, **rel_attr):
166
+    def link_together(cls, lesup, lesub, rank = 'last', **rel_attr):
167
         if lesub.__class__ not in lesup._linked_types.keys():
167
         if lesub.__class__ not in lesup._linked_types.keys():
168
             raise LeObjectError("Relation error : %s cannot be linked with %s"%(lesup.__class__.__name__, lesub.__class__.__name__))
168
             raise LeObjectError("Relation error : %s cannot be linked with %s"%(lesup.__class__.__name__, lesub.__class__.__name__))
169
 
169
 
179
             if rel_attr == e_attrs:
179
             if rel_attr == e_attrs:
180
                 raise LeObjectError("Relation error : a relation with the same attributes already exists")
180
                 raise LeObjectError("Relation error : a relation with the same attributes already exists")
181
 
181
 
182
-        return cls._datasource.add_related(lesup, lesub, **rel_attr)
182
+        return cls._datasource.add_related(lesup, lesub, rank, **rel_attr)
183
     
183
     
184
     ## @brief Get related objects
184
     ## @brief Get related objects
185
     # @param leo LeType(instance) : LeType child class instance
185
     # @param leo LeType(instance) : LeType child class instance

Loading…
Cancel
Save