Browse Source

sqlwrapper deletion and sqlsetup enhancement

Moved the database initialisation functionnality to sqlsetup and fixed all the code to handle the sqlwrapper deletion
Yann Weber 9 years ago
parent
commit
a3b9daf319

+ 0
- 85
Database/sqlquerybuilder.py View File

1
-# -*- coding: utf-8 -*-
2
-
3
-import os
4
-import logging as logger
5
-
6
-import sqlalchemy as sql
7
-
8
-from Database.sqlwrapper import SqlWrapper
9
-
10
-class SqlQueryBuilder():
11
-
12
-    def __init__(self, sqlwrapper, table):
13
-        if not type(sqlwrapper) is SqlWrapper:
14
-             logger.error("Unable to instanciate, bad argument...")
15
-             raise TypeError('Excepted a SqlWrapper not a '+str(type(sqlwrapper)))
16
-        self.table = table
17
-        self.sqlwrapper = sqlwrapper
18
-        self.proxy = None
19
-
20
-
21
-    def Select(self, arg):
22
-        """ Alias for select clause
23
-            @param arg iterable: arg must be a Python list or other iterable and contain eather table name/type or colum/literal_column
24
-        """
25
-
26
-        self.proxy = sql.select(arg)
27
-        return self.proxy
28
-
29
-    def Where(self, arg):
30
-        """ Alias for where clause
31
-            @param arg SQL expression object or string
32
-        """
33
-        self.proxy = self.proxy.where(arg)
34
-
35
-    def From(self, arg):
36
-        """ Alias for select_from clause
37
-            @param arg Table or table('tablename') or join clause
38
-        """
39
-        self.proxy = self.proxy.select_from(arg)
40
-
41
-    def Update(self):
42
-        self.proxy = self.table.update()
43
-
44
-    def Insert(self):
45
-        self.proxy = self.table.insert()
46
-
47
-    def Delete(self):
48
-        self.proxy = self.proxy.delete()
49
-
50
-    def Value(self, arg):
51
-        """
52
-        Allow you to specifies the VALUES or SET clause of the statement.
53
-        @param arg: VALUES or SET clause
54
-        """
55
-        self.proxy = self.proxy.values(arg)
56
-
57
-    def Execute(self, bindedparam):
58
-        """
59
-        Execute the sql query constructed in the proxy and return the result.
60
-        If no query then return False.
61
-        @return: query result on success else False
62
-        """
63
-        if(self.proxy.__str__().split() == 'SELECT'):
64
-            if('bindparam' in self.proxy.__str__()):
65
-                #on test separement la présence de la clause bindparam et le type de l'argument correspondant
66
-                #car si la clause est présente mais que l'argument est defectueux on doit renvoyer False et non pas executer la requete
67
-                if(type(bindedparam) is list and type(bindedparam[0]) is dict):
68
-                    return self.sqlwrapper.rconn.execute(self.proxy, bindedparam)
69
-                else:
70
-                    return False
71
-            else:
72
-                return self.sqlwrapper.rconn.execute(self.proxy)
73
-        elif(self.proxy is not None):
74
-            if('bindparam' in self.proxy.__str__()):
75
-                if(type(bindedparam) is list and type(bindedparam[0]) is dict):
76
-                    return self.sqlwrapper.wconn.execute(self.proxy, bindedparam)
77
-                else:
78
-                    return False
79
-            else:
80
-                return self.sqlwrapper.wconn.execute(self.proxy)
81
-        else:
82
-            return False
83
-
84
-
85
-

+ 184
- 113
Database/sqlsetup.py View File

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
 
2
 
3
-from Database.sqlwrapper import SqlWrapper
4
 import sqlalchemy as sql
3
 import sqlalchemy as sql
5
-
6
-class SQLSetup(object): 
7
-
8
-    def initDb(self, dbconfname = 'default', alchemy_logs=None):
9
-        db = SqlWrapper(read_db = dbconfname, write_db = dbconfname, alchemy_logs=alchemy_logs)
10
-        tables = self.get_schema()
11
-        db.dropAll()
12
-        db.createAllFromConf(tables)
13
-
14
-    def get_schema(self):
15
-        tables = []
16
-
17
-        default_columns = [
18
-            {"name":"uid",          "type":"INTEGER", "extra":{"foreignkey":"uids.uid", "nullable":False, "primarykey":True}},
19
-            {"name":"name",         "type":"VARCHAR(50)", "extra":{"nullable":False, "unique":True}},
20
-            {"name":"string",       "type":"TEXT"},
21
-            {"name":"help",         "type":"TEXT"},
22
-            {"name":"rank",         "type":"INTEGER"},
23
-            {"name":"date_create",  "type":"DATETIME"},
24
-            {"name":"date_update",  "type":"DATETIME"},
25
-        ]
26
-
27
-        # Table listing all objects created by lodel, giving them an unique id
28
-        uids = {
29
-                "name":"uids",
30
-                "columns":[
31
-                    {"name":"uid",          "type":"INTEGER", "extra":{"nullable":False, "primarykey":True, 'autoincrement':True}},
32
-                    {"name":"table",        "type":"VARCHAR(50)"}
33
-                ]
34
-            }
35
-        tables.append(uids)
36
-
37
-
38
-        # Table listing the classes
39
-        em_class = {"name":"em_class"}
40
-        em_class['columns'] = default_columns + [
41
-            {"name":"classtype",    "type":"VARCHAR(50)"},
42
-            {"name":"sortcolumn",   "type":"VARCHAR(50)", "extra":{"default":"rank"}},
43
-            {"name":"icon",         "type":"INTEGER"},
44
-        ]
45
-        tables.append(em_class)
46
-
47
-
48
-        # Table listing the types
49
-        em_type = {"name":"em_type"}
50
-        em_type['columns'] = default_columns + [
51
-            {"name":"class_id",     "type":"INTEGER", "extra":{"foreignkey":"em_class.uid", "nullable":False}},
52
-            {"name":"sortcolumn",   "type":"VARCHAR(50)", "extra":{"default":"rank"}},
53
-            {"name":"icon",         "type":"INTEGER"},
54
-        ]
55
-        tables.append(em_type)
56
-
57
-        # relation between types: which type can be a child of another
58
-        em_type_hierarchy = {"name":"em_type_hierarchy"}
59
-        em_type_hierarchy['columns'] = [
60
-            {"name":"superior_id",    "type":"INTEGER", "extra":{"foreignkey":"em_type.uid", "nullable":False, "primarykey":True}},
61
-            {"name":"subordinate_id", "type":"INTEGER", "extra":{"foreignkey":"em_type.uid", "nullable":False, "primarykey":True}},
62
-            {"name":"nature",         "type":"VARCHAR(50)", "extra":{"primarykey":True}},
63
-        ]
64
-        tables.append(em_type_hierarchy)
65
-
66
-       # Table listing the fieldgroups of a class
67
-        em_fieldgroup = {"name":"em_fieldgroup"}
68
-        em_fieldgroup['columns'] = default_columns + [
69
-            {"name":"class_id",     "type":"INTEGER", "extra":{"foreignkey":"em_class.uid", "nullable":False}},
70
-        ]
71
-        tables.append(em_fieldgroup)
72
-
73
-        # Table listing the fields of a fieldgroup
74
-        em_field = {"name":"em_field"}
75
-        em_field['columns'] = default_columns + [
76
-            {"name":"fieldtype",   "type":"VARCHAR(50)", "extra":{"nullable":False}},
77
-            {"name":"fieldgroup_id",  "type":"INTEGER", "extra":{"foreignkey":"em_fieldgroup.uid", "nullable":False}},
78
-            {"name":"rel_to_type_id", "type":"INTEGER", "extra":{"foreignkey":"em_type.uid", "nullable":True, "server_default": sql.text('NULL')}}, # if relational: type this field refer to
79
-            {"name":"rel_field_id",   "type":"INTEGER", "extra":{"foreignkey":"em_type.uid", "nullable":True, "server_default": sql.text('NULL')}}, # if relational: field that specify the rel_to_type_id
80
-            {"name":"optional",       "type":"BOOLEAN"},
81
-            {"name":"internal",       "type":"BOOLEAN"},
82
-            {"name":"icon",           "type":"INTEGER"},
83
-        ]
84
-        tables.append(em_field)
85
-
86
-        # selected field for each type
87
-        em_field_type = {"name":"em_field_type"}
88
-        em_field_type['columns'] = [
89
-            {"name":"type_id",   "type":"INTEGER", "extra":{"foreignkey":"em_type.uid", "nullable":False, "primarykey":True}},
90
-            {"name":"field_id",  "type":"INTEGER", "extra":{"foreignkey":"em_field.uid", "nullable":False, "primarykey":True}},
91
-        ]
92
-        tables.append(em_field_type)
93
-
94
-        # Table of the objects created by the user (instance of the types)
95
-        objects = {
96
-            "name":"objects",
97
-            "columns":[
98
-                {"name":"uid",         "type":"INTEGER", "extra":{"foreignkey":"uids.uid", "nullable":False, "primarykey":True}},
99
-                {"name":"string",      "type":"VARCHAR(50)"},
100
-                {"name":"class_id",    "type":"INTEGER", "extra":{"foreignkey":"em_class.uid"}},
101
-                {"name":"type_id",     "type":"INTEGER", "extra":{"foreignkey":"em_type.uid"}},
102
-                {"name":"date_create", "type":"DATETIME"},
103
-                {"name":"date_update", "type":"DATETIME"},
104
-                {"name":"history",     "type":"TEXT"}
105
-            ]
106
-        }
107
-        tables.append(objects)
108
-
109
-        # Table listing all files
110
-        # TODO Préciser les colonnes à ajouter
111
-        files = {
112
-            "name":"files",
4
+import re #Converting string to sqlalchemy types
5
+from Database import sqlutils
6
+
7
+
8
+def init_db(dbconfname = 'default', alchemy_logs=None, schema=None):
9
+
10
+    dbe = sqlutils.getEngine(dbconfname, alchemy_logs)
11
+    meta = sqlutils.meta(dbe)
12
+    meta.reflect()
13
+    meta.drop_all(dbe)
14
+    #refresh meta (maybe useless)
15
+    meta = sqlutils.meta(dbe)
16
+    meta.reflect()
17
+    
18
+    if schema is None:
19
+        schema = get_schema()
20
+
21
+    for table in schema:
22
+        topt = table.copy()
23
+        del topt['columns']
24
+        name = topt['name']
25
+        del topt['name']
26
+        cur_table = sql.Table(name, meta, **topt)
27
+        for col in table['columns']:
28
+            cur_col = create_column(**col)
29
+            cur_table.append_column(cur_col)
30
+
31
+    meta.create_all(bind=dbe)
32
+    pass
33
+    
34
+
35
+
36
+def get_schema():
37
+    tables = []
38
+
39
+    default_columns = [
40
+        {"name":"uid",          "type":"INTEGER", "extra":{"foreignkey":"uids.uid", "nullable":False, "primarykey":True}},
41
+        {"name":"name",         "type":"VARCHAR(50)", "extra":{"nullable":False, "unique":True}},
42
+        {"name":"string",       "type":"TEXT"},
43
+        {"name":"help",         "type":"TEXT"},
44
+        {"name":"rank",         "type":"INTEGER"},
45
+        {"name":"date_create",  "type":"DATETIME"},
46
+        {"name":"date_update",  "type":"DATETIME"},
47
+    ]
48
+
49
+    # Table listing all objects created by lodel, giving them an unique id
50
+    uids = {
51
+            "name":"uids",
113
             "columns":[
52
             "columns":[
114
-                {"name":"uid",     "type":"INTEGER", "extra":{"foreignkey":"uids.uid", "nullable":False, "primarykey":True}},
115
-                {"name":"field1",  "type":"VARCHAR(50)"}
53
+                {"name":"uid",          "type":"INTEGER", "extra":{"nullable":False, "primarykey":True, 'autoincrement':True}},
54
+                {"name":"table",        "type":"VARCHAR(50)"}
116
             ]
55
             ]
117
         }
56
         }
118
-        tables.append(files)
57
+    tables.append(uids)
58
+
59
+
60
+    # Table listing the classes
61
+    em_class = {"name":"em_class"}
62
+    em_class['columns'] = default_columns + [
63
+        {"name":"classtype",    "type":"VARCHAR(50)"},
64
+        {"name":"sortcolumn",   "type":"VARCHAR(50)", "extra":{"default":"rank"}},
65
+        {"name":"icon",         "type":"INTEGER"},
66
+    ]
67
+    tables.append(em_class)
68
+
69
+
70
+    # Table listing the types
71
+    em_type = {"name":"em_type"}
72
+    em_type['columns'] = default_columns + [
73
+        {"name":"class_id",     "type":"INTEGER", "extra":{"foreignkey":"em_class.uid", "nullable":False}},
74
+        {"name":"sortcolumn",   "type":"VARCHAR(50)", "extra":{"default":"rank"}},
75
+        {"name":"icon",         "type":"INTEGER"},
76
+    ]
77
+    tables.append(em_type)
78
+
79
+    # relation between types: which type can be a child of another
80
+    em_type_hierarchy = {"name":"em_type_hierarchy"}
81
+    em_type_hierarchy['columns'] = [
82
+        {"name":"superior_id",    "type":"INTEGER", "extra":{"foreignkey":"em_type.uid", "nullable":False, "primarykey":True}},
83
+        {"name":"subordinate_id", "type":"INTEGER", "extra":{"foreignkey":"em_type.uid", "nullable":False, "primarykey":True}},
84
+        {"name":"nature",         "type":"VARCHAR(50)", "extra":{"primarykey":True}},
85
+    ]
86
+    tables.append(em_type_hierarchy)
87
+
88
+   # Table listing the fieldgroups of a class
89
+    em_fieldgroup = {"name":"em_fieldgroup"}
90
+    em_fieldgroup['columns'] = default_columns + [
91
+        {"name":"class_id",     "type":"INTEGER", "extra":{"foreignkey":"em_class.uid", "nullable":False}},
92
+    ]
93
+    tables.append(em_fieldgroup)
94
+
95
+    # Table listing the fields of a fieldgroup
96
+    em_field = {"name":"em_field"}
97
+    em_field['columns'] = default_columns + [
98
+        {"name":"fieldtype",   "type":"VARCHAR(50)", "extra":{"nullable":False}},
99
+        {"name":"fieldgroup_id",  "type":"INTEGER", "extra":{"foreignkey":"em_fieldgroup.uid", "nullable":False}},
100
+        {"name":"rel_to_type_id", "type":"INTEGER", "extra":{"foreignkey":"em_type.uid", "nullable":True, "server_default": sql.text('NULL')}}, # if relational: type this field refer to
101
+        {"name":"rel_field_id",   "type":"INTEGER", "extra":{"foreignkey":"em_type.uid", "nullable":True, "server_default": sql.text('NULL')}}, # if relational: field that specify the rel_to_type_id
102
+        {"name":"optional",       "type":"BOOLEAN"},
103
+        {"name":"internal",       "type":"BOOLEAN"},
104
+        {"name":"icon",           "type":"INTEGER"},
105
+    ]
106
+    tables.append(em_field)
107
+
108
+    # selected field for each type
109
+    em_field_type = {"name":"em_field_type"}
110
+    em_field_type['columns'] = [
111
+        {"name":"type_id",   "type":"INTEGER", "extra":{"foreignkey":"em_type.uid", "nullable":False, "primarykey":True}},
112
+        {"name":"field_id",  "type":"INTEGER", "extra":{"foreignkey":"em_field.uid", "nullable":False, "primarykey":True}},
113
+    ]
114
+    tables.append(em_field_type)
115
+
116
+    # Table of the objects created by the user (instance of the types)
117
+    objects = {
118
+        "name":"objects",
119
+        "columns":[
120
+            {"name":"uid",         "type":"INTEGER", "extra":{"foreignkey":"uids.uid", "nullable":False, "primarykey":True}},
121
+            {"name":"string",      "type":"VARCHAR(50)"},
122
+            {"name":"class_id",    "type":"INTEGER", "extra":{"foreignkey":"em_class.uid"}},
123
+            {"name":"type_id",     "type":"INTEGER", "extra":{"foreignkey":"em_type.uid"}},
124
+            {"name":"date_create", "type":"DATETIME"},
125
+            {"name":"date_update", "type":"DATETIME"},
126
+            {"name":"history",     "type":"TEXT"}
127
+        ]
128
+    }
129
+    tables.append(objects)
130
+
131
+    # Table listing all files
132
+    # TODO Préciser les colonnes à ajouter
133
+    files = {
134
+        "name":"files",
135
+        "columns":[
136
+            {"name":"uid",     "type":"INTEGER", "extra":{"foreignkey":"uids.uid", "nullable":False, "primarykey":True}},
137
+            {"name":"field1",  "type":"VARCHAR(50)"}
138
+        ]
139
+    }
140
+    tables.append(files)
141
+
142
+    return tables
143
+
144
+def create_column(**kwargs):
145
+    #Converting parameters
146
+    if 'type_' not in kwargs and 'type' in kwargs:
147
+        kwargs['type_'] = _strToSqlAType(kwargs['type'])
148
+        del kwargs['type']
149
+
150
+    if 'extra' in kwargs:
151
+        #put the extra keys in kwargs
152
+        for exname in kwargs['extra']:
153
+            kwargs[exname] = kwargs['extra'][exname]
154
+        del kwargs['extra']
155
+
156
+    if 'foreignkey' in kwargs:
157
+        #Instanciate a fk
158
+        fk = sql.ForeignKey(kwargs['foreignkey'])
159
+        del kwargs['foreignkey']
160
+    else:
161
+        fk = None
162
+
163
+    if 'primarykey' in kwargs:
164
+        #renaming primary_key in primarykey in kwargs
165
+        kwargs['primary_key'] = kwargs['primarykey']
166
+        del kwargs['primarykey']
167
+
168
+    col = sql.Column(**kwargs)
169
+
170
+    if fk != None:
171
+        col.append_foreign_key(fk)
172
+
173
+    return col
174
+
175
+def _strToSqlAType(strtype):
176
+    """ Convert a string to an sqlAlchemy column type """
177
+    if 'VARCHAR' in strtype:
178
+        return _strToVarchar(strtype)
179
+    else:
180
+        try:
181
+            return getattr(sql, strtype)
182
+        except AttributeError:
183
+            raise NameError("Unknown type '"+strtype+"'")
184
+    pass
185
+
186
+def _strToVarchar(vstr):
187
+    """ Convert a string like 'VARCHAR(XX)' (with XX an integer) to a SqlAlchemy varchar type"""
188
+    check_length = re.search(re.compile('VARCHAR\(([\d]+)\)', re.IGNORECASE), vstr)
189
+    column_length = int(check_length.groups()[0]) if check_length else None
190
+    return sql.VARCHAR(length=column_length)
119
 
191
 
120
-        return tables

+ 1
- 1
Database/sqlutils.py View File

75
 # @param engine sqlalchemy.engine : A sqlalchemy engine
75
 # @param engine sqlalchemy.engine : A sqlalchemy engine
76
 # @return an sql alechemy MetaData instance bind to engine
76
 # @return an sql alechemy MetaData instance bind to engine
77
 def meta(engine):
77
 def meta(engine):
78
-    res = sqla.MetaData()
78
+    res = sqla.MetaData(bind=engine)
79
     res.reflect(bind=engine)
79
     res.reflect(bind=engine)
80
     return res
80
     return res
81
 
81
 

+ 0
- 515
Database/sqlwrapper.py View File

1
-# -*- coding: utf-8 -*-
2
-import os
3
-import re
4
-import logging
5
-
6
-import sqlalchemy as sqla
7
-from sqlalchemy.ext.compiler import compiles
8
-from django.conf import settings
9
-
10
-from Database.sqlalter import *
11
-
12
-#Logger config
13
-#logger.getLogger().setLevel('WARNING')
14
-logger = logging.getLogger('lodel2.Database.sqlwrapper')
15
-logger.setLevel('CRITICAL')
16
-logger.propagate = False
17
-#To be able to use dango confs
18
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Lodel.settings")
19
-
20
-class SqlWrapper(object):
21
-    """ A wrapper class to sqlalchemy
22
-
23
-        Usefull to provide a standart API
24
-
25
-        __Note__ : This class is not thread safe (sqlAlchemy connections are not). Create a new instance of the class to use in different threads or use SqlWrapper::copy
26
-    """
27
-    ENGINES = {'mysql': {
28
-                    'driver': 'pymysql',
29
-                    'encoding': 'utf8'
30
-                },
31
-                'postgresql': {
32
-                    'driver': 'psycopg2',
33
-                    'encoding': 'utf8',
34
-                },
35
-                'sqlite': {
36
-                    'driver': 'pysqlite',
37
-                    'encoding': 'utf8',
38
-                },
39
-    }
40
-    
41
-    ##Configuration dict alias for class access
42
-    config=settings.LODEL2SQLWRAPPER
43
-    
44
-    ##Wrapper instance list
45
-    wrapinstance = dict()
46
-
47
-    def __init__(self, name=None, alchemy_logs=None, read_db = "default", write_db = "default"):
48
-        """ Instanciate a new SqlWrapper
49
-            @param name str: The wrapper name
50
-            @param alchemy_logs bool: If true activate sqlalchemy logger
51
-            @param read_db str: The name of the db conf
52
-            @param write_db str: The name of the db conf
53
-
54
-            @todo Better use of name (should use self.cfg['wrapper'][name] to get engines configs
55
-            @todo Is it a really good idea to store instance in class scope ? Maybe not !!
56
-        """
57
-        
58
-        self.sqlalogging = False if alchemy_logs == None else bool(alchemy_logs)
59
-
60
-        if name == None:
61
-            self.name = read_db+':'+write_db
62
-        else:
63
-            self.name = name
64
-    
65
-        self.r_dbconf = read_db
66
-        self.w_dbconf = write_db
67
-
68
-        self._checkConf() #raise if errors in configuration
69
-
70
-        if self.name in self.__class__.wrapinstance:
71
-            logger.warning("A SqlWrapper with the name "+self.name+" allready exist. Replacing the old one by the new one")
72
-        SqlWrapper.wrapinstance[self.name] = self
73
-
74
-        #Engine and wrapper initialisation
75
-        self.r_engine = self._getEngine(True, self.sqlalogging)
76
-        self.w_engine = self._getEngine(False, self.sqlalogging)
77
-        self.r_conn = None
78
-        self.w_conn = None
79
-
80
-
81
-        self.metadata = None #TODO : use it to load all db schema in 1 request and don't load it each table instanciation
82
-        self.meta_crea = None
83
-
84
-        logger.debug("New wrapper instance : <"+self.name+" read:"+str(self.r_engine)+" write:"+str(self.w_engine))
85
-        pass
86
-
87
-    @classmethod
88
-    def getEngine(c, ename = 'default', logs = None):
89
-        return c(read_db = ename, write_db = ename, alchemy_logs = logs).r_engine
90
-
91
-    @property
92
-    def cfg(self):
93
-        """ Return the SqlWrapper.config dict """
94
-        return self.__class__.config;
95
-    @property
96
-    def _engines_cfg(self):
97
-        return self.__class__.ENGINES;
98
-
99
-    @property
100
-    def meta(self):
101
-        if self.metadata == None:
102
-            self.renewMetaData()
103
-        return self.metadata
104
-
105
-    def renewMetaData(self):
106
-        """ (Re)load the database schema """
107
-        self.metadata = sqla.MetaData(bind=self.r_engine)
108
-        self.metadata.reflect()
109
-
110
-    @property
111
-    def rconn(self):
112
-        """ Return the read connection
113
-            @warning Do not store the connection, call this method each time you need it
114
-        """
115
-        return self._getConnection(True)
116
-    @property
117
-    def wconn(self):
118
-        """ Return the write connection
119
-            @warning Do not store the connection, call this method each time you need it
120
-        """
121
-        return self._getConnection(False)
122
-
123
-    def _getConnection(self, read):
124
-        """ Return an opened connection
125
-            @param read bool: If true return the reading connection
126
-            @return A sqlAlchemy db connection
127
-            @private
128
-        """
129
-        if read:
130
-            r = self.r_conn
131
-        else:
132
-            r = self.w_conn
133
-
134
-        if r == None:
135
-            #Connection not yet opened
136
-            self.connect(read)
137
-            r = self._getConnection(read) #TODO : Un truc plus safe/propre qu'un appel reccursif ?
138
-        return r
139
-
140
-
141
-    def connect(self, read = None):
142
-        """ Open a connection to a database
143
-            @param read bool|None: If None connect both, if True only connect the read side (False the write side)
144
-            @return None
145
-        """
146
-        if read or read == None:
147
-            if self.r_conn != None:
148
-                logger.debug(' SqlWrapper("'+self.name+'") Unable to connect, already connected')
149
-            else:
150
-                self.r_conn = self.r_engine.connect()
151
-
152
-        if not read or read == None:
153
-            if self.w_conn != None:
154
-                logger.debug(' SqlWrapper("'+self.name+'") Unable to connect, already connected')
155
-            else:
156
-                self.w_conn = self.w_engine.connect()
157
-
158
-    def disconnect(self, read = None):
159
-        """ Close a connection to a database
160
-            @param read bool|None: If None disconnect both, if True only connect the read side (False the write side)
161
-            @return None
162
-        """
163
-        if read or read == None:
164
-            if self.r_conn == None:
165
-                logger.info('Unable to close read connection : connection not opened')
166
-            else:
167
-                self.r_conn.close()
168
-            self.r_conn = None
169
-
170
-        if not read or read == None:
171
-            if self.r_conn == None:
172
-                logger.info('Unable to close write connection : connection not opened')
173
-            else:
174
-                self.w_conn.close()
175
-            self.w_conn = None
176
-
177
-    def reconnect(self, read = None):
178
-        """ Close and reopen a connection to a database
179
-            @param read bool|None: If None disconnect both, if True only connect the read side (False the write side)
180
-            @return None
181
-        """
182
-        self.disconnect(read)
183
-        self.connect(read)
184
-
185
-    @classmethod
186
-    def reconnectAll(c, read = None):
187
-        """ Reconnect all the wrappers 
188
-            @static
189
-        """
190
-        for wname in c.wrapinstance:
191
-            c.wrapinstance[wname].reconnect(read)
192
-            
193
-    def Table(self, tname):
194
-        """ Instanciate a new SqlAlchemy Table
195
-            @param tname str: The table name
196
-            @return A new instance of SqlAlchemy::Table
197
-        """
198
-        if not isinstance(tname, str):
199
-            return TypeError('Excepting a <class str> but got a '+str(type(tname)))
200
-        #return sqla.Table(tname, self.meta, autoload_with=self.r_engine, autoload=True)
201
-        return sqla.Table(tname, self.meta)
202
-
203
-    def _getEngine(self, read=True, sqlalogging = None):
204
-        """ Return a sqlalchemy engine
205
-            @param read bool: If True return the read engine, else 
206
-            return the write one
207
-            @return a sqlachemy engine instance
208
-
209
-            @todo Put the check on db config in SqlWrapper.checkConf()
210
-        """
211
-        #Loading confs
212
-        cfg = self.cfg['db'][self.r_dbconf if read else self.w_dbconf]
213
-
214
-        edata = self._engines_cfg[cfg['ENGINE']] #engine infos
215
-        conn_str = ""
216
-
217
-        if cfg['ENGINE'] == 'sqlite':
218
-            #Sqlite connection string
219
-            conn_str = '%s+%s:///%s'%( cfg['ENGINE'],
220
-                edata['driver'],
221
-                cfg['NAME'])
222
-        else:
223
-            #Mysql and Postgres connection string
224
-            user = cfg['USER']
225
-            user += (':'+cfg['PASSWORD'] if 'PASSWORD' in cfg else '')
226
-            
227
-            if 'HOST' not in cfg:
228
-                logger.info('Not HOST in configuration, using localhost')
229
-                host = 'localhost'
230
-            else:
231
-                host = cfg['HOST']
232
-
233
-            host += (':'+cfg['PORT'] if 'PORT' in cfg else '')
234
-
235
-            conn_str = '%s+%s://'%(cfg['ENGINE'], edata['driver'])
236
-            conn_str += '%s@%s/%s'%(user,host,cfg['NAME'])
237
-
238
-
239
-        ret = sqla.create_engine(conn_str, encoding=edata['encoding'], echo=self.sqlalogging)
240
-
241
-        logger.debug("Getting engine :"+str(ret))
242
-
243
-        return ret
244
-
245
-    @classmethod
246
-    def getWrapper(c, name):
247
-        """ Return a wrapper instance from a wrapper name
248
-            @param name str: The wrapper name
249
-            @return a SqlWrapper instance
250
-
251
-            @throw KeyError
252
-        """
253
-        if name not in c.wrapinstance:
254
-            raise KeyError("No wrapper named '"+name+"' exists")
255
-        return c.wrapinstance[name]
256
-
257
-    def _checkConf(self):
258
-        """ Class method that check the configuration
259
-            
260
-            Configuration looks like
261
-            - db (mandatory)
262
-             - ENGINE (mandatory)
263
-             - NAME (mandatory)
264
-             - USER
265
-             - PASSWORD
266
-            - engines (mandatory)
267
-             - driver (mandatory)
268
-             - encoding (mandatory)
269
-            - dbread (mandatory if no default db)
270
-            - dbwrite (mandatory if no default db)
271
-        """
272
-        err = []
273
-        if 'db' not in self.cfg:
274
-            err.append('Missing "db" in configuration')
275
-        else:
276
-            for dbname in [self.r_dbconf, self.w_dbconf]:
277
-                if dbname not in self.cfg['db']:    
278
-                    err.append('Missing "'+dbname+'" db configuration')
279
-                else:
280
-                    db = self.cfg['db'][dbname]
281
-                    if db['ENGINE'] not in self._engines_cfg:
282
-                        err.append('Unknown engine "'+db['ENGINE']+'"')
283
-                    elif db['ENGINE'] != 'sqlite' and 'USER' not in db:
284
-                        err.append('Missing "User" in configuration of database "'+dbname+'"')
285
-                    if 'NAME' not in db:
286
-                        err.append('Missing "NAME" in database "'+dbname+'"')
287
-                        
288
-        if len(err)>0:
289
-            err_str = "\n"
290
-            for e in err:
291
-                err_str += "\t\t"+e+"\n"
292
-            raise NameError('Configuration errors in LODEL2SQLWRAPPER:'+err_str)
293
-    
294
-    def dropAll(self):
295
-        """ Drop ALL tables from the database """
296
-        if not settings.DEBUG:
297
-            logger.critical("Trying to drop all tables but we are not in DEBUG !!!")
298
-            raise RuntimeError("Trying to drop all tables but we are not in DEBUG !!!")
299
-        meta = sqla.MetaData(bind=self.w_engine)
300
-        meta.reflect()
301
-        meta.drop_all()
302
-        pass
303
-
304
-    def createAllFromConf(self, schema):
305
-        """ Create a bunch of tables from a schema
306
-            @param schema list: A list of table schema
307
-            @see SqlWrapper::createTable()
308
-        """
309
-        self.meta_crea = sqla.MetaData()
310
-
311
-        logger.info("Running function createAllFromConf")
312
-        for i,table in enumerate(schema):
313
-            if not isinstance(table, dict):
314
-                raise TypeError("Excepted a list of dict but got a "+str(type(schema))+" in the list")
315
-            self.createTable(**table)
316
-        
317
-        self.meta_crea.create_all(bind = self.w_engine)
318
-        logger.info("All tables created")
319
-        self.meta_crea = None
320
-        self.renewMetaData()
321
-        pass
322
-            
323
-    def createTable(self, name, columns, **kw):
324
-        """ Create a table
325
-            @param name str: The table name
326
-            @param columns list: A list of columns description dict
327
-            @param extra dict: Extra arguments for table creation
328
-            @see SqlWrapper::createColumn()
329
-        """
330
-
331
-        if self.meta_crea == None:
332
-            self.meta_crea = sqla.MetaData()
333
-            crea_now = True
334
-        else:
335
-            crea_now = False
336
-
337
-        if not isinstance(name, str):
338
-            raise TypeError("<class str> excepted for table name, but got "+type(name))
339
-
340
-        #if not 'mysql_engine' in kw and self.w_engine.dialect.name == 'mysql':
341
-        #    kw['mysql_engine'] = 'InnoDB'
342
-
343
-        res = sqla.Table(name, self.meta_crea, **kw)
344
-        for i,col in enumerate(columns):
345
-            res.append_column(self.createColumn(**col))
346
-
347
-        if crea_now:
348
-            self.meta_crea.create_all(self.w_engine)
349
-            logger.debug("Table '"+name+"' created")
350
-
351
-        pass
352
-
353
-    def createColumn(self, **kwargs):
354
-        """ Create a Column
355
-            
356
-            Accepte named parameters :
357
-                - name : The column name
358
-                - type : see SqlWrapper::_strToSqlAType()
359
-                - extra : a dict like { "primarykey":True, "nullable":False, "default":"test"...}
360
-            @param **kwargs 
361
-        """
362
-        if not 'name' in kwargs or ('type' not in kwargs and 'type_' not in kwargs):
363
-            pass#ERROR
364
-
365
-        #Converting parameters
366
-        if 'type_' not in kwargs and 'type' in kwargs:
367
-            kwargs['type_'] = self._strToSqlAType(kwargs['type'])
368
-            del kwargs['type']
369
-
370
-        if 'extra' in kwargs:
371
-            #put the extra keys in kwargs
372
-            for exname in kwargs['extra']:
373
-                kwargs[exname] = kwargs['extra'][exname]
374
-            del kwargs['extra']
375
-
376
-        if 'foreignkey' in kwargs:
377
-            #Instanciate a fk
378
-            fk = sqla.ForeignKey(kwargs['foreignkey'])
379
-            del kwargs['foreignkey']
380
-        else:
381
-            fk = None
382
-
383
-        if 'primarykey' in kwargs:
384
-            #renaming primary_key in primarykey in kwargs
385
-            kwargs['primary_key'] = kwargs['primarykey']
386
-            del kwargs['primarykey']
387
-
388
-        res = sqla.Column(**kwargs)
389
-
390
-        if fk != None:
391
-            res.append_foreign_key(fk)
392
-
393
-        #logger.debug("Column '"+kwargs['name']+"' created")
394
-        return res
395
-    
396
-    def _strToSqlAType(self, strtype):
397
-        """ Convert a string to an sqlAlchemy column type """
398
-
399
-        if 'VARCHAR' in strtype:
400
-            return self._strToVarchar(strtype)
401
-        else:
402
-            try:
403
-                return getattr(sqla, strtype)
404
-            except AttributeError:
405
-                raise NameError("Unknown type '"+strtype+"'")
406
-        pass
407
-
408
-    def _strToVarchar(self, vstr):
409
-        """ Convert a string like 'VARCHAR(XX)' (with XX an integer) to a SqlAlchemy varchar type"""
410
-        check_length = re.search(re.compile('VARCHAR\(([\d]+)\)', re.IGNORECASE), vstr)
411
-        column_length = int(check_length.groups()[0]) if check_length else None
412
-        return sqla.VARCHAR(length=column_length)
413
-     
414
-
415
-    def dropColumn(self, tname, colname):
416
-        """ Drop a column from a table
417
-            @param tname str|sqlalchemy.Table: The table name or a Table object
418
-            @param colname str|sqlalchemy.Column: The column name or a column object
419
-            @return None
420
-        """
421
-        if tname not in self.meta.tables: #Useless ?
422
-            raise NameError("The table '"+tname+"' dont exist")
423
-        table = self.Table(tname)
424
-        col = sqla.Column(colname)
425
-
426
-        ddl = DropColumn(table, col)
427
-        sql = ddl.compile(dialect=self.w_engine.dialect)
428
-        sql = str(sql)
429
-        logger.debug("Executing SQL  : '"+sql+"'")
430
-        ret = bool(self.w_engine.execute(sql))
431
-
432
-        self.renewMetaData()
433
-        return ret
434
-
435
-    
436
-    def addColumn(self, tname, colname, coltype):
437
-        """ Add a column to a table
438
-            @param tname str: The table name
439
-            @param colname str: The column name
440
-            @param coltype str: The new column type
441
-
442
-            @return True if query success False if it fails
443
-        """
444
-        if tname not in self.meta.tables: #Useless ?
445
-            raise NameError("The table '"+tname+"' dont exist")
446
-        table = self.Table(tname)
447
-        newcol = self.createColumn(name=colname, type_ = coltype)
448
-
449
-        ddl = AddColumn(table, newcol)
450
-        sql = ddl.compile(dialect=self.w_engine.dialect)
451
-        sql = str(sql)
452
-        logger.debug("Executing SQL  : '"+sql+"'")
453
-        ret = bool(self.wconn.execute(sql))
454
-
455
-        self.renewMetaData()
456
-        return ret
457
-
458
-    ## AddColumnObject
459
-    #
460
-    # Adds a column from a SQLAlchemy Column Object
461
-    #
462
-    # @param tname str: Name of the table in which to add the column
463
-    # @param column Column: Column object to add to the table
464
-    # @return True if query is successful, False if it fails
465
-    def addColumnObject(self, tname, column):
466
-        if tname not in self.meta.tables:
467
-            raise NameError("The table '%s' doesn't exist" % tname)
468
-        table = self.Table(tname)
469
-
470
-        ddl = AddColumn(table, column)
471
-        sql = ddl.compile(dialect=self.w_engine.dialect)
472
-        sql = str(sql)
473
-        logger.debug("Executing SQL : '%s'" % sql)
474
-        ret = bool(self.wconn.execute(sql))
475
-        self.renewMetaData()
476
-        return ret
477
-
478
-    def alterColumn(self, tname, colname, col_newtype):
479
-        """ Change the type of a column
480
-            @param tname str: The table name
481
-            @param colname str: The column name
482
-            @param col_newtype str: The column new type
483
-
484
-            @return True if query successs False if it fails
485
-        """
486
-        
487
-        if tname not in self.meta.tables: #Useless ?
488
-            raise NameError("The table '"+tname+"' dont exist")
489
-
490
-        col = self.createColumn(name=colname, type_=col_newtype)
491
-        table = self.Table(tname)
492
-
493
-        ddl = AlterColumn(table, newcol)
494
-        sql = ddl.compile(dialect=self.w_engine.dialect)
495
-        sql = str(sql)
496
-        logger.debug("Executing SQL  : '"+sql+"'")
497
-        ret = bool(self.wconn.execute(sql))
498
-
499
-        self.renewMetaData()
500
-        return ret
501
-
502
-    def _debug__printSchema(self):
503
-        """ Debug function to print the db schema """
504
-        print(self.meta)
505
-        for tname in self.meta.tables:
506
-            self._debug__printTable(tname)
507
-
508
-    def _debug__printTable(self, tname):
509
-        t = self.meta.tables[tname]
510
-        tstr = 'Table : "'+tname+'" :\n'
511
-        for c in t.c:
512
-            tstr += '\t\t"'+c.name+'"('+str(c.type)+') \n'
513
-        print(tstr)
514
-            
515
-

+ 1
- 2
EditorialModel/components.py View File

175
         dbe = cls.db_engine()
175
         dbe = cls.db_engine()
176
         conn = dbe.connect()
176
         conn = dbe.connect()
177
 
177
 
178
-        #kwargs['rank'] = cls.get_max_rank(kwargs[cls.ranked_in]) + 1 #Warning !!!
179
-        kwargs['rank'] = -1
178
+        kwargs['rank'] = -1 #Warning !!!
180
 
179
 
181
         table = sql.Table(cls.table, sqlutils.meta(dbe))
180
         table = sql.Table(cls.table, sqlutils.meta(dbe))
182
         req = table.insert(kwargs)
181
         req = table.insert(kwargs)

+ 2
- 4
EditorialModel/test/test_classes.py View File

16
 from EditorialModel.fields import EmField
16
 from EditorialModel.fields import EmField
17
 import EditorialModel.fieldtypes  as fieldTypes
17
 import EditorialModel.fieldtypes  as fieldTypes
18
 
18
 
19
-from Database.sqlsetup import SQLSetup
20
-from Database import sqlutils
19
+from Database import sqlutils, sqlsetup
21
 import sqlalchemy as sqla
20
 import sqlalchemy as sqla
22
 
21
 
23
 
22
 
33
     # run before every instanciation of the class
32
     # run before every instanciation of the class
34
     @classmethod
33
     @classmethod
35
     def setUpClass(cls):
34
     def setUpClass(cls):
36
-        sql = SQLSetup()
37
-        sql.initDb()
35
+        sqlsetup.init_db()
38
 
36
 
39
     # run before every function of the class
37
     # run before every function of the class
40
     def setUp(self):
38
     def setUp(self):

+ 5
- 12
EditorialModel/test/test_component.py View File

21
 
21
 
22
 from Lodel.utils.mlstring import MlString
22
 from Lodel.utils.mlstring import MlString
23
 
23
 
24
-from Database.sqlsetup import SQLSetup
25
-from Database.sqlwrapper import SqlWrapper
26
 from Database import sqlutils
24
 from Database import sqlutils
25
+from Database import sqlsetup
27
 import sqlalchemy as sqla
26
 import sqlalchemy as sqla
28
 
27
 
29
 
28
 
57
     logging.basicConfig(level=logging.CRITICAL)
56
     logging.basicConfig(level=logging.CRITICAL)
58
 
57
 
59
     #testDB setup
58
     #testDB setup
60
-    sqls = SQLSetup()
61
-    tables = sqls.get_schema()
59
+    tables = sqlsetup.get_schema()
62
     ttest = {   'name':'ttest',
60
     ttest = {   'name':'ttest',
63
                 'columns':  [
61
                 'columns':  [
64
                     {"name":"uid",          "type":"INTEGER", "extra":{"foreignkey":"uids.uid", "nullable":False, "primarykey":True}},
62
                     {"name":"uid",          "type":"INTEGER", "extra":{"foreignkey":"uids.uid", "nullable":False, "primarykey":True}},
73
             }
71
             }
74
     tables.append(ttest)
72
     tables.append(ttest)
75
 
73
 
76
-    sqlwrap = globals()['dbwrapper'] = SqlWrapper(read_db='default', write_db = 'default', alchemy_logs=False)
77
     globals()['tables'] = tables
74
     globals()['tables'] = tables
78
 
75
 
79
     #Creating db structure
76
     #Creating db structure
81
     initTestDb(TEST_COMPONENT_DBNAME)
78
     initTestDb(TEST_COMPONENT_DBNAME)
82
     setDbConf(TEST_COMPONENT_DBNAME)
79
     setDbConf(TEST_COMPONENT_DBNAME)
83
 
80
 
84
-    sqlwrap.createAllFromConf(tables)
81
+    sqlsetup.init_db('default', False, tables)   
85
 
82
 
86
-    dbe = sqlwrap.r_engine
83
+    dbe = sqlutils.getEngine('default')
87
 
84
 
88
     # Insertion of testings datas
85
     # Insertion of testings datas
89
     conn = dbe.connect()
86
     conn = dbe.connect()
143
         { 'uid': 1025, 'name': 'name', 'string': '{}', 'help': '{}', 'rank': 5},
140
         { 'uid': 1025, 'name': 'name', 'string': '{}', 'help': '{}', 'rank': 5},
144
     ]
141
     ]
145
 
142
 
146
-    @property
147
-    def db(self):
148
-        return globals()['dbwrapper']
149
     @property
143
     @property
150
     def tables(self):
144
     def tables(self):
151
         return globals()['tables']
145
         return globals()['tables']
152
     
146
     
153
     def setUp(self):
147
     def setUp(self):
154
-        self.dber = globals()['dbwrapper'].r_engine
155
-        self.dbew = globals()['dbwrapper'].w_engine
148
+        self.dber = sqlutils.getEngine('default')
156
         self.test_values = self.__class__.test_values
149
         self.test_values = self.__class__.test_values
157
         #Db RAZ
150
         #Db RAZ
158
         #shutil.copyfile(TEST_COMPONENT_DBNAME+'_bck', globals()['component_test_dbfilename'])
151
         #shutil.copyfile(TEST_COMPONENT_DBNAME+'_bck', globals()['component_test_dbfilename'])

+ 1
- 5
EditorialModel/test/test_field.py View File

16
 from EditorialModel.test.utils import *
16
 from EditorialModel.test.utils import *
17
 from EditorialModel.fieldtypes import *
17
 from EditorialModel.fieldtypes import *
18
 
18
 
19
-from Database.sqlsetup import SQLSetup
20
-from Database.sqlwrapper import SqlWrapper
21
-from Database.sqlquerybuilder import SqlQueryBuilder
22
 from Database import sqlutils
19
 from Database import sqlutils
23
 
20
 
24
 import sqlalchemy as sqla
21
 import sqlalchemy as sqla
45
 
42
 
46
     @classmethod
43
     @classmethod
47
     def setUpClass(cls):
44
     def setUpClass(cls):
48
-        sqls = SQLSetup()
49
-        sqls.initDb()
45
+        sqlsetup.init_db()
50
 
46
 
51
         # Generation of the test data
47
         # Generation of the test data
52
         testclass = EmClass.create("testclass1",EmClassType.entity)
48
         testclass = EmClass.create("testclass1",EmClassType.entity)

+ 0
- 1
EditorialModel/test/test_fieldgroups.py View File

18
 
18
 
19
 from EditorialModel.test.utils import *
19
 from EditorialModel.test.utils import *
20
 
20
 
21
-from Database.sqlsetup import SQLSetup
22
 from Database import sqlutils
21
 from Database import sqlutils
23
 
22
 
24
 import sqlalchemy as sqla
23
 import sqlalchemy as sqla

+ 2
- 2
EditorialModel/test/utils.py View File

3
 import shutil
3
 import shutil
4
 
4
 
5
 from django.conf import settings
5
 from django.conf import settings
6
-from Database.sqlsetup import SQLSetup
6
+from Database import sqlsetup
7
 
7
 
8
 
8
 
9
 _TESTDB_DEFAULT_DIR = '/tmp/'
9
 _TESTDB_DEFAULT_DIR = '/tmp/'
42
             'ENGINE': 'sqlite',
42
             'ENGINE': 'sqlite',
43
             'NAME': db_default,
43
             'NAME': db_default,
44
         }
44
         }
45
-        SQLSetup().initDb(dbconfname = 'dbtest_default')
45
+        sqlsetup.init_db(dbconfname = 'dbtest_default')
46
         #Make the backup copy
46
         #Make the backup copy
47
         shutil.copyfile(db_default, db_copy)
47
         shutil.copyfile(db_default, db_copy)
48
     
48
     

Loading…
Cancel
Save