~ubuntu-branches/ubuntu/jaunty/python-django/jaunty

« back to all changes in this revision

Viewing changes to django/core/management/sql.py

  • Committer: Bazaar Package Importer
  • Author(s): Scott James Remnant, Eddy Mulyono
  • Date: 2008-09-16 12:18:47 UTC
  • mfrom: (1.1.5 upstream) (4.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080916121847-mg225rg5mnsdqzr0
Tags: 1.0-1ubuntu1
* Merge from Debian (LP: #264191), remaining changes:
  - Run test suite on build.

[Eddy Mulyono]
* Update patch to workaround network test case failures.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from django.core.management.base import CommandError
 
2
import os
 
3
import re
 
4
 
 
5
try:
 
6
    set
 
7
except NameError:
 
8
    from sets import Set as set   # Python 2.3 fallback
 
9
 
 
10
def sql_create(app, style):
 
11
    "Returns a list of the CREATE TABLE SQL statements for the given app."
 
12
    from django.db import connection, models
 
13
    from django.conf import settings
 
14
 
 
15
    if settings.DATABASE_ENGINE == 'dummy':
 
16
        # This must be the "dummy" database backend, which means the user
 
17
        # hasn't set DATABASE_ENGINE.
 
18
        raise CommandError("Django doesn't know which syntax to use for your SQL statements,\n" +
 
19
            "because you haven't specified the DATABASE_ENGINE setting.\n" +
 
20
            "Edit your settings file and change DATABASE_ENGINE to something like 'postgresql' or 'mysql'.")
 
21
 
 
22
    # Get installed models, so we generate REFERENCES right.
 
23
    # We trim models from the current app so that the sqlreset command does not
 
24
    # generate invalid SQL (leaving models out of known_models is harmless, so
 
25
    # we can be conservative).
 
26
    app_models = models.get_models(app)
 
27
    final_output = []
 
28
    tables = connection.introspection.table_names()
 
29
    known_models = set([model for model in connection.introspection.installed_models(tables) if model not in app_models])
 
30
    pending_references = {}
 
31
 
 
32
    for model in app_models:
 
33
        output, references = connection.creation.sql_create_model(model, style, known_models)
 
34
        final_output.extend(output)
 
35
        for refto, refs in references.items():
 
36
            pending_references.setdefault(refto, []).extend(refs)
 
37
            if refto in known_models:
 
38
                final_output.extend(connection.creation.sql_for_pending_references(refto, style, pending_references))
 
39
        final_output.extend(connection.creation.sql_for_pending_references(model, style, pending_references))
 
40
        # Keep track of the fact that we've created the table for this model.
 
41
        known_models.add(model)
 
42
 
 
43
    # Create the many-to-many join tables.
 
44
    for model in app_models:
 
45
        final_output.extend(connection.creation.sql_for_many_to_many(model, style))
 
46
 
 
47
    # Handle references to tables that are from other apps
 
48
    # but don't exist physically.
 
49
    not_installed_models = set(pending_references.keys())
 
50
    if not_installed_models:
 
51
        alter_sql = []
 
52
        for model in not_installed_models:
 
53
            alter_sql.extend(['-- ' + sql for sql in
 
54
                connection.creation.sql_for_pending_references(model, style, pending_references)])
 
55
        if alter_sql:
 
56
            final_output.append('-- The following references should be added but depend on non-existent tables:')
 
57
            final_output.extend(alter_sql)
 
58
 
 
59
    return final_output
 
60
 
 
61
def sql_delete(app, style):
 
62
    "Returns a list of the DROP TABLE SQL statements for the given app."
 
63
    from django.db import connection, models
 
64
    from django.db.backends.util import truncate_name
 
65
    from django.contrib.contenttypes import generic
 
66
 
 
67
    # This should work even if a connection isn't available
 
68
    try:
 
69
        cursor = connection.cursor()
 
70
    except:
 
71
        cursor = None
 
72
 
 
73
    # Figure out which tables already exist
 
74
    if cursor:
 
75
        table_names = connection.introspection.get_table_list(cursor)
 
76
    else:
 
77
        table_names = []
 
78
 
 
79
    output = []
 
80
 
 
81
    # Output DROP TABLE statements for standard application tables.
 
82
    to_delete = set()
 
83
 
 
84
    references_to_delete = {}
 
85
    app_models = models.get_models(app)
 
86
    for model in app_models:
 
87
        if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names:
 
88
            # The table exists, so it needs to be dropped
 
89
            opts = model._meta
 
90
            for f in opts.local_fields:
 
91
                if f.rel and f.rel.to not in to_delete:
 
92
                    references_to_delete.setdefault(f.rel.to, []).append( (model, f) )
 
93
 
 
94
            to_delete.add(model)
 
95
 
 
96
    for model in app_models:
 
97
        if connection.introspection.table_name_converter(model._meta.db_table) in table_names:
 
98
            output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style))
 
99
 
 
100
    # Output DROP TABLE statements for many-to-many tables.
 
101
    for model in app_models:
 
102
        opts = model._meta
 
103
        for f in opts.local_many_to_many:
 
104
            if cursor and connection.introspection.table_name_converter(f.m2m_db_table()) in table_names:
 
105
                output.extend(connection.creation.sql_destroy_many_to_many(model, f, style))
 
106
 
 
107
    # Close database connection explicitly, in case this output is being piped
 
108
    # directly into a database client, to avoid locking issues.
 
109
    if cursor:
 
110
        cursor.close()
 
111
        connection.close()
 
112
 
 
113
    return output[::-1] # Reverse it, to deal with table dependencies.
 
114
 
 
115
def sql_reset(app, style):
 
116
    "Returns a list of the DROP TABLE SQL, then the CREATE TABLE SQL, for the given module."
 
117
    return sql_delete(app, style) + sql_all(app, style)
 
118
 
 
119
def sql_flush(style, only_django=False):
 
120
    """
 
121
    Returns a list of the SQL statements used to flush the database.
 
122
    
 
123
    If only_django is True, then only table names that have associated Django
 
124
    models and are in INSTALLED_APPS will be included.
 
125
    """
 
126
    from django.db import connection
 
127
    if only_django:
 
128
        tables = connection.introspection.django_table_names()
 
129
    else:
 
130
        tables = connection.introspection.table_names()
 
131
    statements = connection.ops.sql_flush(style, tables, connection.introspection.sequence_list())
 
132
    return statements
 
133
 
 
134
def sql_custom(app, style):
 
135
    "Returns a list of the custom table modifying SQL statements for the given app."
 
136
    from django.db.models import get_models
 
137
    output = []
 
138
 
 
139
    app_models = get_models(app)
 
140
    app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql'))
 
141
 
 
142
    for model in app_models:
 
143
        output.extend(custom_sql_for_model(model, style))
 
144
 
 
145
    return output
 
146
 
 
147
def sql_indexes(app, style):
 
148
    "Returns a list of the CREATE INDEX SQL statements for all models in the given app."
 
149
    from django.db import connection, models
 
150
    output = []
 
151
    for model in models.get_models(app):
 
152
        output.extend(connection.creation.sql_indexes_for_model(model, style))
 
153
    return output
 
154
 
 
155
def sql_all(app, style):
 
156
    "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
 
157
    return sql_create(app, style) + sql_custom(app, style) + sql_indexes(app, style)
 
158
 
 
159
def custom_sql_for_model(model, style):
 
160
    from django.db import models
 
161
    from django.conf import settings
 
162
 
 
163
    opts = model._meta
 
164
    app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql'))
 
165
    output = []
 
166
 
 
167
    # Post-creation SQL should come before any initial SQL data is loaded.
 
168
    # However, this should not be done for fields that are part of a a parent
 
169
    # model (via model inheritance).
 
170
    nm = opts.init_name_map()
 
171
    post_sql_fields = [f for f in opts.local_fields if hasattr(f, 'post_create_sql')]
 
172
    for f in post_sql_fields:
 
173
        output.extend(f.post_create_sql(style, model._meta.db_table))
 
174
 
 
175
    # Some backends can't execute more than one SQL statement at a time,
 
176
    # so split into separate statements.
 
177
    statements = re.compile(r";[ \t]*$", re.M)
 
178
 
 
179
    # Find custom SQL, if it's available.
 
180
    sql_files = [os.path.join(app_dir, "%s.%s.sql" % (opts.object_name.lower(), settings.DATABASE_ENGINE)),
 
181
                 os.path.join(app_dir, "%s.sql" % opts.object_name.lower())]
 
182
    for sql_file in sql_files:
 
183
        if os.path.exists(sql_file):
 
184
            fp = open(sql_file, 'U')
 
185
            for statement in statements.split(fp.read().decode(settings.FILE_CHARSET)):
 
186
                # Remove any comments from the file
 
187
                statement = re.sub(ur"--.*([\n\Z]|$)", "", statement)
 
188
                if statement.strip():
 
189
                    output.append(statement + u";")
 
190
            fp.close()
 
191
 
 
192
    return output
 
193
 
 
194
 
 
195
def emit_post_sync_signal(created_models, verbosity, interactive):
 
196
    from django.db import models
 
197
    from django.dispatch import dispatcher
 
198
    # Emit the post_sync signal for every application.
 
199
    for app in models.get_apps():
 
200
        app_name = app.__name__.split('.')[-2]
 
201
        if verbosity >= 2:
 
202
            print "Running post-sync handlers for application", app_name
 
203
        models.signals.post_syncdb.send(sender=app, app=app,
 
204
            created_models=created_models, verbosity=verbosity,
 
205
            interactive=interactive)