Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

magic-removal: Refactored a bunch of django.core.management, includin…

…g fixes to syncdb and permissions.

git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@2460 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 7e11865b844a872f211a61a15b0984df77bc508e 1 parent c6ab599
Jacob Kaplan-Moss authored March 02, 2006

Showing 1 changed file with 151 additions and 176 deletions. Show diff stats Hide diff stats

  1. 327  django/core/management.py
327  django/core/management.py
@@ -65,10 +65,10 @@ def get_version():
65 65
     if VERSION[3]:
66 66
         v += ' (%s)' % VERSION[3]
67 67
     return v
68  
-
69  
-def sql_for_table(model):
70  
-    from django.db import backend, get_creation_module, models
71  
-
  68
+    
  69
+def get_sql_create(app):
  70
+    "Returns a list of the CREATE TABLE SQL statements for the given app."
  71
+    from django.db import get_creation_module, models
72 72
     data_types = get_creation_module().DATA_TYPES
73 73
 
74 74
     if not data_types:
@@ -79,10 +79,41 @@ def sql_for_table(model):
79 79
             "Edit your settings file and change DATABASE_ENGINE to something like 'postgresql' or 'mysql'.\n")
80 80
         sys.exit(1)
81 81
 
82  
-    opts = model._meta
  82
+    final_output = []
  83
+    models_output = set()
  84
+    pending_references = {}
  85
+
  86
+    app_models = models.get_models(app)
  87
+
  88
+    for klass in app_models:
  89
+        output, references = _get_sql_model_create(klass, models_output)
  90
+        final_output.extend(output)
  91
+        pending_references.update(references)
  92
+        final_output.extend(_get_sql_for_pending_references(klass, pending_references))
  93
+        # Keep track of the fact that we've created the table for this model.
  94
+        models_output.add(klass)
  95
+
  96
+    # Create the many-to-many join tables.
  97
+    for klass in app_models:
  98
+        final_output.extend(_get_many_to_many_sql_for_model(klass))
  99
+
  100
+    return final_output
  101
+get_sql_create.help_doc = "Prints the CREATE TABLE SQL statements for the given app name(s)."
  102
+get_sql_create.args = APP_ARGS
83 103
 
84  
-    field_metadata = []
85  
-    references = []
  104
+def _get_sql_model_create(klass, models_already_seen=set()):
  105
+    """
  106
+    Get the SQL required to create a single model.
  107
+    
  108
+    Returns list_of_sql, pending_references_dict
  109
+    """
  110
+    from django.db import backend, get_creation_module, models
  111
+    data_types = get_creation_module().DATA_TYPES
  112
+    
  113
+    opts = klass._meta
  114
+    final_output = []
  115
+    table_output = []
  116
+    pending_references = {}
86 117
     for f in opts.fields:
87 118
         if isinstance(f, models.ForeignKey):
88 119
             rel_field = f.rel.get_related_field()
@@ -100,121 +131,78 @@ def sql_for_table(model):
100 131
             if f.primary_key:
101 132
                 field_output.append('PRIMARY KEY')
102 133
             if f.rel:
103  
-                references.append((f.rel.to, model, f))
104  
-            field_metadata.append(field_output)
  134
+                 if f.rel.to in models_already_seen:
  135
+                     field_output.append('REFERENCES %s (%s)' % \
  136
+                         (backend.quote_name(f.rel.to._meta.db_table),
  137
+                         backend.quote_name(f.rel.to._meta.get_field(f.rel.field_name).column)))
  138
+                 else:
  139
+                     # We haven't yet created the table to which this field
  140
+                     # is related, so save it for later.
  141
+                     pr = pending_references.setdefault(f.rel.to, []).append((klass, f))
  142
+            table_output.append(' '.join(field_output))
105 143
     if opts.order_with_respect_to:
106  
-        field_metadata.append((backend.quote_name('_order'), data_types['IntegerField'], 'NULL'))
107  
-
108  
-    table_metadata = []
  144
+        table_output.append('%s %s NULL' % (backend.quote_name('_order'), data_types['IntegerField']))
109 145
     for field_constraints in opts.unique_together:
110  
-        table_metadata.append(['UNIQUE (%s)' % \
111  
-            ", ".join([backend.quote_name(opts.get_field(f).column) for f in field_constraints])])
112  
-    return field_metadata, table_metadata, references
  146
+        table_output.append('UNIQUE (%s)' % \
  147
+            ", ".join([backend.quote_name(opts.get_field(f).column) for f in field_constraints]))
113 148
 
114  
-def get_sql_create(app):
115  
-    "Returns a list of the CREATE TABLE SQL statements for the given app."
116  
-    from django.db import backend, get_creation_module, models
  149
+    full_statement = ['CREATE TABLE %s (' % backend.quote_name(opts.db_table)]
  150
+    for i, line in enumerate(table_output): # Combine and add commas.
  151
+        full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
  152
+    full_statement.append(');')
  153
+    final_output.append('\n'.join(full_statement))
  154
+    
  155
+    return final_output, pending_references
117 156
 
  157
+def _get_sql_for_pending_references(klass, pending_references):
  158
+    """
  159
+    Get any ALTER TABLE statements to add constraints after the fact.
  160
+    """
  161
+    from django.db import backend, get_creation_module
118 162
     data_types = get_creation_module().DATA_TYPES
119  
-
120  
-    if not data_types:
121  
-        # This must be the "dummy" database backend, which means the user
122  
-        # hasn't set DATABASE_ENGINE.
123  
-        sys.stderr.write("Error: Django doesn't know which syntax to use for your SQL statements,\n" +
124  
-            "because you haven't specified the DATABASE_ENGINE setting.\n" +
125  
-            "Edit your settings file and change DATABASE_ENGINE to something like 'postgresql' or 'mysql'.\n")
126  
-        sys.exit(1)
127  
-
  163
+    
128 164
     final_output = []
129  
-    models_output = set()
130  
-    pending_references = {}
131  
-
132  
-    app_models = models.get_models(app)
133  
-
134  
-    for klass in app_models:
  165
+    if backend.supports_constraints:
135 166
         opts = klass._meta
136  
-        table_output = []
137  
-        for f in opts.fields:
138  
-            if isinstance(f, models.ForeignKey):
139  
-                rel_field = f.rel.get_related_field()
140  
-                data_type = get_rel_data_type(rel_field)
141  
-            else:
142  
-                rel_field = f
143  
-                data_type = f.get_internal_type()
144  
-            col_type = data_types[data_type]
145  
-            if col_type is not None:
146  
-                # Make the definition (e.g. 'foo VARCHAR(30)') for this field.
147  
-                field_output = [backend.quote_name(f.column), col_type % rel_field.__dict__]
148  
-                field_output.append('%sNULL' % (not f.null and 'NOT ' or ''))
149  
-                if f.unique:
150  
-                    field_output.append('UNIQUE')
151  
-                if f.primary_key:
152  
-                    field_output.append('PRIMARY KEY')
153  
-                if f.rel:
154  
-                     if f.rel.to in models_output:
155  
-                         field_output.append('REFERENCES %s (%s)' % \
156  
-                             (backend.quote_name(f.rel.to._meta.db_table),
157  
-                             backend.quote_name(f.rel.to._meta.get_field(f.rel.field_name).column)))
158  
-                     else:
159  
-                         # We haven't yet created the table to which this field
160  
-                         # is related, so save it for later.
161  
-                         pr = pending_references.setdefault(f.rel.to, []).append((klass, f))
162  
-                table_output.append(' '.join(field_output))
163  
-        if opts.order_with_respect_to:
164  
-            table_output.append('%s %s NULL' % (backend.quote_name('_order'), data_types['IntegerField']))
165  
-        for field_constraints in opts.unique_together:
166  
-            table_output.append('UNIQUE (%s)' % \
167  
-                ", ".join([backend.quote_name(opts.get_field(f).column) for f in field_constraints]))
168  
-
169  
-        full_statement = ['CREATE TABLE %s (' % backend.quote_name(opts.db_table)]
170  
-        for i, line in enumerate(table_output): # Combine and add commas.
171  
-            full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
172  
-        full_statement.append(');')
173  
-        final_output.append('\n'.join(full_statement))
174  
-
175  
-        # Take care of any ALTER TABLE statements to add constraints
176  
-        # after the fact.
177  
-        if backend.supports_constraints:
178  
-            if klass in pending_references:
179  
-                for rel_class, f in pending_references[klass]:
180  
-                    rel_opts = rel_class._meta
181  
-                    r_table = rel_opts.db_table
182  
-                    r_col = f.column
183  
-                    table = opts.db_table
184  
-                    col = opts.get_field(f.rel.field_name).column
185  
-                    final_output.append('ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \
186  
-                        (backend.quote_name(r_table),
187  
-                        backend.quote_name("%s_referencing_%s_%s" % (r_col, table, col)),
188  
-                        backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col)))
189  
-                del pending_references[klass]
  167
+        if klass in pending_references:
  168
+            for rel_class, f in pending_references[klass]:
  169
+                rel_opts = rel_class._meta
  170
+                r_table = rel_opts.db_table
  171
+                r_col = f.column
  172
+                table = opts.db_table
  173
+                col = opts.get_field(f.rel.field_name).column
  174
+                final_output.append('ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \
  175
+                    (backend.quote_name(r_table),
  176
+                    backend.quote_name("%s_referencing_%s_%s" % (r_col, table, col)),
  177
+                    backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col)))
  178
+            del pending_references[klass]
  179
+    return final_output
190 180
 
191  
-        # Keep track of the fact that we've created the table for this model.
192  
-        models_output.add(klass)
  181
+def _get_many_to_many_sql_for_model(klass):
  182
+    from django.db import backend, get_creation_module
  183
+    data_types = get_creation_module().DATA_TYPES
193 184
 
194  
-    # Create the many-to-many join tables.
195  
-    for klass in app_models:
196  
-        opts = klass._meta
197  
-        for f in opts.many_to_many:
198  
-            table_output = ['CREATE TABLE %s (' % backend.quote_name(f.m2m_db_table())]
199  
-            table_output.append('    %s %s NOT NULL PRIMARY KEY,' % (backend.quote_name('id'), data_types['AutoField']))
200  
-            table_output.append('    %s %s NOT NULL REFERENCES %s (%s),' % \
201  
-                (backend.quote_name(f.m2m_column_name()),
202  
-                data_types[get_rel_data_type(opts.pk)] % opts.pk.__dict__,
203  
-                backend.quote_name(opts.db_table),
204  
-                backend.quote_name(opts.pk.column)))
205  
-            table_output.append('    %s %s NOT NULL REFERENCES %s (%s),' % \
206  
-                (backend.quote_name(f.m2m_reverse_name()),
207  
-                data_types[get_rel_data_type(f.rel.to._meta.pk)] % f.rel.to._meta.pk.__dict__,
208  
-                backend.quote_name(f.rel.to._meta.db_table),
209  
-                backend.quote_name(f.rel.to._meta.pk.column)))
210  
-            table_output.append('    UNIQUE (%s, %s)' % \
211  
-                (backend.quote_name(f.m2m_column_name()),
212  
-                backend.quote_name(f.m2m_reverse_name())))
213  
-            table_output.append(');')
214  
-            final_output.append('\n'.join(table_output))
  185
+    opts = klass._meta
  186
+    final_output = []
  187
+    for f in opts.many_to_many:
  188
+        table_output = ['CREATE TABLE %s (' % backend.quote_name(f.m2m_db_table())]
  189
+        table_output.append('    %s %s NOT NULL PRIMARY KEY,' % (backend.quote_name('id'), data_types['AutoField']))
  190
+        table_output.append('    %s %s NOT NULL REFERENCES %s (%s),' % \
  191
+            (backend.quote_name(f.m2m_column_name()),
  192
+            data_types[get_rel_data_type(opts.pk)] % opts.pk.__dict__,
  193
+            backend.quote_name(opts.db_table),
  194
+            backend.quote_name(opts.pk.column)))
  195
+        table_output.append('    %s %s NOT NULL REFERENCES %s (%s),' % \
  196
+            (backend.quote_name(f.m2m_reverse_name()),
  197
+            data_types[get_rel_data_type(f.rel.to._meta.pk)] % f.rel.to._meta.pk.__dict__,
  198
+            backend.quote_name(f.rel.to._meta.db_table),
  199
+            backend.quote_name(f.rel.to._meta.pk.column)))
  200
+        table_output.append('    UNIQUE (%s, %s)' % \
  201
+            (backend.quote_name(f.m2m_column_name()),
  202
+            backend.quote_name(f.m2m_reverse_name())))
  203
+        table_output.append(');')
  204
+        final_output.append('\n'.join(table_output))
215 205
     return final_output
216  
-get_sql_create.help_doc = "Prints the CREATE TABLE SQL statements for the given app name(s)."
217  
-get_sql_create.args = APP_ARGS
218 206
 
219 207
 def get_sql_delete(app):
220 208
     "Returns a list of the DROP TABLE SQL statements for the given app."
@@ -400,19 +388,30 @@ def get_sql_all(app):
400 388
 
401 389
 # TODO: syncdb() should include initial SQL data
402 390
 # TODO: Put "example.com" site in initial SQL data for sites app
403  
-# TODO: Check for model validation errors before executing SQL
404 391
 def syncdb():
405 392
     "Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created."
406  
-    from django.db import backend, connection, transaction, models, get_creation_module, get_introspection_module
  393
+    from django.db import connection, transaction, models, get_creation_module, get_introspection_module
  394
+
  395
+    # Check that there are no validation errors before continuing
  396
+    _check_for_validation_errors()
  397
+
407 398
     introspection_module = get_introspection_module()
408 399
     data_types = get_creation_module().DATA_TYPES
409 400
 
410 401
     cursor = connection.cursor()
  402
+    
411 403
     # Get a list of all existing database tables,
412 404
     # so we know what needs to be added.
413 405
     table_list = introspection_module.get_table_list(cursor)
414  
-
415  
-    pending_references = []
  406
+    
  407
+    # Get a list of already installed *models* so that references work right.
  408
+    all_models = []
  409
+    for app in models.get_apps():
  410
+        for model in models.get_models(app):
  411
+            all_models.append(model)
  412
+    seen_models = set([m for m in all_models if m._meta.db_table in table_list])
  413
+    created_models = set()
  414
+    pending_references = {}
416 415
 
417 416
     for app in models.get_apps():
418 417
         model_list = models.get_models(app)
@@ -420,55 +419,22 @@ def syncdb():
420 419
             # Create the model's database table, if it doesn't already exist.
421 420
             if model._meta.db_table in table_list:
422 421
                 continue
423  
-            field_metadata, table_metadata, references = sql_for_table(model)
424  
-            pending_references.extend(references)
425  
-            sql = "CREATE TABLE %s (\n    %s\n)" % \
426  
-                (backend.quote_name(model._meta.db_table),
427  
-                ",\n    ".join([' '.join(i) for i in field_metadata + table_metadata]))
  422
+            sql, references = _get_sql_model_create(model, seen_models)
  423
+            seen_models.add(model)
  424
+            created_models.add(model)
  425
+            pending_references.update(references)
  426
+            sql.extend(_get_sql_for_pending_references(model, pending_references))
  427
+            sql = "\n".join(sql)
428 428
             print "Creating table %s" % model._meta.db_table
429 429
             cursor.execute(sql)
430 430
 
431 431
         for model in model_list:
432  
-            # Create the many-to-many join table, if it doesn't already exist.
433  
-            for f in model._meta.many_to_many:
434  
-                if f.m2m_db_table() in table_list:
435  
-                    continue
436  
-                # This table doesn't exist yet and needs to be created.
437  
-                table_output = ['CREATE TABLE %s (' % backend.quote_name(f.m2m_db_table())]
438  
-                table_output.append('    %s %s NOT NULL PRIMARY KEY,' % (backend.quote_name('id'), data_types['AutoField']))
439  
-                table_output.append('    %s %s NOT NULL REFERENCES %s (%s),' % \
440  
-                    (backend.quote_name(f.m2m_column_name()),
441  
-                    data_types[get_rel_data_type(model._meta.pk)] % model._meta.pk.__dict__,
442  
-                    backend.quote_name(model._meta.db_table),
443  
-                    backend.quote_name(model._meta.pk.column)))
444  
-                table_output.append('    %s %s NOT NULL REFERENCES %s (%s),' % \
445  
-                    (backend.quote_name(f.m2m_reverse_name()),
446  
-                    data_types[get_rel_data_type(f.rel.to._meta.pk)] % f.rel.to._meta.pk.__dict__,
447  
-                    backend.quote_name(f.rel.to._meta.db_table),
448  
-                    backend.quote_name(f.rel.to._meta.pk.column)))
449  
-                table_output.append('    UNIQUE (%s, %s)' % \
450  
-                    (backend.quote_name(f.m2m_column_name()),
451  
-                    backend.quote_name(f.m2m_reverse_name())))
452  
-                table_output.append(')')
453  
-                sql = '\n'.join(table_output)
454  
-                print "Creating table %s" % f.m2m_db_table()
455  
-                cursor.execute(sql)
456  
-
457  
-    # Create the pending references.
458  
-    # Take care of any ALTER TABLE statements to add constraints
459  
-    # after the fact.
460  
-    if backend.supports_constraints:
461  
-        for model, rel_class, field in pending_references:
462  
-            rel_opts = rel_class._meta
463  
-            r_table = rel_opts.db_table
464  
-            r_col = field.column
465  
-            table = model._meta.db_table
466  
-            col = model._meta.get_field(field.rel.field_name).column
467  
-            sql = 'ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \
468  
-                (backend.quote_name(r_table),
469  
-                backend.quote_name("%s_referencing_%s_%s" % (r_col, table, col)),
470  
-                backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col))
471  
-            cursor.execute(sql)
  432
+            if model in created_models:
  433
+                sql = _get_many_to_many_sql_for_model(model)
  434
+                if sql:
  435
+                    sql = '\n'.join(sql).strip()
  436
+                    print "Creating many-to-many tables for %s model" % model.__name__
  437
+                    cursor.execute(sql)
472 438
 
473 439
     transaction.commit_unless_managed()
474 440
 syncdb.args = ''
@@ -500,18 +466,13 @@ def get_admin_index(app):
500 466
 def install(app):
501 467
     "Executes the equivalent of 'get_sql_all' in the current database."
502 468
     from django.db import connection, transaction
503  
-    from cStringIO import StringIO
  469
+
504 470
     app_name = app.__name__[app.__name__.rindex('.')+1:]
505 471
     app_label = app_name.split('.')[-1]
506 472
 
507 473
     # First, try validating the models.
508  
-    s = StringIO()
509  
-    num_errors = get_validation_errors(s, app)
510  
-    if num_errors:
511  
-        sys.stderr.write("Error: %s couldn't be installed, because there were errors in your model:\n" % app_name)
512  
-        s.seek(0)
513  
-        sys.stderr.write(s.read())
514  
-        sys.exit(1)
  474
+    _check_for_validation_errors(app)
  475
+
515 476
     sql_list = get_sql_all(app)
516 477
 
517 478
     try:
@@ -578,20 +539,20 @@ def reset(app):
578 539
 
579 540
 def installperms(app):
580 541
     "Installs any permissions for the given app, if needed."
  542
+    from django.contrib.contenttypes.models import ContentType
581 543
     from django.contrib.auth.models import Permission
582  
-    from django.contrib.contenttypes.models import Package
583 544
     from django.db.models import get_models
584 545
     num_added = 0
585 546
     app_models = get_models(app)
586 547
     app_label = app_models[0]._meta.app_label
587  
-    package = Package.objects.get(pk=app_label)
588 548
     for klass in app_models:
589 549
         opts = klass._meta
  550
+        ctype = ContentType.objects.get_for_model(klass)
590 551
         for codename, name in _get_all_permissions(opts):
591 552
             try:
592  
-                Permission.objects.get(name=name, codename=codename, package__label__exact=package.label)
  553
+                Permission.objects.get(name=name, codename=codename, content_type__pk=ctype.id)
593 554
             except Permission.DoesNotExist:
594  
-                p = Permission(name=name, package=package, codename=codename)
  555
+                p = Permission(name=name, codename=codename, content_type=ctype)
595 556
                 p.save()
596 557
                 print "Added permission '%r'." % p
597 558
                 num_added += 1
@@ -996,6 +957,20 @@ def validate(outfile=sys.stdout):
996 957
         outfile.write("Skipping validation because things aren't configured properly.")
997 958
 validate.args = ''
998 959
 
  960
+def _check_for_validation_errors(app=None):
  961
+    """Check that an app has no validation errors, and exit with errors if it does."""
  962
+    try:
  963
+        from cStringIO import StringIO
  964
+    except ImportError:
  965
+        from StringIO import StringIO
  966
+    s = StringIO()
  967
+    num_errors = get_validation_errors(s, app)
  968
+    if num_errors:
  969
+        sys.stderr.write("Error: %s couldn't be installed, because there were errors in your model:\n" % app)
  970
+        s.seek(0)
  971
+        sys.stderr.write(s.read())
  972
+        sys.exit(1)
  973
+
999 974
 def runserver(addr, port):
1000 975
     "Starts a lightweight Web server for development."
1001 976
     from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException
@@ -1046,7 +1021,7 @@ def createcachetable(tablename):
1046 1021
     table_output = []
1047 1022
     index_output = []
1048 1023
     for f in fields:
1049  
-        field_output = [backend.quote_name(f.column), data_types[f.get_internal_type()] % f.__dict__]
  1024
+        field_output = [backend.quote_name(f.name), data_types[f.get_internal_type()] % f.__dict__]
1050 1025
         field_output.append("%sNULL" % (not f.null and "NOT " or ""))
1051 1026
         if f.unique:
1052 1027
             field_output.append("UNIQUE")
@@ -1055,8 +1030,8 @@ def createcachetable(tablename):
1055 1030
         if f.db_index:
1056 1031
             unique = f.unique and "UNIQUE " or ""
1057 1032
             index_output.append("CREATE %sINDEX %s_%s ON %s (%s);" % \
1058  
-                (unique, tablename, f.column, backend.quote_name(tablename),
1059  
-                backend.quote_name(f.column)))
  1033
+                (unique, tablename, f.name, backend.quote_name(tablename),
  1034
+                backend.quote_name(f.name)))
1060 1035
         table_output.append(" ".join(field_output))
1061 1036
     full_statement = ["CREATE TABLE %s (" % backend.quote_name(tablename)]
1062 1037
     for i, line in enumerate(table_output):

0 notes on commit 7e11865

Please sign in to comment.
Something went wrong with that request. Please try again.