mirror of
https://github.com/yweber/lodel2.git
synced 2026-03-17 08:42:01 +01:00
Implements relation stuff + a lot of fixes in tests and in leo fieldtypes validation stack
This commit is contained in:
parent
bde008a68c
commit
67dbe5c097
6 changed files with 90 additions and 90 deletions
|
|
@ -3,7 +3,7 @@
|
|||
import leapi.letype as letype
|
||||
import leapi.leclass as leclass
|
||||
|
||||
from .generic import GenericFieldType
|
||||
from .generic import GenericFieldType, FieldTypeError
|
||||
|
||||
class EmFieldType(GenericFieldType):
|
||||
|
||||
|
|
@ -24,13 +24,30 @@ class EmFieldType(GenericFieldType):
|
|||
return (value, None)
|
||||
|
||||
## @brief If field value is an integer, returns a partially instanciated LeObject (only with an ID)
|
||||
# @todo what should we do if the get fails ? Raise ?
|
||||
def construct_data(self, lec, fname, datas):
|
||||
if isinstance(datas[fname], str):
|
||||
# Cast to int
|
||||
try:
|
||||
datas[fname] = int(datas[fname])
|
||||
except ValueError as e:
|
||||
raise e # Raise Here !?
|
||||
if datas[fname].is_leobject():
|
||||
# Its an object not populated (we dont now its type)
|
||||
datas[fname] = datas[fname].lodel_id #Optimize here giving only class_id and type_id to populate ?
|
||||
if isinstance(datas[fname], int):
|
||||
leobject = lec.name2class('LeObject')
|
||||
return leobject(datas[fname])
|
||||
else:
|
||||
return datas[fname]
|
||||
# Get instance with id
|
||||
resget = lec.name2class('LeObject').get(['lodel_id = %d' % datas[fname]])
|
||||
if resget is None or len(resget) != 1:
|
||||
# Bad filter or bad id... raise ?
|
||||
raise Exception("BAAAAAD")
|
||||
return datas[fname]
|
||||
|
||||
## @brief checks datas consistency
|
||||
# @param lec LeCrud : A LeCrud child instance
|
||||
# @param fname str : concerned field name
|
||||
# @param datas dict : Datas of lec
|
||||
# @return True if ok else an Exception instance
|
||||
def check_data_consistency(self, lec, fname, datas):
|
||||
if self.superior:
|
||||
return self.check_sup_consistency(lec, fname, datas)
|
||||
|
|
@ -38,7 +55,14 @@ class EmFieldType(GenericFieldType):
|
|||
return self.check_sub_consistency(lec, fname, datas)
|
||||
|
||||
def check_sup_consistency(self, lec, fname, datas):
|
||||
if lec.implements_lerel2type():
|
||||
# Checking consistency for a rel2type relation
|
||||
lesup = datas['lesup']
|
||||
lesub = datas['lesub']
|
||||
if lesub.__class__ not in lesup._linked_types:
|
||||
return FieldTypeError("Rel2type not authorized between %s and %s"%(lesup, lesub))
|
||||
pass
|
||||
|
||||
|
||||
def check_sub_consistency(self, lec, fname, datas):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -272,7 +272,9 @@ class _LeCrud(object):
|
|||
@classmethod
|
||||
def insert(cls, datas, classname = None):
|
||||
callcls = cls if classname is None else cls.name2class(classname)
|
||||
if not callcls.is_letype() and not callcls.implements_lerelation():
|
||||
if not callcls:
|
||||
raise LeApiErrors("Error when inserting",[ValueError("The class '%s' was not found"%classname)])
|
||||
if not callcls.implements_letype() and not callcls.implements_lerelation():
|
||||
raise ValueError("You can only insert relations and LeTypes objects but tying to insert a '%s'"%callcls.__name__)
|
||||
insert_datas = callcls.prepare_datas(datas, complete = True, allow_internal = False)
|
||||
return callcls._datasource.insert(callcls, **insert_datas)
|
||||
|
|
@ -303,6 +305,7 @@ class _LeCrud(object):
|
|||
## @brief Build a filter to select an object with a specific ID
|
||||
# @warning assert that the uid is not composed with multiple fieldtypes
|
||||
# @return A filter of the form tuple(UID, '=', self.UID)
|
||||
# @todo This method should not be private
|
||||
def _id_filter(self):
|
||||
id_name = self.uidname()
|
||||
return ( id_name, '=', getattr(self, id_name) )
|
||||
|
|
|
|||
|
|
@ -55,38 +55,19 @@ class _LeRelation(lecrud._LeCrud):
|
|||
# @return prepared and checked filters
|
||||
@classmethod
|
||||
def _prepare_filters(cls, filters_l):
|
||||
filters = list()
|
||||
res = list()
|
||||
rel = list()
|
||||
|
||||
for filter_item in filters_l:
|
||||
if isinstance(filter_item, tuple):
|
||||
filters.append(filter_item)
|
||||
else:
|
||||
filter_item_data = filter_item.split(" ")
|
||||
if len(filter_item_data) == 3:
|
||||
if filter_item_data[0] in cls._lesub_fieldtype.keys():
|
||||
filter_item_data[2] = cls._lesub_fieldtype[filter_item_data[0]].construct_data(
|
||||
cls,
|
||||
filter_item_data[0],
|
||||
{filter_item_data[0]: int(filter_item_data[2])}
|
||||
)
|
||||
elif filter_item_data[0] in cls._lesup_fieldtype.keys():
|
||||
filter_item_data[2] = cls._lesup_fieldtype[filter_item_data[0]].construct_data(
|
||||
cls,
|
||||
filter_item_data[0],
|
||||
{filter_item_data[0]: int(filter_item_data[2])}
|
||||
)
|
||||
|
||||
filters.append(tuple(filter_item_data))
|
||||
|
||||
for field, operator, value in filters:
|
||||
if field.startswith('superior') or field.startswith('subordinate'):
|
||||
rel.append((field, operator, value))
|
||||
else:
|
||||
res.append((field, operator, value))
|
||||
|
||||
return (res, rel)
|
||||
filters, rel_filters = super()._prepare_filters(filters_l)
|
||||
res_filters = list()
|
||||
for field, op, value in filters:
|
||||
if field in ['lesup', 'lesub']:
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError as e:
|
||||
raise LeApiDataCheckError("Wrong value given for '%s'"%field)
|
||||
if isinstance(value, int):
|
||||
value = cls.name2class('LeObject')(value)
|
||||
res_filters.append( (field, op, value) )
|
||||
return res_filters, rel_filters
|
||||
|
||||
@classmethod
|
||||
## @brief deletes a relation between two objects
|
||||
|
|
@ -115,12 +96,18 @@ class _LeRel2Type(_LeRelation):
|
|||
## @brief Delete current instance from DB
|
||||
def delete(self):
|
||||
lecrud._LeCrud._delete(self)
|
||||
|
||||
|
||||
## @brief Implements insert for rel2type
|
||||
# @todo checks when autodetecing the rel2type class
|
||||
@classmethod
|
||||
def insert(cls, datas, classname):
|
||||
def insert(cls, datas, classname = None):
|
||||
#Set the nature
|
||||
if 'nature' not in datas:
|
||||
datas['nature'] = None
|
||||
cls.name2class('LeCrud').insert(datas, classname)
|
||||
if cls == cls.name2class('LeRel2Type') and classname is None:
|
||||
# autodetect the rel2type child class
|
||||
classname = relname(datas['lesup'], datas['lesub'])
|
||||
super().insert(datas, classname)
|
||||
|
||||
## @brief Given a superior and a subordinate, returns the classname of the give rel2type
|
||||
# @param lesupclass LeClass : LeClass child class (can be a LeType or a LeClass child)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
# @note LeObject will be generated by leapi.lefactory.LeFactory
|
||||
|
||||
import leapi
|
||||
from leapi.lecrud import _LeCrud, LeApiDataCheckError
|
||||
from leapi.lecrud import _LeCrud, LeApiDataCheckError, LeApiQueryError
|
||||
from leapi.leclass import _LeClass
|
||||
from leapi.leobject import LeObjectError
|
||||
|
||||
|
|
@ -82,8 +82,11 @@ class _LeType(_LeClass):
|
|||
rel_filters = []
|
||||
|
||||
fdatas = self._datasource.select(self.__class__, field_list, filters, rel_filters)
|
||||
for fname, fdats in fdatas[0].items():
|
||||
setattr(self, name, value)
|
||||
if fdatas is None or len(fdatas) == 0:
|
||||
raise LeApiQueryError("Error when trying to populate an object. For type %s id : %d"% (self.__class__.__name__, self.lodel_id))
|
||||
|
||||
for fname, fval in fdatas[0].items():
|
||||
setattr(self, fname, fval)
|
||||
|
||||
## @brief Get a fieldname:value dict
|
||||
# @return A dict with field name as key and the field value as value
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
import unittest
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
from collections import OrderedDict
|
||||
|
||||
import EditorialModel
|
||||
import DataSource.dummy
|
||||
|
|
@ -129,16 +130,18 @@ class LeHierarch(LeRelationTestCase):
|
|||
queries = [
|
||||
(
|
||||
{
|
||||
'lesup': Rubrique(7),
|
||||
'lesub': Numero(42),
|
||||
'lesup': Rubrique(7, class_id = Rubrique._class_id, type_id = Rubrique._type_id),
|
||||
'lesub': Numero(42, class_id = Numero._class_id, type_id = Numero._type_id),
|
||||
'nature': 'parent',
|
||||
},
|
||||
{
|
||||
'lesup': Rubrique(7),
|
||||
'lesub': Numero(42),
|
||||
'nature': 'parent',
|
||||
}
|
||||
},
|
||||
),
|
||||
]
|
||||
""" # Those tests are not good
|
||||
(
|
||||
{
|
||||
'lesup': 7,
|
||||
|
|
@ -176,6 +179,7 @@ class LeHierarch(LeRelationTestCase):
|
|||
}
|
||||
)
|
||||
]
|
||||
"""
|
||||
for query, equery in queries:
|
||||
LeHierarch.insert(query)
|
||||
dsmock.assert_called_once_with(LeHierarch, **equery)
|
||||
|
|
@ -207,7 +211,6 @@ class LeHierarch(LeRelationTestCase):
|
|||
|
||||
class LeRel2TypeTestCase(LeRelationTestCase):
|
||||
|
||||
@unittest.skip("Wait for implmentation (mainly implements nature = none for non hierarch)")
|
||||
@patch('DataSource.dummy.leapidatasource.DummyDatasource.insert')
|
||||
def test_insert(self, dsmock):
|
||||
""" test LeHierach update method"""
|
||||
|
|
@ -224,20 +227,14 @@ class LeRel2TypeTestCase(LeRelationTestCase):
|
|||
'lesub': Personne(24),
|
||||
'adresse': None,
|
||||
},
|
||||
{
|
||||
'lesup': 42,
|
||||
'lesub': Personne(24),
|
||||
'adresse': None,
|
||||
},
|
||||
|
||||
{
|
||||
'lesup': Article(42),
|
||||
'lesub': Personnes(24),
|
||||
'lesub': Personne(24),
|
||||
'adresse': "bar",
|
||||
},
|
||||
{
|
||||
'lesup': Textes(42),
|
||||
'lesub': Personnes(24),
|
||||
'lesub': Personne(24),
|
||||
'adresse': "foo",
|
||||
},
|
||||
]
|
||||
|
|
@ -246,7 +243,7 @@ class LeRel2TypeTestCase(LeRelationTestCase):
|
|||
Rel_Textes2Personne.insert(query)
|
||||
|
||||
eres = {'nature': None}
|
||||
eres.uopdate(query)
|
||||
eres.update(query)
|
||||
for fname in ('lesup', 'lesub'):
|
||||
if isinstance(eres[fname], int):
|
||||
eres[fname] = LeObject(eres[fname])
|
||||
|
|
@ -254,12 +251,11 @@ class LeRel2TypeTestCase(LeRelationTestCase):
|
|||
dsmock.assert_called_once_with(Rel_Textes2Personne, **eres)
|
||||
dsmock.reset_mock()
|
||||
|
||||
LeRel2Type.insert(query, "Rel_textes2personne")
|
||||
LeRel2Type.insert(query, "Rel_Textes2Personne")
|
||||
|
||||
dsmock.assert_called_once_with(Rel_Textes2Personne, **eres)
|
||||
dsmock.reset_mock()
|
||||
|
||||
@unittest.skip("Wait implementation to unskip")
|
||||
@patch('DataSource.dummy.leapidatasource.DummyDatasource.insert')
|
||||
def test_insert_fails(self, dsmock):
|
||||
""" test LeHierach update method"""
|
||||
|
|
@ -276,7 +272,7 @@ class LeRel2TypeTestCase(LeRelationTestCase):
|
|||
},
|
||||
{
|
||||
'lesup': Rubrique(42),
|
||||
'lesub': LeObject(24),
|
||||
'lesub': Rubrique(24),
|
||||
'adresse': None,
|
||||
},
|
||||
{
|
||||
|
|
@ -285,38 +281,24 @@ class LeRel2TypeTestCase(LeRelationTestCase):
|
|||
'adresse': 'foo',
|
||||
},
|
||||
{
|
||||
'lesup': 42,
|
||||
'lesub': 24,
|
||||
},
|
||||
{
|
||||
'lesup': 42,
|
||||
'lesub': 24,
|
||||
'adresse': None,
|
||||
'nature': "parent",
|
||||
},
|
||||
{
|
||||
'lesup': 42,
|
||||
'lesub': 24,
|
||||
'adresse': None,
|
||||
'nature': None,
|
||||
},
|
||||
{
|
||||
'lesup': 42,
|
||||
'lesub': 24,
|
||||
'adresse': None,
|
||||
'foofield': None,
|
||||
},
|
||||
{
|
||||
'lesup': 42,
|
||||
'lesub': 24,
|
||||
'id_relation': 1337,
|
||||
'adresse': None,
|
||||
'lesup': Article(42),
|
||||
'lesub': Numero(24),
|
||||
'adresse': 'foo',
|
||||
},
|
||||
]
|
||||
|
||||
for query in queries:
|
||||
with self.assertRaises(lecrud.LeApiQueryError):
|
||||
try:
|
||||
LeRel2Type.insert(query, 'Rel_textes2personne')
|
||||
|
||||
with self.assertRaises(lecrud.LeApiQueryError):
|
||||
self.fail("No exception raised")
|
||||
except Exception as e:
|
||||
if not isinstance(e, lecrud.LeApiErrors) and not isinstance(e, lecrud.LeApiDataCheckError):
|
||||
self.fail("Bad exception raised : ", e)
|
||||
|
||||
try:
|
||||
Rel_Textes2Personne.insert(query)
|
||||
self.fail("No exception raised")
|
||||
except Exception as e:
|
||||
if not isinstance(e, lecrud.LeApiErrors) and not isinstance(e, lecrud.LeApiDataCheckError):
|
||||
self.fail("Bad exception raised : ", e)
|
||||
|
|
|
|||
|
|
@ -72,8 +72,9 @@ class LeTypeMockDsTestCase(TestCase):
|
|||
|
||||
num = Numero(1, type_id = Numero._type_id)
|
||||
missing_fields = [f for f in Numero._fields if not (f in ['lodel_id', 'type_id'])]
|
||||
num.populate()
|
||||
dsmock.assert_called_once_with(Numero, missing_fields, [('lodel_id','=',1)],[])
|
||||
with self.assertRaises(leapi.lecrud.LeApiQueryError):
|
||||
num.populate()
|
||||
dsmock.assert_called_once_with(Numero, missing_fields, [('lodel_id','=',1)],[])
|
||||
|
||||
@patch('DataSource.dummy.leapidatasource.DummyDatasource.update')
|
||||
def test_update(self, dsmock):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue