Index: django/db/models/options.py
===================================================================
--- django/db/models/options.py	(revision 9904)
+++ django/db/models/options.py	(working copy)
@@ -21,7 +21,7 @@
 DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering',
                  'unique_together', 'permissions', 'get_latest_by',
                  'order_with_respect_to', 'app_label', 'db_tablespace',
-                 'abstract')
+                 'abstract', 'managed')
 
 class Options(object):
     def __init__(self, meta, app_label=None):
@@ -42,6 +42,7 @@
         self.pk = None
         self.has_auto_field, self.auto_field = False, None
         self.abstract = False
+        self.managed = True
         self.parents = SortedDict()
         self.duplicate_targets = {}
         # Managers that have been inherited from abstract base classes. These
Index: django/db/backends/__init__.py
===================================================================
--- django/db/backends/__init__.py	(revision 9904)
+++ django/db/backends/__init__.py	(working copy)
@@ -450,8 +450,9 @@
         tables = set()
         for app in models.get_apps():
             for model in models.get_models(app):
-                tables.add(model._meta.db_table)
-                tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many])
+                if model._meta.managed:
+                    tables.add(model._meta.db_table)
+                    tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many])
         if only_existing:
             tables = [t for t in tables if self.table_name_converter(t) in self.table_names()]
         return tables
@@ -476,6 +477,8 @@
 
         for app in apps:
             for model in models.get_models(app):
+                if not model._meta.managed:
+                    continue
                 for f in model._meta.local_fields:
                     if isinstance(f, models.AutoField):
                         sequence_list.append({'table': model._meta.db_table, 'column': f.column})
Index: django/db/backends/creation.py
===================================================================
--- django/db/backends/creation.py	(revision 9904)
+++ django/db/backends/creation.py	(working copy)
@@ -33,6 +33,8 @@
         from django.db import models
 
         opts = model._meta
+        if not opts.managed:
+            return [], {}         
         final_output = []
         table_output = []
         pending_references = {}
@@ -112,6 +114,8 @@
         "Returns any ALTER TABLE statements to add constraints after the fact."
         from django.db.backends.util import truncate_name
 
+        if not model._meta.managed:
+            return []
         qn = self.connection.ops.quote_name
         final_output = []
         opts = model._meta
@@ -225,6 +229,8 @@
 
     def sql_indexes_for_model(self, model, style):
         "Returns the CREATE INDEX SQL statements for a single model"
+        if not model._meta.managed:
+            return []
         output = []
         for f in model._meta.local_fields:
             output.extend(self.sql_indexes_for_field(model, f, style))
@@ -255,6 +261,8 @@
 
     def sql_destroy_model(self, model, references_to_delete, style):
         "Return the DROP TABLE and restraint dropping statements for a single model"
+        if not model._meta.managed:
+            return []
         # Drop the table now
         qn = self.connection.ops.quote_name
         output = ['%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
@@ -271,6 +279,8 @@
     def sql_remove_table_constraints(self, model, references_to_delete, style):
         from django.db.backends.util import truncate_name
 
+        if not model._meta.managed:
+            return []
         output = []
         qn = self.connection.ops.quote_name
         for rel_class, f in references_to_delete[model]:
Index: django/core/management/commands/syncdb.py
===================================================================
--- django/core/management/commands/syncdb.py	(revision 9904)
+++ django/core/management/commands/syncdb.py	(working copy)
@@ -71,7 +71,7 @@
                     if refto in seen_models:
                         sql.extend(connection.creation.sql_for_pending_references(refto, self.style, pending_references))
                 sql.extend(connection.creation.sql_for_pending_references(model, self.style, pending_references))
-                if verbosity >= 1:
+                if verbosity >= 1 and sql:
                     print "Creating table %s" % model._meta.db_table
                 for statement in sql:
                     cursor.execute(statement)
Index: tests/modeltests/unmanaged_models/__init__.py
===================================================================
--- tests/modeltests/unmanaged_models/__init__.py	(revision 0)
+++ tests/modeltests/unmanaged_models/__init__.py	(revision 0)
@@ -0,0 +1,2 @@
+
+
Index: tests/modeltests/unmanaged_models/models.py
===================================================================
--- tests/modeltests/unmanaged_models/models.py	(revision 0)
+++ tests/modeltests/unmanaged_models/models.py	(revision 0)
@@ -0,0 +1,200 @@
+"""
+xx. unmanaged_models
+ 
+Models can have a ``managed`` attribute, which specifies whether the 
+SQL code is generated for the table on various manage.py operations.
+The default is True.
+""" 
+ 
+from django.db import models 
+ 
+""" 
+General test strategy: 
+* All tests are numbered (01, 02, 03... etc). 
+* Each test contains three models (A, B, C, followed with the number of test), 
+  containing both indexed and non-indexed fields (to verify sql_index), usual
+  fields (model A), foreign keys (model B) and many-to-many fields (model C). 
+  D table is generated automatically as intermediate M2M one. 
+* The normal (default; managed = True) behaviour during the manage.py 
+  operations is not thoroughly checked; it is the duty of the appropriate tests 
+  for the primary functionality of these operations. 
+  The most attention is paid to whether the managed = False disables the SQL
+  generation properly. 
+* The intermediate table for M2M relations is not ever verified explicitly, 
+  because it is not ever marked with managed explicitly. 
+""" 
+ 
+# This dictionary maps the name of the model/SQL table (like 'A01') 
+# to the boolean specifying whether this name should appear in the final SQL 
+checks = {} 
+ 
+ 
+""" 
+01: managed is not set. 
+    In such case, it should be equal (by default) to True, 
+    and SQL is generated for all three models. 
+""" 
+checks['A01'] = True 
+checks['B01'] = True 
+checks['C01'] = True 
+ 
+class A01(models.Model): 
+    class Meta: db_table = 'A01' 
+ 
+    f_a = models.TextField(db_index = True) 
+    f_b = models.IntegerField() 
+ 
+class B01(models.Model): 
+    class Meta: db_table = 'B01' 
+ 
+    fk_a = models.ForeignKey(A01) 
+    f_a = models.TextField(db_index = True) 
+    f_b = models.IntegerField() 
+ 
+class C01(models.Model): 
+    class Meta: db_table = 'C01' 
+ 
+    mm_a = models.ManyToManyField(A01, db_table = 'D01') 
+    f_a = models.TextField(db_index = True) 
+    f_b = models.IntegerField() 
+ 
+""" 
+02: managed is set to True. 
+    SQL is generated for all three models. 
+""" 
+checks['A02'] = True 
+checks['B02'] = True 
+checks['C02'] = True 
+ 
+class A02(models.Model): 
+    class Meta: 
+        db_table = 'A02' 
+        managed = True 
+ 
+    f_a = models.TextField(db_index = True) 
+    f_b = models.IntegerField() 
+ 
+class B02(models.Model): 
+    class Meta: 
+        db_table = 'B02' 
+        managed = True 
+ 
+    fk_a = models.ForeignKey(A02) 
+    f_a = models.TextField(db_index = True) 
+    f_b = models.IntegerField() 
+ 
+class C02(models.Model): 
+    class Meta: 
+        db_table = 'C02' 
+        managed = True 
+ 
+    mm_a = models.ManyToManyField(A02, db_table = 'D02') 
+    f_a = models.TextField(db_index = True) 
+    f_b = models.IntegerField() 
+ 
+ 
+""" 
+03: managed is set to False. 
+    SQL is NOT generated for any of the three models. 
+""" 
+checks['A03'] = False 
+checks['B03'] = False 
+checks['C03'] = False 
+ 
+class A03(models.Model): 
+    class Meta: 
+        db_table = 'A03' 
+        managed = False 
+ 
+    f_a = models.TextField(db_index = True) 
+    f_b = models.IntegerField() 
+ 
+class B03(models.Model): 
+    class Meta: 
+        db_table = 'B03' 
+        managed = False 
+ 
+    fk_a = models.ForeignKey(A03) 
+    f_a = models.TextField(db_index = True) 
+    f_b = models.IntegerField() 
+ 
+class C03(models.Model): 
+    class Meta: 
+        db_table = 'C03' 
+        managed = False 
+ 
+    mm_a = models.ManyToManyField(A03, db_table = 'D03') 
+    f_a = models.TextField(db_index = True) 
+    f_b = models.IntegerField() 
+ 
+ 
+# We will use short names for these templates 
+sql_templates = { 
+        'create table': 'CREATE TABLE "%s"', 
+        'create index': 'CREATE INDEX "%s_f_a"', 
+        'drop table': 'DROP TABLE "%s"', 
+        'delete from': 'DELETE FROM "%s"' 
+} 
+ 
+def get_failed_models(arr_sql, sql_template_names): 
+    """ 
+    Find the models which should not be in the SQL but they are present, 
+    or they should be in the SQL but they are missing. 
+    """ 
+    txt_sql = ' '.join(arr_sql) 
+    for (model, should_be_present) in checks.iteritems(): 
+        # Do we expect to see the model name in the SQL text? 
+        for sql_template_name in sql_template_names: 
+            # We are interested not in just the model name like "A01", 
+            # but in the whole string like 'CREATE TABLE "A01"' 
+            # so we apply the model name to the template 
+            # to find out the expected string 
+            expected = (sql_templates[sql_template_name])%model 
+            if ((expected in txt_sql) != should_be_present): 
+                # Our expectations failed! 
+                yield 'The string %s %s present in SQL but it %s.'%( 
+                    expected, 
+                    {False: 'is not', True: 'is'}[expected in txt_sql], 
+                    {False: 'should not be', True: 'should be'}[should_be_present] 
+                    ) 
+ 
+ 
+__test__ = {'API_TESTS':""" 
+>>> from django.db.models import get_app 
+>>> from django.core.management.sql import * 
+>>> from django.core.management.color import no_style 
+>>> import sys 
+ 
+>>> myapp = get_app('unmanaged_models') 
+>>> mystyle = no_style() 
+ 
+# a. Verify sql_create 
+>>> list(get_failed_models( sql_create(myapp, mystyle), ['create table'] )) 
+[] 
+ 
+# b. Verify sql_delete 
+>>> list(get_failed_models( sql_delete(myapp, mystyle), ['drop table'] )) 
+[] 
+ 
+# c. Verify sql_reset 
+>>> list(get_failed_models( sql_reset(myapp, mystyle), ['drop table', 'create table', 'create index'] )) 
+[] 
+ 
+# d. Verify sql_flush 
+>>> # sql_flush(mystyle) 
+>>> list(get_failed_models( sql_flush(mystyle), ['delete from'] )) 
+[] 
+ 
+# e. Verify sql_custom 
+# No custom data provided, should not be no output. 
+>>> sql_custom(myapp,mystyle) 
+[] 
+ 
+# f. Verify sql_indexes 
+>>> list(get_failed_models( sql_indexes(myapp, mystyle), ['create index'] )) 
+[] 
+ 
+# g. Verify sql_all 
+>>> list(get_failed_models( sql_all(myapp, mystyle), ['create table', 'create index'] )) 
+[] 
+"""} 
Index: AUTHORS
===================================================================
--- AUTHORS	(revision 9904)
+++ AUTHORS	(working copy)
@@ -443,6 +443,7 @@
     Mykola Zamkovoi <nickzam@gmail.com>
     Jarek Zgoda <jarek.zgoda@gmail.com>
     Cheng Zhang
+    Alexander Myodov <alex@myodov.com>
 
 A big THANK YOU goes to:
 
Index: docs/ref/models/options.txt
===================================================================
--- docs/ref/models/options.txt	(revision 9904)
+++ docs/ref/models/options.txt	(working copy)
@@ -181,3 +181,15 @@
     verbose_name_plural = "stories"
 
 If this isn't given, Django will use :attr:`~Options.verbose_name` + ``"s"``.
+
+``managed``
+-----------------------
+
+.. attribute:: Options.managed
+
+.. versionadded:: 1.1
+
+If ``False``, Django's database-management facilities (such as syncdb) will
+avoid processing this model.  Set this option on models that use
+manually-created database tables or views.
+
