python实战笔记

本片博客仅用于记录开发个人博客中的心得体会。

目标:

使用aiohttp, aiomysql开发异步服务器。aiohttp中文文档

第一阶段:开发orm框架

orm框架的作用就是直接在代码中对对象进行操作,映射到数据库的操作,这样就屏蔽了数据库操作的具体细节。相当于hibernate的作用。

第一步:

使用了aiohttp那么所有的I/O都要用异步技术,数据库也不例外,要用aiomysql。

首先使用aiomysql模块,创建一个数据库连接池和crud方法。 这个根据aiomysql模块很容易实现。

第二步:

orm框架的基本原理就是用metaclass在实例化对象之前去解析出映射关系。可以参考之前写的简单实现

为了让我们的orm框架也是异步的,那么就需要在Model类的crud方法中,调用前一步的数据库连接池和crud方法。

所以思路就是:orm框架解析映射,拼凑sql,然后调用aiomysql数据库连接池真正去执行。

封装顺序依次是,aiomysql利用asyncio封装了mysql-connector, 我们首先封装aiomysql提供一个连接和数据库操作,然后再用orm框架去封装aiomysql。提供直接操作对象的异步数据库操作。

需要注意的是orm的工作基本原理:假设User(Model)

  1. User继承了基类Model,所以其__new__交给了ModelMetaclass去创建,于是ModelMetaclass就把User指定的静态属性(也就是映射)保存到__mappings__(这里的属性是齐全的,User类定义了多少,就是多少),之后把静态属性都删掉。
  2. User类被实例化之后,就通过__mappings__保存了映射,(当然还有__table__,__fields__,都是一样的原理)。当想要进行数据库操作时,需要两个内容,列和值。列已经保存在__mappings__中,值是通过User类初始化参数提供的。
  3. 因为User是dict, 所以初始化参数有可能不齐全,所以需要在映射关系中提供默认值,这个默认值可以是函数,去自动计算日期或者主键。当dict获取不到需要的列时,证明User实例化的时候没有提供,这时就去__mappings__(这里肯定存在,上面说了)中找,使用默认值。
  4. 当调用查询操作的时候,返回值是dict,把dict作为关键字参数传递给User类 就创建了一个User类。

第三步:

开发需要的Model类,这个就很简单了。根据数据库中的表去开发即可。相当于Java中的bean。

第二阶段:开发web框架

说开发有些言过其实,因为aiohttp比较底层,用起来会比较麻烦,所以封装起来,方便使用。不能理解的话,把一个基本的aiohttp开发服务器的用法写出来,然后看着封装会很有帮助,最简单用法aiohttp最简单服务器

第一步:

为url处理函数提供@get(path)和@post(path)带参数的装饰器 方法见之前一篇博客python装饰器。作用是为处理函数绑定url和请求方法。

就像 __method__='GET'  __route__ = path这样。

第二步:

aiohttp提供的路由是app.router.add_route('GET', '/', index) 这样的,需要一个个手动设置。上一步每个处理函数都带上了url和method,也就是说这三个参数都包含在处理函数中了。

  1. 封装一个add_route(fn)方法 去添加路由
  2. 把处理函数都放到一个文件里,再封装一个add_routes('handlers') 去扫描这个文件,对符合要求的函数 调用add_route(fn)注册路由

第三步:

这时,只用在handlers中写处理函数即可,但是处理函数 fn(request):的参数只有一个request,我们必须去自己从request中获取想要的参数。所以可以考虑像java中spring mvc,在处理函数的参数列表中添加想要的参数,就可以在函数体中直接使用这个参数。也就是说,需要去写一个类RequestHandler获取到fn想要的参数,然后再调用fn去处理逻辑。

  1. __init__ RequestHandler(fn) 获取fn参数列表的各种信息
  2. __call__ 封装fn,获取fn的参数信息,然后调用fn
  3.  add_route(fn) 内部实际上添加的路由处理函数应该是 RequetsHandler(fn)

第四步:

获取完request中的数据,进行了业务处理之后,就是返回。aiohttp返回web.Response(...) 。当然要用上模板,这里选jinja2。我们需要继续封装成这样去返回响应。

return {
   '__template__': 'test.html',
   'data': data
}

提供模板和数据。

那么就需要在返回响应之后,有一个拦截器去拦截住这个dict去进行解析,把它做成web.Response 并套用上指定的模板和数据。

aiohttp里的拦截器叫做中间件。编写好中间件,创建web服务器的时候直接传进去即可。

所以,我们编写一个response_factory中间件去做上述的过程。

第三阶段:前端框架

前端这个问题说起来其实没有太大难度,但是很费事儿,毕竟前端是脸面,想要一个好看的UI真得花大把时间。之前我只用过bootstrap, 这次项目中使用的UIkit2。项目完成后计划更新到UIkit3。

第一步:

还是通用的页眉和页脚, 如果是纯html或jsp页面, 那就用标签直接引入就好了。但是我们用了jinja2模板有一个遍历,可以像类那样去继承,去覆盖父类。所以第一步,先写一个__base__.html 里面写好通用的页眉和页脚,还有需要覆盖的地方用{% block xxx %} {% endblock %}指明

第二步:

开始写需要的模板xxx.html 使用{% extends '__base__.html'%} 继承模板。 然后在{% block xxx %} {% endblock %} 里面实现覆盖。

第四阶段:实现功能

当真正写功能的时候,就会涉及到大量前端的东西,我们这里需要用到vue.js和jquery。去绑定数据和进行交互。做到这里觉得很难,后来深入做进去之后,发现难是因为vue和jquery不熟悉,所以就搞不懂前后台怎么交互的。只能去熟悉vue和jquery。

第一步:用户注册登陆

注册登陆退出,想简单的实现这个功能也是很简单的,并且一般学习也就止步于此了。

先看注册:

注册的逻辑很简单:

  1. 注册页面提交注册信息到服务器
  2. 服务器检查注册信息,合法就构建成User对象,调用orm框架save进数据库

需要注意:

  1. 用户提交的password 在html页面提交的时候 已经加密, sha1(email + ':' + password)
  2. 数据库保存的正是加密后的password

再看登陆:

登陆也就是要求保持http有状态,能识别我是登陆过的或者没登陆过的,http本身无状态需要借助session或cookie来保存状态。可参考http的无状态

思路是这样的,每个url请求都去验证cookie。具体做法如下,

第一次登陆的时候,我肯定是没有登陆过的,这个时候要做的事情有两件:

  1. 验证账号密码
  2. 设置cookie ['xxx'],user信息

当我登陆了之后,我再去访问别的url,如果我没有使用cookie,那浏览器就不知道我是否登陆,使用了cookie就可以去验证cookie:

  1. 获取cookie['xxx'],如果没有,就是没登陆
  2. 如果有,就可以从中取出user信息

基本逻辑就是上面这个样子,

但是为了做得更安全,还需要在有cookie['xxx']后去确认这个cookie['xxx']是否有效(有可能是伪造的,有可能过期了)

验证cookie['xxx']  设置的时候在里面加上sha1(uid password max_age 服务器特定的salt)

然后在获取的时候 根据传过来的uid 查询去用户信息去计算sha1(..) 和cookie['xxx']中传过来的sha1(...)对比即可验证。

为了做得更简单,每个url都需要搞上面的的一套验证才能正确取出useri信息,所以用拦截器统一处理。

做一个中间件,auth_factory去做上面这一系列验证,并把验证结果绑定到request.__user__去。

这样每个url处理函数只需要看request.__user__是否为null 就知道用户是否登陆,并且直接获取用户信息了。

如果登陆了,就把request.__user__绑定到模板就行了。关于绑定到模板这个事情,也不用自己在每个handler中写,直接在response_factory 添加 request.__user__ 这个属性就好了。

退出

退出相比登陆就简单多了,只需要把cookie['xxx'] 的max_age设置为0即可。

第二步:博客创建,查阅

vue jquery jinja2 uikit 混合起来 如果不熟悉其中任何一个,很容易就不知道数据是如何传递的了。

首先分析一下handler.py里都有些什么:

大致可以分为三类:

  1. 操作资源的,作为web service的,rest风格的 api。这都是一个意思,就是crud数据库中的数据,返回的json格式。提供这类api,可以方便web上的其他人,其他应用调用。
  2. 普通的get url处理函数 主要用来访问html
  3. 普通的函数,一些通用的功能,提取出来,抽象成函数。

来看查阅博客的流程:

  1. 访问url:get /manage/blogs ==> manage_blogs.html
  2. html上的JQuery加载运行:manage_blogs.html ==> 访问url: /api/blogs 获取数据库中所有博客
  3. 获取数据后,vue绑定view和model

从上述流程可以看到,访问html和访问数据不仅在handler上分开了,在html页面页分开了,这样就可以先看到html静态页面,然后等数据请求完毕后,再看到数据加载。更快。

接着看创建博客流程:

  1. 操作html: mange_blogs.html 访问 url: get /manage/blogs/create ==> manage_blog_edit.html
  2. 操作html 提交表单 访问url: post: /api/blogs 保存提交的博客

连续看这两个流程,就很清楚了:

就是先去访问url到达html页面,然后访问rest api操作数据。

需要注意的是:访问url(rest api也是url)有很多种方法:form表单,Jquery请求, vue的方法,直接在地址栏输入数据。所以真正该记住的是先访问页面,再加载数据的思想。这样才能以知道为什么要设计rest api,以及如何使用它们。

第三步:

开发完了博客的crud,剩下就是苦力活了。照葫芦画瓢就好。分别要完成:

评论的管理: curd

用户的管理:在前面的基础上加上查询就可以了。

完工。撒花。

总结:

功能列表:

普通用户:

  1. 注册
  2. 登陆
  3. 查阅博客
  4. 评论博客

管理员用户:

  1. 创建博客
  2. 删除博客
  3. 更改博客
  4. 查阅评论
  5. 删除评论
  6. 查看用户

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值