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.

migration_handler.py 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. # -*- coding: utf-8 -*-
  2. import datetime
  3. from lodel.leapi.datahandlers.base_classes import DataHandler
  4. from lodel.datasource.generic.migrationhandler import GenericMigrationHandler
  5. import lodel.datasource.mongodb.utils as utils
  6. from lodel.editorial_model.components import EmClass, EmField
  7. from lodel.editorial_model.model import EditorialModel
  8. class MigrationHandlerChangeError(Exception):
  9. pass
  10. class MongoDbMigrationHandler(GenericMigrationHandler):
  11. COMMANDS_IFEXISTS_DROP = 'drop'
  12. COMMANDS_IFEXISTS_NOTHING = 'nothing'
  13. ## @brief constructs a MongoDbMigrationHandler
  14. # @param conn_args dict : a dictionary containing connection options
  15. # @param **kwargs : extra arguments given to the connection method
  16. def __init__(self, conn_args=None, **kwargs):
  17. if conn_args is None:
  18. conn_args = {} # TODO : get the connection parameters in the settings
  19. self.connection_name = conn_args['name']
  20. self.database = utils.mongodbconnect(self.connection_name)
  21. # === Migration settings ===
  22. # TODO reimplement the settings management here
  23. mh_settings = {'dry_run': False, 'foreign_keys': True, 'drop_if_exists': False}
  24. self.dryrun = kwargs['dryrun'] if 'dryrun' in kwargs else mh_settings['dryrun']
  25. self.foreign_keys = kwargs['foreign_keys'] if 'foreign_keys' in kwargs else mh_settings['forein_keys']
  26. self.drop_if_exists = kwargs['drop_if_exists'] if 'drop_if_exists' in kwargs else mh_settings['drop_if_exists']
  27. self._main_collection_name = 'object'
  28. self._relation_collection_name = 'relations'
  29. self._install_tables()
  30. def _install_tables(self):
  31. self._create_collection(self._main_collection_name)
  32. self._create_collection(self._relation_collection_name)
  33. ## @brief Records a change in the EditorialModel and indicates whether or not it is possible to commit it in the database
  34. # @note The states contains only the changing fields
  35. # @param model EditorialModel : The EditorialModel object providing the global context
  36. # @param uid str : the uid of the changing component
  37. # @param initial_state dict|None: dict representing the original state ({field_name: field_value, ...}). None means the component is created
  38. # @param new_state dict|None: dict representing the new state ({field_name: field_value, ...}). None means the component is deleted
  39. def register_change(self, model, uid, initial_state, new_state):
  40. if initial_state is None:
  41. state_change = 'new'
  42. elif new_state is None:
  43. state_change = 'del'
  44. else:
  45. state_change = 'upgrade'
  46. component_class_name = model.classes(uid).__class__.name
  47. handler_func(component_class_name.lower() + '_' + state_change)
  48. if hasattr(self, handler_func):
  49. getattr(self, handler_func)(model, uid, initial_state, new_state)
  50. def register_model_state(self, em, state_hash):
  51. pass
  52. def emclass_new(self, model, uid, initial_state, new_state):
  53. class_collection_name = model.classes(uid).name
  54. self._create_collection(class_collection_name)
  55. def emclass_del(self, model, uid, initial_state, new_state):
  56. emclass = model.classes(uid)
  57. if not isinstance(emclass, EmClass):
  58. raise ValueError("The given uid is not an EmClass uid")
  59. class_collection_name = utils.object.collection_name(emclass.uid)
  60. self._delete_collection(class_collection_name)
  61. ## @brief creates a new collection for a new class
  62. # @param model EditorialModel
  63. # @param uid str
  64. # @param initial_state dict|None: dict with field name as key and field value as value. Represents the original state.
  65. # @param new_state dict|None : dict with field name as key and field value as value. Represents the new state.
  66. def emfield_new(self, model, uid, initial_state, new_state):
  67. if new_state['data_handler'] == 'relation':
  68. # find relational_type name, and class_name of the field
  69. class_name = self._class_collection_name_from_field(model, new_state)
  70. self._create_field_in_collection(class_name, uid, new_state)
  71. return True
  72. if new_state['internal']:
  73. collection_name = ''
  74. # TODO ?
  75. elif new_state['rel_field_id']:
  76. class_name = self._class_collection_name_from_field(model, new_state)
  77. # TODO deal this case
  78. else:
  79. collection_name = self._class_collection_name_from_field(model, new_state)
  80. field_definition = self._field_definition(new_state['data_handler'], new_state)
  81. self._create_field_in_collection(collection_name, uid, field_definition)
  82. def emfield_del(self, model, uid, initial_state, new_state):
  83. # TODO
  84. pass
  85. def _field_definition(self, fieldtype, options):
  86. basic_type = DataHandler.from_name(fieldtype).ftype
  87. field_definition = {'default_value':None}
  88. if basic_type == 'datetime':
  89. if 'now_on_create' in options and options['now_on_create']:
  90. return {'default': datetime.datetime.utcnow()}
  91. if basic_type == 'relation':
  92. return {'default' : []}
  93. return {'default': ''}
  94. def _class_collection_name_from_field(self, model, field):
  95. class_id = field['class_id']
  96. class_name = model.classes(class_id).name
  97. class_collection_name = utils.object_collection_name(class_name)
  98. return class_collection_name
  99. def _create_collection(self, collection_name, charset='utf8', if_exists=MongoDbMigrationHandler.COMMANDS_IFEXISTS_NOTHING):
  100. if if_exists == self.__class__.COMMANDS_IFEXISTS_DROP:
  101. if collection_name in self.database.collection_names(include_system_collections = False):
  102. self._delete_collection(collection_name)
  103. self.database.create_collection(name=collection_name)
  104. def _delete_collection(self, collection_name):
  105. collection = self.database[collection_name]
  106. collection.drop_indexes()
  107. collection.drop()
  108. def _create_field_in_collection(self, collection_name, field, options):
  109. self.database[collection_name].update_many({field: {'$exists': False}}, {'$set': {field: options['default']}},
  110. False)
  111. def _add_fk(self, src_collection_name, dst_collection_name, src_field_name, dst_field_name, fk_name=None):
  112. if fk_name is None:
  113. fk_name = utils.get_fk_name(src_collection_name, dst_collection_name)
  114. self._del_fk(src_collection_name, dst_collection_name, fk_name)
  115. self.database[src_collection_name].update_many({fk_name: {'$exists': False}}, {'$set': {fk_name: []}}, False)
  116. def del_fk(self, src_collection_name, dst_collection_name, fk_name=None):
  117. if fk_name is None:
  118. fk_name = utils.get_fk_name(src_collection_name, dst_collection_name)
  119. self.database[src_collection_name].update_many({}, {'$unset': {fk_name:1}}, False)