Browse Source

Editorial Model: first shot at the SQL migration handler (bye-bye django)

ArnAud 9 years ago
parent
commit
ce219de48a
3 changed files with 154 additions and 1 deletions
  1. 122
    0
      EditorialModel/migrationhandler/sql.py
  2. 31
    0
      Lodel/utils/mosql.py
  3. 1
    1
      requirements.txt

+ 122
- 0
EditorialModel/migrationhandler/sql.py View File

@@ -0,0 +1,122 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+## @package EditorialModel.migrationhandler.sql
4
+# @brief A dummy migration handler
5
+#
6
+# According to it every modifications are possible
7
+#
8
+
9
+from EditorialModel.migrationhandler.dummy import DummyMigrationHandler
10
+from EditorialModel.model import Model
11
+from mosql.db import Database
12
+from Lodel.utils.mosql import create, alter_add
13
+
14
+
15
+## Manage Model changes
16
+class SQLMigrationHandler(DummyMigrationHandler):
17
+
18
+    fieldtype_to_sql = {
19
+        'char': "CHAR(255)",
20
+        'int': 'INT'
21
+    }
22
+
23
+    def __init__(self, module=None, *conn_args, **conn_kargs):
24
+        self.db = Database(module, *conn_args, **conn_kargs)
25
+        super(SQLMigrationHandler, self).__init__(False)
26
+        # @todo vérification de l'existance de la table objects et de la table relation
27
+        self._install_tables()
28
+
29
+    ## @brief Record a change in the EditorialModel and indicate wether or not it is possible to make it
30
+    # @note The states ( initial_state and new_state ) contains only fields that changes
31
+    # @param model model : The EditorialModel.model object to provide the global context
32
+    # @param uid int : The uid of the change EmComponent
33
+    # @param initial_state dict | None : dict with field name as key and field value as value. Representing the original state. None mean creation of a new component.
34
+    # @param new_state dict | None : dict with field name as key and field value as value. Representing the new state. None mean component deletion
35
+    # @throw EditorialModel.exceptions.MigrationHandlerChangeError if the change was refused
36
+    def register_change(self, model, uid, initial_state, new_state):
37
+        #print(uid, initial_state, new_state)
38
+        # find type of component change
39
+        component = Model.name_from_emclass(type(model.component(uid)))
40
+        #print ("ça", component, type(model.component(uid)))
41
+        if initial_state is None:
42
+            state_change = 'new'
43
+        elif new_state is None:
44
+            state_change = 'del'
45
+        else:
46
+            state_change = 'upgrade'
47
+
48
+        if component == 'EmType' and len(new_state) == 1:
49
+            if 'superiors_list' in new_state:
50
+                what = 'superiors_list'
51
+            elif 'fields_list' in new_state:
52
+                what = 'fields_list'
53
+        else:
54
+            what = component
55
+
56
+        handler_func = what + '_' + state_change
57
+        #print (handler_func)
58
+        if hasattr(self, handler_func):
59
+            getattr(self, handler_func)(model, uid, initial_state, new_state)
60
+        #print(handler_func, uid, initial_state, new_state)
61
+
62
+    # New Class, a table must be created
63
+    def EmClass_new(self, model, uid, initial_state, new_state):
64
+        class_table_name = self._class_table_name(new_state['name'])
65
+        self._query_bd(
66
+            create(table=class_table_name, column='id_lodel INT PRIMARY_KEY AUTOINCREMENT NOT NULL')
67
+        )
68
+
69
+    # New Field, must create a column in Class table or in Class_Type relational attribute table
70
+    def EmField_new(self, model, uid, initial_state, new_state):
71
+        # field is internal, create a column in the objects table
72
+        if new_state['internal']:
73
+            return
74
+        # field is of type rel2type, create the relational class_type table
75
+        elif new_state['fieldtype'] == 'rel2type':
76
+            # find relational_type name, and class name of the field
77
+            class_name = self._class_table_name_from_field(model, new_state)
78
+            type_name = model.component(new_state['rel_to_type_id']).name
79
+            table_name = class_name + '_' + type_name
80
+            self._query_bd(
81
+                create(table=table_name, column='id_lodel INT PRIMARY_KEY AUTOINCREMENT NOT NULL'),
82
+            )
83
+            #print('create rel2type table', class_name, type_name)
84
+            return
85
+        # field is relational (rel_field_id), create a column in the class_type table
86
+        elif new_state['rel_field_id']:
87
+            class_name = self._class_table_name_from_field(model, new_state)
88
+            rel_type_id = model.component(new_state['rel_field_id']).rel_to_type_id
89
+            type_name = model.component(rel_type_id).name
90
+            table_name = class_name + '_' + type_name
91
+            #print('create field in rel2type table', new_state, class_name, rel_type_id, type_name)
92
+        # else create a column in the class table
93
+        else:
94
+            # find name of the class table, and type of the field
95
+            table_name = self._class_table_name_from_field(model, new_state)
96
+
97
+        fieldtype = SQLMigrationHandler.fieldtype_to_sql[new_state['fieldtype']]
98
+        self._query_bd(
99
+            alter_add(table=table_name, column=new_state['name'] + ' ' + fieldtype)
100
+        )
101
+
102
+    # Test if internal tables must be created, create it if it must
103
+    def _install_tables(self):
104
+        self._query_bd(
105
+            create(table='object', column='id_lodel INT PRIMARY_KEY AUTOINCREMENT NOT NULL'),
106
+            create(table='relation', column=('id_relation INT PRIMARY_KEY AUTOINCREMENT NOT NULL', 'id_superior INT', 'id_subdordinate INT', 'nature CHAR(255)', 'depth INT', 'rank INT'))
107
+        )
108
+
109
+    def _query_bd(self, *queries):
110
+        with self.db as cur:
111
+            for query in queries:
112
+                print(query)
113
+                cur.execute(query)
114
+
115
+    def _class_table_name(self, class_name):
116
+        return class_name
117
+
118
+    def _class_table_name_from_field(self, model, field):
119
+        fieldgroup_id = model.component(field['fieldgroup_id']).class_id
120
+        class_name = model.component(fieldgroup_id).name
121
+        class_table_name = self._class_table_name(class_name)
122
+        return class_table_name

+ 31
- 0
Lodel/utils/mosql.py View File

@@ -0,0 +1,31 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+## @package Lodel.utils.mosql
4
+# @brief Helper function to use mosql library
5
+#
6
+# define create_database and drop_database Query to manipulate databases
7
+# define create, drop and alter Query to manipulate tables
8
+# define create_user, delete_user and grant to manipulate user and rights
9
+
10
+from mosql.util import Clause, Statement, Query, value, identifier, concat_by_comma, paren
11
+
12
+# chain definitions
13
+column_list = (identifier, concat_by_comma, paren)
14
+
15
+# Clauses
16
+create_clause = Clause('create table', alias='create', no_argument=True)
17
+if_not_exists = Clause('if not exists', alias='if_not_exists', no_argument=True, default=True)
18
+table_clause = Clause('table', (identifier, ), alias='table', hidden=True)
19
+character_clause = Clause('CHARACTER SET', (value, ), alias='character')
20
+column_clause = Clause('column', column_list, alias='column', hidden=True)
21
+
22
+alter_clause = Clause('alter table', (identifier, ), alias='table')
23
+add_clause = Clause('add column', (identifier, ), alias='column')
24
+
25
+# Statements
26
+create_statement = Statement([create_clause, if_not_exists, table_clause, column_clause, character_clause])
27
+alter_add_statement = Statement([alter_clause, add_clause])
28
+
29
+# queries
30
+create = Query(create_statement, ('table', 'column', 'if_not_exists'), {'create':True})
31
+alter_add = Query(alter_add_statement, ('table', 'column'))

+ 1
- 1
requirements.txt View File

@@ -1,2 +1,2 @@
1
-Django==1.7.8
2 1
 mysqlclient
2
+mosql==0.11

Loading…
Cancel
Save