mirror of
https://github.com/yweber/lodel2.git
synced 2026-09-15 22:20:29 +02:00
Some adjustments and corresions
This commit is contained in:
parent
a6ddb0a972
commit
2d8a9811e6
7 changed files with 193 additions and 142 deletions
|
|
@ -1,4 +1,4 @@
|
|||
#-*- co:ding: utf-8 -*-
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
## @package lodel.leapi.datahandlers.base_classes Define all base/abstract class for data handlers
|
||||
#
|
||||
|
|
@ -15,7 +15,7 @@ from lodel import logger
|
|||
##@brief Base class for all data handlers
|
||||
#@ingroup lodel2_datahandlers
|
||||
class DataHandler(object):
|
||||
|
||||
base_type = "type"
|
||||
_HANDLERS_MODULES = ('datas_base', 'datas', 'references')
|
||||
##@brief Stores the DataHandler childs classes indexed by name
|
||||
_base_handlers = None
|
||||
|
|
@ -27,7 +27,7 @@ class DataHandler(object):
|
|||
|
||||
##@brief List fields that will be exposed to the construct_data_method
|
||||
_construct_datas_deps = []
|
||||
|
||||
|
||||
directly_editable = True
|
||||
##@brief constructor
|
||||
# @param internal False | str : define whether or not a field is internal
|
||||
|
|
@ -38,9 +38,7 @@ class DataHandler(object):
|
|||
def __init__(self, **kwargs):
|
||||
if self.__class__ == DataHandler:
|
||||
raise NotImplementedError("Abstract class")
|
||||
|
||||
self.__arguments = kwargs
|
||||
|
||||
self.nullable = True
|
||||
self.uniq = False
|
||||
self.immutable = False
|
||||
|
|
@ -62,7 +60,7 @@ class DataHandler(object):
|
|||
@classmethod
|
||||
def is_reference(cls):
|
||||
return issubclass(cls, Reference)
|
||||
|
||||
|
||||
@classmethod
|
||||
def is_singlereference(cls):
|
||||
return issubclass(cls, SingleRef)
|
||||
|
|
@ -70,7 +68,6 @@ class DataHandler(object):
|
|||
def is_primary_key(self):
|
||||
return self.primary_key
|
||||
|
||||
|
||||
##@brief checks if a fieldtype is internal
|
||||
# @return bool
|
||||
def is_internal(self):
|
||||
|
|
@ -78,7 +75,7 @@ class DataHandler(object):
|
|||
|
||||
##brief check if a value can be nullable
|
||||
#@param value *
|
||||
#@throw DataNoneValid if value is None and nullable. LodelExceptions if not nullable
|
||||
#@throw DataNoneValid if value is None and nullable. LodelExceptions if not nullable
|
||||
#@return value (if not None)
|
||||
# @return value
|
||||
def _check_data_value(self, value):
|
||||
|
|
@ -88,7 +85,6 @@ class DataHandler(object):
|
|||
raise DataNoneValid("None with a nullable. This exeption is allowed")
|
||||
return value
|
||||
|
||||
|
||||
##@brief calls the data_field (defined in derived class) _check_data_value() method
|
||||
#@param value *
|
||||
#@return tuple (value|None, None|error) value can be cast if NoneError
|
||||
|
|
@ -125,7 +121,6 @@ class DataHandler(object):
|
|||
data_handler = None
|
||||
if fname in emcomponent_fields:
|
||||
data_handler = emcomponent_fields[fname]
|
||||
|
||||
new_val = cur_value
|
||||
if fname in datas.keys():
|
||||
pass
|
||||
|
|
@ -134,7 +129,7 @@ class DataHandler(object):
|
|||
elif data_handler is not None and data_handler.nullable:
|
||||
new_val = None
|
||||
return self._construct_data(emcomponent, fname, datas, new_val)
|
||||
|
||||
|
||||
##@brief Designed to be reimplemented by child classes
|
||||
#@param emcomponent EmComponent : An EmComponent child class instance
|
||||
#@param fname str : The field name
|
||||
|
|
@ -144,7 +139,6 @@ class DataHandler(object):
|
|||
#@see construct_data() lodel2_dh_check_impl
|
||||
def _construct_data(self, empcomponent, fname, datas, cur_value):
|
||||
return cur_value
|
||||
|
||||
|
||||
##@brief Check datas consistency
|
||||
#@ingroup lodel2_dh_checks
|
||||
|
|
@ -160,7 +154,7 @@ class DataHandler(object):
|
|||
#@todo A implémenter
|
||||
def check_data_consistency(self, emcomponent, fname, datas):
|
||||
return self._check_data_consistency(emcomponent, fname, datas)
|
||||
|
||||
|
||||
##@brief Designed to be reimplemented by child classes
|
||||
#@param emcomponent EmComponent : An EmComponent child class instance
|
||||
#@param fname : the field name
|
||||
|
|
@ -175,10 +169,10 @@ class DataHandler(object):
|
|||
# @param fname : the field name
|
||||
# @param datas dict : dict storing fields values
|
||||
# @return an Exception instance if fails else True
|
||||
# @todo A implémenter
|
||||
# @todo A implémenter
|
||||
def make_consistency(self, emcomponent, fname, datas):
|
||||
pass
|
||||
|
||||
|
||||
##@brief This method is use by plugins to register new data handlers
|
||||
@classmethod
|
||||
def register_new_handler(cls, name, data_handler):
|
||||
|
|
@ -187,7 +181,7 @@ class DataHandler(object):
|
|||
if not issubclass(data_handler, DataHandler):
|
||||
raise ValueError("A data handler HAS TO be a child class of DataHandler")
|
||||
cls.__custom_handlers[name] = data_handler
|
||||
|
||||
|
||||
##@brief Load all datahandlers
|
||||
@classmethod
|
||||
def load_base_handlers(cls):
|
||||
|
|
@ -213,7 +207,7 @@ class DataHandler(object):
|
|||
if name not in all_handlers:
|
||||
raise NameError("No data handlers named '%s'" % (name,))
|
||||
return all_handlers[name]
|
||||
|
||||
|
||||
##@brief Return the module name to import in order to use the datahandler
|
||||
# @param data_handler_name str : Data handler name
|
||||
# @return a str
|
||||
|
|
@ -222,10 +216,10 @@ class DataHandler(object):
|
|||
name = name.lower()
|
||||
handler_class = cls.from_name(name)
|
||||
return '{module_name}.{class_name}'.format(
|
||||
module_name = handler_class.__module__,
|
||||
class_name = handler_class.__name__
|
||||
module_name=handler_class.__module__,
|
||||
class_name=handler_class.__name__
|
||||
)
|
||||
|
||||
|
||||
##@brief __hash__ implementation for fieldtypes
|
||||
def __hash__(self):
|
||||
hash_dats = [self.__class__.__module__]
|
||||
|
|
@ -248,17 +242,16 @@ class DataField(DataHandler):
|
|||
#@todo Check data consistency implementation : check that LeObject instance
|
||||
#is from an allowed class
|
||||
class Reference(DataHandler):
|
||||
base_type="ref"
|
||||
base_type = "ref"
|
||||
|
||||
##@brief Instanciation
|
||||
# @param allowed_classes list | None : list of allowed em classes if None no restriction
|
||||
# @param back_reference tuple | None : tuple containing (LeObject child class, fieldname)
|
||||
# @param internal bool : if False, the field is not internal
|
||||
# @param **kwargs : other arguments
|
||||
def __init__(self, allowed_classes = None, back_reference = None, internal=False, **kwargs):
|
||||
def __init__(self, allowed_classes=None, back_reference=None, internal=False, **kwargs):
|
||||
self.__allowed_classes = set() if allowed_classes is None else set(allowed_classes)
|
||||
self.allowed_classes = list() if allowed_classes is None else allowed_classes
|
||||
|
||||
if back_reference is not None:
|
||||
if len(back_reference) != 2:
|
||||
raise ValueError("A tuple (classname, fieldname) expected but got '%s'" % back_reference)
|
||||
|
|
@ -266,8 +259,8 @@ class Reference(DataHandler):
|
|||
# raise TypeError("Back reference was expected to be a tuple(<class LeObject>, str) but got : (%s, %s)" % (back_reference[0], back_reference[1]))
|
||||
self.__back_reference = back_reference
|
||||
super().__init__(internal=internal, **kwargs)
|
||||
|
||||
##@brief Method designed to return an empty value for this kind of
|
||||
|
||||
##@brief Method designed to return an empty value for this kind of
|
||||
#multipleref
|
||||
@classmethod
|
||||
def empty(cls):
|
||||
|
|
@ -277,8 +270,8 @@ class Reference(DataHandler):
|
|||
@property
|
||||
def back_reference(self):
|
||||
return copy.copy(self.__back_reference)
|
||||
|
||||
##@brief Property that takes value of datahandler of the backreference or
|
||||
|
||||
##@brief Property that takes value of datahandler of the backreference or
|
||||
#None
|
||||
@property
|
||||
def back_ref_datahandler(self):
|
||||
|
|
@ -296,7 +289,7 @@ class Reference(DataHandler):
|
|||
|
||||
##@brief Check and cast value in appropriate type
|
||||
#@param value *
|
||||
#@throw FieldValidationError if value is an appropriate type
|
||||
#@throw FieldValidationError if value is an appropriate type
|
||||
#@return value
|
||||
#@todo implement the check when we have LeObject uid check value
|
||||
def _check_data_value(self, value):
|
||||
|
|
@ -328,34 +321,29 @@ class Reference(DataHandler):
|
|||
return rep
|
||||
if self.back_reference is None:
|
||||
return True
|
||||
|
||||
# !! Reimplement instance fetching in construct data !!
|
||||
dh = emcomponent.field(fname)
|
||||
uid = datas[emcomponent.uid_fieldname()[0]] #multi uid broken here
|
||||
target_class = self.back_reference[0]
|
||||
target_field = self.back_reference[1]
|
||||
target_uidfield = target_class.uid_fieldname()[0] #multi uid broken here
|
||||
value = datas[fname]
|
||||
|
||||
obj = target_class.get([(target_uidfield , '=', value)])
|
||||
|
||||
obj = target_class.get([(target_uidfield, '=', value)])
|
||||
if len(obj) == 0:
|
||||
logger.warning('Object referenced does not exist')
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
##@brief This class represent a data_handler for single reference to another object
|
||||
#
|
||||
# The fields using this data handlers are like "foreign key" on another object
|
||||
class SingleRef(Reference):
|
||||
|
||||
def __init__(self, allowed_classes = None, **kwargs):
|
||||
super().__init__(allowed_classes = allowed_classes, **kwargs)
|
||||
|
||||
|
||||
def __init__(self, allowed_classes=None, **kwargs):
|
||||
super().__init__(allowed_classes=allowed_classes)
|
||||
|
||||
|
||||
##@brief Check and cast value in appropriate type
|
||||
#@param value: *
|
||||
#@throw FieldValidationError if value is unappropriate or can not be cast
|
||||
#@throw FieldValidationError if value is unappropriate or can not be cast
|
||||
#@return value
|
||||
def _check_data_value(self, value):
|
||||
value = super()._check_data_value(value)
|
||||
|
|
@ -364,20 +352,22 @@ class SingleRef(Reference):
|
|||
# raise FieldValidationError("List or string expected for a set field")
|
||||
return value
|
||||
|
||||
|
||||
|
||||
##@brief This class represent a data_handler for multiple references to another object
|
||||
#@ingroup lodel2_datahandlers
|
||||
#
|
||||
# The fields using this data handlers are like SingleRef but can store multiple references in one field
|
||||
# @note for the moment split on ',' chars
|
||||
class MultipleRef(Reference):
|
||||
|
||||
|
||||
##
|
||||
# @param max_item int | None : indicate the maximum number of item referenced by this field, None mean no limit
|
||||
def __init__(self, max_item = None, **kwargs):
|
||||
def __init__(self, max_item=None, **kwargs):
|
||||
self.max_item = max_item
|
||||
super().__init__(**kwargs)
|
||||
|
||||
##@brief Method designed to return an empty value for this kind of
|
||||
|
||||
##@brief Method designed to return an empty value for this kind of
|
||||
#multipleref
|
||||
@classmethod
|
||||
def empty(cls):
|
||||
|
|
@ -385,11 +375,11 @@ class MultipleRef(Reference):
|
|||
|
||||
##@brief Check and cast value in appropriate type
|
||||
#@param value *
|
||||
#@throw FieldValidationError if value is unappropriate or can not be cast
|
||||
#@throw FieldValidationError if value is unappropriate or can not be cast
|
||||
#@return value
|
||||
#@TODO Writing test error for errors when stored multiple references in one field
|
||||
def _check_data_value(self, value):
|
||||
value = DataHandler._check_data_value(self,value)
|
||||
value = DataHandler._check_data_value(self, value)
|
||||
if not hasattr(value, '__iter__'):
|
||||
raise FieldValidationError("MultipleRef has to be an iterable or a string, '%s' found" % value)
|
||||
if self.max_item is not None:
|
||||
|
|
@ -397,29 +387,93 @@ class MultipleRef(Reference):
|
|||
raise FieldValidationError("Too many items")
|
||||
new_val = list()
|
||||
error_list = list()
|
||||
for i,v in enumerate(value):
|
||||
for i, v in enumerate(value):
|
||||
try:
|
||||
v = super()._check_data_value(v)
|
||||
new_val.append(v)
|
||||
except (FieldValidationError) as f:
|
||||
except (FieldValidationError):
|
||||
error_list.append(repr(v))
|
||||
if len(error_list) > 0:
|
||||
raise FieldValidationError("MultipleRef have for invalid values [%s] :" % (",".join(error_list)))
|
||||
return new_val
|
||||
|
||||
##@brief Construct a multiple ref data
|
||||
def construct_data(self, emcomponent, fname, datas, cur_value):
|
||||
cur_value = super().construct_data(emcomponent, fname, datas, cur_value)
|
||||
if cur_value is not None:
|
||||
if self.back_reference is not None:
|
||||
br_class = self.back_reference[0]
|
||||
for br_id in cur_value:
|
||||
query_filters = list()
|
||||
query_filters.append((br_class.uid_fieldname()[0], '=', br_id))
|
||||
br_obj = br_class.get(query_filters)
|
||||
if len(br_obj) != 0:
|
||||
br_list = br_obj[0].data(self.back_reference[1])
|
||||
if br_list is None:
|
||||
br_list = list()
|
||||
if br_id not in br_list:
|
||||
br_list.append(br_id)
|
||||
return cur_value
|
||||
|
||||
## @brief Checks the backreference, updates it if it is not complete
|
||||
# @param emcomponent EmComponent : An EmComponent child class instance
|
||||
# @param fname : the field name
|
||||
# @param datas dict : dict storing fields values
|
||||
# @note Not done in case of delete
|
||||
def make_consistency(self, emcomponent, fname, datas, type_query):
|
||||
dh = emcomponent.field(fname)
|
||||
logger.info('Warning : multiple uid capabilities are broken here')
|
||||
uid = datas[emcomponent.uid_fieldname()[0]]
|
||||
if self.back_reference is not None:
|
||||
target_class = self.back_reference[0]
|
||||
target_field = self.back_reference[1]
|
||||
target_uidfield = target_class.uid_fieldname()[0]
|
||||
l_value = datas[fname]
|
||||
|
||||
if l_value is not None:
|
||||
for value in l_value:
|
||||
query_filters = list()
|
||||
query_filters.append((target_uidfield, '=', value))
|
||||
obj = target_class.get(query_filters)
|
||||
if len(obj) == 0:
|
||||
logger.warning('Object referenced does not exist')
|
||||
return False
|
||||
l_uids_ref = obj[0].data(target_field)
|
||||
if l_uids_ref is None:
|
||||
l_uids_ref = list()
|
||||
if uid not in l_uids_ref:
|
||||
l_uids_ref.append(uid)
|
||||
obj[0].set_data(target_field, l_uids_ref)
|
||||
obj[0].update()
|
||||
|
||||
if type_query == 'update':
|
||||
query_filters = list()
|
||||
query_filters.append((uid, ' in ', target_field))
|
||||
objects = target_class.get(query_filters)
|
||||
if l_value is None:
|
||||
l_value = list()
|
||||
if len(objects) != len(l_value):
|
||||
for obj in objects:
|
||||
l_uids_ref = obj.data(target_field)
|
||||
if obj.data(target_uidfield) not in l_value:
|
||||
l_uids_ref.remove(uid)
|
||||
obj.set_data(target_field, l_uids_ref)
|
||||
obj.update()
|
||||
|
||||
|
||||
|
||||
## @brief Class designed to handle datas access will fieldtypes are constructing datas
|
||||
#@ingroup lodel2_datahandlers
|
||||
#
|
||||
# This class is designed to allow automatic scheduling of construct_data calls.
|
||||
# This class is designed to allow automatic scheduling of construct_data calls.
|
||||
#
|
||||
# In theory it's able to detect circular dependencies
|
||||
# @todo test circular deps detection
|
||||
# @todo test circulat deps false positiv
|
||||
class DatasConstructor(object):
|
||||
|
||||
|
||||
## @brief Init a DatasConstructor
|
||||
# @param lec LeCrud : @ref LeObject child class
|
||||
# @param lec LeCrud : @ref LeObject child class
|
||||
# @param datas dict : dict with field name as key and field values as value
|
||||
# @param fields_handler dict : dict with field name as key and data handler instance as value
|
||||
def __init__(self, leobject, datas, fields_handler):
|
||||
|
|
@ -433,11 +487,11 @@ class DatasConstructor(object):
|
|||
self._constructed = []
|
||||
## Stores construct calls list
|
||||
self._construct_calls = []
|
||||
|
||||
|
||||
## @brief Implements the dict.keys() method on instance
|
||||
def keys(self):
|
||||
return self._datas.keys()
|
||||
|
||||
|
||||
## @brief Allows to access the instance like a dict
|
||||
def __getitem__(self, fname):
|
||||
if fname not in self._constructed:
|
||||
|
|
@ -447,10 +501,10 @@ class DatasConstructor(object):
|
|||
self._datas[fname] = self._fields_handler[fname].construct_data(self._leobject, fname, self, cur_value)
|
||||
self._constructed.append(fname)
|
||||
return self._datas[fname]
|
||||
|
||||
|
||||
## @brief Allows to set instance values like a dict
|
||||
# @warning Should not append in theory
|
||||
def __setitem__(self, fname, value):
|
||||
self._datas[fname] = value
|
||||
warnings.warn("Setting value of an DatasConstructor instance")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#-
|
||||
#- THE CONTENT OF THIS FILE IS DESIGNED TO BE INCLUDED IN DYNAMICALLY
|
||||
#- THE CONTENT OF THIS FILE IS DESIGNED TO BE INCLUDED IN DYNAMICALLY
|
||||
#- GENERATED CODE
|
||||
#-
|
||||
#- All lines that begins with #- will be deleted from dynamically generated
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ class LeObject(object):
|
|||
_rw_datasource = None
|
||||
##@brief Store the list of child classes
|
||||
_child_classes = None
|
||||
##@brief Name of the datasource plugin
|
||||
_datasource_name = None
|
||||
|
||||
def __new__(cls, **kwargs):
|
||||
|
||||
|
|
@ -367,13 +369,13 @@ class LeObject(object):
|
|||
for fname in self.fieldnames(include_ro = True):
|
||||
try:
|
||||
field = self._fields[fname]
|
||||
self.__datas[fname] = fields.construct_data( self,
|
||||
self.__datas[fname] = field.construct_data( self,
|
||||
fname,
|
||||
self.__datas,
|
||||
self.__datas[fname]
|
||||
)
|
||||
except Exception as e:
|
||||
err_list[fname] = e
|
||||
except Exception as exp:
|
||||
err_list[fname] = exp
|
||||
# Datas consistency check
|
||||
if len(err_list) == 0:
|
||||
for fname in self.fieldnames(include_ro = True):
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class LeQuery(object):
|
|||
##@brief Hookname prefix
|
||||
_hook_prefix = None
|
||||
##@brief arguments for the LeObject.check_data_value()
|
||||
_data_check_args = { 'complete': False, 'allow_internal': False }
|
||||
_data_check_args = {'complete': False, 'allow_internal': False}
|
||||
|
||||
##@brief Abstract constructor
|
||||
# @param target_class LeObject : class of object the query is about
|
||||
|
|
@ -30,7 +30,7 @@ class LeQuery(object):
|
|||
self._target_class = target_class
|
||||
self._ro_datasource = target_class._ro_datasource
|
||||
self._rw_datasource = target_class._rw_datasource
|
||||
|
||||
|
||||
##@brief Execute a query and return the result
|
||||
#@param **datas
|
||||
#@return the query result
|
||||
|
|
@ -44,11 +44,11 @@ class LeQuery(object):
|
|||
self._target_class.prepare_datas(datas) #not yet implemented
|
||||
if self._hook_prefix is None:
|
||||
raise NotImplementedError("Abstract method")
|
||||
LodelHook.call_hook( self._hook_prefix+'_pre',
|
||||
LodelHook.call_hook(self._hook_prefix+'_pre',
|
||||
self._target_class,
|
||||
datas)
|
||||
ret = self._query(datas = datas)
|
||||
ret = LodelHook.call_hook( self._hook_prefix+'_post',
|
||||
ret = self._query(datas=datas)
|
||||
ret = LodelHook.call_hook(self._hook_prefix+'_post',
|
||||
self._target_class,
|
||||
ret)
|
||||
return ret
|
||||
|
|
@ -71,7 +71,6 @@ class LeQuery(object):
|
|||
|
||||
##@brief Abstract class handling query with filters
|
||||
class LeFilteredQuery(LeQuery):
|
||||
|
||||
##@brief The available operators used in query definitions
|
||||
_query_operators = [
|
||||
' = ',
|
||||
|
|
@ -84,16 +83,16 @@ class LeFilteredQuery(LeQuery):
|
|||
' not in ',
|
||||
' like ',
|
||||
' not like ']
|
||||
|
||||
|
||||
##@brief Regular expression to process filters
|
||||
_query_re = None
|
||||
|
||||
##@brief Abtract constructor for queries with filter
|
||||
#@param target_class LeObject : class of object the query is about
|
||||
#@param query_filters list : with a tuple (only one filter) or a list of
|
||||
#@param query_filters list : with a tuple (only one filter) or a list of
|
||||
# tuple or a dict: {OP,list(filters)} with OP = 'OR' or 'AND for tuple
|
||||
# (FIELD,OPERATOR,VALUE)
|
||||
def __init__(self, target_class, query_filters = None):
|
||||
def __init__(self, target_class, query_filters=None):
|
||||
super().__init__(target_class)
|
||||
##@brief The query filter tuple(std_filter, relational_filters)
|
||||
self._query_filter = None
|
||||
|
|
@ -108,7 +107,7 @@ class LeFilteredQuery(LeQuery):
|
|||
##@brief Abstract FilteredQuery execution method
|
||||
#
|
||||
# This method takes care to execute subqueries before calling super execute
|
||||
def execute(self, datas = None):
|
||||
def execute(self, datas=None):
|
||||
#copy originals filters
|
||||
orig_filters = copy.copy(self._query_filter)
|
||||
std_filters, rel_filters = self._query_filter
|
||||
|
|
@ -143,7 +142,7 @@ class LeFilteredQuery(LeQuery):
|
|||
if isinstance(query_filter, str):
|
||||
query_filter = [query_filter]
|
||||
#Query filter prepration
|
||||
filters_orig , rel_filters = self._prepare_filters(query_filter)
|
||||
filters_orig, rel_filters = self._prepare_filters(query_filter)
|
||||
# Here we now that each relational filter concern only one datasource
|
||||
# thank's to _prepare_relational_fields
|
||||
|
||||
|
|
@ -165,16 +164,16 @@ class LeFilteredQuery(LeQuery):
|
|||
# The line below brake multi UID support
|
||||
#
|
||||
if tfield == tclass.uid_fieldname()[0]:
|
||||
#This relational filter can be simplified as
|
||||
#This relational filter can be simplified as
|
||||
# ref_field, op, value
|
||||
# Note : we will have to dedup filters_orig
|
||||
filters_orig.append((rfield, op, value))
|
||||
del(ref_dict[tclass])
|
||||
if len(ref_dict) == 0:
|
||||
continue
|
||||
#Determine what to do with other relational filters given
|
||||
#Determine what to do with other relational filters given
|
||||
# referenced class datasource
|
||||
#Remember : each class in a relational filter has the same
|
||||
#Remember : each class in a relational filter has the same
|
||||
# datasource
|
||||
tclass = list(ref_dict.keys())[0]
|
||||
cur_ds = tclass._datasource_name
|
||||
|
|
@ -191,7 +190,7 @@ class LeFilteredQuery(LeQuery):
|
|||
filters_cp = set()
|
||||
if not isinstance(filters_orig, set):
|
||||
for i, cfilt in enumerate(filters_orig):
|
||||
a,b,c = cfilt
|
||||
a, b, c = cfilt
|
||||
if isinstance(c, list): #list are not hashable
|
||||
newc = tuple(c)
|
||||
else:
|
||||
|
|
@ -210,12 +209,12 @@ class LeFilteredQuery(LeQuery):
|
|||
(rfield, ref_dict), op, value = rfilter
|
||||
for tclass, tfield in ref_dict.items():
|
||||
query = LeGetQuery(
|
||||
target_class = tclass,
|
||||
query_filters = [(tfield, op, value)],
|
||||
field_list = [tfield])
|
||||
target_class=tclass,
|
||||
query_filters=[(tfield, op, value)],
|
||||
field_list=[tfield])
|
||||
subq.append((rfield, query))
|
||||
self.subqueries = subq
|
||||
|
||||
|
||||
##@return informations
|
||||
def dump_infos(self):
|
||||
ret = super().dump_infos()
|
||||
|
|
@ -227,17 +226,17 @@ class LeFilteredQuery(LeQuery):
|
|||
res = "<{classname} target={target_class} query_filter={query_filter}"
|
||||
res = res.format(
|
||||
classname=self.__class__.__name__,
|
||||
query_filter = self._query_filter,
|
||||
target_class = self._target_class)
|
||||
query_filter=self._query_filter,
|
||||
target_class=self._target_class)
|
||||
if len(self.subqueries) > 0:
|
||||
for n,subq in enumerate(self.subqueries):
|
||||
for n, subq in enumerate(self.subqueries):
|
||||
res += "\n\tSubquerie %d : %s"
|
||||
res %= (n, subq)
|
||||
res += '>'
|
||||
return res
|
||||
|
||||
## @brief Prepare filters for datasource
|
||||
#
|
||||
#
|
||||
#A filter can be a string or a tuple with len = 3.
|
||||
#
|
||||
#This method divide filters in two categories :
|
||||
|
|
@ -248,13 +247,13 @@ class LeFilteredQuery(LeQuery):
|
|||
#the content, etc.) They are composed of three elements : FIELDNAME OP
|
||||
# VALUE . Where :
|
||||
#- FIELDNAME is the name of the field
|
||||
#- OP is one of the authorized comparison operands ( see
|
||||
#- OP is one of the authorized comparison operands (see
|
||||
#@ref LeFilteredQuery.query_operators )
|
||||
#- VALUE is... a value
|
||||
#
|
||||
#@par Relational filters
|
||||
#
|
||||
#Those filters concerns on reference fields ( see the corresponding
|
||||
#Those filters concerns on reference fields (see the corresponding
|
||||
#abstract datahandler @ref lodel.leapi.datahandlers.base_classes.Reference)
|
||||
#The filter as quite the same composition than simple filters :
|
||||
# FIELDNAME[.REF_FIELD] OP VALUE . Where :
|
||||
|
|
@ -269,12 +268,12 @@ class LeFilteredQuery(LeQuery):
|
|||
#@todo move this doc in another place (a dedicated page ?)
|
||||
#@warning Does not supports multiple UID for an EmClass
|
||||
def _prepare_filters(self, filters_l):
|
||||
filters = list()
|
||||
filters=list()
|
||||
res_filters = list()
|
||||
rel_filters = list()
|
||||
err_l = dict()
|
||||
#Splitting in tuple if necessary
|
||||
for i,fil in enumerate(filters_l):
|
||||
for i, fil in enumerate(filters_l):
|
||||
if len(fil) == 3 and not isinstance(fil, str):
|
||||
filters.append(tuple(fil))
|
||||
else:
|
||||
|
|
@ -292,8 +291,8 @@ class LeFilteredQuery(LeQuery):
|
|||
elif len(field_spl) == 1:
|
||||
ref_field = None
|
||||
else:
|
||||
err_l[field] = NameError( "'%s' is not a valid relational \
|
||||
field name" % fieldname)
|
||||
err_l[field] = NameError("'%s' is not a valid relational \
|
||||
field name" % field)
|
||||
continue
|
||||
# Checking field against target_class
|
||||
ret = self._check_field(self._target_class, field)
|
||||
|
|
@ -302,13 +301,13 @@ field name" % fieldname)
|
|||
continue
|
||||
field_datahandler = self._target_class.field(field)
|
||||
if isinstance(field_datahandler, Exception):
|
||||
err_l[field] = error
|
||||
err_l[field] = field_datahandler
|
||||
continue
|
||||
if ref_field is not None and not field_datahandler.is_reference():
|
||||
# inconsistency
|
||||
err_l[field] = NameError( "The field '%s' in %s is not \
|
||||
err_l[field] = NameError("The field '%s' in %s is not \
|
||||
a relational field, but %s.%s was present in the filter"
|
||||
% ( field,
|
||||
% (field,
|
||||
self._target_class.__name__,
|
||||
field,
|
||||
ref_field))
|
||||
|
|
@ -321,7 +320,7 @@ a relational field, but %s.%s was present in the filter"
|
|||
# This piece of code does not supports multiple UID for an
|
||||
# emclass
|
||||
#
|
||||
ref_uid = [
|
||||
ref_uid = [
|
||||
lc._uid[0] for lc in field_datahandler.linked_classes]
|
||||
|
||||
if len(set(ref_uid)) == 1:
|
||||
|
|
@ -410,7 +409,7 @@ field to use for the relational filter"
|
|||
#Relational filters are composed of a tuple like the simple filters
|
||||
#but the first element of this tuple is a tuple to :
|
||||
#
|
||||
#<code>( (FIELDNAME, {REF_CLASS: REF_FIELD}), OP, VALUE)</code>
|
||||
#<code>((FIELDNAME, {REF_CLASS: REF_FIELD}), OP, VALUE)</code>
|
||||
# Where :
|
||||
#- FIELDNAME is the field name is the target class
|
||||
#- the second element is a dict with :
|
||||
|
|
@ -429,7 +428,7 @@ field to use for the relational filter"
|
|||
# {
|
||||
# auteur: 'lodel_id',
|
||||
# traducteur: 'lodel_id'
|
||||
# }
|
||||
# }
|
||||
# ),
|
||||
# ' IN ',
|
||||
# [ 1,2,3,5 ])</pre>
|
||||
|
|
@ -439,7 +438,7 @@ field to use for the relational filter"
|
|||
#@param ref_field str|None : The referenced field name (if None use
|
||||
#uniq identifiers as referenced field
|
||||
#@return a well formed relational filter tuple or an Exception instance
|
||||
def _prepare_relational_fields(self, fieldname, ref_field = None):
|
||||
def _prepare_relational_fields(self, fieldname, ref_field=None):
|
||||
datahandler = self._target_class.field(fieldname)
|
||||
# now we are going to fetch the referenced class to see if the
|
||||
# reference field is valid
|
||||
|
|
@ -464,29 +463,28 @@ the relational filter %s"
|
|||
msg %= (ref_class.__name__, ref_field)
|
||||
logger.debug(msg)
|
||||
if len(ref_dict) == 0:
|
||||
return NameError( "No field named '%s' in referenced objects [%s]"
|
||||
return NameError("No field named '%s' in referenced objects [%s]"
|
||||
% (ref_field,
|
||||
','.join([rc.__name__ for rc in ref_classes])))
|
||||
return (fieldname, ref_dict)
|
||||
|
||||
|
||||
|
||||
##@brief A query to insert a new object
|
||||
class LeInsertQuery(LeQuery):
|
||||
|
||||
_hook_prefix = 'leapi_insert_'
|
||||
_data_check_args = { 'complete': True, 'allow_internal': False }
|
||||
_data_check_args = {'complete': True, 'allow_internal': False}
|
||||
|
||||
def __init__(self, target_class):
|
||||
if target_class.is_abstract():
|
||||
raise LeApiQueryError("Trying to create an insert query on an \
|
||||
abstract LeObject : %s" % target_class )
|
||||
abstract LeObject : %s" % target_class)
|
||||
super().__init__(target_class)
|
||||
|
||||
## @brief Implements an insert query operation, with only one insertion
|
||||
# @param new_datas : datas to be inserted
|
||||
def _query(self, datas):
|
||||
datas = self._target_class.prepare_datas(datas, True, False)
|
||||
id_inserted = self._rw_datasource.insert(self._target_class,datas)
|
||||
id_inserted = self._rw_datasource.insert(self._target_class, datas)
|
||||
return id_inserted
|
||||
"""
|
||||
## @brief Implements an insert query operation, with multiple insertions
|
||||
|
|
@ -501,7 +499,7 @@ abstract LeObject : %s" % target_class )
|
|||
|
||||
## @brief Execute the insert query
|
||||
def execute(self, datas):
|
||||
return super().execute(datas = datas)
|
||||
return super().execute(datas=datas)
|
||||
|
||||
|
||||
##@brief A query to update datas for a given object
|
||||
|
|
@ -509,10 +507,9 @@ abstract LeObject : %s" % target_class )
|
|||
#@todo Change behavior, Huge optimization problem when updating using filters
|
||||
#and not instance. We have to run a GET and then 1 update by fecthed object...
|
||||
class LeUpdateQuery(LeFilteredQuery):
|
||||
|
||||
_hook_prefix = 'leapi_update_'
|
||||
_data_check_args = { 'complete': False, 'allow_internal': False }
|
||||
|
||||
_data_check_args = {'complete': False, 'allow_internal': False}
|
||||
|
||||
##@brief Instanciate an update query
|
||||
#
|
||||
#If a class and not an instance is given, no query_filters are expected
|
||||
|
|
@ -523,12 +520,12 @@ class LeUpdateQuery(LeFilteredQuery):
|
|||
#@param query_filters list|None
|
||||
#@todo change strategy with instance update. We have to accept datas for
|
||||
#the execute method
|
||||
def __init__(self, target, query_filters = None):
|
||||
def __init__(self, target, query_filters=None):
|
||||
##@brief This attr is set only if the target argument is an
|
||||
#instance of a LeObject subclass
|
||||
self.__leobject_instance_datas = None
|
||||
target_class = target
|
||||
|
||||
|
||||
if not inspect.isclass(target):
|
||||
if query_filters is not None:
|
||||
msg = "No query_filters accepted when an instance is given as \
|
||||
|
|
@ -539,7 +536,7 @@ target to LeUpdateQuery constructor"
|
|||
self.__leobject_instance_datas = target.datas(True)
|
||||
else:
|
||||
query_filters = [(target._uid[0], '=', target.uid())]
|
||||
|
||||
|
||||
super().__init__(target_class, query_filters)
|
||||
|
||||
##@brief Implements an update query
|
||||
|
|
@ -547,7 +544,7 @@ target to LeUpdateQuery constructor"
|
|||
#@param rel_filters list : see @ref LeFilteredQuery
|
||||
#@param datas dict : datas to update
|
||||
#@returns the number of updated items
|
||||
#@todo change stategy for instance update. Datas should be allowed
|
||||
#@todo change stategy for instance update. Datas should be allowed
|
||||
#for execute method (and query)
|
||||
def _query(self, datas):
|
||||
uid_name = self._target_class._uid[0]
|
||||
|
|
@ -578,48 +575,47 @@ target to LeUpdateQuery constructor"
|
|||
self._target_class, filters, [],
|
||||
res_datas)
|
||||
return res
|
||||
|
||||
|
||||
## @brief Execute the update query
|
||||
def execute(self, datas = None):
|
||||
def execute(self, datas=None):
|
||||
if self.__leobject_instance_datas is not None and datas is not None:
|
||||
raise LeApiQueryError("No datas expected when running an update \
|
||||
query on an instance")
|
||||
if self.__leobject_instance_datas is None and datas is None:
|
||||
raise LeApiQueryError("Datas are mandatory when running an update \
|
||||
query on a class with filters")
|
||||
return super().execute(datas = datas)
|
||||
return super().execute(datas=datas)
|
||||
|
||||
|
||||
##@brief A query to delete an object
|
||||
class LeDeleteQuery(LeFilteredQuery):
|
||||
|
||||
_hook_prefix = 'leapi_delete_'
|
||||
|
||||
def __init__(self, target_class, query_filter):
|
||||
super().__init__(target_class, query_filter)
|
||||
|
||||
## @brief Execute the delete query
|
||||
def execute(self, datas = None):
|
||||
def execute(self, datas=None):
|
||||
return super().execute()
|
||||
|
||||
|
||||
##@brief Implements delete query operations
|
||||
#@param filters list : see @ref LeFilteredQuery
|
||||
#@param rel_filters list : see @ref LeFilteredQuery
|
||||
#@returns the number of deleted items
|
||||
def _query(self, datas = None):
|
||||
def _query(self, datas=None):
|
||||
filters, rel_filters = self._query_filter
|
||||
nb_deleted = self._rw_datasource.delete(
|
||||
self._target_class, filters, rel_filters)
|
||||
return nb_deleted
|
||||
|
||||
class LeGetQuery(LeFilteredQuery):
|
||||
|
||||
_hook_prefix = 'leapi_get_'
|
||||
|
||||
##@brief Instanciate a new get query
|
||||
#@param target_class LeObject : class of object the query is about
|
||||
#@param query_filters dict : {OP, list of query filters }
|
||||
#@param query_filters dict : {OP, list of query filters}
|
||||
# or tuple (FIELD, OPERATOR, VALUE) )
|
||||
#@param field_list list|None : list of string representing fields see
|
||||
#@param field_list list|None : list of string representing fields see
|
||||
# @ref leobject_filters
|
||||
#@param order list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
|
||||
#@param group list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
|
||||
|
|
@ -627,7 +623,6 @@ class LeGetQuery(LeFilteredQuery):
|
|||
#@param offset int : offset
|
||||
def __init__(self, target_class, query_filters, **kwargs):
|
||||
super().__init__(target_class, query_filters)
|
||||
|
||||
##@brief The fields to get
|
||||
self._field_list = None
|
||||
##@brief An equivalent to the SQL ORDER BY
|
||||
|
|
@ -638,7 +633,7 @@ class LeGetQuery(LeFilteredQuery):
|
|||
self._limit = None
|
||||
##@brief An equivalent to the SQL LIMIT x, OFFSET
|
||||
self._offset = 0
|
||||
|
||||
|
||||
# Checking kwargs and assigning default values if there is some
|
||||
for argname in kwargs:
|
||||
if argname not in (
|
||||
|
|
@ -672,7 +667,7 @@ class LeGetQuery(LeFilteredQuery):
|
|||
except ValueError:
|
||||
msg = "offset argument expected to be an integer >= 0"
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
##@brief Set the field list
|
||||
# @param field_list list | None : If None use all fields
|
||||
# @return None
|
||||
|
|
@ -691,36 +686,36 @@ class LeGetQuery(LeFilteredQuery):
|
|||
msg = "Error while setting field_list in a get query"
|
||||
raise LeApiQueryErrors(msg = msg, exceptions = err_l)
|
||||
self._field_list = list(set(field_list))
|
||||
|
||||
|
||||
##@brief Execute the get query
|
||||
def execute(self, datas = None):
|
||||
def execute(self, datas=None):
|
||||
return super().execute()
|
||||
|
||||
##@brief Implements select query operations
|
||||
# @returns a list containing the item(s)
|
||||
def _query(self, datas = None):
|
||||
def _query(self, datas=None):
|
||||
# select datas corresponding to query_filter
|
||||
fl = list(self._field_list) if self._field_list is not None else None
|
||||
l_datas=self._ro_datasource.select(
|
||||
l_datas=self._ro_datasource.select(
|
||||
target = self._target_class,
|
||||
field_list = fl,
|
||||
filters = self._query_filter[0],
|
||||
relational_filters = self._query_filter[1],
|
||||
order = self._order,
|
||||
group = self._group,
|
||||
limit = self._limit,
|
||||
filters = self._query_filter[0],
|
||||
relational_filters = self._query_filter[1],
|
||||
order = self._order,
|
||||
group = self._group,
|
||||
limit = self._limit,
|
||||
offset = self._offset)
|
||||
return l_datas
|
||||
|
||||
|
||||
##@return a dict with query infos
|
||||
def dump_infos(self):
|
||||
ret = super().dump_infos()
|
||||
ret.update( { 'field_list' : self._field_list,
|
||||
'order' : self._order,
|
||||
'group' : self._group,
|
||||
'limit' : self._limit,
|
||||
'offset': self._offset,
|
||||
})
|
||||
ret.update({ 'field_list' : self._field_list,
|
||||
'order' : self._order,
|
||||
'group' : self._group,
|
||||
'limit' : self._limit,
|
||||
'offset': self._offset,
|
||||
})
|
||||
return ret
|
||||
|
||||
def __repr__(self):
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ DS_PLUGIN_NAME.DS_INSTANCE_NAME. But got %s" % ds_identifier)
|
|||
from lodel.settings import Settings
|
||||
if ds_plugin_name not in Settings.datasource._fields:
|
||||
msg = "Unknown or unconfigured datasource plugin %s"
|
||||
msg %= ds_plugin
|
||||
msg %= ds_plugin_name
|
||||
raise DatasourcePluginError(msg)
|
||||
ds_conf = getattr(Settings.datasource, ds_plugin_name)
|
||||
if ds_identifier not in ds_conf._fields:
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ class Settings(object, metaclass=MetaSettings):
|
|||
# allready instanciated, else not
|
||||
# @throw RuntimeError
|
||||
@classmethod
|
||||
def singleton_assert(cls, expect_instanciated = False):
|
||||
def singleton_assert(cls, expect_instanciated=False):
|
||||
if expect_instanciated:
|
||||
if not cls.started():
|
||||
raise RuntimeError("The Settings class is not started yet")
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ def host_val(value):
|
|||
try:
|
||||
socket.getaddrinfo(value, 80)
|
||||
return value
|
||||
except (TypeError,socket.gaierrror):
|
||||
except (TypeError,socket.gaierror):
|
||||
msg = "The value '%s' is not a valid host"
|
||||
raise SettingsValidationError(msg % value)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue