我的Django项目的权限机制
本文主要介绍我在Django博客使用的权限设置,我的博客中几乎所有的权限设置都是我自己写的。这么做的原因是我在写Flask博客的时候,学到了一些这方面的知识,感觉很不错。因此就借鉴里面的逻辑,自己写了这方面的代码。
1、关于文章的访问权限。
我就从Model和View两层来说。
首先我在Model中定义一个字段,用来指示是否公开,是否允许别人可见: 源代码如下:
ACCESS = {
100:u'公开',
200:u'私人可见'
}
class Article(models.Model):
title = models.CharField(max_length=150,unique=True,verbose_name=u'标题')
alias = models.CharField(max_length=150,verbose_name=u'英文标题')
..........
..........
access = models.IntegerField(default=100,choices=ACCESS.items(),verbose_name=u'文章权限,公开或者私人可见')
access字段指定该字段是否公开。如果设为公开那么久所有人可以访问;如果设为私密,那么就不允许一些用户访问。那此时问题来了,该如何设置限制访问逻辑。我的代码如下,该方法也定义在Model中:
def can_access(self,user):
if self.access == 100:
return True
if self.access == 200:
if user is None:
return False
else:
return self.author.id == user.id or user.is_staff
上面的代码很简单,如果是私密的,只有文章作者或者管理员可以访问。
在View中的代码:
class ArticleDetailView(BaseMixin,DetailView):
queryset = Article.objects.filter(status=0)
slug_field = 'alias'
context_object_name = 'article'
template_name = 'article.html'
object = None
def get(self, request, *args, **kwargs):
alias = self.kwargs.get('slug')
try:
self.object = self.queryset.get(alias=alias)
except Article.DoesNotExist:
logger.error('article does not exsists')
#I should rewrite the 404 html later
return HttpResponseNotFound('<h1>Page not Found</h1>')
# add permission,if the article has set permission,the web will raise one exveption
if not self.object.can_access(request.user):
raise PermissionDenied
看这段代码最后面,如果can_acees方法返回False,就抛出一个禁止Django内置的禁止访问的异常,即返回403页面。
2、自定义视图权限装饰器
首先自己定义一个装饰器函数,用来修饰要设置权限的视图函数或类方法。 装饰器函数源代码:
from functools import wraps
from django.http import HttpResponse,HttpResponseRedirect,HttpResponseNotFound
def permission_forbidden(http_exception=403,next_url='/account/login/'):
"""
Usage:
@permission_forbidden(403)
def test(request):
return HttpResposne('hello world')
when decorated by permission_forbidden,if the user is not staff,
it will raise one PerissionDenied exception
:param http_exception:
:return:the return value of decorated function
"""
def decorator(func):
@wraps(func)
def wrapper(request,**kwargs):
if http_exception == 403:
if request.user.is_staff:
rv = func(request,**kwargs)
return rv
else:
raise PermissionDenied
elif http_exception == 401:
if not request.user.is_authenticated():
return HttpResponseRedirect(next_url)
rv = func(request,**kwargs)
return rv
return wrapper
return decorator
这是自己写的一个三层装饰器函数,代码很简单。如果http_exception为403,当不是管理员权限就抛出禁止访问异常;当http_exception为401,当用户没有登录就跳转到登录页面。
在View中:
@permission_forbidden(http_exception=401)
def TestForm(request):
if 'start' in request.GET and request.GET['start']:
return render_to_response('cost.html',{'current':'haibo','start':request.GET['start']})
else:
return render_to_response('cost.html',{'current':''})
这是一个用于测试的视图函数。
上面讲解了如何使用装饰器函数装饰视图函数,那么该如何装饰基于类的视图呢?
在Django中的官网上给出了答案。
最简单的方法是在URLconf 中装饰,装饰类的as_view方法:
如现在我在View中定义了一个基于类的视图:
class AuthorView(BaseMixin,ListView):
template_name = 'author_information.html'
context_object_name = 'categories'
def get_queryset(self):
categories = Category.available_categories()
return categories
def get_context_data(self,*args,**kwargs):
return super(AuthorView,self).get_context_data(**kwargs)
直接在URl中配置: url(r'^author/$', permission_forbidden(http_exception=403)(AuthorView.as_view()),name='blog_author'),
这样就可实现上面对视图函数相同的装饰。这个方法在每个实例的基础上运用装饰器。如果想让视图的每个实例都被装饰,你需要一种不同的方法。
下面是摘抄自官方文档:
类的方法和独立的函数不完全相同,所以你不可以直接将函数装饰器运用到方法上 —— 你首先需要将它转换成一个方法装饰器。method_decorator 装饰器将函数装饰器转换成方法装饰器,这样它就可以用于实例方法上。例如:
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
class ProtectedView(TemplateView):
template_name = 'secret.html'
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ProtectedView, self).dispatch(*args, **kwargs)
在这个例子中,ProtectedView 的每个实例都将有登录保护。
不过有一点要说明:我用这个方法测试没有成功!!!
上面说了这么多我自定义的权限设置,这里也说一下Django内置的权限系统。
详细的内容请参考下面的两个链接,一个是个人博客,一个是Django官方文档:
http://www.cnblogs.com/esperyong/archive/2012/12/20/2826690.html
http://python.usyiyi.cn/django/topics/auth/default.html
我这里只说两点:
一是分配权限的问题。
默认情况下,Django会为每一个Model自动生成一add,delete,change的权限,并保存在auth_permission数据表中。
我们要想把权限分配给用户,就要用User的user_permissions属性。
def user_gains_perms(request, user_id):
user = get_object_or_404(User, pk=user_id)
# any permission check will cache the current set of permissions
user.has_perm('myapp.change_bar')
permission = Permission.objects.get(codename='change_bar')
user.user_permissions.add(permission)
# Checking the cached permission set
user.has_perm('myapp.change_bar') # False
# Request new instance of User
# Be aware that user.refresh_from_db() won't clear the cache.
user = get_object_or_404(User, pk=user_id)
# Permission cache is repopulated from the database
user.has_perm('myapp.change_bar')
对于超级用户来讲,他拥有所有的权限,并不需要再做分配。
2、模板中的权限变量
在setting.py中,我们定义了:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR,'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
context_processor会自动添加一些上下文,当然,其中包括perms.源代码:
def auth(request):
"""
Returns context variables required by apps that use Django's authentication
system.
If there is no 'user' attribute in the request, uses AnonymousUser (from
django.contrib.auth).
"""
if hasattr(request, 'user'):
user = request.user
else:
from django.contrib.auth.models import AnonymousUser
user = AnonymousUser()
return {
'user': user,
'perms': PermWrapper(user),
}
它添加了,user和perms两个变量,perms指的是当前用户的所有权限。 这样,在模板中,就可以使用perms变量了。
微信分享/微信扫码阅读