在前一段时间做开发的时候,碰到一个case,要求在User表中增加一个字段,我当时想了一想,想出了以下几法:
- 第一反应就是,既然是扩展django自带的model表,当然只有去修改django的源代码了,加个字段一句话的事。但是想到此招不行,修改源码乃是大忌,因为这不利于项目的迁移以及django的版本升级;
- 那我把django 的user以及认证部分的源代码拷贝到自己的app下面,然后修改,配置,这样就不需要改动django的代码了,我傻啊,为了加一字段,搞出这么大一动静;
- 那我就自定义一个表呗,然后和User表做个关联,每次有关User表的操作,我就同步到新建的表,在我的项目中呢,我就用我自定义的表,不就ok了嘛,但是此法一是比较麻烦,得把之前和User有关的都改到新表,另外就是每次都得同步,比较耗资源;
- 这个方法是官方推崇的办法,profile 方式扩展.,大体思路是创建 user profile 类,直接继承于 User ,然后扩展扩展认证机制,再在setting中,修改
AUTHENTICATION_BACKENDS
为你新建的认证机制,这样就用你继承的新的User表,代替了Django的User表,以后所有有关User的操作都用这张表即可,听起来似乎不错,但我不还得修改之前和User相关的代码么。
要是能有这么一种方法,能在不修改源码,不增加新表的情况下,就能扩展User表,想必那是极好的。经过多番搜索,找到了一种通过元类来达到目的的方法,现分享给大伙:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
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
- 本文固定链接: http://hanx.xin/2017/08/10/django中如何扩展user表的字段/
- 转载请注明: hanxin 于 DJANGO那些事儿 发表