Python 网站开发(1) – 搭建开发环境
用pip安装网站开发所需要的第三方库:
- 异步框架的 aiohttp
- 前端模板引擎 jinja2
- 数据库 MySQL的Python异步驱动程序 aiomysql (需要先下载安装最新版的 MySQL, 选择免费的 MySQL Community Server 下载安装就好)
- 轻量级标记语言Markdown, 将文本转换为有效的HTML
$ pip3 install aiohttp jinja2 aiomysql markdown
构建项目结构
选择一个工作目录,建立如下的目录结构:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15awesome-website/ <-- 根目录
|
+- backup/ <-- 备份目录
|
+- conf/ <-- 配置文件
|
+- dist/ <-- 打包目录
|
+- www/ <-- Web目录,存放.py文件
| |
| +- static/ <-- 存放静态文件
| |
| +- templates/ <-- 存放模板文件
|
+- LICENSE <-- 代码LICENSE
创建好项目的目录结构后,建议同时建立git仓库并同步到GitHub, 保证代码储存及修改的安全。
Python 网站开发(2) – 编写网站骨架
编写网站骨架
为了搭建一个高效的网站,网站的IO处理要建立在asyncio(异步io)的基础上, 我们可以用 aiohttp 写一个基本的服务器应用 app.py 存放在www目录:
1 | import logging; logging.basicConfig(level=logging.INFO) |
在www目录下运行这个 app.py, 服务器将在9000端口持续监听 http 请求,并异步对首页 / 进行响应:1
2
3$ python3 app.py
======== Running on http://127.0.0.1:9000 ========
(Press CTRL+C to quit)
打开浏览器输入地址 http://127.0.0.1:9000 进行测试,如果返回我们设定好的Awesome Website字符串,就说明我们网站服务器应用的框架已经搭好了。
Python 网站开发(3) – 编写ORM
编写ORM*
对象关系映射 (Object Relational Mapping, 简称ORM) 模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。换句话说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。
在一个网站中,所有的数据(包括用户,日志,评论等)都存储在数据库中。我们的网站awesome-website选择用MySQL作为数据库。访问数据库需要创建数据库连接,游标对象,执行SQL语句,处理异常,清理资源。这些访问数据库的代码如果分散到每个函数中去,十分难以维护,效率低下不利于复用。因此,我们将常用的MySQL数据库操作用函数封装起来,便于网站调用。
由于我们的网站基于异步编程,系统的每一层都必须是异步。aiomysql为MySQL数据库提供了异步IO的驱动。
创建连接池
我们需要创建一个全局的连接池,每个HTTP请求都可以从连接池中直接获取数据库连接。使用连接池的好处是不必频繁地打开和关闭数据库连接,而是能复用就尽量复用。
连接池由全局变量__pool存储,缺省情况下将编码设置为utf8,自动提交事务。在www目录下新建 orm.py加入以下代码:
1 | import asyncio, logging, aiomysql |
Select
要执行SELECT语句,我们用select函数执行,需要传入SQL语句和SQL参数。缀加以下代码至orm.py:
1 | async def select(sql, args, size=None): |
SQL语句的占位符是?,而MySQL的占位符是%s,select()函数在内部自动替换。注意要始终坚持使用带参数的SQL,而不是自己拼接SQL字符串,这样可以防止SQL注入攻击。
Insert, Update, Delete
要执行INSERT、UPDATE、DELETE语句,可以定义一个通用的execute()函数,因为这3种SQL的执行都需要相同的参数,以及返回一个整数表示影响的行数。缀加以下代码至orm.py:
1 | async def execute(sql, args): |
execute()函数和select()函数所不同的是,cursor对象不返回结果集,而是通过rowcount返回结果数。
ORM
首先要定义的是所有ORM映射的基类Model。缀加以下代码至orm.py:
1 | class Model(dict, metaclass=ModelMetaclass): |
以及Field和各种Field子类。缀加以下代码至orm.py:
1 | class Field(object): |
注意到Model只是一个基类,要将具体的子类如User的映射信息读取出来需要通过metaclass:ModelMetaclass。 这样,任何继承自Model的类(比如User),会自动通过ModelMetaclass扫描映射关系,并存储到自身的类属性如table、mappings中。缀加以下代码至Model代码之前:
1 | def create_args_string(num): |
然后,我们往Model类添加class方法,就可以让所有子类调用class方法:
1 | class Model(dict): |
往Model类添加实例方法,就可以让所有子类调用实例方法:
1 | class Model(dict): |
Python 网站开发(4) – 编写Model
编写Model
orm.py编写完成后,就可以把网站应用需要的三个表(user, blog, comment)用Model表示出来。在www目录下,新建models.py:
1 | import time, uuid |
初始化数据库表
由于网站表的数量较少,可以手动创建SQL脚本schema.sql到根目录:
1 | -- schema.sql |
把SQL脚本 schema.sql放到MySQL命令行里执行,就完成了数据库表的初始化:
$ mysql -u root -p < schema.sql
然后我们可以编写数据访问代码test.py测试一下。如新建一个User的对象:1
2
3
4
5
6
7
8
9
10
11
12import orm
import asyncio
from models import User, Blog, Comment
async def test(loop):
await orm.create_pool(loop=loop, user='root', password='root', db='awesome')
u = User(name='Test', email='test@qq.com', passwd='1234567890', image='about:blank')
await u.save()
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(test(loop))
loop.close()
运行test.py后,可以在MySQL客户端命令行查询,看看测试的数据是不是正常储存到MySQL里面。