1 from django.core.management.base import CommandError |
|
2 import os |
1 import os |
3 import re |
2 import re |
4 |
3 |
5 try: |
4 from django.conf import settings |
6 set |
5 from django.contrib.contenttypes import generic |
7 except NameError: |
6 from django.core.management.base import CommandError |
8 from sets import Set as set # Python 2.3 fallback |
7 from django.dispatch import dispatcher |
|
8 from django.db import models |
|
9 from django.db.models import get_models |
|
10 from django.db.backends.util import truncate_name |
9 |
11 |
10 def sql_create(app, style): |
12 def sql_create(app, style, connection): |
11 "Returns a list of the CREATE TABLE SQL statements for the given app." |
13 "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 |
14 |
15 if settings.DATABASE_ENGINE == 'dummy': |
15 if connection.settings_dict['ENGINE'] == 'django.db.backends.dummy': |
16 # This must be the "dummy" database backend, which means the user |
16 # This must be the "dummy" database backend, which means the user |
17 # hasn't set DATABASE_ENGINE. |
17 # hasn't set ENGINE for the databse. |
18 raise CommandError("Django doesn't know which syntax to use for your SQL statements,\n" + |
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" + |
19 "because you haven't specified the ENGINE setting for the database.\n" + |
20 "Edit your settings file and change DATABASE_ENGINE to something like 'postgresql' or 'mysql'.") |
20 "Edit your settings file and change DATBASES['default']['ENGINE'] to something like\n" + |
|
21 "'django.db.backends.postgresql' or 'django.db.backends.mysql'") |
21 |
22 |
22 # Get installed models, so we generate REFERENCES right. |
23 # Get installed models, so we generate REFERENCES right. |
23 # We trim models from the current app so that the sqlreset command does not |
24 # 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 # generate invalid SQL (leaving models out of known_models is harmless, so |
25 # we can be conservative). |
26 # we can be conservative). |
26 app_models = models.get_models(app) |
27 app_models = models.get_models(app, include_auto_created=True) |
27 final_output = [] |
28 final_output = [] |
28 tables = connection.introspection.table_names() |
29 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 known_models = set([model for model in connection.introspection.installed_models(tables) if model not in app_models]) |
30 pending_references = {} |
31 pending_references = {} |
31 |
32 |
95 |
89 |
96 for model in app_models: |
90 for model in app_models: |
97 if connection.introspection.table_name_converter(model._meta.db_table) in table_names: |
91 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)) |
92 output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style)) |
99 |
93 |
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 |
94 # Close database connection explicitly, in case this output is being piped |
108 # directly into a database client, to avoid locking issues. |
95 # directly into a database client, to avoid locking issues. |
109 if cursor: |
96 if cursor: |
110 cursor.close() |
97 cursor.close() |
111 connection.close() |
98 connection.close() |
112 |
99 |
113 return output[::-1] # Reverse it, to deal with table dependencies. |
100 return output[::-1] # Reverse it, to deal with table dependencies. |
114 |
101 |
115 def sql_reset(app, style): |
102 def sql_reset(app, style, connection): |
116 "Returns a list of the DROP TABLE SQL, then the CREATE TABLE SQL, for the given module." |
103 "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) |
104 return sql_delete(app, style, connection) + sql_all(app, style, connection) |
118 |
105 |
119 def sql_flush(style, only_django=False): |
106 def sql_flush(style, connection, only_django=False): |
120 """ |
107 """ |
121 Returns a list of the SQL statements used to flush the database. |
108 Returns a list of the SQL statements used to flush the database. |
122 |
109 |
123 If only_django is True, then only table names that have associated Django |
110 If only_django is True, then only table names that have associated Django |
124 models and are in INSTALLED_APPS will be included. |
111 models and are in INSTALLED_APPS will be included. |
125 """ |
112 """ |
126 from django.db import connection |
|
127 if only_django: |
113 if only_django: |
128 tables = connection.introspection.django_table_names(only_existing=True) |
114 tables = connection.introspection.django_table_names(only_existing=True) |
129 else: |
115 else: |
130 tables = connection.introspection.table_names() |
116 tables = connection.introspection.table_names() |
131 statements = connection.ops.sql_flush(style, tables, connection.introspection.sequence_list()) |
117 statements = connection.ops.sql_flush(style, tables, connection.introspection.sequence_list()) |
132 return statements |
118 return statements |
133 |
119 |
134 def sql_custom(app, style): |
120 def sql_custom(app, style, connection): |
135 "Returns a list of the custom table modifying SQL statements for the given app." |
121 "Returns a list of the custom table modifying SQL statements for the given app." |
136 from django.db.models import get_models |
|
137 output = [] |
122 output = [] |
138 |
123 |
139 app_models = get_models(app) |
124 app_models = get_models(app) |
140 app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql')) |
125 app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql')) |
141 |
126 |
142 for model in app_models: |
127 for model in app_models: |
143 output.extend(custom_sql_for_model(model, style)) |
128 output.extend(custom_sql_for_model(model, style, connection)) |
144 |
129 |
145 return output |
130 return output |
146 |
131 |
147 def sql_indexes(app, style): |
132 def sql_indexes(app, style, connection): |
148 "Returns a list of the CREATE INDEX SQL statements for all models in the given app." |
133 "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 = [] |
134 output = [] |
151 for model in models.get_models(app): |
135 for model in models.get_models(app): |
152 output.extend(connection.creation.sql_indexes_for_model(model, style)) |
136 output.extend(connection.creation.sql_indexes_for_model(model, style)) |
153 return output |
137 return output |
154 |
138 |
155 def sql_all(app, style): |
139 def sql_all(app, style, connection): |
156 "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module." |
140 "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) |
141 return sql_create(app, style, connection) + sql_custom(app, style, connection) + sql_indexes(app, style, connection) |
158 |
142 |
159 def custom_sql_for_model(model, style): |
143 def custom_sql_for_model(model, style, connection): |
160 from django.db import models |
|
161 from django.conf import settings |
|
162 |
|
163 opts = model._meta |
144 opts = model._meta |
164 app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql')) |
145 app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql')) |
165 output = [] |
146 output = [] |
166 |
147 |
167 # Post-creation SQL should come before any initial SQL data is loaded. |
148 # 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 |
149 # However, this should not be done for models that are unmanaged or |
169 # model (via model inheritance). |
150 # for fields that are part of a parent model (via model inheritance). |
170 nm = opts.init_name_map() |
151 if opts.managed: |
171 post_sql_fields = [f for f in opts.local_fields if hasattr(f, 'post_create_sql')] |
152 post_sql_fields = [f for f in opts.local_fields if hasattr(f, 'post_create_sql')] |
172 for f in post_sql_fields: |
153 for f in post_sql_fields: |
173 output.extend(f.post_create_sql(style, model._meta.db_table)) |
154 output.extend(f.post_create_sql(style, model._meta.db_table)) |
174 |
155 |
175 # Some backends can't execute more than one SQL statement at a time, |
156 # Some backends can't execute more than one SQL statement at a time, |
176 # so split into separate statements. |
157 # so split into separate statements. |
177 statements = re.compile(r";[ \t]*$", re.M) |
158 statements = re.compile(r";[ \t]*$", re.M) |
178 |
159 |
179 # Find custom SQL, if it's available. |
160 # 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)), |
161 backend_name = connection.settings_dict['ENGINE'].split('.')[-1] |
|
162 sql_files = [os.path.join(app_dir, "%s.%s.sql" % (opts.object_name.lower(), backend_name)), |
181 os.path.join(app_dir, "%s.sql" % opts.object_name.lower())] |
163 os.path.join(app_dir, "%s.sql" % opts.object_name.lower())] |
182 for sql_file in sql_files: |
164 for sql_file in sql_files: |
183 if os.path.exists(sql_file): |
165 if os.path.exists(sql_file): |
184 fp = open(sql_file, 'U') |
166 fp = open(sql_file, 'U') |
185 for statement in statements.split(fp.read().decode(settings.FILE_CHARSET)): |
167 for statement in statements.split(fp.read().decode(settings.FILE_CHARSET)): |