django是基于python的web框架。现在比较流行,有时间具体写django的学习笔记。先直面今天的问题。继承重写django User类。

django自带User、Group和Permission类,分别对应用户、用户组、权限,可以完成登录、权限控制等功能。django虽然不错,但是实际的需求是多种多样的,许多人都需要实现自己特定的用户管理。就我来说,django的登录没问题,问题在于django的权限控制,它的权限控制是基于table的,是粗粒度的,而我做的项目需要更细粒度的权限控制。所以,我需要满足我需求的User、Group、Permission,但是django的User写的不错,直接重写工作量大也怪可惜了。所以我决定重写针对项目的CGroup和Perm(分别对应用户组和权限),继承django原有的User类,添加新的成员perm和cgroup,对应与CGroup和Perm的多对多关系。代码如下:

# coding: utf-8 
from django.db import models 
from django.forms import ModelForm 
from django.contrib.auth.models import User, UserManager  

# user is Inherited, perm and cgroup is rewriten 
# model
class Perm(models.Model):  
    name = models.CharField(u"名称", max_length=40, unique=True)     
    table = models.CharField(u"数据表", max_length=50)     
    describe = models.CharField(u"描述", max_length=100)     
    remark = models.CharField(u"备注", null=True, blank=True,max_length=100)          
    
    def __unicode__(self):         
        return self.name     
        
    class Meta:     
        ordering = ['name']  
        

class CGroup(models.Model): 
    name = models.CharField(u"名称", max_length=50,unique=True)     
    # level = models.IntegerField(u"等级", unique=True)     
    describe = models.CharField(u"描述", max_length=100)     
    perm = models.ManyToManyField(Perm, blank=True, verbose_name=u'权限')      
    
    def __unicode__(self):     
        return self.name     
        
    class Meta:      
        ordering = ['name']         
        # ordering = ['level']  

                
class User(User):     
    objects = UserManager()     
    perm = models.ManyToManyField(Perm, blank=True, verbose_name=u'权限')      
    cgroup = models.ManyToManyField(CGroup, blank=True, verbose_name=u'用户组')      
    
    def has_perm(self,permission):     
        if self.is_superuser == True:        
            return True          
            
        permlist = self.perm.all()         
        for perm in permlist:         
            if perm.name == permission:             
                return True          
                
        grouplist = self.cgroup.all()         
        for group in grouplist:        
            permlist = group.perm.all()             
            for perm in permlist:            
                if perm.name == permission:                 
                    return True                          
        
        return False      
        
    def get_all_permission(self):     
        permlist = self.perm.all()         

        if self.is_superuser == True:           
            permlist = Perm.objects.all()        
            return permlist         
            
        else:         
            perm_set = set(permlist)             
            grouplist = user.cgroup.all()             
            for group in grouplist:             
                permlist_tmp = group.perm.all()              
                perm_set_tmp = set(permlist_tmp)             
                perm_set = perm_set.union(perm_set_tmp)                  
                permlist = list(perm_set)             
                permlist.sort()         
                return permlist  
                
      
# form          
class UserForm(ModelForm):  
    class Meta:     
        model = User         
        fields = ('username', 'first_name', 'last_name', 'password', 'is_active', 'is_superuser', 'last_login', 'date_joined', 'cgroup') 
        
class PermForm(ModelForm):     
    class Meta:         
        model = Perm 
        
class CGroupForm(ModelForm):     
    class Meta:     
        model = CGroup # over #

关于Perm和CGroup,我是根据我的项目写的,大家根据自己的需求具体实现。重点是User类,其中包括objects = UserManager() ,这样User就可以使用原User的很多方法。此外,针对新的Perm和CGroup,新写了两个方法,has_perm、get_all_permission,分别用来判断用户有无特定权限和获得用户权限列表。

到这里好像一切都结束了,但其实还没有。还有一个问题,我们利用request.user获取的user是一个原有的User的实例,而不是重载的User实例,所以无法获取扩展的字段与方法。哈欠涟涟(http://plq168.blog.163.com/blog/static/531014622010927105232669/)发现了这个问题。为了解决这个问题,他写了自己的登录后台,获取CustomUser(重载后的User)的实例而不是原来User的实例。

我不清楚哈欠涟涟还有没有其他更进一步的需求,不过就我的需求以及他的博文中描述的问题来看,我认为没有这个必要。重写后台较为麻烦。我的做法是,通过request.user.name,获取用户的name,在通过get_object_or_404方法,获取重载后的User。由于User的name的唯一的,所以这种方法是可行的,我采取这种方法做的工程测试也是正常的。

username = request.user.username 
user = get_object_or_404(User,username=username)

解决了这个问题后,还有一些细节问题,比如Perm添加。django的Permission可以根据数据表自动添加,所以重写User、Perm、CGroup后,也要注意这个问题。

最后,上一张用户编辑界面的截图(部分)。