任务: html
在数据库中按用户id生成表。 python
由于用户的数量在增长,因此表的生成时动态的。 sql
Django框架里,使用Model类把每条数据库记录生成一个对象。一个Model对应一张表。换句话说就是建立动态Model。 数据库
官方文档中简述建立Model类: django
Internally, Django uses metaclasses to create models based on a class you provide in your source code. …, that means that rather than your classes being the actual models, Django receives a description of your class, which it uses to create a model in its place. 从内部来看,Django是根据你在源代码中提供的类,用动态建立类建立Model。也就是说,Django只是收到了你提供的类在其所在 位置建立Model的描述,而不是实际的Model。 app
由于BDFL的积极推进,Unifying types and classes in Python 2.2,因此建立一个类可使用type句型。 框架
tpye建立Model: ide
from Django.db import models Secretcode = type('Secretcode', (models.Model,), { 'timestamp': models.DateTimeField(auto_now=True), 'uid': models.CharField(max_length=32,), 'secretcode':models.CharField(max_length=10), 'cid':models.CharField(max_length=20,blank=True,null=True), })
这实际上等价于: ui
from Django.db import models class Secretcode(models.Model): timestamp=models.DateTimeField(autonow=True) uid= models.CharField(max_length=32,) secretcode=models.CharField(max_length=10) cid=models.CharField(max_length=20,blank=True,null=True)
以此能够实现一个动态建立Model的普适方法(Officail Docs provided): this
def create_model(name, fields=None, app_label='', module='', options=None, admin_opts=None): """ 建立指定model """ class Meta: # Using type('Meta', ...) gives a dictproxy error during model creation pass if app_label: # app_label必须用Meta内部类来设定 setattr(Meta, 'app_label', app_label) # 若提供了options参数,就要用更新Meta类 if options is not None: for key, value in options.iteritems(): setattr(Meta, key, value) # 建立一个字典来模拟类的声明,module和当前所在的module对应 attrs = {'__module__': module, 'Meta': Meta} # 加入全部提供的字段 if fields: attrs.update(fields) # 建立这个类,这会触发ModelBase来处理 model = type(name, (models.Model,), attrs) # 若是提供了admin参数,那么建立Admin类 if admin_opts is not None: class Admin(admin.ModelAdmin): pass for key, value in admin_opts: setattr(Admin, key, value) admin.site.register(model, Admin) return model
app_label和module能够指定为想要依附的app的对应信息。
其实,models.Model类只是封装了数据库操做。换句话说,假若用户了解数据库的中某张表描述信息,那么用上边的方法建立对应的Model也能够正确地对该表进行操做。
因此,创建一个Model最重要的是提供对正确的表的描述,以便于在任意时刻都能用上边的办法创建或重建咱们想要的Model(它也能够被表述成:指定数据表操做集)
考虑要重建,那么保存好建立初提供的Model信息就相当重要。
数据库驱动方法——由于动态Model的所有信息可按类别分开,将这些信息保存到按类别建立的数据表中,重建的时候只要取出Model信息再使用上边的办法便可。
这是一个绝妙的方法。
!如下实现代码未验证。
from django.core.validators import ValidationError from django.db import models class MyApp(models.Model): name = models.CharField(max_length=255) module = models.CharField(max_length=255) def __str__(self): return self.name class MyModel(models.Model): app = models.ForeignKey(MyApp, related_name='mymodels') name = models.CharField(max_length=255) def __str__(self): return self.name def get_django_model(self): "Returns a functional Django model based on current data" # Get all associated fields into a list ready for dict() fields = [(f.name, f.get_django_field()) for f in self.myfields.all()] # Use the create_model function defined above return create_model(self.name, dict(fields), self.app.name, self.app.module) class Meta: unique_together = (('app', 'name'),) def is_valid_field(self, field_data, all_data): if hasattr(models, field_data) and issubclass(getattr(models, field_data), models.Field): # It exists and is a proper field type return raise ValidationError("This is not a valid field type.") class MyField(models.Model): model = models.ForeignKey(Model, related_name='myfields') name = models.CharField(max_length=255) type = models.CharField(max_length=255, validators=[is_valid_field]) def get_django_field(self): "Returns the correct field type, instantiated with applicable settings" # Get all associated settings into a list ready for dict() settings = [(s.name, s.value) for s in self.mysettings.all()] # Instantiate the field with the settings as **kwargs return getattr(models, self.type)(*dict(settings)) class Meta: unique_together = (('model', 'name'),) class MySetting(models.Model): field = models.ForeignKey(Field, related_name='mysettings') name = models.CharField(max_length=255) value = models.CharField(max_length=255) class Meta: unique_together = (('field', 'name'),)
官方提供的这个实现代码很是严谨,从上到下,自第二个起先后创建外键关系,前面的class反向检索用的关系名(related_name)是后面的class_name的小写。
上面几个方法建立的Model仅仅在Runtime时的cache里面,它们的对象不能往数据库里写入一点实质内容。由于它们在数据库里没有他们对应的表。
通常状况下,表的建立须要经过manage.py命令——syncdb实现,这里可使用Djangp自带的sql语句生成执行钩子:
One workaround for basic models uses an internal portion of django.core.management to install a basic table definition to the database.
def install(model): from django.core.management import sql, color 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. # This installs only the basic table definition. # disable terminal colors in the sql statements style = color.no_style() cursor = connection.cursor() statements, pending = sql.sql_model_create(model, style) for sql in statements: cursor.execute(sql)
还可使用金牌插件south提供的钩子:
def create_db_table(model_class): """ Takes a Django model class and create a database table, if necessary. """ # XXX Create related tables for ManyToMany etc db.start_transaction() table_name = model_class._meta.db_table # Introspect the database to see if it doesn't already exist if (connection.introspection.table_name_converter(table_name) not in connection.introspection.table_names()): fields = _get_fields(model_class) db.create_table(table_name, fields) # Some fields are added differently, after table creation # eg GeoDjango fields db.execute_deferred_sql() logger.debug("Created table '%s'" % table_name) db.commit_transaction() def delete_db_table(model_class): table_name = model_class._meta.db_table db.start_transaction() db.delete_table(table_name) logger.debug("Deleted table '%s'" % table_name) db.commit_transaction()
这里推荐后者,不只是由于其鲁棒性更好,还由于其比起Django自带database migration更加良心。
Dynamic Model的基本实现方法和原理以上就是了,可是还有不少人为此研究更加适合production的具体方法:
Django dynamic model fields:As of today, there are four available approaches, two of them requiring a certain storage backend…
- Django-eav
- Django-hstore
- Django MongoDB
- Dynamic models based on syncdb and South-hooks
这里须要单独说明的是Will Hardy‘s approach,也就是上面引用中提到的第四种方法。考虑周全,严丝合缝,提供的demo源代码阅读起来有些不适。但其关键代码在utils.py中,其余模块是为这个demo实现功能服务的。切莫一叶障目。
总结:
参考:
Dynamic models:https://code.djangoproject.com/wiki/DynamicModels
Stack Overflow-Django dynamic model fields:http://stackoverflow.com/questions/7933596/django-dynamic-model-fields/7934577#7934577
Runtime Dynamic Models with Django:http://dynamic-models.readthedocs.org/en/latest/index.html#runtime-dynamic-models-with-django