Index: tests/modeltests/dynamic_models/__init__.py
===================================================================
Index: tests/modeltests/dynamic_models/models.py
===================================================================
--- tests/modeltests/dynamic_models/models.py	(revision 0)
+++ tests/modeltests/dynamic_models/models.py	(revision 0)
@@ -0,0 +1,176 @@
+"""
+Dynamic Models
+
+In some cases, it can be useful for models to be created at run-time,
+rather than being defined in source files.
+"""
+
+from django.db import models
+
+def create_model(name, fields=None, app_label='', module='', options=None, admin=None):
+    "One example of how to create a model dynamically at run-time"
+
+    app_name = app_label
+
+    # app_label must be set using the Meta inner class
+    class Meta:
+        # Using type('Meta', ...) gives a dictproxy error during model creation
+        app_label = app_name
+
+    # Update Meta with any options that were provided
+    if options is not None:
+        for key, value in options.items():
+            setattr(Meta, key, value)
+
+    # Set up a dictionary to simulate declarations within a class
+    attrs = {'__module__': module, 'Meta': Meta}
+
+    # Add in any fields that were provided
+    if fields:
+        attrs.update(fields)
+
+    # Create an Admin inner class if admin options were provided
+    if admin is not None:
+        class Admin:
+            pass
+        for key, value in admin:
+            setattr(Admin, key, value)
+        attrs['Admin'] = Admin
+
+    # Create the class, which automatically triggers ModelBase processing
+    return type(name, (models.Model,), attrs)
+
+def install(model):
+    from django.core import management
+    from django.db import connection
+
+    # Standard syncdb expects models to be in reliable locations,
+    # so dynamic models need to bypass django.core.management.syncdb.
+    # On the plus side, this allows individual models to be installed
+    # without installing the entire project structure.
+    # On the other hand, this means that things like relationships and
+    # indexes will have to be handled manually.
+    # For these tests, just the basic table will be installed.
+
+    cursor = connection.cursor()
+    statements, pending = management._get_sql_model_create(model)
+    for sql in statements:
+        cursor.execute(sql)
+
+__test__ = {'API_TESTS':"""
+# Create an empty model and make sure it works as expected
+
+>>> model = create_model('EmptyModel')
+>>> model.__name__
+'EmptyModel'
+>>> model.__module__
+''
+>>> model._meta.app_label
+''
+
+# ID is added automatically
+>>> len(model._meta.fields)
+1
+
+# Create a model with custom references
+
+>>> model = create_model('CustomModel', app_label='dynamic_models', module='tests.dynamic_models.models')
+>>> model.__name__
+'CustomModel'
+>>> model.__module__
+'tests.dynamic_models.models'
+>>> model._meta.app_label
+'dynamic_models'
+
+# Check that the model was correctly registered with Django
+>>> from django.db.models import loading
+>>> loading.get_model('dynamic_models', 'CustomModel') == model
+True
+
+# Create a model with some Meta options
+
+>>> meta = {
+...     'db_table': 'dynamic_table',
+...     'verbose_name': 'dynamic model',
+... }
+>>> model = create_model('MetaModel', options=meta)
+>>> model._meta.verbose_name
+'dynamic model'
+>>> model._meta.verbose_name_plural
+'dynamic models'
+
+# Create a model with some fields
+
+>>> fields = {
+...     'name': models.CharField(maxlength=255),
+...     'age': models.SmallIntegerField(null=True),
+... }
+>>> model = create_model('FieldModel', fields)
+
+# ID is added automatically
+>>> len(model._meta.fields)
+3
+
+# Install a dynamic model in the test database
+
+>>> install(model)
+>>> model.objects.count()
+0
+
+# Test basic CRUD features of a dynamic model
+
+# CREATE
+
+>>> test1 = model.objects.create(name='Test Object', age=25)
+>>> test2 = model.objects.create(name='Another Test')
+>>> test3 = model.objects.create(age=30)
+
+# SELECT
+
+>>> model.objects.count()
+3
+>>> for obj in model.objects.all():
+...     assert isinstance(obj, model), 'This should not fail'
+
+# UPDATE
+
+>>> test1.name = 'Updated Object'
+>>> test1.save()
+>>> test2.age = 15
+>>> test2.save()
+
+# DELETE
+
+>>> test3.delete()
+
+# A more advanced query for good measure
+
+>>> model.objects.filter(age__gt=20).count()
+1
+"""}
+        
+class DynamicModelTestCase(object):
+
+    def test_options(self):
+        "Create a model with some Meta options"
+
+        meta = {
+            'db_table': 'dynamic_table',
+            'verbose_name': 'dynamic model',
+        }
+
+        model = create_model('MetaModel', options=meta)
+
+        # Make sure the verbose names were set proper
+        self.assertEquals(model._meta.verbose_name, 'dynamic model')
+        self.assertEquals(model._meta.verbose_name_plural, 'dynamic models')
+
+        # Install the model to make sure db_table works
+        install(model)
+
+        # Test it out
+        model.objects.create()
+        model.objects.create()
+
+        for obj in model.objects.all():
+            self.assert_(isinstance(obj, model))
Index: tests/modeltests/dynamic_models/__init__.py
===================================================================
Index: tests/modeltests/dynamic_models/models.py
===================================================================
--- tests/modeltests/dynamic_models/models.py	(revision 0)
+++ tests/modeltests/dynamic_models/models.py	(revision 0)
@@ -0,0 +1,176 @@
+"""
+Dynamic Models
+
+In some cases, it can be useful for models to be created at run-time,
+rather than being defined in source files.
+"""
+
+from django.db import models
+
+def create_model(name, fields=None, app_label='', module='', options=None, admin=None):
+    "One example of how to create a model dynamically at run-time"
+
+    app_name = app_label
+
+    # app_label must be set using the Meta inner class
+    class Meta:
+        # Using type('Meta', ...) gives a dictproxy error during model creation
+        app_label = app_name
+
+    # Update Meta with any options that were provided
+    if options is not None:
+        for key, value in options.items():
+            setattr(Meta, key, value)
+
+    # Set up a dictionary to simulate declarations within a class
+    attrs = {'__module__': module, 'Meta': Meta}
+
+    # Add in any fields that were provided
+    if fields:
+        attrs.update(fields)
+
+    # Create an Admin inner class if admin options were provided
+    if admin is not None:
+        class Admin:
+            pass
+        for key, value in admin:
+            setattr(Admin, key, value)
+        attrs['Admin'] = Admin
+
+    # Create the class, which automatically triggers ModelBase processing
+    return type(name, (models.Model,), attrs)
+
+def install(model):
+    from django.core import management
+    from django.db import connection
+
+    # Standard syncdb expects models to be in reliable locations,
+    # so dynamic models need to bypass django.core.management.syncdb.
+    # On the plus side, this allows individual models to be installed
+    # without installing the entire project structure.
+    # On the other hand, this means that things like relationships and
+    # indexes will have to be handled manually.
+    # For these tests, just the basic table will be installed.
+
+    cursor = connection.cursor()
+    statements, pending = management._get_sql_model_create(model)
+    for sql in statements:
+        cursor.execute(sql)
+
+__test__ = {'API_TESTS':"""
+# Create an empty model and make sure it works as expected
+
+>>> model = create_model('EmptyModel')
+>>> model.__name__
+'EmptyModel'
+>>> model.__module__
+''
+>>> model._meta.app_label
+''
+
+# ID is added automatically
+>>> len(model._meta.fields)
+1
+
+# Create a model with custom references
+
+>>> model = create_model('CustomModel', app_label='dynamic_models', module='tests.dynamic_models.models')
+>>> model.__name__
+'CustomModel'
+>>> model.__module__
+'tests.dynamic_models.models'
+>>> model._meta.app_label
+'dynamic_models'
+
+# Check that the model was correctly registered with Django
+>>> from django.db.models import loading
+>>> loading.get_model('dynamic_models', 'CustomModel') == model
+True
+
+# Create a model with some Meta options
+
+>>> meta = {
+...     'db_table': 'dynamic_table',
+...     'verbose_name': 'dynamic model',
+... }
+>>> model = create_model('MetaModel', options=meta)
+>>> model._meta.verbose_name
+'dynamic model'
+>>> model._meta.verbose_name_plural
+'dynamic models'
+
+# Create a model with some fields
+
+>>> fields = {
+...     'name': models.CharField(maxlength=255),
+...     'age': models.SmallIntegerField(null=True),
+... }
+>>> model = create_model('FieldModel', fields)
+
+# ID is added automatically
+>>> len(model._meta.fields)
+3
+
+# Install a dynamic model in the test database
+
+>>> install(model)
+>>> model.objects.count()
+0
+
+# Test basic CRUD features of a dynamic model
+
+# CREATE
+
+>>> test1 = model.objects.create(name='Test Object', age=25)
+>>> test2 = model.objects.create(name='Another Test')
+>>> test3 = model.objects.create(age=30)
+
+# SELECT
+
+>>> model.objects.count()
+3
+>>> for obj in model.objects.all():
+...     assert isinstance(obj, model), 'This should not fail'
+
+# UPDATE
+
+>>> test1.name = 'Updated Object'
+>>> test1.save()
+>>> test2.age = 15
+>>> test2.save()
+
+# DELETE
+
+>>> test3.delete()
+
+# A more advanced query for good measure
+
+>>> model.objects.filter(age__gt=20).count()
+1
+"""}
+        
+class DynamicModelTestCase(object):
+
+    def test_options(self):
+        "Create a model with some Meta options"
+
+        meta = {
+            'db_table': 'dynamic_table',
+            'verbose_name': 'dynamic model',
+        }
+
+        model = create_model('MetaModel', options=meta)
+
+        # Make sure the verbose names were set proper
+        self.assertEquals(model._meta.verbose_name, 'dynamic model')
+        self.assertEquals(model._meta.verbose_name_plural, 'dynamic models')
+
+        # Install the model to make sure db_table works
+        install(model)
+
+        # Test it out
+        model.objects.create()
+        model.objects.create()
+
+        for obj in model.objects.all():
+            self.assert_(isinstance(obj, model))
