Browse Source

Ajout du module Database

Roland Haroutiounian 9 years ago
parent
commit
d8d0f9f303
5 changed files with 655 additions and 0 deletions
  1. 0
    0
      Database/__init__.py
  2. 36
    0
      Database/sqlsettings.py
  3. 135
    0
      Database/sqlsetup.py
  4. 481
    0
      Database/sqlwrapper.py
  5. 3
    0
      Database/tests.py

+ 0
- 0
Database/__init__.py View File


+ 36
- 0
Database/sqlsettings.py View File

@@ -0,0 +1,36 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+class SQLSettings(object):
4
+    
5
+    DEFAULT_HOSTNAME = 'localhost'
6
+        
7
+    dbms_list = {
8
+        'postgresql':{
9
+            'driver': 'psycopg2',
10
+            'encoding': 'utf8',
11
+        },
12
+        'mysql':{
13
+            # TODO à définir
14
+            'driver':'',
15
+            'encoding':'',
16
+        }
17
+    }
18
+
19
+    DB_READ_CONNECTION_NAME='' # TODO A configurer
20
+    DB_WRITE_CONNECTION_NAME='' # TODO A configurer
21
+    
22
+    querystrings = {
23
+        'add_column':{
24
+            'default':'ALTER TABLE %s ADD COLUMN %s %s'
25
+        },
26
+        'alter_column':{
27
+            'postgresql':'ALTER TABLE %s ALTER COLUMN %s TYPE %s',
28
+            'mysql':'ALTER TABLE %s ALTER COLUMN %s %s'
29
+        },
30
+        'drop_column':{
31
+            'default': 'ALTER TABLE %s DROP COLUMN %s'
32
+        }
33
+    }
34
+    
35
+    ACTION_TYPE_WRITE = 'write'
36
+    ACTION_TYPE_READ = 'read'

+ 135
- 0
Database/sqlsetup.py View File

@@ -0,0 +1,135 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from sql_settings import SqlSettings as sqlsettings
4
+from sqlmanager import SQLManager
5
+
6
+class SQLSetup(object): 
7
+
8
+    def initDb(self):
9
+        
10
+        sqlmanager = SQLManager()
11
+        
12
+        tables = []
13
+
14
+        # Table em_object
15
+        tables.append(
16
+            {
17
+                "name":"em_object",
18
+                "columns":[
19
+                    {"name":"id_global","type":"VARCHAR(50)", "extra":{"nullable":False,"unique":True}},
20
+                    {"name":"type","type":"VARCHAR(50)"}
21
+                ]
22
+            }
23
+        )
24
+
25
+        # Table em_document
26
+        tables.append(
27
+            {
28
+                "name":"em_document",
29
+                "columns":[
30
+                    {"name":"id_global","type":"VARCHAR(50)","extra":{"nullable":False,"unique":True}}, # TODO Foreign Key ?
31
+                    {"name":"string","type":"VARCHAR(50)"},
32
+                    {"name":"slug","type":"VARCHAR(50)"},
33
+                    {"name":"id_class","type":"VARCHAR(50)", "extra":{"foreignkey":"em_class.id_global"}},
34
+                    {"name":"id_type","type":"VARCHAR(50)", "extra":{"foreignkey":"em_type.id_global"}},
35
+                    {"name":"status","type":"VARCHAR(50)"},
36
+                    {"name":"date_update","type":"DATE"},
37
+                    {"name":"date_create","type":"DATE"},
38
+                    {"name":"history","type":"TEXT"}
39
+                ]
40
+            }
41
+        )
42
+
43
+        # Table em_file
44
+        # TODO Préciser les colonnes à ajouter
45
+        tables.append(
46
+            {
47
+                "name":"em_file",
48
+                "columns":[
49
+                    {"name":"id_global","type":"VARCHAR(50)","extra":{"nullable":False,"unique":True}}, # TODO Foreign Key ?
50
+                    {"name":"field1","type":"VARCHAR(50)"}
51
+                ]
52
+            }
53
+        )
54
+
55
+        # Table em_class
56
+        tables.append(      
57
+            {
58
+                "name":"em_class",
59
+                "columns":[
60
+                    {"name":"id_global","type":"VARCHAR(50)","extra": {"nullable":False, "unique":True}},
61
+                    {"name":"name","type":"VARCHAR(50)", "extra":{"nullable":False, "unique":True}},
62
+                    {"name":"classtype","type":"INTEGER"},
63
+                    {"name":"sortcolumn","type":"VARCHAR(50)", "extra":{"default":"rank"}},
64
+                    {"name":"string","type":"TEXT", "extra":{"default":"name"}},
65
+                    {"name":"help", "type":"TEXT"},
66
+                    {"name":"icon", "type":"VARCHAR(50)"},
67
+                    {"name":"rank", "type":"INTEGER"},
68
+                    {"name":"date_update", "type":"DATE"},
69
+                    {"name":"date_create", "type":"DATE"}
70
+                ]
71
+            }
72
+        )
73
+
74
+        # Table em_type
75
+        tables.append(
76
+            {
77
+                "name":"em_type",
78
+                "columns":[
79
+                    {"name":"globalid","type":"VARCHAR(50)","extra":{"nullable":False, "unique":True}},
80
+                    {"name":"id_class","type":"VARCHAR(50)","extra":{"nullable":False, "primarykey":True, "foreignkey":"em_class.id_global"}},
81
+                    {"name":"name","type":"VARCHAR(50)","extra":{"nullable":False, "primarykey":True}},
82
+                    {"name":"string", "type": "TEXT","extra":{"default":"name"}},
83
+                    {"name":"help", "type": "TEXT"},
84
+                    {"name":"sortcolumn","type":"VARCHAR(50)", "extra":{"default":"rank"}},
85
+                    {"name":"icon","type":"VARCHAR(50)"},
86
+                    {"name":"rank","type":"INTEGER"},
87
+                    {"name":"date_update","type":"DATE"},
88
+                    {"name":"date_create","type":"DATE"}
89
+                ]
90
+            }
91
+        )
92
+
93
+        # Table em_fieldgroup
94
+        tables.append(
95
+            {
96
+                "name":"em_fieldgroup",
97
+                "columns":[
98
+                    {"name":"globalid","type":"VARCHAR(50)","extra":{"nullable":False, "unique":True}},
99
+                    {"name":"id_class","type":"VARCHAR(50)","extra":{"nullable":False, "primarykey":True, "foreignkey":"em_class.id_global"}},
100
+                    {"name":"name","type":"VARCHAR(50)","extra":{"nullable":False, "primarykey":True}},
101
+                    {"name":"string","type":"TEXT","extra":{"default":"name"}},
102
+                    {"name":"help", "type":"TEXT"},
103
+                    {"name":"rank","type":"INTEGER"},
104
+                    {"name":"date_update","type":"DATE"},
105
+                    {"name":"date_create","type":"DATE"}
106
+                ]
107
+            }
108
+        )
109
+
110
+        # Table em_field
111
+        tables.append(
112
+            {
113
+                "name":"em_field",
114
+                "columns":[
115
+                    {"name":"globalid","type":"VARCHAR(50)","extra":{"nullable":False,"unique":True}},
116
+                    {"name":"id_fieldgroup","type":"VARCHAR(50)","extra":{"nullable":False,"foreignkey":"em_fieldgroup.globalid"}},
117
+                    {"name":"id_type","type":"VARCHAR(50)","extra":{"nullable":False,"foreignkey":"em_type.id_globalid"}},
118
+                    {"name":"name", "type":"VARCHAR(50)", "extra":{"nullable":False,"unique":True}},
119
+                    {"name":"id_fieldtype","type":"VARCHAR(50)","extra":{"nullable":False, "foreignkey":"em_type.globalid"}},
120
+                    {"name":"string","type":"TEXT", "extra":{"default":"name"}},
121
+                    {"name":"help","type":"TEXT"},
122
+                    {"name":"rank","type":"INTEGER"},
123
+                    {"name":"date_update","type":"DATE"},
124
+                    {"name":"date_create","type":"DATE"},
125
+                    {"name":"date_optional","type":"BOOLEAN"},
126
+                    {"name":"id_relation_field","type":"INTEGER",{"nullable":False}}, #TODO Foreign key ?
127
+                    {"name":"internal", "type":"BOOLEAN"},
128
+                    {"name":"defaultvalue","type":"VARCHAR(50)"},
129
+                    {"name":"params","type":"VARCHAR(50)"},
130
+                    {"name":"value","type":"VARCHAR(50)"}
131
+                ]  
132
+            }
133
+        )
134
+        
135
+        return sqlmanager.create_table(tables)

+ 481
- 0
Database/sqlwrapper.py View File

@@ -0,0 +1,481 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from sqlalchemy import * # TODO ajuster les classes à importer
4
+from sql_settings import SqlSettings as sqlsettings
5
+from django.conf import settings
6
+import re
7
+
8
+class SqlWrapper(object):
9
+
10
+    def __init__(self):
11
+        # TODO Passer ces deux éléments dans les settings django (au niveau de l'init de l'appli)
12
+        self.read_engine = self.get_engine(sqlsettings.DB_READ_CONNECTION_NAME)
13
+        self.write_engine = self.get_engine(sqlsettings.DB_WRITE_CONNECTION_NAME)
14
+
15
+    def get_engine(self, connection_name):
16
+        """Crée un moteur logique sur une base de données
17
+
18
+        Args:
19
+            connection_name (str): Nom de la connexion telle que définie dans les settings django
20
+
21
+        Returns:
22
+            Engine.
23
+        """
24
+
25
+        connection_params = settings.DATABASES[connection_name]
26
+
27
+        dialect = None
28
+        for dbms in sqlsettings.dbms_list:
29
+            if dbms in connection_params['ENGINE']:
30
+                dialect=dbms
31
+                break
32
+
33
+        driver = sqlsettings.dbms_list[dialect]['driver']
34
+        username = connection_params['USER']
35
+        password = connection_params['PASSWORD']
36
+        hostname = connection_params['HOST'] if connection_params['HOST']!='' else sqlsettings.DEFAULT_HOSTNAME
37
+        port = connection_params['PORT']
38
+        host = hostname if port=='' else '%s:%s' % (hostname, port)
39
+        database = connection_params['NAME']
40
+
41
+        connection_string = '%s+%s://%s:%s@%s/%s' % (dialect, driver, username, password, host, database)
42
+        engine = create_engine(connection_string, encoding=sqlsettings.dbms_list[dialect]['encoding'], echo=True, poolclass=NullPool)
43
+        return engine
44
+
45
+    def get_read_engine(self):
46
+        return self.read_engine
47
+
48
+    def get_write_engine(self):
49
+        return self.write_engine
50
+
51
+    def execute(self, queries, action_type):
52
+        """Exécute une série de requêtes en base
53
+
54
+        Args:
55
+            queries (list): une liste de requêtes
56
+            action_type (str): le type d'action ('read'|'write')
57
+
58
+        Returns:
59
+            List. Tableau de résultats sous forme de tuples (requête, résultat)
60
+        """
61
+
62
+        db = self.get_write_engine() if action_type==sqlsettings.ACTION_TYPE_WRITE else self.get_read_engine()
63
+        connection = db.connect()
64
+        transaction= connection.begin()
65
+        result = []
66
+
67
+        queries = queries if isinstance(queries, list) else [queries]
68
+
69
+
70
+        try:
71
+            for query in queries:
72
+                result.append((query, connection.execute(query)))
73
+            transaction.commit()
74
+            connection.close()
75
+        except:
76
+            transaction.rollback()
77
+            connection.close()
78
+            result = None
79
+            raise
80
+
81
+        return result
82
+
83
+    def create_foreign_key_constraint_object(self, localcol, remotecol, constraint_name=None):
84
+        """Génère une contrainte Clé étrangère
85
+
86
+        Args:
87
+            localcol (str) : Colonne locale
88
+            remotecol (str) : Colonne distante (syntaxe : Table.col)
89
+            constraint_name (str) : Nom de la contrainte (par défaut : fk_localcol_remotecol)
90
+
91
+        Returns:
92
+            ForeignKeyConstraint.
93
+        """
94
+
95
+        foreignkeyobj = ForeignKeyConstraint([localcol], [remotecol], name=constraint_name)
96
+        return foreignkeyobj
97
+
98
+    def create_column_object(self, column_name, column_type, column_extra=None):
99
+        """Génère un objet colonne
100
+
101
+        Args:
102
+            column_name (str): Nom de la colonne
103
+            column_name (str): Type de la colonne
104
+            column_extra (objet) : Objet json contenant les paramètres optionnels comme PRIMARY KEY, NOT NULL, etc ...
105
+
106
+        Returns:
107
+            Column. La méthode renvoie "None" si l'opération échoue
108
+
109
+        Ex d'objet json "extra" :
110
+        {
111
+            "primarykey":True,
112
+            "nullable":False,
113
+            "default":"test" ...
114
+        }
115
+        """
116
+
117
+        # Traitement du type (mapping avec les types SQLAlchemy)
118
+        # TODO créer une méthode qui fait un mapping plus complet
119
+        column = None
120
+        if column_type=='INTEGER':
121
+            column = Column(column_name, INTEGER)
122
+        elif 'VARCHAR' in column_type:
123
+            check_length = re.search(re.compile('VARCHAR\(([\d]+)\)', re.IGNORECASE), column_type)
124
+            column_length = int(check_length.groups()[0]) if check_length else None
125
+            column = Column(column_name, VARCHAR(length=column_length))
126
+        elif column_type=='TEXT':
127
+            column = Column(column_name, TEXT)
128
+        elif column_type=='DATE':
129
+            column = Column(column_name, DATE)
130
+        elif column_type=='BOOLEAN':
131
+            column = Column(column_name, BOOLEAN)
132
+
133
+        if column and column_extra:
134
+            if 'nullable' in column_extra:
135
+                column.nullable = column_extra['nullable']
136
+            if 'primarykey' in column_extra:
137
+                column.primary_key = column_extra['primarykey']
138
+            if 'default' in column_extra:
139
+                column.default = column_extra['default']
140
+            if 'foreingkey' in column_extra:
141
+                column.append_foreign_key(ForeignKey(column_extra['foreignkey']))
142
+
143
+
144
+        return column
145
+
146
+    def create_table(self, tableparams):
147
+        """Crée une nouvelle table
148
+        Args:
149
+            tableparams (object): Objet Json contenant les paramétrages de la table
150
+
151
+        Returns:
152
+            bool. True si le process est allé au bout, False si on a rencontré une erreur.
153
+
154
+        Le json de paramètres de table est construit suivant le modèle :
155
+        {
156
+            'name':'<nom de la table>',
157
+            'columns':[
158
+                {
159
+                    'name':"<nom de la colonne 1>",
160
+                    'type':"<type de la colonne 1, ex: VARCHAR(50)>",
161
+                    'extra': "indications supplémentaires sous forme d'objet JSON"
162
+                }
163
+            ],
164
+            'constraints':{},
165
+            ...
166
+        }
167
+
168
+        Les deux champs "name" et "columns" seulement sont obligatoires.
169
+        Le champ "extra" de la définition des colonnes est facultatif.
170
+        """
171
+
172
+        metadata = MetaData()
173
+        table = Table(tableparams['name'], metadata)
174
+        columns = tableparams['columns']
175
+        for column in columns:
176
+            column_extra = column['extra'] if 'extra' in column else None
177
+            table.append_column(self.create_column_object(column['name'], column['type'], column_extra))
178
+
179
+        try:
180
+            table.create(self.get_write_engine())
181
+            return True
182
+        except:
183
+            # TODO Ajuster le code d'erreur à retourner
184
+            return False
185
+
186
+
187
+    def get_table(self, table_name, action_type=sqlsettings.ACTION_TYPE_WRITE):
188
+        """Récupère une table dans un objet Table
189
+
190
+        Args:
191
+            table_name (str): Nom de la table
192
+            action_type (str): Type d'action (read|write) (par défaut : "write")
193
+        Returns:
194
+            Table.
195
+        """
196
+        db = self.get_write_engine() if action_type == sqlsettings.ACTION_TYPE_WRITE else self.get_read_engine()
197
+        metadata = MetaData()
198
+
199
+        return Table(table_name, metadata, autoload=True, autoload_with=db)
200
+
201
+
202
+    def drop_table(self, table_name):
203
+        """Supprime une table
204
+
205
+        Args:
206
+            table_name (str): Nom de la table
207
+
208
+        Returns:
209
+            bool. True si le process est allé au bout. False si on a rencontré une erreur
210
+        """
211
+
212
+        try:
213
+            db = self.get_write_engine().connect()
214
+            metadata = MetaData()
215
+            table = Table(table_name, metadata, autoload=True, autoload_with=db)
216
+            # Pour le drop, on utilise checkfirst qui permet de demander la suppression préalable des contraintes liées à la table
217
+            table.drop(db, checkfirst=True)
218
+            db.close()
219
+            return True
220
+        except:
221
+            # TODO ajuster le code d'erreur
222
+            return False
223
+
224
+
225
+    def get_querystring(self, action, dialect):
226
+        string_dialect = dialect if dialect in sqlsettings.querystrings[action] else 'default'
227
+        querystring = sqlsettings.querystrings[action][string_dialect]
228
+        return querystring
229
+
230
+    def add_column(self, table_name, column):
231
+        """Ajoute une colonne à une table existante
232
+
233
+        Args:
234
+            table_name (str): nom de la table
235
+            column (object): colonne à rajouter sous forme d'objet python - {"name":"<nom de la colonne>", "type":"<type de la colonne>"}
236
+
237
+        Returns:
238
+            bool. True si le process est allé au bout, False si on a rencontré une erreur
239
+        """
240
+
241
+        sqlquery = self.get_querystring('add_column', self.get_write_engine().dialect) % (table_name, column['name'], column['type'])
242
+        sqlresult = self.execute(sqlquery, sqlsettings.ACTION_TYPE_WRITE)
243
+        return True if sqlresult else False
244
+
245
+    def alter_column(self, table_name, column):
246
+        """Modifie le type d'une colonne
247
+
248
+        Args:
249
+            table_name (str): nom de la table
250
+            column_name (object): colonne passée sous forme d'objet python - {"name":"<nom de la colonne>","type":"<nouveau type de la colonne>"}
251
+
252
+        Returns:
253
+            bool. True si le process est allé au bout. False si on a rencontré une erreur
254
+        """
255
+
256
+        sqlquery = self.get_querystring('alter_column', self.get_write_engine().dialect) % (table_name, column['name'], column['type'])
257
+        sqlresult = self.execute(sqlquery, sqlsettings.ACTION_TYPE_WRITE)
258
+        return True if sqlresult else False
259
+
260
+    def insert(self, table_name, newrecord):
261
+        """Insère un nouvel enregistrement
262
+
263
+        Args:
264
+            table_name (str): nom de la table
265
+            newrecord (dict): objet python contenant le nouvel enregistrement à insérer - {"column1":"value1", "column2":"value2", etc ...)
266
+
267
+        Returns:
268
+            int. Nombre de lignes insérées
269
+        """
270
+        sqlresult = self.execute(self.get_table(table_name).insert().values(newrecord), sqlsettings.ACTION_TYPE_WRITE)
271
+        return sqlresult.rowcount
272
+
273
+    def delete(self, table_name, whereclauses):
274
+        """Supprime un enregistrement
275
+
276
+        Args:
277
+            table_name (str): nom de la table
278
+            whereclauses (list): liste des conditions sur les enregistrements (sous forme de textes SQL)
279
+
280
+        Returns:
281
+            int. Nombre de lignes supprimées
282
+        """
283
+
284
+        deleteobject = self.get_table(table_name).delete()
285
+
286
+        for whereclause in whereclauses:
287
+            deleteobject = deleteobject.where(whereclause)
288
+
289
+        sqlresult = self.execute(deleteobject, sqlsettings.ACTION_TYPE_WRITE)
290
+        return sqlresult.rowcount
291
+
292
+    def update(self, table_name, whereclauses, newvalues):
293
+        """Met à jour des enregistrements
294
+
295
+        Args:
296
+            table_name (str): nom de la table
297
+            whereclauses (list): liste des conditions sur les enregistrements (sous forme de textes SQL)
298
+            newvalues (dict): objet python contenant les nouvelles valeurs à insérer - {"colonne1":"valeur1", "colonne2":"valeur2", etc ...}
299
+
300
+        Returns:
301
+            int. Nombre de lignes modifiées
302
+
303
+        Raises:
304
+            DataError. Incompatibilité entre la valeur insérée et le type de colonne dans la table
305
+        """
306
+
307
+        table = self.get_table(table_name)
308
+        update_object = table.update()
309
+
310
+        updated_lines_count = 0
311
+
312
+        try:
313
+            for whereclause in whereclauses:
314
+                update_object = update_object.where(whereclause)
315
+
316
+            update_object = update_object.values(newvalues)
317
+
318
+            sqlresult = self.execute(update_object, sqlsettings.ACTION_TYPE_WRITE)
319
+            updated_lines_count = sqlresult.rowcount
320
+
321
+        except DataError:
322
+            # TODO Voir si on garde "-1" ou si on place un "None" ou un "False"
323
+            updated_lines_count = -1
324
+
325
+        return updated_lines_count
326
+
327
+    def select(self, select_params):
328
+        """Récupère un jeu d'enregistrements
329
+
330
+        Args:
331
+            select_params (list): liste d'objets permettant de caractériser la requête.
332
+                                    {
333
+                                        "what":"<liste des colonnes>",
334
+                                        "from":"<liste des tables>",
335
+                                        "where":"<liste des conditions>",
336
+                                        "distinct":True|False,
337
+                                        "join":{"left":"table_de_gauche.champ","right":"table_de_droite.champ", "options":{"outer":True|False}}
338
+                                        ...
339
+                                    }
340
+
341
+                                    Les listes sont supportées sous deux formats :
342
+                                        - texte SQL : "colonne1, colonne2, ..."
343
+                                        - liste Python : ["colonne1", "colonne2", ...]
344
+
345
+        Returns:
346
+            list. Liste des dictionnaires représentant les différents enregistrements.
347
+        """
348
+
349
+        if not 'what' in select_params or not 'from' in select_params:
350
+            # TODO Lever une exception à ce niveau
351
+            raise
352
+
353
+        query_what = select_params['what'] if isinstance(select_params['what'], list) else select_params['what'].replace(' ', '').split(',')
354
+        query_from = select_params['from'] if isinstance(select_params['from'], list) else select_params['from'].replace(' ', '').split(',')
355
+        query_where= select_params['where'] if isinstance(select_params['where'], list) else select_params['where'].replace(' ', '').split(',')
356
+        query_distinct = select_params['distinct'] if 'distinct' in select_params else False
357
+        query_limit = select_params['limit'] if 'limit' in select_params else False
358
+        query_offset = select_params['offset'] if 'offset' in select_params else False
359
+        query_orderby = select_params['order_by'] if 'order_by' in select_params else False
360
+        query_groupby = select_params['group_by'] if 'group_by' in select_params else False
361
+        query_having = select_params['having'] if 'having' in select_params else False
362
+
363
+        columns_list = []
364
+        if len(query_from) > 1:
365
+            for column_id in query_what:
366
+                # Il y a plusieurs tables, les colonnes sont donc nommées sur le format suivant : <nom de la table>.<nom du champ>
367
+                column_id_array = column_id.split('.')
368
+                columns_list.append(getattr(self.get_table(column_id_array[0]).c, column_id_array[1]))
369
+        else:
370
+            table = self.get_table(query_from[0])
371
+
372
+
373
+        select_object = select(columns=columnslist)
374
+
375
+        selected_lines = []
376
+
377
+        try:
378
+            # Traitement des différents paramètres de la requête
379
+            for query_where_item in query_where:
380
+                select_object = select_object.where(query_where_item)
381
+
382
+            select_object = select_object.distinct(True) if query_distinct else select_object
383
+            select_object = select_object.limit(query_limit) if query_limit else select_object
384
+            select_object = select_object.offset(query_offset) if query_offset else select_object
385
+
386
+            if query_orderby:
387
+                for query_orderby_column, query_orderby_direction in query_orderby:
388
+                    select_object = select_object.order_by("%s %s" % (query_orderby_column, query_orderby_direction))
389
+
390
+            #Traitement de la jointure
391
+            if 'join' in select_params:
392
+                select_join = self.create_join_object(select_params['join'])
393
+                if select_join:
394
+                    select_object = select_object.select_from(select_join)
395
+                else:
396
+                    raise # TODO Ajuster l'exception à lever (ici :"Données de jointure invalides ou manquantes")
397
+
398
+            #Traitement du group_by
399
+            if query_groupby:
400
+                for group_by_clause in query_groupby:
401
+                    select_object = select_object.group_by(self.get_table_column_from_sql_string(group_by_clause))
402
+
403
+            # Traitement du having
404
+            if query_groupby and query_having:
405
+                for having_clause in query_having:
406
+                    select_object = select_object.having(having_clause)
407
+
408
+            # Exécution de la requête
409
+            sqlresult = self.execute(select_object, sqlsettings.ACTION_TYPE_READ)
410
+
411
+            # Transformation du résultat en une liste de dictionnaires
412
+            records = sqlresult.fetchall()
413
+            for record in records:
414
+                selected_lines.append(dict(zip(record.keys(), record)))
415
+        except:
416
+            selected_lines = None
417
+
418
+        return selected_lines
419
+
420
+    def get_table_column_from_sql_string(self,sql_string):
421
+
422
+        sql_string_array = sql_string.split('.')
423
+        table_name = sql_string_array[0]
424
+        column_name = sql_string_array[1]
425
+        table_object = self.get_table(table_name,sqlsettings.ACTION_TYPE_READ)
426
+        column_object = getattr(table_object.c, column_name)
427
+
428
+        return column_object
429
+
430
+
431
+    def create_join_object(self, join_params):
432
+        """Génère un objet "Jointure" pour le greffer dans une requête
433
+
434
+        Args:
435
+            join_params (dict) : dictionnaire de données sur la jointure.
436
+                                 On peut avoir les deux formats suivants :
437
+                                 {
438
+                                    "left":"table.champ",
439
+                                    "right":"table.champ",
440
+                                    "options":{"outer":True|False}
441
+                                 }
442
+                                 ou
443
+                                 {
444
+                                    "left":{"table":"nom de la table","field":"nom du champ"},
445
+                                    "right":{"table":"nom de la table","field":"nom du champ"},
446
+                                    "options":{"outer":True|False}
447
+                                 }
448
+        Returns:
449
+             join.
450
+        """
451
+
452
+        join_object = None
453
+
454
+        if 'left' in join_params and 'right' in join_params:
455
+            if isinstance(join_params['left'], dict):
456
+                join_left_table_name = join_params['left']['table']
457
+                join_left_field_name = join_params['left']['field']
458
+            else:
459
+                join_left_array = join_params['left'].split('.')
460
+                join_left_table_name = join_left_array[0]
461
+                join_left_field_name = join_left_array[1]
462
+
463
+            if isinstance(join_params['right'], dict):
464
+                join_right_table_name = join_params['right']['table']
465
+                join_right_field_name = join_params['right']['field']
466
+            else:
467
+                join_right_array = join_params['right'].split('.')
468
+                join_right_table_name = join_right_array[0]
469
+                join_right_field_name = join_right_array[1]
470
+
471
+            left_table = self.get_table(join_left_table_name, sqlsettings.ACTION_TYPE_READ)
472
+            left_field = getattr(left_table.c, join_left_field_name)
473
+
474
+            right_table = self.get_table(join_right_table_name, sqlsettings.ACTION_TYPE_READ)
475
+            right_field = getattr(right_table.c, join_right_field_name)
476
+
477
+            join_option_isouter = True if 'options' in join_params and 'outer' in join_params and join_params['outer']==True else False
478
+
479
+            join_object = join(left_table, right_table, left_field == right_field,isouter=join_option_isouter)
480
+
481
+        return join_object

+ 3
- 0
Database/tests.py View File

@@ -0,0 +1,3 @@
1
+from django.test import TestCase
2
+
3
+# Create your tests here.

Loading…
Cancel
Save