~grupoesoc/cubicerp-addons/7.0

« back to all changes in this revision

Viewing changes to report_geraldo/lib/geraldo/site/newsite/site-geraldo/django/db/backends/creation.py

  • Committer: Cubic ERP
  • Date: 2014-01-07 15:38:09 UTC
  • Revision ID: info@cubicerp.com-20140107153809-4jmif3zoi8rcveve
[ADD] cubicReport

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import sys
 
2
import time
 
3
try:
 
4
    set
 
5
except NameError:
 
6
    # Python 2.3 compat
 
7
    from sets import Set as set
 
8
 
 
9
from django.conf import settings
 
10
from django.core.management import call_command
 
11
 
 
12
# The prefix to put on the default database name when creating
 
13
# the test database.
 
14
TEST_DATABASE_PREFIX = 'test_'
 
15
 
 
16
class BaseDatabaseCreation(object):
 
17
    """
 
18
    This class encapsulates all backend-specific differences that pertain to
 
19
    database *creation*, such as the column types to use for particular Django
 
20
    Fields, the SQL used to create and destroy tables, and the creation and
 
21
    destruction of test databases.
 
22
    """
 
23
    data_types = {}
 
24
 
 
25
    def __init__(self, connection):
 
26
        self.connection = connection
 
27
 
 
28
    def sql_create_model(self, model, style, known_models=set()):
 
29
        """
 
30
        Returns the SQL required to create a single model, as a tuple of:
 
31
            (list_of_sql, pending_references_dict)
 
32
        """
 
33
        from django.db import models
 
34
 
 
35
        opts = model._meta
 
36
        final_output = []
 
37
        table_output = []
 
38
        pending_references = {}
 
39
        qn = self.connection.ops.quote_name
 
40
        for f in opts.local_fields:
 
41
            col_type = f.db_type()
 
42
            tablespace = f.db_tablespace or opts.db_tablespace
 
43
            if col_type is None:
 
44
                # Skip ManyToManyFields, because they're not represented as
 
45
                # database columns in this table.
 
46
                continue
 
47
            # Make the definition (e.g. 'foo VARCHAR(30)') for this field.
 
48
            field_output = [style.SQL_FIELD(qn(f.column)),
 
49
                style.SQL_COLTYPE(col_type)]
 
50
            field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
 
51
            if f.primary_key:
 
52
                field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
 
53
            elif f.unique:
 
54
                field_output.append(style.SQL_KEYWORD('UNIQUE'))
 
55
            if tablespace and f.unique:
 
56
                # We must specify the index tablespace inline, because we
 
57
                # won't be generating a CREATE INDEX statement for this field.
 
58
                field_output.append(self.connection.ops.tablespace_sql(tablespace, inline=True))
 
59
            if f.rel:
 
60
                ref_output, pending = self.sql_for_inline_foreign_key_references(f, known_models, style)
 
61
                if pending:
 
62
                    pr = pending_references.setdefault(f.rel.to, []).append((model, f))
 
63
                else:
 
64
                    field_output.extend(ref_output)
 
65
            table_output.append(' '.join(field_output))
 
66
        if opts.order_with_respect_to:
 
67
            table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \
 
68
                style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \
 
69
                style.SQL_KEYWORD('NULL'))
 
70
        for field_constraints in opts.unique_together:
 
71
            table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
 
72
                ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints]))
 
73
 
 
74
        full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
 
75
        for i, line in enumerate(table_output): # Combine and add commas.
 
76
            full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
 
77
        full_statement.append(')')
 
78
        if opts.db_tablespace:
 
79
            full_statement.append(self.connection.ops.tablespace_sql(opts.db_tablespace))
 
80
        full_statement.append(';')
 
81
        final_output.append('\n'.join(full_statement))
 
82
 
 
83
        if opts.has_auto_field:
 
84
            # Add any extra SQL needed to support auto-incrementing primary keys.
 
85
            auto_column = opts.auto_field.db_column or opts.auto_field.name
 
86
            autoinc_sql = self.connection.ops.autoinc_sql(opts.db_table, auto_column)
 
87
            if autoinc_sql:
 
88
                for stmt in autoinc_sql:
 
89
                    final_output.append(stmt)
 
90
 
 
91
        return final_output, pending_references
 
92
 
 
93
    def sql_for_inline_foreign_key_references(self, field, known_models, style):
 
94
        "Return the SQL snippet defining the foreign key reference for a field"
 
95
        qn = self.connection.ops.quote_name
 
96
        if field.rel.to in known_models:
 
97
            output = [style.SQL_KEYWORD('REFERENCES') + ' ' + \
 
98
                style.SQL_TABLE(qn(field.rel.to._meta.db_table)) + ' (' + \
 
99
                style.SQL_FIELD(qn(field.rel.to._meta.get_field(field.rel.field_name).column)) + ')' +
 
100
                self.connection.ops.deferrable_sql()
 
101
            ]
 
102
            pending = False
 
103
        else:
 
104
            # We haven't yet created the table to which this field
 
105
            # is related, so save it for later.
 
106
            output = []
 
107
            pending = True
 
108
 
 
109
        return output, pending
 
110
 
 
111
    def sql_for_pending_references(self, model, style, pending_references):
 
112
        "Returns any ALTER TABLE statements to add constraints after the fact."
 
113
        from django.db.backends.util import truncate_name
 
114
 
 
115
        qn = self.connection.ops.quote_name
 
116
        final_output = []
 
117
        opts = model._meta
 
118
        if model in pending_references:
 
119
            for rel_class, f in pending_references[model]:
 
120
                rel_opts = rel_class._meta
 
121
                r_table = rel_opts.db_table
 
122
                r_col = f.column
 
123
                table = opts.db_table
 
124
                col = opts.get_field(f.rel.field_name).column
 
125
                # For MySQL, r_name must be unique in the first 64 characters.
 
126
                # So we are careful with character usage here.
 
127
                r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
 
128
                final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
 
129
                    (qn(r_table), truncate_name(r_name, self.connection.ops.max_name_length()),
 
130
                    qn(r_col), qn(table), qn(col),
 
131
                    self.connection.ops.deferrable_sql()))
 
132
            del pending_references[model]
 
133
        return final_output
 
134
 
 
135
    def sql_for_many_to_many(self, model, style):
 
136
        "Return the CREATE TABLE statments for all the many-to-many tables defined on a model"
 
137
        output = []
 
138
        for f in model._meta.local_many_to_many:
 
139
            output.extend(self.sql_for_many_to_many_field(model, f, style))
 
140
        return output
 
141
 
 
142
    def sql_for_many_to_many_field(self, model, f, style):
 
143
        "Return the CREATE TABLE statements for a single m2m field"
 
144
        from django.db import models
 
145
        from django.db.backends.util import truncate_name
 
146
 
 
147
        output = []
 
148
        if f.creates_table:
 
149
            opts = model._meta
 
150
            qn = self.connection.ops.quote_name
 
151
            tablespace = f.db_tablespace or opts.db_tablespace
 
152
            if tablespace:
 
153
                sql = self.connection.ops.tablespace_sql(tablespace, inline=True)
 
154
                if sql:
 
155
                    tablespace_sql = ' ' + sql
 
156
                else:
 
157
                    tablespace_sql = ''
 
158
            else:
 
159
                tablespace_sql = ''
 
160
            table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
 
161
                style.SQL_TABLE(qn(f.m2m_db_table())) + ' (']
 
162
            table_output.append('    %s %s %s%s,' %
 
163
                (style.SQL_FIELD(qn('id')),
 
164
                style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()),
 
165
                style.SQL_KEYWORD('NOT NULL PRIMARY KEY'),
 
166
                tablespace_sql))
 
167
 
 
168
            deferred = []
 
169
            inline_output, deferred = self.sql_for_inline_many_to_many_references(model, f, style)
 
170
            table_output.extend(inline_output)
 
171
 
 
172
            table_output.append('    %s (%s, %s)%s' %
 
173
                (style.SQL_KEYWORD('UNIQUE'),
 
174
                style.SQL_FIELD(qn(f.m2m_column_name())),
 
175
                style.SQL_FIELD(qn(f.m2m_reverse_name())),
 
176
                tablespace_sql))
 
177
            table_output.append(')')
 
178
            if opts.db_tablespace:
 
179
                # f.db_tablespace is only for indices, so ignore its value here.
 
180
                table_output.append(self.connection.ops.tablespace_sql(opts.db_tablespace))
 
181
            table_output.append(';')
 
182
            output.append('\n'.join(table_output))
 
183
 
 
184
            for r_table, r_col, table, col in deferred:
 
185
                r_name = '%s_refs_%s_%x' % (r_col, col,
 
186
                        abs(hash((r_table, table))))
 
187
                output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' %
 
188
                (qn(r_table),
 
189
                truncate_name(r_name, self.connection.ops.max_name_length()),
 
190
                qn(r_col), qn(table), qn(col),
 
191
                self.connection.ops.deferrable_sql()))
 
192
 
 
193
            # Add any extra SQL needed to support auto-incrementing PKs
 
194
            autoinc_sql = self.connection.ops.autoinc_sql(f.m2m_db_table(), 'id')
 
195
            if autoinc_sql:
 
196
                for stmt in autoinc_sql:
 
197
                    output.append(stmt)
 
198
        return output
 
199
 
 
200
    def sql_for_inline_many_to_many_references(self, model, field, style):
 
201
        "Create the references to other tables required by a many-to-many table"
 
202
        from django.db import models
 
203
        opts = model._meta
 
204
        qn = self.connection.ops.quote_name
 
205
 
 
206
        table_output = [
 
207
            '    %s %s %s %s (%s)%s,' %
 
208
                (style.SQL_FIELD(qn(field.m2m_column_name())),
 
209
                style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
 
210
                style.SQL_KEYWORD('NOT NULL REFERENCES'),
 
211
                style.SQL_TABLE(qn(opts.db_table)),
 
212
                style.SQL_FIELD(qn(opts.pk.column)),
 
213
                self.connection.ops.deferrable_sql()),
 
214
            '    %s %s %s %s (%s)%s,' %
 
215
                (style.SQL_FIELD(qn(field.m2m_reverse_name())),
 
216
                style.SQL_COLTYPE(models.ForeignKey(field.rel.to).db_type()),
 
217
                style.SQL_KEYWORD('NOT NULL REFERENCES'),
 
218
                style.SQL_TABLE(qn(field.rel.to._meta.db_table)),
 
219
                style.SQL_FIELD(qn(field.rel.to._meta.pk.column)),
 
220
                self.connection.ops.deferrable_sql())
 
221
        ]
 
222
        deferred = []
 
223
 
 
224
        return table_output, deferred
 
225
 
 
226
    def sql_indexes_for_model(self, model, style):
 
227
        "Returns the CREATE INDEX SQL statements for a single model"
 
228
        output = []
 
229
        for f in model._meta.local_fields:
 
230
            output.extend(self.sql_indexes_for_field(model, f, style))
 
231
        return output
 
232
 
 
233
    def sql_indexes_for_field(self, model, f, style):
 
234
        "Return the CREATE INDEX SQL statements for a single model field"
 
235
        if f.db_index and not f.unique:
 
236
            qn = self.connection.ops.quote_name
 
237
            tablespace = f.db_tablespace or model._meta.db_tablespace
 
238
            if tablespace:
 
239
                sql = self.connection.ops.tablespace_sql(tablespace)
 
240
                if sql:
 
241
                    tablespace_sql = ' ' + sql
 
242
                else:
 
243
                    tablespace_sql = ''
 
244
            else:
 
245
                tablespace_sql = ''
 
246
            output = [style.SQL_KEYWORD('CREATE INDEX') + ' ' +
 
247
                style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' +
 
248
                style.SQL_KEYWORD('ON') + ' ' +
 
249
                style.SQL_TABLE(qn(model._meta.db_table)) + ' ' +
 
250
                "(%s)" % style.SQL_FIELD(qn(f.column)) +
 
251
                "%s;" % tablespace_sql]
 
252
        else:
 
253
            output = []
 
254
        return output
 
255
 
 
256
    def sql_destroy_model(self, model, references_to_delete, style):
 
257
        "Return the DROP TABLE and restraint dropping statements for a single model"
 
258
        # Drop the table now
 
259
        qn = self.connection.ops.quote_name
 
260
        output = ['%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
 
261
                              style.SQL_TABLE(qn(model._meta.db_table)))]
 
262
        if model in references_to_delete:
 
263
            output.extend(self.sql_remove_table_constraints(model, references_to_delete, style))
 
264
 
 
265
        if model._meta.has_auto_field:
 
266
            ds = self.connection.ops.drop_sequence_sql(model._meta.db_table)
 
267
            if ds:
 
268
                output.append(ds)
 
269
        return output
 
270
 
 
271
    def sql_remove_table_constraints(self, model, references_to_delete, style):
 
272
        from django.db.backends.util import truncate_name
 
273
 
 
274
        output = []
 
275
        qn = self.connection.ops.quote_name
 
276
        for rel_class, f in references_to_delete[model]:
 
277
            table = rel_class._meta.db_table
 
278
            col = f.column
 
279
            r_table = model._meta.db_table
 
280
            r_col = model._meta.get_field(f.rel.field_name).column
 
281
            r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table))))
 
282
            output.append('%s %s %s %s;' % \
 
283
                (style.SQL_KEYWORD('ALTER TABLE'),
 
284
                style.SQL_TABLE(qn(table)),
 
285
                style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()),
 
286
                style.SQL_FIELD(truncate_name(r_name, self.connection.ops.max_name_length()))))
 
287
        del references_to_delete[model]
 
288
        return output
 
289
 
 
290
    def sql_destroy_many_to_many(self, model, f, style):
 
291
        "Returns the DROP TABLE statements for a single m2m field"
 
292
        qn = self.connection.ops.quote_name
 
293
        output = []
 
294
        if f.creates_table:
 
295
            output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
 
296
                style.SQL_TABLE(qn(f.m2m_db_table()))))
 
297
            ds = self.connection.ops.drop_sequence_sql("%s_%s" % (model._meta.db_table, f.column))
 
298
            if ds:
 
299
                output.append(ds)
 
300
        return output
 
301
 
 
302
    def create_test_db(self, verbosity=1, autoclobber=False):
 
303
        """
 
304
        Creates a test database, prompting the user for confirmation if the
 
305
        database already exists. Returns the name of the test database created.
 
306
        """
 
307
        if verbosity >= 1:
 
308
            print "Creating test database..."
 
309
 
 
310
        test_database_name = self._create_test_db(verbosity, autoclobber)
 
311
 
 
312
        self.connection.close()
 
313
        settings.DATABASE_NAME = test_database_name
 
314
 
 
315
        call_command('syncdb', verbosity=verbosity, interactive=False)
 
316
 
 
317
        if settings.CACHE_BACKEND.startswith('db://'):
 
318
            cache_name = settings.CACHE_BACKEND[len('db://'):]
 
319
            call_command('createcachetable', cache_name)
 
320
 
 
321
        # Get a cursor (even though we don't need one yet). This has
 
322
        # the side effect of initializing the test database.
 
323
        cursor = self.connection.cursor()
 
324
 
 
325
        return test_database_name
 
326
 
 
327
    def _create_test_db(self, verbosity, autoclobber):
 
328
        "Internal implementation - creates the test db tables."
 
329
        suffix = self.sql_table_creation_suffix()
 
330
 
 
331
        if settings.TEST_DATABASE_NAME:
 
332
            test_database_name = settings.TEST_DATABASE_NAME
 
333
        else:
 
334
            test_database_name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
 
335
 
 
336
        qn = self.connection.ops.quote_name
 
337
 
 
338
        # Create the test database and connect to it. We need to autocommit
 
339
        # if the database supports it because PostgreSQL doesn't allow
 
340
        # CREATE/DROP DATABASE statements within transactions.
 
341
        cursor = self.connection.cursor()
 
342
        self.set_autocommit()
 
343
        try:
 
344
            cursor.execute("CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
 
345
        except Exception, e:
 
346
            sys.stderr.write("Got an error creating the test database: %s\n" % e)
 
347
            if not autoclobber:
 
348
                confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % test_database_name)
 
349
            if autoclobber or confirm == 'yes':
 
350
                try:
 
351
                    if verbosity >= 1:
 
352
                        print "Destroying old test database..."
 
353
                    cursor.execute("DROP DATABASE %s" % qn(test_database_name))
 
354
                    if verbosity >= 1:
 
355
                        print "Creating test database..."
 
356
                    cursor.execute("CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
 
357
                except Exception, e:
 
358
                    sys.stderr.write("Got an error recreating the test database: %s\n" % e)
 
359
                    sys.exit(2)
 
360
            else:
 
361
                print "Tests cancelled."
 
362
                sys.exit(1)
 
363
 
 
364
        return test_database_name
 
365
 
 
366
    def destroy_test_db(self, old_database_name, verbosity=1):
 
367
        """
 
368
        Destroy a test database, prompting the user for confirmation if the
 
369
        database already exists. Returns the name of the test database created.
 
370
        """
 
371
        if verbosity >= 1:
 
372
            print "Destroying test database..."
 
373
        self.connection.close()
 
374
        test_database_name = settings.DATABASE_NAME
 
375
        settings.DATABASE_NAME = old_database_name
 
376
 
 
377
        self._destroy_test_db(test_database_name, verbosity)
 
378
 
 
379
    def _destroy_test_db(self, test_database_name, verbosity):
 
380
        "Internal implementation - remove the test db tables."
 
381
        # Remove the test database to clean up after
 
382
        # ourselves. Connect to the previous database (not the test database)
 
383
        # to do so, because it's not allowed to delete a database while being
 
384
        # connected to it.
 
385
        cursor = self.connection.cursor()
 
386
        self.set_autocommit()
 
387
        time.sleep(1) # To avoid "database is being accessed by other users" errors.
 
388
        cursor.execute("DROP DATABASE %s" % self.connection.ops.quote_name(test_database_name))
 
389
        self.connection.close()
 
390
 
 
391
    def set_autocommit(self):
 
392
        "Make sure a connection is in autocommit mode."
 
393
        if hasattr(self.connection.connection, "autocommit"):
 
394
            if callable(self.connection.connection.autocommit):
 
395
                self.connection.connection.autocommit(True)
 
396
            else:
 
397
                self.connection.connection.autocommit = True
 
398
        elif hasattr(self.connection.connection, "set_isolation_level"):
 
399
            self.connection.connection.set_isolation_level(0)
 
400
 
 
401
    def sql_table_creation_suffix(self):
 
402
        "SQL to append to the end of the test table creation statements"
 
403
        return ''
 
404