9-tornado-Template优化方法、个人信息案例、tornado中ORM的使用(peewee的使用、peewee_async)、WTForms的使用
在很多情况下,前端模板中在很多页面有都重复的内容可以使用,比如页头、页尾、甚至中间的内容都有可能重复。
这时,为了提高开发效率,我们就可以考虑在共同的部分提取出来,
主要方法有如下:
1. 模板继承
2. UI模板
1 模板继承
common/base.html
{% block content %}
{% end %}
shop2.html
{% extends 'common/base.html'%}
{% block content %}
{% end %}
2 UI模板
Tornado中支持累死Vue中的组件功能,就是也公共的内容提取出来当成组件。
具体的使用方式是用tornado.web.UIModule
class Entry(tornado.web.UIModule):
def render(self, entry, show_comments=False):
return self.render_string(
"module-entry.html", entry=entry, show_comments=show_comments)
settings = {
"Entry": Entry,
}
3 个人信息案例
环境搭建
在网站中,少了数据CRUD的操作,但在tornado中,我们知道若想操作的话尽量使用异步的操作,这样效率才会高。
那应该如何编写呢,下面我们来一起做下吧。首先我们先一起搭建下环境。
具体的操作如下:
前端
copy 原生素材
html —> templates
css —> static/css
js —> static/js
img —> static/img
到项目中
后端
from tornado import web, ioloop
from tornado.web import StaticFileHandler
class IndexHandler(web.RequestHandler):
def get(self):
self.render('personal.html')
import os
base_url = os.path.dirname(os.path.abspath(__file__))
settings={
'static_path':os.path.join(base_url,'static'),
'static_url_prefix':'/static/',
'template_path':os.path.join(base_url,'templates')
}
if __name__ == "__main__":
app = web.Application([
web.URLSpec('/',IndexHandler,name='index'),
],
debug=True,
**settings
)
app.listen(8000)
ioloop.IOLoop.current().start()
MySQL
CREATE TABLE `tornado_demo1`.`t_user` (
`id` int(10) NOT NULL,
`username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`nick_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`email` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`passsword` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`phone` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`language` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
熟悉Mysql操作
之前我们了解过pymysql操作数据库,但那是同步的。
我们需要使用一个异步的框架,在这,我们推荐使用aiomysql,它的底层大量的使用了pymysql,只是它通过asyncio实现的访问数据库
安装方式
pip install aiomysql
使用方式
import asyncio
import aiomysql
async def test_example(loop):
pool = await aiomysql.create_pool(host='127.0.0.1', port=3306,
user='root', password='root',
db='mysql', loop=loop)
async with pool.acquire() as conn:
async with conn.cursor() as cur:
await cur.execute("SELECT 42;")
print(cur.description)
(r,) = await cur.fetchone()
assert r == 42
pool.close()
await pool.wait_closed()
loop = asyncio.get_event_loop()
loop.run_until_complete(test_example(loop))
4 ORM的使用
4.1 peewee的使用
官网链接
Peewee是一个简单而小型的ORM。使其易于学习且使用直观。
- 一个小的,表达力很强的ORM
- python 2.7+和3.4+(使用3.11开发)
- 支持sqlite,mysql,postgresql和cockroachdb
- 大量的扩展
安装
pip install peewee
tip 需要安装pymysql,不然会报错
peewee.ImproperlyConfigured: MySQL driver not installed!
### 使用方式
#### 创建表
from peewee import *
db = MySQLDatabase('message', host="127.0.0.1", port=3306, user="root", password="root")
class BaseModel(Model):
create_time = DateTimeField(default=datetime.now, verbose_name="添加时间")
class Music(BaseModel):
name = CharField(index=True)
singer = CharField(max_length=11, verbose_name="演唱者")
duration = CharField(max_length=11, verbose_name="时长")
_type = CharField(max_length =11,verbose_name="音乐类型")
commany = ForeignKeyField(Commany,verbose_name="版权",backref = "musics")
class Meta:
database = db
table_name = 't_music'
class Commany(BaseModel):
name = CharField()
address = CharField()
year - CharField()
db.create_tables([Cmmany,Music])
增加数据
c = Commany()
c.name = '北京尚学堂'
c.full_name = '北京'
c.year = 2003
c.save()
print(c.id)
m = Music(name='中国', singer='中国1', duration='1:50',
_type='流行', commany=c.id)
m.save()
案例数据
commanys = [
{
'name': '滚石唱片',
'full_name': '滚石国际音乐股份有限公司',
'year': 1980
},
{
'name': '华谊兄弟',
'full_name': '华谊兄弟传媒股份有限公司',
'year': 1994
},
{
'name': '海蝶音乐',
'full_name': '北京太合音乐文化发展有限公司',
'year': 1986
},
]
musics = [
{
"name": "你是我左边的风景",
"singer": "林志炫",
"duration": "2:20",
"_type": "摇滚",
"commany": 1
},
{
"name": "把你揉碎捏成苹果",
"singer": "薛之谦",
"duration": "2:10",
"_type": "摇滚",
"commany": 3
},
{
"name": "游戏人间",
"singer": "童安格",
"duration": "1:20",
"_type": "流行",
"commany": 2
},
{
"name": "故乡的云",
"singer": "费翔",
"duration": "2:40",
"_type": "摇滚",
"commany": 1
},
{
"name": "诺言Jason",
"singer": "青城山下白素贞",
"duration": "1:10",
"_type": "古典",
"commany": 3
},
{
"name": "勇敢的幸福",
"singer": "Sweety",
"duration": "1:23",
"_type": "古典",
"commany": 2
},
{
"name": "爱丫爱丫",
"singer": "By2",
"duration": "2:22",
"_type": "流行",
"commany": 1
},
{
"name": "我也曾像你一样",
"singer": "马天宇",
"duration": "2:28",
"_type": "流行",
"commany": 1
}
]
#### 查询数据
The following types of comparisons are supported by peewee:
| Comparison | Meaning |
| ---------- | --------------------------------------- |
| `==` | x equals y |
| `<` | x is less than y |
| `<=` | x is less than or equal to y |
| `>` | x is greater than y |
| `>=` | x is greater than or equal to y |
| `!=` | x is not equal to y |
| `<<` | x IN y, where y is a list or query |
| `>>` | x IS y, where y is None/NULL |
| `%` | x LIKE y where y may contain wildcards |
| `**` | x ILIKE y where y may contain wildcards |
| `^` | x XOR y |
| `~` | Unary negation (e.g., NOT x) |
Because I ran out of operators to override, there are some additional query operations available as methods:
| Method | Meaning |
| --------------------- | ----------------------------------------------- |
| `.in_(value)` | IN lookup (identical to `<<`). |
| `.not_in(value)` | NOT IN lookup. |
| `.is_null(is_null)` | IS NULL or IS NOT NULL. Accepts boolean param. |
| `.contains(substr)` | Wild-card search for substring. |
| `.startswith(prefix)` | Search for values beginning with `prefix`. |
| `.endswith(suffix)` | Search for values ending with `suffix`. |
| `.between(low, high)` | Search for values between `low` and `high`. |
| `.regexp(exp)` | Regular expression match (case-sensitive). |
| `.iregexp(exp)` | Regular expression match (case-insensitive). |
| `.bin_and(value)` | Binary AND. |
| `.bin_or(value)` | Binary OR. |
| `.concat(other)` | Concatenate two strings or objects using `||`. |
| `.distinct()` | Mark column for DISTINCT selection. |
| `.collate(collation)` | Specify column with the given collation. |
| `.cast(type)` | Cast the value of the column to the given type. |
To combine clauses using logical operators, use:
| Operator | Meaning | Example |
| ---------- | -------------------- | ---------------------------------------------------- |
| `&` | AND | `(User.is_active == True) & (User.is_admin == True)` |
| `\|` (pipe) | OR | `(User.is_admin) \| (User.is_superuser)` |
| `~` | NOT (unary negation) | `~(User.username.contains('admin'))` |
Here is how you might use some of these query operators:
# Find the user whose username is "charlie".
User.select().where(User.username == 'charlie')
# Find the users whose username is in [charlie, huey, mickey]
User.select().where(User.username.in_(['charlie', 'huey', 'mickey']))
Employee.select().where(Employee.salary.between(50000, 60000))
Employee.select().where(Employee.name.startswith('C'))
Blog.select().where(Blog.title.contains(search_string))
更新数据
def update_music():
# 方法1:获取数据,在对象上直接修改
# m = Music.get_by_id(1)
# m.singer='林志炫666'
# m.save()
# 方法2:利用类方法
# update table set ??? where ???
Music.update(singer='林志炫').where(Music.id == 1).execute()
删除数据
def delete_music():
# 方法1:直接删除对象
# m = Music.get_by_id(8)
# m.delete_instance()
# 方法2:利用类方法
Music.delete().where(Music.id >5).execute()
4.2 peewee_async
peewee_async
安装
PostgreSQL
pip install --pre peewee-async
pip install aiopg
MySQL
pip install --pre peewee-async
pip install aiomysql
案例
import asyncio
import peewee
import peewee_async
# Nothing special, just define model and database:
database = peewee_async.PostgresqlDatabase(
database='db_name',
user='user',
host='127.0.0.1',
port='5432',
password='password'
)
class TestModel(peewee.Model):
text = peewee.CharField()
class Meta:
database = database
# Look, sync code is working!
TestModel.create_table(True)
TestModel.create(text="Yo, I can do it sync!")
database.close()
# Create async models manager:
objects = peewee_async.Manager(database)
# No need for sync anymore!
database.set_allow_sync(False)
async def handler():
await objects.create(TestModel, text="Not bad. Watch this, I'm async!")
all_objects = await objects.execute(TestModel.select())
for obj in all_objects:
print(obj.text)
loop = asyncio.get_event_loop()
loop.run_until_complete(handler())
loop.close()
# Clean up, can do it sync again:
with objects.allow_sync():
TestModel.drop_table(True)
# Expected output:
# Yo, I can do it sync!
# Not bad. Watch this, I'm async!
5 WTForms的使用
简介
WTForms是用于Python Web开发的灵活的表单验证和呈现库。它可以与您选择的任何Web框架和模板引擎一起使用。
WTForms文档
WTForms_Tornado
安装
pip install wtforms-tornado
案例
import tornado.ioloop
import tornado.web
from wtforms.fields import IntegerField
from wtforms.validators import Required
from wtforms_tornado import Form
class SumForm(Form):
a = IntegerField(validators=[Required()])
b = IntegerField(validators=[Required()])
class SumHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def post(self):
form = SumForm(self.request.arguments)
if form.validate():
self.write(str(form.data['a'] + form.data['b']))
else:
self.set_status(400)
self.write("" % form.errors)
application = tornado.web.Application([
(r"/", SumHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
表单验证
wftform.py
from wtforms.fields import IntegerField,StringField
from wtforms_tornado import Form
from wtforms.validators import DataRequired, Length
class UserForm(Form):
id = IntegerField('ID')
username = StringField('用户名',validators=[DataRequired(message='请输入用户名'),Length(min=3,max=8,message='请输入3-8长度的用户名')])
nick_name = StringField('昵称')
email = StringField('Email')
password = StringField('密码')
phone = StringField('手机号')
language = StringField('语言')
handler.py
forms = UserForm(self.request.arguments)
if forms.validate():
del forms.data['id']
await objs.create(User,**forms.data)
self.render('personal26.html',user_form = forms)
else:
self.render('personal26.html',user_form = forms)
HTML生成
{% autoescape None %}
{% for field in user_form %}
{% if field.label.text == "ID"%}
<div class="form-group">
{{ field(class_="au-input au-input--full",placeholder=field.label.text)}}
</div>
{% else %}
<div class="form-group">
{{ field.label }}
{{ field(class_="au-input au-input--full",placeholder=field.label.text)}}
{% if field.errors %}
{{field.errors}}
{% end %}
</div>
{% end %}
{% end %}