因为用到 vue.js, 然后 UI module 里面定义了一些 vue.js 的 Directives, 必须要在 body 以前将 Directives 定义出来.
但是 tornado 的 UI Module 默认是将 js append 到</body> 前的, 这时候为时已晚了.
怎么办呢!
为了不修改 tornador 的代码,只有继承并重载了.
js 的处理是在web.py的render方法里面来做的, 是个非常大的方法, 这里并没有做拆分.我只有全部 copy 过来了
这是最终的实现版本
class BaseHandler(RequestHandler):
'''
create by bigzhu at 15/01/29 22:53:07 自定义一些基础的方法
设置 pg
设定 current_user 为 cookie user_id
modify by bigzhu at 15/01/30 09:59:46 直接返回 user_info
modify by bigzhu at 15/01/30 10:32:37 默认返回 user_info 的拆离出去
modify by bigzhu at 15/02/21 00:41:23 修改 js_embed 的位置到 </head> 前
'''
def initialize(self):
self.pg = self.settings['pg']
def get_current_user(self):
return self.get_secure_cookie("user_id")
def render(self, template_name, **kwargs):
"""Renders the template with the given arguments as the response.
create by bigzhu at 15/02/21 01:50:52 就是为了把embedded_javascript 的位置换一下
"""
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_type)):
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_type)):
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))
def is_absolute(path):
return any(path.startswith(x) for x in ["/", "http:", "https:"])
if js_files:
# Maintain order of JavaScript files given by modules
paths = []
unique_paths = set()
for path in js_files:
if not is_absolute(path):
path = self.static_url(path)
if path not in unique_paths:
paths.append(path)
unique_paths.add(path)
js = ''.join('<script src="' + escape.xhtml_escape(p) +
'" type="text/javascript"></script>'
for p in paths)
sloc = html.rindex(b'</body>')
html = html[:sloc] + utf8(js) + b'\n' + html[sloc:]
if js_embed:
js = b'<script type="text/javascript">\n//<![CDATA[\n' + \
b'\n'.join(js_embed) + b'\n//]]>\n</script>'
sloc = html.rindex(b'</head>')
html = html[:sloc] + js + b'\n' + html[sloc:]
if css_files:
paths = []
unique_paths = set()
for path in css_files:
if not is_absolute(path):
path = self.static_url(path)
if path not in unique_paths:
paths.append(path)
unique_paths.add(path)
css = ''.join('<link href="' + escape.xhtml_escape(p) + '" '
'type="text/css" rel="stylesheet"/>'
for p in paths)
hloc = html.index(b'</head>')
html = html[:hloc] + utf8(css) + b'\n' + html[hloc:]
if css_embed:
css = b'<style type="text/css">\n' + b'\n'.join(css_embed) + \
b'\n</style>'
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)
def render_string(self, template_name, **kwargs):
"""Generate the given template with the given arguments.
We return the generated byte string (in utf8). To generate and
write a template as a response, use render() above.
create by bigzhu at 15/02/21 01:49:51 为了设定 _getframe,也得把这个方法重载一遍.否则 template 路径会按 bz 的来找
"""
# If no template_path is specified, use the path of the calling file
template_path = self.get_template_path()
if not template_path:
frame = sys._getframe(1)
web_file = frame.f_code.co_filename
while frame.f_code.co_filename == web_file:
frame = frame.f_back
template_path = os.path.dirname(frame.f_code.co_filename)
with RequestHandler._template_loader_lock:
if template_path not in RequestHandler._template_loaders:
loader = self.create_template_loader(template_path)
RequestHandler._template_loaders[template_path] = loader
else:
loader = RequestHandler._template_loaders[template_path]
t = loader.load(template_name)
namespace = self.get_template_namespace()
namespace.update(kwargs)
return t.generate(**namespace)
过程很纠结和复杂,慢慢来讲吧.
背景
因为我的BaseHandler并没有和具体项目放在一起,而是抽离出来作为公用的方法,加入到PYTHONPATH里面来使用了
重载 render
当我将render重载进来,并且简单的将里面的</body>修改为</head>
if js_embed:
js = b'<script type="text/javascript">\n//<![CDATA[\n' + \
b'\n'.join(js_embed) + b'\n//]]>\n</script>'
sloc = html.rindex(b'</head>')
html = html[:sloc] + js + b'\n' + html[sloc:]
然后报错了!并不是因为这个修改报错,而是找不到模板报错:
Traceback (most recent call last):
File "/Library/Python/2.7/site-packages/tornado/web.py", line 1332, in _execute
result = method(*self.path_args, **self.path_kwargs)
File "yemai.py", line 287, in get
self.render(tornado_bz.getTName(self, 'sites'), sites=sites)
File "/Users/bigzhu/Dropbox/lib/python_lib_bz/tornado_bz.py", line 44, in render
html = self.render_string(template_name, **kwargs)
File "/Library/Python/2.7/site-packages/tornado/web.py", line 769, in render_string
t = loader.load(template_name)
File "/Library/Python/2.7/site-packages/tornado/template.py", line 343, in load
self.templates[name] = self._create_template(name)
File "/Library/Python/2.7/site-packages/tornado/template.py", line 370, in _create_template
with open(path, "rb") as f:
IOError: [Errno 2] No such file or directory: '/Users/bigzhu/Dropbox/lib/python_lib_bz/template/sites.html'
因为重载了,导致并没有像以前一样得到的是template/sites.html从而根据不同项目能找到模板,反而定位到了我定义的公用的方法的路径里面.
简直??百思不得其解.
解决模板问题
仔细研读.模板是在render_string里面来组合的.
以外的发现setting可以设置template_path参数,妄图设置了来解决这个问题.于是 Module 的模板找不到了.
仔细思索了一下,需要实现以下功能
对于继承BaseHandler实现的方法,用这个项目下的路径
对于 module, 又得用绝对路径
发现使用奇怪的sys._getframe来取相对路径的,项目用的时候,取到的是'',空的,于是正常了.
原来是用栈的级别来解决这个问题的, 我拿出来重载了,层级不对,导致路径不对了
继续重载render_string,把sys._getframe(0)改为sys._getframe(1),搞定,一切正常!