context processor学习

来源:互联网 发布:蜜蜡和琥珀哪个贵 知乎 编辑:程序博客网 时间:2024/05/17 21:39

学习nitrate的源码时,在它的template里面看到很多参数能够自动传入模板内,估计是context processor的效果,所以现在来整体学习一下context processor。
在百度上搜索了以下文章,先看一下:http://www.cnblogs.com/btchenguang/archive/2012/09/01/2666763.html

这篇讲的是template的高阶用法,里面就有提到context processor

文章第一节复习了template的用法,没有问题,这个懂,无法就是一些模板函数以及变量的引用,就像文中说的,view.py使用模板的时候,是需要创建类似字典的context对象用作传参,譬如代码里面的done、nowtime都是需要传参的。

复习一下模板语言的用法
{# 模板tag的用法 #}
{% if done %}
Over
{% else %}
wait
{% endif %}
{# 模板变量的用法 #}
Now is {{ nowtime }}
在views.py中使用模板的时候:
1. 通过模板名,获得模板对象
2. 创建context对象,类似字典,用于像模板提供变量实际的值
3. 使用context对象进行模板的渲染,返回的是html网页的内容

文章然后介绍了使用requestcontext对上下文内容进行重用的方法,看了前面的介绍,对重用的概念不理解,继续往下看例子

使用RequestContext对上下文内容进行重用
当渲染一个模板的时候,我们通常使用的是django.template.Context的对象,
这里要介绍另外一个它的子类,django.template.RequestContext,
RequestContext提供了一种把不同的context内容中公共的部分提取出来的方法,
让context的内容重用。

例子1. Context版

from django.template import loader, Contextfrom django.http import HttpResponsedef view_1(request):    t = loader.get_template('template1.html')    c = Context({        'app': 'My app',        'user': request.user,        'ip_address': request.META['REMOTE_ADDR'],        'message': 'I am view 1.'    })    return HttpResponse(t.render(c))def view_2(request):    # ...    t = loader.get_template('template2.html')    c = Context({        'app': 'My app',        'user': request.user,        'ip_address': request.META['REMOTE_ADDR'],        'message': 'I am the second view.'    })    return HttpResponse(t.render(c))

这个例子能够理解,就是import context对象后,然后定义一个自己的context对象c,然后给c定义一些字典参数,如app、user、message等。然后文中最后一句话说两个view的context对象有些context内容是重复的,也就是说想将所有context内容打包放在一起,然后都传给template,你能用就用,不用也不用管,或者反过来,template在这个池子里面找要用的参数值。

例子2. 下面改写成RequestContext版

from django.template import loader, RequestContextfrom django.http import HttpResponse#使用context processro去提供一些context内容中公共的部分,也就是返回一个字典而已。def custom_proc(request):    "A context processor that provides 'app', 'user' and 'ip_address'."    return {        'app': 'My app',        'user': request.user,        'ip_address': request.META['REMOTE_ADDR']    }def view_1(request):    # ...    t = loader.get_template('template1.html')    # 创建方式不同,需要提供的参数有三个,request对象,字典类型,processors可选     c = RequestContext(request, {'message': 'I am view 1.'},            processors=[custom_proc])    return HttpResponse(t.render(c))def view_2(request):    # ...    t = loader.get_template('template2.html')    c = RequestContext(request, {'message': 'I am the second view.'},            processors=[custom_proc])    return HttpResponse(t.render(c))

可以看到所谓的context processors其实就是一个函数,参数为request,
返回一个字典类型。这就是它所做的所有的事。在这里custom_proc返回的
是包含共同的那三个参数的字典

RequestContext构造函数中的第三个参数processors是可选的,可以是
多个custom_proc函数的列表或是元组,在这里我们只传递了一个,可以为多个。

赞同文中说的话,这个context processors就是一个函数,接收request,返回公用的字典。而requestcontext就是比context多了一个request和processor,request应该是为了给后面的processors用的,这样processor就可以就根据传参request返回公用字典,requestcontext获得公用字典后,其实和context就没有什么区别了。

结合RequestContext使用render_to_response函数直接返回HttpResponse对象

return render_to_response('template2.html',        {'message': 'I am the second view.'},         context_instance=RequestContext(request, processors=[custom_proc]))

以上代码就可以一步到位。

用了这个render_to_response以后,就可以省却了loader.get_templatet.render(c)等等麻烦步骤,而且关键在于最后一个参数,可以通过context_instance来直接引用RequestContext里面返回的公用字典,特别注意,这里的RequestContext干脆没用第二个字典参数,直接用了processors。

但是又引入了另一个问题,在每次使用render_to_response函数时,都要向
RequestContext指定所需要的context processors,因为这个原因,Django又给
我们提供了全局的processors,它默认是被传递给RequestContext对象的,这个设置
对应的是D:\Python27\Lib\site-packages\django\conf\global_setings.py文件
TEMPLATE_CONTEXT_PROCESSORS属性,这样使用RequestContext的时候,就
不需要每次指定processors了。

是的,每次使用render_to_response函数不单要给给它一个processors,还要import RequestContext,然后又要赋值给contextinstance的确比较麻烦。按文中说的,Django给了一个全局的processors,默认会传给RequestContext,那就省了一些事,起码不用定义processor,然后render_to_response里面的context_instance可以直接等于RequestContext(request)了,省了那么一点事。

然后这个全局的processors应该是在setting里面的,而且默认生成的setting文件里面是没有这货的,必须自己手动加,上Django官网可以找到下面这堆鸡肠,Django1.8之后是这样的:

A tuple of callables that are used to populate the context in RequestContext. These callables take a request object as their argument and return a dictionary of items to be merged into the context.

TEMPLATES = [    {        'BACKEND': 'django.template.backends.django.DjangoTemplates',        'DIRS': [            # insert your TEMPLATE_DIRS here        ],        'APP_DIRS': True,        'OPTIONS': {            'context_processors': [                # Insert your TEMPLATE_CONTEXT_PROCESSORS here or use this                # list if you haven't customized them:                'django.contrib.auth.context_processors.auth',                'django.template.context_processors.debug',                'django.template.context_processors.i18n',                'django.template.context_processors.media',                'django.template.context_processors.static',                'django.template.context_processors.tz',                'django.contrib.messages.context_processors.messages',            ],        },    },]

而至于每个processors里面有什么东西,已经这个默认processors怎么被默认传入RequestConext,那就要继续看了。

然后打开第一个processor看看,就是那个auth:

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),    }

可以看到,获取request参数,判断是否存在user属性(用了hasattr函数),是就将user属性的值赋给user变量,否就import系统的AnonymousUser对象,然后将对象的返回值赋给user。最后返回两个值,一个user,前面已经赋值了;另外一个perms,这东西调用了一个PermWrapper函数,传参是user,这不知道是啥,先查一下。

查了Djangoproject网站,但是查不到,难道不是built-in的方法?再查查模块里面,应该有这个函数。

果然,在上面看到了这段:

class PermWrapper(object):    def __init__(self, user):        self.user = user    def __getitem__(self, app_label):        return PermLookupDict(self.user, app_label)    def __iter__(self):        # I am large, I contain multitudes.        raise TypeError("PermWrapper is not iterable.")    def __contains__(self, perm_name):        """        Lookup by "someapp" or "someapp.someperm" in perms.        """        if '.' not in perm_name:            # The name refers to module.            return bool(self[perm_name])        app_label, perm_name = perm_name.split('.', 1)        return self[app_label][perm_name]

分析一下这个类是干嘛的,嗯。。。

先初始化,然后将传参数赋给self.user,ok。
定义一个方法getitem,getitem方法是这样用的,以下两个语句是相等的,用字典式的索引时会自动调用gettiem方法:

PermWrapper['x']PermWrapper.__getitem__('x')

getitem方法获取了传进来的app_label,然后将self.userapp_label作为参数调用了另外一个函数PermLookupDict,这个函数是下面这样的:

class PermLookupDict(object):    def __init__(self, user, app_label):        self.user, self.app_label = user, app_label    def __repr__(self):        return str(self.user.get_all_permissions())    def __getitem__(self, perm_name):        return self.user.has_perm("%s.%s" % (self.app_label, perm_name))    def __iter__(self):        # To fix 'item in perms.someapp' and __getitem__ iteraction we need to        # define __iter__. See #18979 for details.        raise TypeError("PermLookupDict is not iterable.")    def __bool__(self):        return self.user.has_module_perms(self.app_label)    def __nonzero__(self):      # Python 2 compatibility        return type(self).__bool__(self)

看着跟PermWrapper有点像,一段一段来分析一下,好烦啊,尼玛。

    def __init__(self, user, app_label):        self.user, self.app_label = user, app_label

首先还是init,class被instance的时候就会自动运行,没问题的,但是,尼玛,有两个参数哦,一个user,一个app_label哦,分别赋值给了self.userself.app_label,变成了一个class里面的局部有效的参数,后面的方法就可以直接调用了,ok。

    def __repr__(self):        return str(self.user.get_all_permissions())

好,然后这货又定义了一个__repr__方法,__repr__方法是被repr(instance)的时候就会调用。然后看看它的返回值,user对象的get_all_permissions方法得到的返回值。但是这个方法我不知道是干嘛的,查这个网页,里面user对象的所有方法都有了:https://docs.djangoproject.com/en/dev/ref/contrib/auth/
然后我们看看方法介绍:

get_all_permissions(obj=None)¶
Returns a set of permission strings that the user has, both through group and user permissions.
If obj is passed in, only returns the permissions for this specific object.

按官方的意思就是返回user对象一系列的权限strings(字符串),包括组权限和用户权限的,一般是不用传参数的,如果你将一个对象作为参数传进去了,那就显示这个指定对象的权限咯。嗯,看是看懂了,不知道效果怎样,后面有机会要试试。

    def __getitem__(self, perm_name):        return self.user.has_perm("%s.%s" % (self.app_label, perm_name))

还是__getitem__嘛,就是获取perm_name然后将init里面获得的self.app_labelperm_name一起以一定的字符格式返回去,没啥的。

    def __iter__(self):        # To fix 'item in perms.someapp' and __getitem__ iteraction we need to        # define __iter__. See #18979 for details.        raise TypeError("PermLookupDict is not iterable.")

看来哥python没学好啊,尼玛,继续查,不过看样子就是迭代器了。

查了一下,是的,__iter__就是迭代器啦,iter(class object)的时候就会调用__iter__方法,一般是返回一个列表(或者tuple、Dict?按官方的话是:the argument must supply its own iterator, or be a sequence),而这里的话直接然后一个Error。

    def __bool__(self):        return self.user.has_module_perms(self.app_label)

__bool__求对象的布尔值,一般来说就是被bool(class object)的时候会调用了,然后引进了一个user的新方法has_module_perms(),官文如下:

>
has_module_perms(package_name)
Returns True if the user has any permissions in the given package (the Django app label). If the user is inactive, this method will always return False.

就是说看user里面有没有这个package(
说完PermLookupDict,回到我们原来的PermWrapper,在定义完gettiem方法以后继续定义了一个iter方法如下:

0 0
原创粉丝点击