基于Tornado的MTV脚手架
将常用到的功能和模块封装成一个MTV脚手架。主要加入了jinja2模板引擎(异步),aioredis,peewee和peewee_async,vuejs,elementui,axios。
目前异步数据库和异步缓存仅加入mysql和redis配置。
使用方法:
克隆到本地
git clone git@github.com:yuchanns/tweb.git
安装环境
在项目根目录执行
pip install -r requirements.txt
创建App
以下所有相对路径皆以项目根目录为起始。
以blog为例:
进入项目根目录,运行python manage.py --cmd=startapp --app=blog
在项目根目录下就会生成一个blog文件夹。
在./core/settings.py中,将blog/templates加入TEMPLATE_PATH:
# ./core/settings.py
# ...
TEMPLATE_PATH = (
os.path.join(BASE_DIR, 'blog/templates')
)
在./core/urls.py中,导入blog.urls.routes:
# ./core/urls.py
from blog.urls import routes as blog
routes = blog # + another_routes + ...
然后运行python manage.py --cmd=runserver --port=8080,访问localhost:8080/blog即可。
XSRF
manage.py提供一个生成cookie_secrete的方法,运行python manage.py --cmd=generate即可获得一串密钥,写入./core/settings.py的COOKIE_SECRET并开启XSRF_COOKIES = True即可使用Tornado自带的XSRF功能。
- 当你使用
form进行表单提交时
在模板文件的<form>表单中加入{{ xsrf_form_html }}即可。 - 当你使用
ajax进行表单提交时
在模板文件中引入./static/js/helpers.js。
在<script>标签中使用getCookie("_xsrf")获得XSRFToken,并在headers中添加X-XSRFToken字段:
注意:由于tornado自身的get_argument方法无法识别dataType=json的数据,所以XSRFToken一定要加在headers中。
<!DOCTYPE html>
<html>
<head>
<title>blog</title>
<script src="{{ static_url('js/helpers.js') }}"></script>
<script src="{{ static_url('js/axios.min.js') }}"></script>
</head>
<body>
<script>
function fetch() {
axios.post(url, data, {
headers: {
'X-XSRFToken': getCookie('_xsrf')
}
}).then(function(response) {
// do something
}).catch(function(err)) {
console.log(err)
}
}
</script>
</body>
</html>
当然你也可以通过{{ xsrf_token }}直接获得XSRFToken
关于对ajax请求数据的接收
由于tornado自身的get_argument方法无法识别dataType=json的数据,所以我在CoreHandler中简单封装了一个post_argument方法,用于接收ajax请求数据:
from core.handlers import CoreHandler
class Handler(CoreHandler):
def post(self):
value = self.post_argument('key', None)
关于jinja2和vuejs的Mustache语法冲突
三种解决方法:
- 更改
jinja2的变量引用符号
# .core/jinja2_env.py
# ...
async def render_template(self, template_name, **kwargs):
env = Environment(loader=FileSystemLoader(self.settings['template_path']), enable_async=True)
env.variable_start_string = '{[' # 更改变量引用符的开头
env.variable_end_string = ']}' # 更改变量引用符的结尾
# ...
- 更改
vuejs的变量引用符号
new Vue({
delimiters: ['{[', ']}'],
// ...
})
- 使用模板引擎的
{% raw %}转义功能(个人推荐)
<div id='app'>标签中的Mustache语法会被模板引擎忽视,而<script>中的将会被模板引擎处理。
<!DOCTYPE html>
<html>
<head>
<title>blog</title>
<script src="{{ static_url('js/vue.js') }}"></script>
</head>
<body>
{% raw %}
<div id='app'>
{{ greeting }}
</div>
{% endraw %}
<script>
new Vue({
el: '#app',
data: function() {
return {
greeting: '{{ greeting }}'
}
}
})
</script>
</body>
</html>
生成models
使用peewee的pwiz方法生成models.py之后,需要对该文件进行修改以支持异步:
# .core/models.py
from peewee import *
from peewee_async import Manager # 导入Manager
from core.core import database # 将database替换成从core.core导入
class UnknownField(object):
def __init__(self, *_, **__): pass
class BaseModel(Model):
object = Manager(database) # 新增object成员变量
atomic = database.atomic_async # 新增异步事务支持
class Meta:
database = database
# ...
关于redis缓存
如果在.core/settings.py中开启了redis,并且debug = False,将会自动开启页面缓存,过期时间5分钟。可在.core/handlers.py的prepare()中进行修改。
在继承了CoreHandler的Handler中使用缓存的方式:
from core.handlers import CoreHandler
class Handler(CoreHandler):
async def get(self):
await self.cache.set('key', 'value')
value = await self.cache.get('key')
关于路由
urls.py默认使用tornado.web.URLSpec,具有别名(alias)功能。在模板页面可使用{{ reverse_url('alias') }}对别名进行反向解析获得路由地址。
其他
关于各模块的更多使用,请参考开头列出的各自官方使用手册。






网友评论