Django

Code

Ticket #4144: dynamic_models.diff

File dynamic_models.diff, 10.5 kB (added by Marty Alchin <gulopine@gamemusic.org>, 1 year ago)

Some basic tests for dynamic models

  • tests/modeltests/dynamic_models/models.py

    old new  
     1""" 
     2Dynamic Models 
     3 
     4In some cases, it can be useful for models to be created at run-time, 
     5rather than being defined in source files. 
     6""" 
     7 
     8from django.db import models 
     9 
     10def create_model(name, fields=None, app_label='', module='', options=None, admin=None): 
     11    "One example of how to create a model dynamically at run-time" 
     12 
     13    app_name = app_label 
     14 
     15    # app_label must be set using the Meta inner class 
     16    class Meta: 
     17        # Using type('Meta', ...) gives a dictproxy error during model creation 
     18        app_label = app_name 
     19 
     20    # Update Meta with any options that were provided 
     21    if options is not None: 
     22        for key, value in options.items(): 
     23            setattr(Meta, key, value) 
     24 
     25    # Set up a dictionary to simulate declarations within a class 
     26    attrs = {'__module__': module, 'Meta': Meta} 
     27 
     28    # Add in any fields that were provided 
     29    if fields: 
     30        attrs.update(fields) 
     31 
     32    # Create an Admin inner class if admin options were provided 
     33    if admin is not None: 
     34        class Admin: 
     35            pass 
     36        for key, value in admin: 
     37            setattr(Admin, key, value) 
     38        attrs['Admin'] = Admin 
     39 
     40    # Create the class, which automatically triggers ModelBase processing 
     41    return type(name, (models.Model,), attrs) 
     42 
     43def install(model): 
     44    from django.core import management 
     45    from django.db import connection 
     46 
     47    # Standard syncdb expects models to be in reliable locations, 
     48    # so dynamic models need to bypass django.core.management.syncdb. 
     49    # On the plus side, this allows individual models to be installed 
     50    # without installing the entire project structure. 
     51    # On the other hand, this means that things like relationships and 
     52    # indexes will have to be handled manually. 
     53    # For these tests, just the basic table will be installed. 
     54 
     55    cursor = connection.cursor() 
     56    statements, pending = management._get_sql_model_create(model) 
     57    for sql in statements: 
     58        cursor.execute(sql) 
     59 
     60__test__ = {'API_TESTS':""" 
     61# Create an empty model and make sure it works as expected 
     62 
     63>>> model = create_model('EmptyModel') 
     64>>> model.__name__ 
     65'EmptyModel' 
     66>>> model.__module__ 
     67'' 
     68>>> model._meta.app_label 
     69'' 
     70 
     71# ID is added automatically 
     72>>> len(model._meta.fields) 
     731 
     74 
     75# Create a model with custom references 
     76 
     77>>> model = create_model('CustomModel', app_label='dynamic_models', module='tests.dynamic_models.models') 
     78>>> model.__name__ 
     79'CustomModel' 
     80>>> model.__module__ 
     81'tests.dynamic_models.models' 
     82>>> model._meta.app_label 
     83'dynamic_models' 
     84 
     85# Check that the model was correctly registered with Django 
     86>>> from django.db.models import loading 
     87>>> loading.get_model('dynamic_models', 'CustomModel') == model 
     88True 
     89 
     90# Create a model with some Meta options 
     91 
     92>>> meta = { 
     93...     'db_table': 'dynamic_table', 
     94...     'verbose_name': 'dynamic model', 
     95... } 
     96>>> model = create_model('MetaModel', options=meta) 
     97>>> model._meta.verbose_name 
     98'dynamic model' 
     99>>> model._meta.verbose_name_plural 
     100'dynamic models' 
     101 
     102# Create a model with some fields 
     103 
     104>>> fields = { 
     105...     'name': models.CharField(maxlength=255), 
     106...     'age': models.SmallIntegerField(null=True), 
     107... } 
     108>>> model = create_model('FieldModel', fields) 
     109 
     110# ID is added automatically 
     111>>> len(model._meta.fields) 
     1123 
     113 
     114# Install a dynamic model in the test database 
     115 
     116>>> install(model) 
     117>>> model.objects.count() 
     1180 
     119 
     120# Test basic CRUD features of a dynamic model 
     121 
     122# CREATE 
     123 
     124>>> test1 = model.objects.create(name='Test Object', age=25) 
     125>>> test2 = model.objects.create(name='Another Test') 
     126>>> test3 = model.objects.create(age=30) 
     127 
     128# SELECT 
     129 
     130>>> model.objects.count() 
     1313 
     132>>> for obj in model.objects.all(): 
     133...     assert isinstance(obj, model), 'This should not fail' 
     134 
     135# UPDATE 
     136 
     137>>> test1.name = 'Updated Object' 
     138>>> test1.save() 
     139>>> test2.age = 15 
     140>>> test2.save() 
     141 
     142# DELETE 
     143 
     144>>> test3.delete() 
     145 
     146# A more advanced query for good measure 
     147 
     148>>> model.objects.filter(age__gt=20).count() 
     1491 
     150"""} 
     151         
     152class DynamicModelTestCase(object): 
     153 
     154    def test_options(self): 
     155        "Create a model with some Meta options" 
     156 
     157        meta = { 
     158            'db_table': 'dynamic_table', 
     159            'verbose_name': 'dynamic model', 
     160        } 
     161 
     162        model = create_model('MetaModel', options=meta) 
     163 
     164        # Make sure the verbose names were set proper 
     165        self.assertEquals(model._meta.verbose_name, 'dynamic model') 
     166        self.assertEquals(model._meta.verbose_name_plural, 'dynamic models') 
     167 
     168        # Install the model to make sure db_table works 
     169        install(model) 
     170 
     171        # Test it out 
     172        model.objects.create() 
     173        model.objects.create() 
     174 
     175        for obj in model.objects.all(): 
     176            self.assert_(isinstance(obj, model)) 
  • tests/modeltests/dynamic_models/models.py

    old new  
     1""" 
     2Dynamic Models 
     3 
     4In some cases, it can be useful for models to be created at run-time, 
     5rather than being defined in source files. 
     6""" 
     7 
     8from django.db import models 
     9 
     10def create_model(name, fields=None, app_label='', module='', options=None, admin=None): 
     11    "One example of how to create a model dynamically at run-time" 
     12 
     13    app_name = app_label 
     14 
     15    # app_label must be set using the Meta inner class 
     16    class Meta: 
     17        # Using type('Meta', ...) gives a dictproxy error during model creation 
     18        app_label = app_name 
     19 
     20    # Update Meta with any options that were provided 
     21    if options is not None: 
     22        for key, value in options.items(): 
     23            setattr(Meta, key, value) 
     24 
     25    # Set up a dictionary to simulate declarations within a class 
     26    attrs = {'__module__': module, 'Meta': Meta} 
     27 
     28    # Add in any fields that were provided 
     29    if fields: 
     30        attrs.update(fields) 
     31 
     32    # Create an Admin inner class if admin options were provided 
     33    if admin is not None: 
     34        class Admin: 
     35            pass 
     36        for key, value in admin: 
     37            setattr(Admin, key, value) 
     38        attrs['Admin'] = Admin 
     39 
     40    # Create the class, which automatically triggers ModelBase processing 
     41    return type(name, (models.Model,), attrs) 
     42 
     43def install(model): 
     44    from django.core import management 
     45    from django.db import connection 
     46 
     47    # Standard syncdb expects models to be in reliable locations, 
     48    # so dynamic models need to bypass django.core.management.syncdb. 
     49    # On the plus side, this allows individual models to be installed 
     50    # without installing the entire project structure. 
     51    # On the other hand, this means that things like relationships and 
     52    # indexes will have to be handled manually. 
     53    # For these tests, just the basic table will be installed. 
     54 
     55    cursor = connection.cursor() 
     56    statements, pending = management._get_sql_model_create(model) 
     57    for sql in statements: 
     58        cursor.execute(sql) 
     59 
     60__test__ = {'API_TESTS':""" 
     61# Create an empty model and make sure it works as expected 
     62 
     63>>> model = create_model('EmptyModel') 
     64>>> model.__name__ 
     65'EmptyModel' 
     66>>> model.__module__ 
     67'' 
     68>>> model._meta.app_label 
     69'' 
     70 
     71# ID is added automatically 
     72>>> len(model._meta.fields) 
     731 
     74 
     75# Create a model with custom references 
     76 
     77>>> model = create_model('CustomModel', app_label='dynamic_models', module='tests.dynamic_models.models') 
     78>>> model.__name__ 
     79'CustomModel' 
     80>>> model.__module__ 
     81'tests.dynamic_models.models' 
     82>>> model._meta.app_label 
     83'dynamic_models' 
     84 
     85# Check that the model was correctly registered with Django 
     86>>> from django.db.models import loading 
     87>>> loading.get_model('dynamic_models', 'CustomModel') == model 
     88True 
     89 
     90# Create a model with some Meta options 
     91 
     92>>> meta = { 
     93...     'db_table': 'dynamic_table', 
     94...     'verbose_name': 'dynamic model', 
     95... } 
     96>>> model = create_model('MetaModel', options=meta) 
     97>>> model._meta.verbose_name 
     98'dynamic model' 
     99>>> model._meta.verbose_name_plural 
     100'dynamic models' 
     101 
     102# Create a model with some fields 
     103 
     104>>> fields = { 
     105...     'name': models.CharField(maxlength=255), 
     106...     'age': models.SmallIntegerField(null=True), 
     107... } 
     108>>> model = create_model('FieldModel', fields) 
     109 
     110# ID is added automatically 
     111>>> len(model._meta.fields) 
     1123 
     113 
     114# Install a dynamic model in the test database 
     115 
     116>>> install(model) 
     117>>> model.objects.count() 
     1180 
     119 
     120# Test basic CRUD features of a dynamic model 
     121 
     122# CREATE 
     123 
     124>>> test1 = model.objects.create(name='Test Object', age=25) 
     125>>> test2 = model.objects.create(name='Another Test') 
     126>>> test3 = model.objects.create(age=30) 
     127 
     128# SELECT 
     129 
     130>>> model.objects.count() 
     1313 
     132>>> for obj in model.objects.all(): 
     133...     assert isinstance(obj, model), 'This should not fail' 
     134 
     135# UPDATE 
     136 
     137>>> test1.name = 'Updated Object' 
     138>>> test1.save() 
     139>>> test2.age = 15 
     140>>> test2.save() 
     141 
     142# DELETE 
     143 
     144>>> test3.delete() 
     145 
     146# A more advanced query for good measure 
     147 
     148>>> model.objects.filter(age__gt=20).count() 
     1491 
     150"""} 
     151         
     152class DynamicModelTestCase(object): 
     153 
     154    def test_options(self): 
     155        "Create a model with some Meta options" 
     156 
     157        meta = { 
     158            'db_table': 'dynamic_table', 
     159            'verbose_name': 'dynamic model', 
     160        } 
     161 
     162        model = create_model('MetaModel', options=meta) 
     163 
     164        # Make sure the verbose names were set proper 
     165        self.assertEquals(model._meta.verbose_name, 'dynamic model') 
     166        self.assertEquals(model._meta.verbose_name_plural, 'dynamic models') 
     167 
     168        # Install the model to make sure db_table works 
     169        install(model) 
     170 
     171        # Test it out 
     172        model.objects.create() 
     173        model.objects.create() 
     174 
     175        for obj in model.objects.all(): 
     176            self.assert_(isinstance(obj, model))