基于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') }}
对别名进行反向解析获得路由地址。
其他
关于各模块的更多使用,请参考开头列出的各自官方使用手册。
网友评论