django 注册、登录及第三方接口程序(2):扩展User表

一、django的用户模型和其存在的缺陷

这一点大牛们已经很清楚的告诉了我们(点击)。
扩展User表是为了满足更多的需求,毕竟自带的字段很少。我用的版本是1.2.3,下面的例子是基于这个版面进行的。

二、自定义Profile Model

这是django官网提供的方法.意思就是定义一个Profile Model,里面包含一个unique=True的ForeignKey ,在settings.py 添加一个选项 AUTH_PROFILE_MODULE 指向该Profile Model,那么自带User Model的实例就可以通过get_profile()来获取相应的profile的信息了。
这里我定义如下:

复制代码
from django.db import models
from django.contrib.auth.models import User

class Profile(models.Model):
'''帐号附加信息'''

    type = models.IntegerField()#职业类型
    user = models.ForeignKey(User,unique=True)  #User 外键且唯一
    nickname = models.CharField(max_length=16,null=True, default='')#昵称
    sex = models.IntegerField() #性别
    question = models.CharField(max_length=128, null=True, default='')  #密码提示问题
    answer = models.CharField(max_length=128, null=True, default='')#密码提示问题的答案
    scores = models.IntegerField()  #用户积分

    def __unicode__(self):
    return self.user.username

    class Meta:
    db_table = 'account_profile'
复制代码
settings.py
AUTH_PROFILE_MODULE = 'account.Profile'     #app名称.类名

 

数据如下:


如果要获得User表某个用户的附加信息,则可执行如下:

复制代码
>>> from django.contrib.auth.models import User
>>> user = User.objects.get(id=1)
>>> user.get_profile()
<Profile: admin>
>>> s=user.get_profile()
>>> s.type
1L
>>> s.nickname
u'Boss'
>>> s.question
u'\u6211\u662f\u8c01'
复制代码

这样基本上算是一个解决方案了,但是细看就能发现缺陷了,一个是由于要多次请求另一个表(Profile),如果很多用户的话那么效率就很差;其次是如果要更新或插入附加信息,则必须更新Profile表,然后对应User表,麻烦又低效;三是当需要定义像friends这样的指向 'self' 的 ManyToManyField时该怎么办?定义在profile中就显得很不合理了,一个用户和另一个用户做朋友,而不是和ta的profile做朋友,更何况定义在profile中的话,当做friends的反向查询的时候,取得的queryset是profiles,这时候又想取得这些profile指向的user的信息的时候就得再做大量查询了。
但是这个附加信息表也很有用的,我们可以用在一些很少处理的场合,如附加个人基本信息,附加一些不常用的信息,尽量减少与其他表关联等。

三、不修改django源码也不增加profile表的扩展用户模型:

这个参考大神的方法(点击)

复制代码
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin

class ProfileBase(type):                    #对于传统类,他们的元类都是types.ClassType
    def __new__(cls,name,bases,attrs):      #带参数的构造器,__new__一般用于设置不变数据类型的子类
        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 MyProfile(ProfileUser):  
    real_name = models.CharField(max_length = 255)      #真实姓名
    level = models.IntegerField(default=0)              #级别 
    
复制代码

解析如下:(Python新式类这块)

上面的代码中定义了一个ProfileBase的元类,然后定义了一个作为Profile基类的Profile类,并且它的元类为ProfileBase。
元类的作用简单来讲,就是创建其他类的类。也就是元类的实例是普通类,而普通类的实例就是普通的实例。
如果你不理解这些也没关系,只要知道,上面的代码中,当解释器看到你在定义一个Profile类的子类,而Profile类的元类是ProfileBase,所以MyProfilede的元类也是ProfileBase,也就是在定义任何Profile的子类的时候,它就会执行元类ProfileBase中的new中代码,并且将正在定义的类的(名字,基类,类属性)作为参数传递给new
MyProfilede中的field采用了和普通Model相同的声明语法,所以这些field和那个自定义方法istodaybirthday会作为attrs中的属性传递过去,然后元类中将这些信息提取出来通过 User.addtoclass(objname, obj) 加入到User类中,addto_class也是普通Model中定义field时采用的方式,因为普通Model也有一个元类在做相似的事情,只不过这里定义的这个元类专门往User Model中加东西。
在添加完这些field和自定义方法后,前面识别出哪些属性是field,然后加入到UserAdmin.fieldsets中,那样在admin中就可以和其他User fields 一起编辑这些新加的field了。

如果你有其他app也想往User Model中加field或方法,都只要通过子类Profile类,然后使用声明语法进行定义即可,所有其他工作都有元类帮你完成。

通过这种方式添加到User Model中的field和方法的效果就和直接修改Django User Model的源码一样,只不过现在这些修改不用再像修改源码这么暴力了,而且想添的时候随便可以添。

需要注意的地方:

1、以上元类的定义包括MyProfile的定义,最好就和普通Model一样,放在app的models.py文件中, 以便django可以方便的碰到这些定义,否则django没看到这些代码也就没效果了,而且放在其他地方非常容易引起导入的依赖问题。

2、如果你已经通过syncdb同步了数据库,再使用此方法扩展用户模型,那么Django虽然知道你定义了这些field,但是再次运行syncdb时,不会再为这些field自动创建数据库字段,因为django的syncdb只负责创建新表的字段。这与你已经定义了一个Model并运行syncdb,然后往那个Model上又加了fields是同一种情况。

这种情况如果字段少可手动添加,字段多则推荐使用django 第三方app south,专门迁移数据。

效果如下:

参考:
1.非profile方式扩展Django User Model
2.djnaog user

下一步:

1、网站注册流程分析

2、一套完整的注册程序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值