No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lerelation.py 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #-*- coding: utf-8 -*-
  2. import copy
  3. import re
  4. import EditorialModel.fieldtypes.leo as ft_leo
  5. from . import lecrud
  6. from . import leobject
  7. from . import lefactory
  8. ## @brief Main class for relations
  9. class _LeRelation(lecrud._LeCrud):
  10. _lesup_name = None
  11. _lesub_name = None
  12. ## @brief Stores the list of fieldtypes that are common to all relations
  13. _rel_fieldtypes = dict()
  14. def __init__(self, rel_id, **kwargs):
  15. self.id_relation = rel_id
  16. ## @brief Forge a filter to match the superior
  17. @classmethod
  18. def sup_filter(self, leo):
  19. if isinstance(leo, leobject._LeObject):
  20. return (self._lesup_name, '=', leo)
  21. ## @brief Forge a filter to match the superior
  22. @classmethod
  23. def sub_filter(self, leo):
  24. if isinstance(leo, leobject._LeObject):
  25. return (self._lesub_name, '=', leo)
  26. ## @return a dict with field name as key and fieldtype instance as value
  27. @classmethod
  28. def fieldtypes(cls):
  29. rel_ft = dict()
  30. rel_ft.update(cls._uid_fieldtype)
  31. rel_ft.update(cls._rel_fieldtypes)
  32. if cls.implements_lerel2type():
  33. rel_ft.update(cls._rel_attr_fieldtypes)
  34. return rel_ft
  35. @classmethod
  36. def _prepare_relational_fields(cls, field):
  37. return lecrud.LeApiQueryError("Relational field '%s' given but %s doesn't is not a LeObject" % (field,
  38. cls.__name__))
  39. ## @brief Prepare filters before sending them to the datasource
  40. # @param cls : Concerned class
  41. # @param filters_l list : List of filters
  42. # @return prepared and checked filters
  43. @classmethod
  44. def _prepare_filters(cls, filters_l):
  45. filters, rel_filters = super()._prepare_filters(filters_l)
  46. res_filters = list()
  47. for field, op, value in filters:
  48. if field in [cls._lesup_name, cls._lesub_name]:
  49. if isinstance(value, str):
  50. try:
  51. value = int(value)
  52. except ValueError as e:
  53. raise LeApiDataCheckError("Wrong value given for '%s'"%field)
  54. if isinstance(value, int):
  55. value = cls.name2class('LeObject')(value)
  56. res_filters.append( (field, op, value) )
  57. return res_filters, rel_filters
  58. @classmethod
  59. ## @brief deletes a relation between two objects
  60. # @param filters_list list
  61. # @param target_class str
  62. def delete(cls, filters_list, target_class):
  63. filters, rel_filters = cls._prepare_filters(filters_list)
  64. if isinstance(target_class, str):
  65. target_class = cls.name2class(target_class)
  66. ret = cls._datasource.delete(target_class, filters)
  67. return True if ret == 1 else False
  68. ## @brief move to the first rank
  69. # @return True in case of success, False in case of failure
  70. def move_first(self):
  71. return self.set_rank('first')
  72. ## @brief move to the last rank
  73. # @return True in case of success, False in case of failure
  74. def move_last(self):
  75. return self.set_rank('last')
  76. ## @brief move to the given rank defined by a shift step
  77. # @param step int : The step
  78. # @return True in case of success, False in case of failure
  79. # @throw ValueError if step is not castable into an integer
  80. def shift_rank(self, step):
  81. step = int(step)
  82. return self.set_rank(self.rank + step)
  83. ## @brief modify a relation rank
  84. # @param new_rank int|str : The new rank can be an integer > 1 or strings 'first' or 'last'
  85. # @return True in case of success, False in case of failure
  86. # @throw ValueError if step is not castable into an integer
  87. def set_rank(self, new_rank):
  88. max_rank = self.get_max_rank()
  89. try:
  90. new_rank = int(new_rank)
  91. except ValueError:
  92. if new_rank == 'first':
  93. new_rank = 1
  94. elif new_rank == 'last':
  95. new_rank = max_rank
  96. else:
  97. raise ValueError("The new rank can be an integer > 1 or strings 'first' or 'last', but %s given"%new_rank)
  98. if self.rank == new_rank:
  99. return True
  100. if new_rank < 1:
  101. if strict:
  102. raise ValueError("Rank must be >= 1, but %d given"%rank)
  103. new_rank = 1
  104. elif new_rank > max_rank:
  105. if strict:
  106. raise ValueError("Rank is too big (max_rank = %d), but %d given"%(max_rank,rank))
  107. new_rank = max_rank
  108. self._datasource.update_rank(self, new_rank)
  109. ## @returns The maximum assignable rank for this relation
  110. # @todo implementation
  111. def get_max_rank(self):
  112. max_rank_result = self.__class__.get(field_list=['rank'], order=[('rank', 'DESC')], limit=1)
  113. max_rank = int(max_rank_result[0].rank)
  114. return max_rank+1
  115. ## @brief Abstract class to handle hierarchy relations
  116. class _LeHierarch(_LeRelation):
  117. ## @brief Delete current instance from DB
  118. def delete(self):
  119. lecrud._LeCrud._delete(self)
  120. ## @brief Abstract class to handle rel2type relations
  121. class _LeRel2Type(_LeRelation):
  122. ## @brief Stores the list of fieldtypes handling relations attributes
  123. _rel_attr_fieldtypes = dict()
  124. ## @brief Delete current instance from DB
  125. def delete(self):
  126. lecrud._LeCrud._delete(self)
  127. ## @brief Implements insert for rel2type
  128. # @todo checks when autodetecing the rel2type class
  129. @classmethod
  130. def insert(cls, datas, classname = None):
  131. #Set the nature
  132. if 'nature' not in datas:
  133. datas['nature'] = None
  134. if cls == cls.name2class('LeRel2Type') and classname is None:
  135. # autodetect the rel2type child class
  136. classname = relname(datas[self._lesup_name], datas[self._lesub_name])
  137. return super().insert(datas, classname)
  138. ## @brief Given a superior and a subordinate, returns the classname of the give rel2type
  139. # @param lesupclass LeClass : LeClass child class (not an instance) (can be a LeType or a LeClass child)
  140. # @param lesubclass LeType : A LeType child class (not an instance)
  141. # @return a name as string
  142. @staticmethod
  143. def relname(lesupclass, lesubclass):
  144. supname = lesupclass._leclass.__name__ if lesupclass.implements_letype() else lesupclass.__name__
  145. subname = lesubclass.__name__
  146. return "Rel_%s2%s" % (supname, subname)