Browse Source

Tests + bugfix on LeObject.get method

- Changed the way of instanciating results object in get (using __new__ )
Yann Weber 8 years ago
parent
commit
33b9ad84c6
3 changed files with 99 additions and 68 deletions
  1. 41
    64
      lodel/leapi/leobject.py
  2. 4
    4
      lodel/leapi/query.py
  3. 54
    0
      tests/leapi/test_leobject.py

+ 41
- 64
lodel/leapi/leobject.py View File

26
     # @param set_callback method : The LeObject.set_datas() method of corresponding LeObject class
26
     # @param set_callback method : The LeObject.set_datas() method of corresponding LeObject class
27
     # @param get_callback method : The LeObject.get_datas() method of corresponding LeObject class
27
     # @param get_callback method : The LeObject.get_datas() method of corresponding LeObject class
28
     def __init__(self, fieldnames_callback, set_callback, get_callback):
28
     def __init__(self, fieldnames_callback, set_callback, get_callback):
29
-        self.__setter = set_callback
30
-        self.__getter = get_callback
29
+        self._setter = set_callback
30
+        self._getter = get_callback
31
     
31
     
32
     ##@brief Provide read access to datas values
32
     ##@brief Provide read access to datas values
33
     # @note Read access should be provided for all fields
33
     # @note Read access should be provided for all fields
34
     # @param fname str : Field name
34
     # @param fname str : Field name
35
     def __getattribute__(self, fname):
35
     def __getattribute__(self, fname):
36
-        return self.__getter(fname)
36
+        getter = super().__getattribute__('_getter')
37
+        return getter(fname)
37
     
38
     
38
     ##@brief Provide write access to datas values
39
     ##@brief Provide write access to datas values
39
     # @note Write acces shouldn't be provided for internal or immutable fields
40
     # @note Write acces shouldn't be provided for internal or immutable fields
40
     # @param fname str : Field name
41
     # @param fname str : Field name
41
     # @param fval * : the field value
42
     # @param fval * : the field value
42
     def __setattribute__(self, fname, fval):
43
     def __setattribute__(self, fname, fval):
43
-        return self.__setter(fname, fval)
44
+        setter = super().__getattribute__('_setter')
45
+        return setter(fname, fval)
44
         
46
         
45
 
47
 
46
 class LeObject(object):
48
 class LeObject(object):
56
     ##@breif Read & write datasource ( see @ref lodel2_datasources )
58
     ##@breif Read & write datasource ( see @ref lodel2_datasources )
57
     _rw_datasource = None
59
     _rw_datasource = None
58
 
60
 
59
-    ##@brief Construct an object representing an Editorial component
60
-    # @note Can be considered as EmClass instance
61
-    def __init__(self, **kwargs):
62
-        if self._abstract:
63
-            raise NotImplementedError("%s is abstract, you cannot instanciate it." % self.__class__.__name__ )
61
+    def __new__(cls, **kwargs):
62
+        
63
+        self = object.__new__(cls)
64
         ##@brief A dict that stores fieldvalues indexed by fieldname
64
         ##@brief A dict that stores fieldvalues indexed by fieldname
65
         self.__datas = { fname:None for fname in self._fields }
65
         self.__datas = { fname:None for fname in self._fields }
66
         ##@brief Store a list of initianilized fields when instanciation not complete else store True
66
         ##@brief Store a list of initianilized fields when instanciation not complete else store True
67
         self.__initialized = list()
67
         self.__initialized = list()
68
         ##@brief Datas accessor. Instance of @ref LeObjectValues
68
         ##@brief Datas accessor. Instance of @ref LeObjectValues
69
         self.d = LeObjectValues(self.fieldnames, self.set_data, self.data)
69
         self.d = LeObjectValues(self.fieldnames, self.set_data, self.data)
70
+        for fieldname, fieldval in kwargs.items():
71
+            self.__datas[fieldname] = fieldval
72
+            self.__initialized.append(fieldname)
73
+        self.__set_initialized()
74
+        return self
75
+
76
+    ##@brief Construct an object representing an Editorial component
77
+    # @note Can be considered as EmClass instance
78
+    def __init__(self, **kwargs):
79
+        if self._abstract:
80
+            raise NotImplementedError("%s is abstract, you cannot instanciate it." % self.__class__.__name__ )
70
 
81
 
71
         # Checks that uid is given
82
         # Checks that uid is given
72
         for uid_name in self._uid:
83
         for uid_name in self._uid:
75
             self.__datas[uid_name] = kwargs[uid_name]
86
             self.__datas[uid_name] = kwargs[uid_name]
76
             del(kwargs[uid_name])
87
             del(kwargs[uid_name])
77
             self.__initialized.append(uid_name)
88
             self.__initialized.append(uid_name)
78
-        
89
+
79
         # Processing given fields
90
         # Processing given fields
80
         allowed_fieldnames = self.fieldnames(include_ro = False)
91
         allowed_fieldnames = self.fieldnames(include_ro = False)
81
-        err_list = list()
92
+        err_list = dict()
82
         for fieldname, fieldval in kwargs.items():
93
         for fieldname, fieldval in kwargs.items():
83
             if fieldname not in allowed_fieldnames:
94
             if fieldname not in allowed_fieldnames:
84
                 if fieldname in self._fields:
95
                 if fieldname in self._fields:
85
-                    err_list.append(
86
-                        LeApiError("Value given for internal field : '%s'" % fieldname)
87
-                    )
96
+                    err_list[fieldname] = LeApiError(
97
+                        "Value given but the field is internal")
88
                 else:
98
                 else:
89
-                    err_list.append(
90
-                        LeApiError("Unknown fieldname : '%s'" % fieldname)
91
-                    )
99
+                    err_list[fieldname] = LeApiError(
100
+                        "Unknown fieldname : '%s'" % fieldname)
92
             else:
101
             else:
93
                 self.__datas[fieldname] = fieldval
102
                 self.__datas[fieldname] = fieldval
94
                 self.__initialized.append(fieldname)
103
                 self.__initialized.append(fieldname)
95
         if len(err_list) > 0:
104
         if len(err_list) > 0:
96
-            raise LeApiErrors(err_list)
105
+            raise LeApiErrors(msg = "Unable to __init__ %s" % self.__class__,
106
+                exceptions = err_list)
97
         self.__set_initialized()
107
         self.__set_initialized()
98
     
108
     
99
     #-----------------------------------#
109
     #-----------------------------------#
537
         deleted += result
547
         deleted += result
538
         return deleted
548
         return deleted
539
             
549
             
540
-    
541
-    ## @brief Load an instance of LeObject
542
-    #@param uid a list of tuple (uid_field_nane, value) ou a single value
543
-    #@return an instance of a subclass of LeObject
544
-    @classmethod
545
-    def load(cls, uid_tuples):
546
-        query_filter = list()
547
-        uids = cls._uid
548
-        if uids.isinstance(tuple):
549
-            if not uid_tuples.isinstance(list):
550
-                raise AttributeError ("In %s:load : uid must be a list of tuple" % cls.__name__)
551
-            elif len(uid_tuples) != len(uids):
552
-                raise AttributeError ("In %s:load : must have %d uid fields" % len(uids))
553
-            for fieldname, fieldvalue in uid_tuples:
554
-                if fieldname in uids:
555
-                    dhdl = cls.data_handler(fieldname)
556
-                    if dhdl.check_data_value(fieldvalue)[1] is None:
557
-                        query_filter.append((fieldname, '=', fieldvalue))
558
-                    else:
559
-                        raise AttributeError("n %s:load :%s not a valid value for %s" % (fieldvalue, fieldname))
560
-                else:
561
-                    raise AttributeError ("In %s:load :%s not a uid field for class %s" % (fieldname, cls.__name__))
562
-        else:
563
-            dhdl = cls.data_handler(uids)
564
-            if dhdl.check_data_value(uid_tuples)[1] is None:
565
-                query_filter.append((uids, '=', uid_tuples))
566
-            else:
567
-                raise AttributeError("n %s:load :%s not a valid value for %s" % (uid_tuples, uids))
568
-        query = LeGetQuery(cls, query_filter, limit = 1)
569
-        try:
570
-            result=query.execute()
571
-        except LeQueryError as err:
572
-            print("Unable to load object of type %s" % cls.__name__)
573
-            raise err
574
-        
575
-        return cls.name2class(res[CLASS_IDENTIFIER])(result[0])
576
-        
577
     ## @brief Get instances of LeObject
550
     ## @brief Get instances of LeObject
578
     #
551
     #
579
     #@param target_class LeObject : class of object the query is about
552
     #@param target_class LeObject : class of object the query is about
589
     #@return a list of items (lists of (fieldname, fieldvalue))
562
     #@return a list of items (lists of (fieldname, fieldvalue))
590
     @classmethod
563
     @classmethod
591
     def get(cls, query_filters, field_list=None, order=None, group=None, limit=None, offset=0):
564
     def get(cls, query_filters, field_list=None, order=None, group=None, limit=None, offset=0):
592
-        if isinstance(cls._uids, tuple):
593
-            for uid in cls._uids:
594
-                if uid not in field_list:
595
-                    raise AttributeError("In %s:get : Cannot instanciate a LeObject without it's identifier" % cls.__name__)
565
+        if field_list is None:
566
+            field_list = self.fieldnames(True)
596
         else:
567
         else:
597
-            if uid not in field_list:
598
-                raise AttributeError("In %s:get : Cannot instanciate a LeObject without it's identifier" % cls.__name__)
599
-
568
+            for uid in [ uidname
569
+                for uidname in cls.uid_fieldname()
570
+                if uidname not in field_list ]:
571
+                field_list.append(uid)
572
+            if CLASS_ID_FIELDNAME not in field_list:
573
+                field_list.append(CLASS_ID_FIELDNAME)
600
         try:
574
         try:
601
-            query = LeGetQuery(cls, query_filter, field_list = field_list, order = order, group = group, limit = limit, offset = offset)
575
+            query = LeGetQuery(
576
+                cls, query_filters = query_filters, field_list = field_list,
577
+                order = order, group = group, limit = limit, offset = offset)
602
         except ValueError as err:
578
         except ValueError as err:
603
             raise err
579
             raise err
604
             
580
             
609
         
585
         
610
         objects = list()
586
         objects = list()
611
         for res in result:
587
         for res in result:
612
-            inst = cls.name2class(res[CLASS_ID_FIELDNAME])(res)
588
+            res_cls = cls.name2class(res[CLASS_ID_FIELDNAME])
589
+            inst = res_cls.__new__(res_cls,**res)
613
             objects.append(inst)
590
             objects.append(inst)
614
         
591
         
615
         return objects
592
         return objects

+ 4
- 4
lodel/leapi/query.py View File

199
                 for tclass, tfield in ref_dict.items():
199
                 for tclass, tfield in ref_dict.items():
200
                     query = LeGetQuery(
200
                     query = LeGetQuery(
201
                         target_class = tclass,
201
                         target_class = tclass,
202
-                        query_filter = [(tfield, op, value)],
202
+                        query_filters = [(tfield, op, value)],
203
                         field_list = [tfield])
203
                         field_list = [tfield])
204
                     subq.append((rfield, query))
204
                     subq.append((rfield, query))
205
         self.subqueries = subq
205
         self.subqueries = subq
598
     #@param group list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
598
     #@param group list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
599
     #@param limit int : The maximum number of returned results
599
     #@param limit int : The maximum number of returned results
600
     #@param offset int : offset
600
     #@param offset int : offset
601
-    def __init__(self, target_class, query_filter, **kwargs):
602
-        super().__init__(target_class, query_filter)
601
+    def __init__(self, target_class, query_filters, **kwargs):
602
+        super().__init__(target_class, query_filters)
603
         
603
         
604
         ##@brief The fields to get
604
         ##@brief The fields to get
605
         self.__field_list = None
605
         self.__field_list = None
631
             self.__group = kwargs['group']
631
             self.__group = kwargs['group']
632
         if 'limit' in kwargs:
632
         if 'limit' in kwargs:
633
             try:
633
             try:
634
-                self.__limit = int(kwargs[limit])
634
+                self.__limit = int(kwargs['limit'])
635
                 if self.__limit <= 0:
635
                 if self.__limit <= 0:
636
                     raise ValueError()
636
                     raise ValueError()
637
             except ValueError:
637
             except ValueError:

+ 54
- 0
tests/leapi/test_leobject.py View File

189
                     pass
189
                     pass
190
                 mock_init.assert_called_once_with(
190
                 mock_init.assert_called_once_with(
191
                     dyncode.Person, [('lodel_id', '=', 1)])
191
                     dyncode.Person, [('lodel_id', '=', 1)])
192
+        
193
+        with patch.object(
194
+            LeUpdateQuery, 'execute', return_value = None) as mock_update:
195
+            with patch.object(
196
+                LeObject, 'datas', return_value = {
197
+                    'lodel_id': 1, 'firstname': 'foo', 'lastname': 'bar',
198
+                    'fullname': 'Foo Bar', 'alias': None }) as mock_datas:
199
+            
200
+                inst = dyncode.Person(
201
+                    lodel_id = 1, firstname = "foo", lastname = "bar")
202
+                inst.update()
203
+                mock_update.assert_called_once_with({
204
+                    'lodel_id': 1, 'firstname': 'foo', 'lastname': 'bar',
205
+                    'fullname': 'Foo Bar', 'alias': None })
206
+                    
207
+    
208
+    def test_get(self):
209
+        """ Checking that LeObject.get method calls LeGetQuery
210
+            correctly """
211
+        get_args = {
212
+            'query_filters': ['lodel_id = 1'],
213
+            'field_list': ['firstname'],
214
+            'order': ['firstname'],
215
+            'group': ['alias'],
216
+            'limit': 42,
217
+            'offset': 24}
218
+
219
+        with patch.object(
220
+            LeGetQuery, '__init__', return_value = None) as mock_init:
221
+            
222
+            try:
223
+                dyncode.Person.get(**get_args)
224
+            except AttributeError:
225
+                pass
226
+
227
+            mock_init.assert_called_once_with(
228
+                dyncode.Person,
229
+                **get_args)
230
+
231
+        ret_val = [{
232
+            'lodel_id': 1,
233
+            'firstname': 'foo',
234
+            'lastname': 'bar',
235
+            'fullname': 'foo bar',
236
+            'alias': None,
237
+            'classname': 'Person'}]
238
+        with patch.object(
239
+            LeGetQuery, 'execute', return_value = ret_val) as mock_execute:
240
+            results = dyncode.Person.get(**get_args)
241
+            mock_execute.assert_called_once_with()
242
+            res = results[0]
243
+            self.assertEqual(res.d.lodel_id, 1)
244
+            self.assertEqual(res.d.firstname, 'foo')
245
+            self.assertEqual(res.d.lastname, 'bar')

Loading…
Cancel
Save