廖雪峰实战项目笔记

以前写过android app,感觉自己比较明白在一个android的project中,各个部分承担了什么工作,以及他们之间如何相互协作,使一个app正常运行,实现最初自己的需求。但是,来到python实现的一个web app时,感觉要学习的东西还是不少,所以这里写一篇blog,用来详细记录在廖老师网站学习python,以及在最后的实战中的思考。自己第一篇blog,尽量写好。当然,目的就是记录自己在学习之路上的思考历程,仅此而已。

首先,想要写实战这样的project,当然是现实中有了某种需求:开发一个可以注册用户,用户能发blog,用户可以评论blog的简单网站。

然后首先把这三块抽象成class:User,Blog,Comment,并且每个class有着因需求而生的一堆属性。为了在对这些类的对象数据进行操作时的便利性,就需要orm框架登场了:

在一个Web App中,所有数据,包括用户信息、发布的日志、评论等,都存储在数据库中。在awesome-python3-webapp中,我们选择MySQL作为数据库。Web App里面有很多地方都要访问数据库。访问数据库需要创建数据库连接、游标对象,然后执行SQL语句,最后处理异常,清理资源。这些访问数据库的代码如果分散到各个函数中,势必无法维护,也不利于代码复用。所以,我们要首先把常用的SELECT、INSERT、UPDATE和DELETE操作用函数封装起来。

解析orm.py模块

1.create_pool(loop, **kw):用来创建一个数据库的全局连接池。这里说一点,通过查看源码,在aiomysql.create_pool的参数列表中,loop是5个默认参数中的一个,loop上面的那些是**kw(关键字参数),所以这里loop=loop:1.出现位置很灵活,函数实际调用时,默认参数出现在关键字参数之后  2.这是默认参数中不按顺序提供默认参数的方式。(这些感觉跟java区别有点大)

2.select(sql, args, size=None):这些个增删改查操作的共同步骤有,1.获取全局的mysql链接,2.获取到游标,3.最后执行相应sql语句。cur.fetchmany()中的默认参数size为None时,就返回一条记录,但是因为下面findAll()有用到select(' '.join(sql), args),所以这里我们改一下需求:当select中的size为None时,返回相应表中的所有记录,所以这里调用cur.fetchall();当size不为None时,是几就返回几条记录。

3.execute(sql, args, autocommit=True):之前没接触过数据库的渣就是这么痛苦

执行过程跟select类似,只不过mysql中对增删改操作叫做“事务”,毕竟这仨操作算是敏感操作,需要用事务机制来保证操作过程中各种操作错了、不想操作了等等需求,这时就需要回滚:conn.rollback()。然后统一说一下,select与execute中的sql.replace('?', '%s')操作:mysql和python的占位符都是%s,这里用到的sql语句,是通过下面的元类ModelMetaclass进行合成的,当然,这里合成的结果是:对应一张数据库表来说,一个sql语句已经合成结束,但该sql语句中的各个属性key所对应的值value事先并不知道,需要执行时才知道,所以这里需要占位符。因为ModelMetaclass构造sql的时候就需要用占位符来合成,以匹配不同数据库表,所以这里也用到了%s占位符,所以这里为了编写代码的简洁,把sql语句中的各个属性所对应的值事先用?占位符代替,先合成针对某张表不针对具体某条记录的sql语句,等到select和execute操作时,再把?替换成%s。所以,总之,这个操作就是为了方便。

4.Field()的两个作用

首先Field用作User,Blog,Comment这些类的属性的类型的父类,好处是,用python的方式封装了mysql的数据类型,且可以赋初值,设置主键操作

然后,让其他数据类型由Field派生,可以简化ModelMetaclass中对attrs参数携带信息的判断。因为要提取,__mappings__,__select__等,所以要判断出attrs中哪些是我们自己封装的数据类型。isinstance(v, Field)

5.ModelMetaclass(type)

元类,控制类的生成过程。当User,Blog,Comment和Model这些类要执行时,要先层层往上找,直到找到metaclass=,这里ModelMetaclass通过传进来的四个参数,进行一系列的操作,为其生成表名__table__,sql操作语句等,是orm

框架最核心部分。注意元类的返回语句 :type.__new__(cls, name, bases, attrs),最终还是要调用python提供的type元类来进行类对象的生成。这里ModelMetaclass.__new__()和type.__new__()的参数传入一致,而中间多的这一堆,就是在创建类之前先对其进行了一定处理,从而说元类控制了类的生成过程。

6.Model(dict, metaclass=ModelMetaclass)

这里继承dict,并在__init__()中调用super(Model, self).__init__(**kw),也就是父类dict的属性,从而使Model拥有dict的功能。Model(xxx=xxx, xxx=xxx, xxx=xxx)就像初始化了一个dict,完事可以进行self[key]形式的调用。

这里解释一下self.__mappings__,cls.__select__等操作,因为type()的第三个参数attrs是一个dict,而dict中的每一个key也就是'__mappings__', 'cls.__select__'等,充当的就是其生成的类的属性,所以在ModelMetaclass中attrs的这些key在这里就可以self.__mappings__,cls.__select__

这里用到了@classmethod,用作就是将下面的函数变成类函数,可以直接用类调用而非类的实例:User.findAll()

--------------------------------------------------------------------------------------------------------------------------------------------------------------

有了便捷操作mysql的orm框架,下面需要写出代表User,Blog,Comment的数据库表

mdodels.py模块

1.next_id()

%015d%s000:%015d的意思是打印15个0,后面的int(time.time() * 1000从15个0的最低位开始进行依次替换

关于在mysql中穿件实际的,此project对应的数据库和数据库中包含的表,可以从Model对象直接通过脚本自动生成SQL脚本,使用更简单

---------------------------------------------------------------------------------------------------------------------------------------------------------------

coroweb.py模块

实现路由器在服务器端的统一注册,并对每个url处理函数所接受的request参数进行合法性判断

1.get(path):

@get('/')

def abc(xxx):

如上装饰器的运行过程:get('/')(abc)(xxx)-->decorator(abc)(xxx)-->wrapper(xxx)-->func(xxx)

2.RequestHandler(object)

这里实现了定制类中的__call__函数,从而RequestHandler的对象就可以通过直接duixiang(request)来执行__call__(request)中的逻辑,我觉得这个方法在add_route()中的app.router.add_route(method, path, RequestHandler(app, fn))得到了巧妙的应用,这种感觉是以前写java没有体会过的。然后,内部经过一系列分类,以及合法性判断,把kw制作出来,并传递给handlers处理函数self._func(**kw)

3.add_routes(app, module_name)

这个函数把module_name模块中所包含的url处理函数一个一个地提取出来,并通过add_route()函数,绑定到服务器端的处理对象app上面

4.add_static(app)

这里说几个os模块提供的路径操作函数

os.path.abspath(__file__):打印当前模块所在的绝对路径(包括当前模块),awesome-python3-webapp/www/coroweb.py
os.path.dirname(os.path.abspath(__file__)):上一步的绝对路径中,去除最里层的模块文件名后的目录路径名,/awesome-python3-webapp/www
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static'):对两个路径进行拼接,我现在理解的就是在两者之间加上/

abspath, dirname两者的功能可以由下面的一句代替:

os.path.abspath('.'):得到当前模块所在路径的目录路径,不包括当前模块名,awesome-python3-webapp/www

猜你喜欢

转载自blog.csdn.net/Saltwind/article/details/82116633
今日推荐