python---redis缓存页面前戏之剖析render源码

Posted 山上有风景

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python---redis缓存页面前戏之剖析render源码相关的知识,希望对你有一定的参考价值。

1.初始代码:

def get(self, *args, **kwargs):
    import time
    tm = time.time()
    self.render(home/index.html, time=tm)

2.代码追踪render:

    # 第一次执行get方法时,通过render方法已经生成了数据,具体存在地方,参见源码
    def render(self, template_name, **kwargs):
        """Renders the template with the given arguments as the response."""
        if self._finished:
            raise RuntimeError("Cannot render() after finish()")
        html = self.render_string(template_name, **kwargs)

        # Insert the additional JS and CSS added by the modules on the page
        js_embed = []
        js_files = []
        css_embed = []
        css_files = []
        html_heads = []
        html_bodies = []
        for module in getattr(self, "_active_modules", {}).values():
            embed_part = module.embedded_javascript()
            if embed_part:
                js_embed.append(utf8(embed_part))
            file_part = module.javascript_files()
            if file_part:
                if isinstance(file_part, (unicode_type, bytes)):
                    js_files.append(file_part)
                else:
                    js_files.extend(file_part)
            embed_part = module.embedded_css()
            if embed_part:
                css_embed.append(utf8(embed_part))
            file_part = module.css_files()
            if file_part:
                if isinstance(file_part, (unicode_type, bytes)):
                    css_files.append(file_part)
                else:
                    css_files.extend(file_part)
            head_part = module.html_head()
            if head_part:
                html_heads.append(utf8(head_part))
            body_part = module.html_body()
            if body_part:
                html_bodies.append(utf8(body_part))

        if js_files:
            # Maintain order of JavaScript files given by modules
            js = self.render_linked_js(js_files)
            sloc = html.rindex(b</body>)
            html = html[:sloc] + utf8(js) + b\n + html[sloc:]
        if js_embed:
            js = self.render_embed_js(js_embed)
            sloc = html.rindex(b</body>)
            html = html[:sloc] + js + b\n + html[sloc:]
        if css_files:
            css = self.render_linked_css(css_files)
            hloc = html.index(b</head>)
            html = html[:hloc] + utf8(css) + b\n + html[hloc:]
        if css_embed:
            css = self.render_embed_css(css_embed)
            hloc = html.index(b</head>)
            html = html[:hloc] + css + b\n + html[hloc:]
        if html_heads:
            hloc = html.index(b</head>)
            html = html[:hloc] + b‘‘.join(html_heads) + b\n + html[hloc:]
        if html_bodies:
            hloc = html.index(b</body>)
            html = html[:hloc] + b‘‘.join(html_bodies) + b\n + html[hloc:]
        self.finish(html)

数据先是保存在html局部变量中,传入finish中作为参数处理

3.追踪finish代码:

    def finish(self, chunk=None):
        """Finishes this response, ending the HTTP request."""
        if self._finished:
            raise RuntimeError("finish() called twice")

        if chunk is not None:
            self.write(chunk)

        # Automatically support ETags and add the Content-Length header if
        # we have not flushed any content yet.
        if not self._headers_written:
            if (self._status_code == 200 and
                    self.request.method in ("GET", "HEAD") and
                    "Etag" not in self._headers):
                self.set_etag_header()
                if self.check_etag_header():
                    self._write_buffer = []
                    self.set_status(304)
            if (self._status_code in (204, 304) or
                    (self._status_code >= 100 and self._status_code < 200)):
                assert not self._write_buffer, "Cannot send body with %s" % self._status_code
                self._clear_headers_for_304()
            elif "Content-Length" not in self._headers:
                content_length = sum(len(part) for part in self._write_buffer)
                self.set_header("Content-Length", content_length)

        if hasattr(self.request, "connection"):
            # Now that the request is finished, clear the callback we
            # set on the HTTPConnection (which would otherwise prevent the
            # garbage collection of the RequestHandler when there
            # are keepalive connections)
            self.request.connection.set_close_callback(None)

        self.flush(include_footers=True)
        self.request.finish()
        self._log()
        self._finished = True
        self.on_finish()
        self._break_cycles()

数据chunk(即是传入的html变量)在write方法中又进行处理

4.追踪write方法:

    def write(self, chunk):
        """Writes the given chunk to the output buffer.

        To write the output to the network, use the flush() method below.

        If the given chunk is a dictionary, we write it as JSON and set
        the Content-Type of the response to be ``application/json``.
        (if you want to send JSON as a different ``Content-Type``, call
        set_header *after* calling write()).

        Note that lists are not converted to JSON because of a potential
        cross-site security vulnerability.  All JSON output should be
        wrapped in a dictionary.  More details at
        http://haacked.com/archive/2009/06/25/json-hijacking.aspx/ and
        https://github.com/facebook/tornado/issues/1009
        """
        if self._finished:
            raise RuntimeError("Cannot write() after finish()")
        if not isinstance(chunk, (bytes, unicode_type, dict)):
            message = "write() only accepts bytes, unicode, and dict objects"
            if isinstance(chunk, list):
                message += ". Lists not accepted for security reasons; see " +                            "http://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler.write"
            raise TypeError(message)
        if isinstance(chunk, dict):
            chunk = escape.json_encode(chunk)
            self.set_header("Content-Type", "application/json; charset=UTF-8")
        chunk = utf8(chunk)
        self._write_buffer.append(chunk)

发现数据存放在类成员变量中_write_buffer

若是想在自定义函数中直接输出该数据,为空

def get(self, *args, **kwargs):
    import time
    tm = time.time()
    self.render(home/index.html, time=tm)
    print(self._write_buffer, len(self._write_buffer))  # 为空

因为在finish方法中蓝色部分函数flush中对于数据进行了下一步处理:

5.追踪flush方法(部分源码):

    但是在执行finish时,先要执行flush(部分源码)
    def flush(self, include_footers=False, callback=None):
        """Flushes the current output buffer to the network.

        The ``callback`` argument, if given, can be used for flow control:
        it will be run when all flushed data has been written to the socket.
        Note that only one flush callback can be outstanding at a time;
        if another flush occurs before the previous flushs callback
        has been run, the previous callback will be discarded.

        .. versionchanged:: 4.0
           Now returns a `.Future` if no callback is given.
        """
        chunk = b"".join(self._write_buffer)
        self._write_buffer = []   #将列表置为空

所以要想获取数据:需要进行其他处理,例如:

(1)修改源码,保存数据

(2)重写父类函数flush,将数据提前截取保存在新的变量中

为了尽量不修改源码,可以选择第二种方法

6.重写父类函数flush

class IndexHandler(tornado.web.RequestHandler):
    def flush(self, include_footers=False, callback=None):
        self._data_html = self._write_buffer
        super(IndexHandler,self).flush(include_footers,callback)

将数据保存在自己定义的数据_data_html 中

使用时可以直接获取数据

    def get(self, *args, **kwargs):
        import time
        tm = time.time()
        self.render(home/index.html,time=tm)
        print(self._data_html)

 

以上是关于python---redis缓存页面前戏之剖析render源码的主要内容,如果未能解决你的问题,请参考以下文章

stark组件前戏之django路由分发的本质include

rest_framake之视图

第七节:框架搭建之页面静态化的剖析

表与表之间建立关系

第四篇:白话tornado源码之褪去模板外衣的前戏

第四篇:白话tornado源码之褪去模板外衣的前戏