jasper的技术小窝

关注DevOps、运维监控、Python、Golang、开源、大数据、web开发、互联网

django中如何扩展User表的字段

作者:jasper | 分类:django | 标签:   | 阅读 3320 次 | 发布:2014-09-21 5:20 p.m.

在前一段时间做开发的时候,碰到一个case,要求在User表中增加一个字段,我当时想了一想,想出了以下几法:

  1. 第一反应就是,既然是扩展django自带的model表,当然只有去修改django的源代码了,加个字段一句话的事。但是想到此招不行,修改源码乃是大忌,因为这不利于项目的迁移以及django的版本升级;
  2. 那我把django 的user以及认证部分的源代码拷贝到自己的app下面,然后修改,配置,这样就不需要改动django的代码了,我傻啊,为了加一字段,搞出这么大一动静;
  3. 那我就自定义一个表呗,然后和User表做个关联,每次有关User表的操作,我就同步到新建的表,在我的项目中呢,我就用我自定义的表,不就ok了嘛,但是此法一是比较麻烦,得把之前和User有关的都改到新表,另外就是每次都得同步,比较耗资源;
  4. 这个方法是官方推崇的办法,profile 方式扩展.,大体思路是创建 user profile 类,直接继承于 User ,然后扩展扩展认证机制,再在setting中,修改AUTHENTICATION_BACKENDS为你新建的认证机制,这样就用你继承的新的User表,代替了Django的User表,以后所有有关User的操作都用这张表即可,听起来似乎不错,但我不还得修改之前和User相关的代码么。

要是能有这么一种方法,能在不修改源码,不增加新表的情况下,就能扩展User表,想必那是极好的。经过多番搜索,找到了一种通过元类来达到目的的方法,现分享给大伙:

from django.db import models  
from django.contrib.auth.models import User  
from django.contrib.auth.admin import UserAdmin  
import datetime  
class ProfileBase(type):  
    def __new__(cls, name, bases, attrs):  #构造器,(名字,基类,类属性)
        module = attrs.pop('__module__')  
        parents = [b for b in bases if isinstance(b, ProfileBase)]  
        if parents:  
            fields = []  
            for obj_name, obj in attrs.items():  
                if isinstance(obj, models.Field): fields.append(obj_name)  
                User.add_to_class(obj_name, obj)       ####最重要的步骤
            UserAdmin.fieldsets = list(UserAdmin.fieldsets)  
            UserAdmin.fieldsets.append((name, {'fields': fields}))  
        return super(ProfileBase, cls).__new__(cls, name, bases, attrs)  

class ProfileUser(object):  
    __metaclass__ = ProfileBase  

class ExtraInfo(ProfileUser):  
    phone_number= models.CharField(max_length = 20, verbose_name=u'电话号码')  

这样就大功告成啦!!!

稍微解释一下这段代码: ProfileBase是自定义的一个元类,继承自types.ClassType,其中ProfileUser为一个基类,其元类为ProfileBase,而ExtraInfo才是我们真正自定义字段的类,之所以把基类ProfileUser和ExtraInfo分开,是为了便于在其他地方引用ProfileUser,进行自定义扩展。简单说来,当解释器看到你在定义一个ProfileUser类的子类,而ProfileUser类的元类是ProfileBase,所以ExtraInfo的元类也是ProfileBase,在定义ProfileUser的子类的时候,它就会执行元类ProfileBase中的new中代码,并且将正在定义的类的(名字,基类,类属性)作为参数传递给new,这里的name就是类名ExtraInfo,attrs中则包含你新加的字段,通过User.add_to_class把新的字段加入到User中,为了能在admin中显示出来,把它加入到UserAdmin.fieldsets中,这样就能在后台编辑这个这个字段,当然,你也可以加入到ist_display,使之在列表中显示。

如果你有其他app也想往User Model中加field或方法,都只要通过子类ProfileUser类,然后使用声明语法进行定义即可,所有其他工作都有元类帮你完成。这也是所有django的model的内部工作,你可以用此方法扩展任何model。


转载请注明出处:http://www.opscoder.info/extend_user.html

其他分类: