django dynamic models and field injections

如同java优秀的ORM框架 hibernate 一样,django也有动态模型和字段注入注入功能。但是截至1.5版本,未将此功能纳入官方文档对外开放。

主要原因个人觉得有二:第一,不可测性。目前django的单元测试框架不能有效的支持到动态模型。比如最简单的,创建了一个动态模型,

由于在测试代码里不能import该模型,导致对于该模型的操作,无法进行。第二,对使用者要求较高,需要十分了解django模型类加载机制,

否则很容易打破django自身的模型加载顺序,导致在复杂模型关系时建模失败。

当然,模型关系比较简单时,这两个手段还是可以使用的。

首先,定义一组配置:

dynamic_models_conf.py  代码如下:可以根据个人喜好随意指定配置格式,此处仅举例

# -*- coding: UTF-8 -*-
""" config for fields injection and dynamic models """

from django.db import models
from django.utils.encoding import smart_unicode


INJECT_FIELDS_CONFS = [
    {"to_class": "UserProfile", "fieldtype": models.IntegerField(u"年龄", default=0, null=True), "fieldname": "age"},
    {"to_class": "UserProfile", "fieldtype": "Department", "null": False, "verbose_name": u"部门"},
]


DYNAMIC_CLASSES_CONFS = [
    {"modelname": "Department",
     "app_label": 'authentication',
     "module": 'authentication.models',
     "fields": {'name': models.CharField(max_length=40),
                '__unicode__': lambda self: smart_unicode(self.name), },
     "options": {'verbose_name': u'部门', "verbose_name_plural": u"部门"},
     "admin_options": {},
     },

]



dynamic_models_tool.py  代码如下:

# -*- coding: UTF-8 -*-
""" fields injection and dynamic models """

from django.db import models
from django.contrib import admin
from django.db.models.signals import class_prepared

from dynamic_models_conf import INJECT_FIELDS_CONFS


def create_dynamic_model(modelname, fields=None, options=None, admin_options={}, app_label='', module='',):
    class Meta:
        pass

    if app_label:
        setattr(Meta, 'app_label', app_label)

    if options is not None:
        for key, value in options.iteritems():
            setattr(Meta, key, value)

    attrs = {'__module__': module, 'Meta': Meta}
    if fields:
        attrs.update(fields)
    model = type(modelname, (models.Model,), attrs)

    # Create an Admin class if admin options were provided
    if admin_options is not None:
        class Admin(admin.ModelAdmin):
            pass
        for key, value in admin_options:
            setattr(Admin, key, value)
        admin.site.register(model, Admin)

    return model


def is_valid_field_conf(field_conf):
    #TODO: do check conf
    return True


def inject_field(sender, **kwargs):
    """ It is strongly recommended that this function should to be connected with class_prepared signal """
    sender_name = sender.__name__

    for field_conf in INJECT_FIELDS_CONFS:
        if field_conf["to_class"]==sender_name and is_valid_field_conf(field_conf):
            field = field_conf["fieldtype"]

            if isinstance(field, str):  # if type is str, should be foreign key,如果要支持多对多,类似的在配置文件中想办法区别出来即可。只需做到注入代码和配置能对得上就行,配置文件形式目前无任何标准。此处仅仅为举例,所以不考虑多对多,字符串直接当外键处理
                fk = models.ForeignKey(field, null=field_conf["null"], blank=False, default=None, verbose_name=field_conf["verbose_name"])
                field = field.lower()
                fk.contribute_to_class(sender, field)
            else:
                field.contribute_to_class(sender, field_conf["fieldname"])
class_prepared.connect(inject_field)

说明:

1.在如上代码基础上,在models.py 只需要读取 DYNAMIC_CLASSES_CONFS ,然后依次调用 create_dynamic_model 即可创建动态模型。

此动态模型可以 和 普通模型一样 通过 sqlall 命令查看,和syncdb同步到数据库。同样也支持South等工具的迁移。

2.字段注入无需显示调用,由于models中import 了 dynamic_models_tool.py 中的 create_dynamic_model函数。

所以,dynamic_models_tool.py会在models.py之前被加载。也就是说 class_prepared.connect(inject_field) 一句代码会在models.py

加载前被执行。即创建任何模型(包括动态和普通模型)之前,先绑定了字段注入的监听器。除此之外,有一点很重要,

class_prepared.connect(inject_field)一句,有意识的不传递一个sender,而是在监听函数inject_field里面去判断sender需不需要被注入(有没有关于它的注入配置),

原因是想不打破django自身的模型加载顺序,避免模型关系过于复杂时,由于顺序不当,建模失败。关于加载顺序此一点,本人也是从一老外博客中得知,未有深入研究。


此外,由于动态模型和字段注入动态性,导致django 普通form不能满足需求,动态的跟随models同步变化,甚至在写form时根本不能确定该模型有哪些属性(会有注入字段),不能硬编码每个字段,建议采用model form。model form可以屏蔽模型的动态变化,功能也很强大,给View层提供一个稳定不变的可用form 对象,而且model form本身也不难使用。前面文章中略有介绍。


附上两个博客地址:

http://blog.jupo.org/2011/11/10/django-model-field-injection/

http://www.b-list.org/weblog/2007/nov/04/working-models/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值