热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

廖雪峰webAPP实战——Day18总结\剖析Day5

廖雪峰webAPP实战——Day1-8总结服务端和客户端init(loop)response_factoryRequestHandlerinit_jinja2add_routesa



廖雪峰webAPP实战——Day1-8总结

  • 服务端和客户端
    • init(loop)
    • response_factory
      • RequestHandler
    • init_jinja2
    • add_routes
      • add_route
    • add_static
  • 服务端和数据库
    • 注册
    • User类
      • self.getValueOrDefault()
      • save()
      • findAll
  • 总结
  • 参考
  • thanks!!!


大家可能都卡在day5, 其中的web的框架真是让头疼,day4 还不容易搞明白了orm,没想到day5 还更加难,多了3个py文件,可以说是4个,其中app.py都差不多全改了。。。


服务端和客户端

下面是关于服务端和客户端的函数,也就是app.py和 coroweb.py的交互。


init(loop)

我们跟着代码跑一遍,程序开头是在app.py的 init(loop)函数。

#app.py
async def init(loop):
await orm.create_pool(loop=loop, host='127.0.0.1', port=3306, user='www-data', password='kx123456', db='awesome')
app = web.Application(middlewares=[logger_factory, response_factory])#去掉loop = loop,loop参数弃用
init_jinja2(app, filters=dict(datetime=datetime_filter))
add_routes(app, 'handlers')#handlers
add_static(app)
srv = await loop.create_server(app.make_handler(), '127.0.0.1', 9000)#app.make_handler()->web.AppRunner(app)
print('server started at http://127.0.0.1:9000...')
return srv
loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
loop.run_forever()

首先要创建事件循环,配合异步处理,异步还不熟的话可以重看廖雪峰的异步IO教程。然后把一个协程coroutine放进run_until_complete里面,其中init(loop)就是一个协程了,有async修饰。

在init函数里面:



  • 1、创建连接池,就是数据库和服务端的交接处,可以控制连接数量等。

  • 2、创建webAPP类,web.Application有很多个参数,其中loop已被弃用,所以我们不用再传入loop。middlware是一种拦截器,一个URL在被某个函数处理前,可以经过一系列的middleware的处理。这里面的函数都是统一的,一个app和一个handler,参数是怎么传进去的?后面会说到。

  • 3、加载模板,jinja2作为模板引擎,在新框架中对jinja2模板进行初始化设置。

  • 4、注册函数,add_routes,就是将handler函数作为app类中或者其子类的属性,和middlware里面的函数联合起来,刚刚说的middlware里面的函数的参数handler,就是要通过add_routes注册,才能获得。

  • 5、添加静态文件add_static,静态文件就是css, js等模板,可以重整HTML排版,使整体更美观,下面可以看一下差距。在这里插入图片描述这个是排版后的,下面是排版前的。在这里插入图片描述是不是差距有点大呢,前者是copy廖雪峰day5的静态文件,后者是在uikit官网主页下载的,是3.x版本, 页面右上角下载。廖雪峰用的是2.x版本。

  • 6、然后就是开启服务了, create_server, 其中的protocol_factory参数,廖雪峰教程中采用app.make_handler(),提示make_handler已经被弃用,但是依然可以运行。

srv = await loop.create_server(app.make_handler(), '127.0.0.1', 9000)#app.make_handler()->web.AppRunner(app)

response_factory

记下来就介绍init里面的各个函数,打通整个架构。在介绍response_factory之前先要了解coroweb.py里面的RequestHandler,因为response_factory的参数除了app还有一个handler,这个handler就是RequestHandler,是通过 app.router.add_route() 注册进去的,其中三个参数是method, path, RequestHandler(app, fn),method 常用的是get、post,path 就是url的通道,就是下面的get后面的参数,通过get包装后,一步一步地传到了add_route里面去。

def get(path):
def decorator(func):
print('in coro get')
@functools.wraps(func)
def wrapper(*args, **kw):
return func(*args, **kw)
wrapper.__method__ = 'GET'
wrapper.__route__ = path
return wrapper
return decorator
@get('/')
async def index(request):
users = await User.findAll()
return {
'__template__': 'test.html',
'users': users
}

最后一个handler参数,就是RequestHandler(app, fn),下面看一下该函数。


RequestHandler

#定义RequestHandler从视图函数中分析其需要接受的参数,从web.Request中获取必要的参数
#调用视图函数,然后把结果转换为web.Response对象,符合aiohttp框架要求
class RequestHandler(object):
def __init__(self, app, fn):
self._app = app
self._func = fn
self._required_kw_args = get_required_kw_args(fn)
self._named_kw_args = get_named_kw_args(fn)
self._has_request_arg = has_request_arg(fn)
self._has_named_kw_arg = has_named_kw_args(fn)
self._has_var_kw_arg = has_var_kw_arg(fn)
# 1.定义kw,用于保存参数
# 2.判断视图函数是否存在关键词参数,如果存在根据POST或者GET方法将request请求内容保存到kw
# 3.如果kw为空(说明request无请求内容),则将match_info列表里的资源映射给kw;若不为空,把命名关键词参数内容给kw
# 4.完善_has_request_arg和_required_kw_args属性
async def __call__(self, request):
print('in RequestHandler call__() request: ',request )
kw = None # 定义kw,用于保存request中参数
if self._has_named_kw_arg or self._has_var_kw_arg: # 若视图函数有命名关键词或关键词参数
if request.method == 'POST':
# 根据request参数中的content_type使用不同解析方法:
if request.content_type == None: # 如果content_type不存在,返回400错误
return web.HTTPBadRequest(text='Missing Content_Type.')
ct = request.content_type.lower() # 小写,便于检查
if ct.startwith('application/json'): # json格式数据
params = await request.json() # 仅解析body字段的json数据
if not isinstance(params, dict): # request.json()返回dict对象
return web.HTTPBadRequest(text='JSON body must be object.')
kw = params
# form表单请求的编码形式
elif ct.startwith('application/x-www-form-urlencoded') or ct.startswith('multipart/form-data'):
params = await request.post() # 返回post的内容中解析后的数据。dict-like对象。
kw = dict(**params) # 组成dict,统一kw格式
else:
return web.HTTPBadRequest(text='Unsupported Content-Type: %s' % request.content_type)
if request.method == 'GET':
qs = request.query_string # 返回URL查询语句,?后的键值。string形式。
print('in call__() qs = request.query_string : ' ,qs)
if qs:
kw = dict()
'''
解析url中?后面的键值对的内容
qs = 'first=f,s&secOnd=s'
parse.parse_qs(qs, True).items()
>>> dict([('first', ['f,s']), ('second', ['s'])])
'''
for k, v in parse.parse_qs(qs, True).items(): # 返回查询变量和值的映射,dict对象。True表示不忽略空格。
kw[k] = v[0]
print('in get:kw: ',kw)
if kw is None: # 若request中无参数
# request.match_info返回dict对象。可变路由中的可变字段{variable}为参数名,传入request请求的path为值
# 若存在可变路由:/a/{name}/c,可匹配path为:/a/jack/c的request
# 则reqwuest.match_info返回{name = jack}
print('in call__() request.match_info ,**request.match_info: ',request.match_info,' ,,,,,,,,, ', **request.match_info)
kw = dict(**request.match_info)
else: # request有参数
if self._has_named_kw_arg and (not self._has_var_kw_arg): # 若视图函数只有命名关键词参数没有关键词参数
copy = dict()
# 只保留命名关键词参数
for name in self._named_kw_args:
if name in kw:
copy[name] = kw[name]
kw = copy # kw中只存在命名关键词参数
print('in call__() kw,copy : ',kw,copy)
# 将request.match_info中的参数传入kw
for k, v in request.match_info.items():
print('in getpost k: ',k,' v: ',v)
# 检查kw中的参数是否和match_info中的重复
if k in kw:
logging.warning('Duplicate arg name in named arg and kw args: %s' % k)
kw[k] = v
if self._has_request_arg: # 视图函数存在request参数
kw['request'] = request
if self._required_kw_args: # 视图函数存在无默认值的命名关键词参数
for name in self._required_kw_args:
if not name in kw: # 若未传入必须参数值,报错。
return web.HTTPBadRequest('Missing argument: %s' % name)
# 至此,kw为视图函数fn真正能调用的参数
# request请求中的参数,终于传递给了视图函数
print('call with args: %s' % str(kw))
# try:
#print('in call__() **kw: ' ,**kw)
r = await self._func(**kw)

return r

函数有两个魔法方法,__ init__() , 和__call__(), 如果把init参数也就是(app, fn)传进去就会初始化函数,其中fn就是通过add_routes解析出来的存在于handler.py的index函数。得到的RequestHandler(app, fn)是一个实例化,由对他再使用()的话就会调用call方法,也就是RequestHandler(app, fn)(request),call()里面的内容就会执行。

回到response_factory,其中的r = await handler(request),就是执行了RequestHandler的call方法,参数request是客户端发过来的,在app内部处理后(已经注册了的话)再交给handler函数。

async def response_factory(app, handler):
print('in response_factory handler: ',handler)
#输出:in response_factory handler: .handler_wrapper at 0x000001C1B44A92F0>
async def response(request):
print('in app response factory requset: ',request)
#输出:in app response factory requset:
print('in response factory Response handler...')
r = await handler(request)
if isinstance(r, web.StreamResponse):
...
...
...
return respone

RequestHandler的功能就是将request里面的内容处里面字典的形式,目前只是处理post和get方法的请求,到day8的时候,请求request也只是而已,最多也就GET /blog/1,不过是404的返回,还没写好处理函数。

在RequestHandler的call的最后得到kw字典参数,作r = wait self._func(**kw) 处理self.__func就是传进RequestHandler的参数fn,也就是通过add_routes解析出来的存在于handlers.py 的index(request)函数,然后返回也是一个字典的形式,就是r。然后就回到了response_factory函数。



  • RequestHandler小结,response_factory的handler是一个已经初始化的函数,原型是RequestHandler(app, fn),服务端接收到request时,就会启动middlware,里面的函数都会跑起来。然后就会触发到RequestHandler的call方法,处理request的各部分参数,然后通过字典的形式返回给response_factory。

现在我们回到response_factory,该函数接收到handler的返回后,对返回值r 进行处理。r其实就是下面的函数的返回值。

#handlers.py
@get('/')
def index(request):
print('in index request: ',request)
summary = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
blogs = [
Blog(id='1', name='Test Blog', summary=summary, created_at=time.time()-120),
Blog(id='2', name='Something New', summary=summary, created_at=time.time()-3600),
Blog(id='3', name='Learn Swift', summary=summary, created_at=time.time()-7200)
]
return {
'__template__': 'blogs.html',
'blogs': blogs
}

我们可以看到返回值是字典形式,所以在response_factory中的==if isinstance(r, dict):==会停下进入。

async def response_factory(app, handler):
async def response(request):
print('in app response factory requset: ',request)#打印 in app response factory requset:
r = await handler(request)
...................
..................
if isinstance(r, dict):
print('Response handler...dict: ',r)
template = r.get('__template__')
if template is None:
resp = web.Response(body=json.dumps(r, ensure_ascii=False, default=lambda o: o.__dict__).encode('utf-8'))
resp.content_type = 'application/json;charset=utf-8'
print('resp: ',resp)
print('resp.__dict__: ',resp.__dict__)
return resp
else:
resp = web.Response(body=app['__templating__'].get_template(template).render(**r).encode('utf-8'))
resp.content_type = 'text/html;charset=utf-8'
print('resp: ',resp)
return resp
................
...............
resp = web.Response(body=str(r).encode('utf-8'))
resp.content_type = 'text/plain;charset=utf-8'
print('resp: ', resp)#打印 resp:
return resp
return response

进入判断条件后就是从r里面提取信息,再对返回的response进行包装。其中print('Response handler…dict: ',r)的结果是:

Response handler...
dict:{
'__template__': 'blogs.html',
'blogs': [
{'id': '1', 'name': 'Test Blog', 'summary': 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', 'created_at': 1565066334.8002357},
{'id': '2', 'name': 'Something New', 'summary': 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', 'created_at': 1565062854.8002357},
{'id': '3', 'name': 'Learn Swift', 'summary': 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', 'created_at': 1565059254.8002357}
]
}

和index函数的返回是一样的,包装完之后就返回给浏览器了。



  • response_factory小结:该函数就是调用RequestHandler来处理request请求,然后返回给response_factory再进行返回值包装,发送给浏览器。


init_jinja2

我们使用jinja2作为模板引擎,在新框架中对jinja2模板进行初始化设置。

def init_jinja2(app, **kw):
logging.info('init jinja2…')
# class Environment(**options)
# 配置options参数
optiOns= dict(
# 自动转义xml/html的特殊字符
autoescape = kw.get('autoescape', True),
# 代码块的开始、结束标志
block_start_string = kw.get('block_start_string', '{%'),
block_end_string = kw.get('block_end_string’, '%}'),
# 变量的开始、结束标志
variable_start_string = kw.get('variable_start_string', '{{'),
variable_end_string = kw.get('variable_end_string', '}}'),
# 自动加载修改后的模板文件
auto_reload = kw.get('auto_reload', True)
)
# 获取模板文件夹路径
path = kw.get('path', None)
if not path:
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')
# Environment类是jinja2的核心类,用来保存配置、全局对象以及模板文件的路径
# FileSystemLoader类加载path路径中的模板文件
env = Environment(loader = FileSystemLoader(path), **options)
# 过滤器集合
filters = kw.get('filters', None)
if filters:
for name, f in filters.items():
# filters是Environment类的属性:过滤器字典
env.filters[name] = f
# 所有的一切是为了给app添加__templating__字段
# 前面将jinja2的环境配置都赋值给env了,这里再把env存入app的dict中,这样app就知道要到哪儿去找模板,怎么解析模板。
app['__template__'] = env # app是一个dict-like对象

初始化jinja2需要以下几步:




  • 1、对 Environment 类的参数 options 进行配置。





  • 2、使用jinja提供的模板加载器加载模板文件,程序中选用FileSystemLoader加载器直接从模板文件夹加载模板。





  • 3、有了加载器和options参数,传递给Environment类,添加过滤器,完成初始化。





add_routes

该函数的作用就是将handlers.py里面的函数都加入注册,注册进app类的内部属性,方便库内的调用,帮我们减少了很多麻烦。

想知道注册函数内部发生了什么就移步到这里。

在for循环之前都是在解析路径,找到handlers.py后就把里面的函数逐个进行注册。

def add_routes(app, module_name):
n = module_name.rfind('.') # 从右侧检索,返回索引。若无,返回-1。
# 导入整个模块
if n == -1:
# __import__ 作用同import语句,但__import__是一个函数,并且只接收字符串作为参数
# __import__('os',globals(),locals(),['path','pip'], 0) ,等价于from os import path, pip
mod = __import__(module_name, globals(), locals, [], 0)
else:
name = module_name[(n+1):]
# 只获取最终导入的模块,为后续调用dir()
mod = getattr(__import__(module_name[:n], globals(), locals, [name], 0), name)
for attr in dir(mod): # dir()迭代出mod模块中所有的类,实例及函数等对象,str形式
if attr.startswith('_'):
continue # 忽略'_'开头的对象,直接继续for循环
fn = getattr(mod, attr)
print('add_routes attr in dir(mod): ',attr)#
# 确保是函数
if callable(fn):
# 确保视图函数存在method和path
method = getattr(fn, '__method__', None)
path = getattr(fn, '__route__', None)
print(fn,' was callable!')
if method and path:
# 注册
add_route(app, fn)

我们看一下 print(‘add_routes attr in dir(mod): ‘,attr)、print(fn,’ was callable!’) 的结果:

add_routes attr in dir(mod): Blog
was callable!
add_routes attr in dir(mod): Comment
was callable!
add_routes attr in dir(mod): User
was callable!
add_routes attr in dir(mod): __author__
add_routes attr in dir(mod): __builtins__
add_routes attr in dir(mod): __cached__
add_routes attr in dir(mod): __doc__
add_routes attr in dir(mod): __file__
add_routes attr in dir(mod): __loader__
add_routes attr in dir(mod): __name__
add_routes attr in dir(mod): __package__
add_routes attr in dir(mod): __spec__
add_routes attr in dir(mod): asyncio
add_routes attr in dir(mod): base64
add_routes attr in dir(mod): get
was callable!
add_routes attr in dir(mod): hashlib
add_routes attr in dir(mod): index
was callable!
add_routes attr in dir(mod): json
add_routes attr in dir(mod): logging
add_routes attr in dir(mod): next_id
was callable!
add_routes attr in dir(mod): post
was callable!
add_routes attr in dir(mod): re
add_routes attr in dir(mod): time

看到有7个是可以callable的,最后真正进行注册的只有index,因为==if method and path:==才能够注册,index被get装饰过,所以拥有method and path属性。


add_route

# 编写一个add_route函数,用来注册一个视图函数
def add_route(app, fn):
method = getattr(fn, '__method__', None)
path = getattr(fn, '__route__', None)
if method is None or path is None:
raise ValueError('@get or @post not defined in %s.' % fn.__name__)
# 判断URL处理函数是否协程并且是生成器
if not asyncio.iscoroutinefunction(fn) and not inspect.isgeneratorfunction(fn):
# 将fn转变成协程
fn = asyncio.coroutine(fn)
logging.info('add route %s %s => %s(%s)' % (method, path, fn.__name__, ','.join(inspect.signature(fn).parameters.keys())))
# 在app中注册经RequestHandler类封装的视图函数
app.router.add_route(method, path, RequestHandler(app, fn))

logging提示的信息是:add route GET / => index(request)。

index函数进行注册。


add_static

该函数函数用于注册静态文件,提供文件路径即可进行注册。

def add_static(app):
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static')
app.router.add_static('/static/', path)
print('add_static path: ',path)
print('add static %s => %s' % ('/static/', path))

服务端和数据库

下面是关于服务端和数据库的函数,也就是app.py和 orm.py的交互情况。


注册

我们拿一个test.py 当作app.py 进行注册用户。

import orm
import asyncio
from models import User, Blog, Comment
async def test(loop):
await orm.create_pool(loop=loop, user='www-data', password='kx123456', db='awesome')
u = User(name='Test', email='test5@example.com',
passwd='123456789', image='about:blank')
await u.save()
loop = asyncio.get_event_loop()
loop.run_until_complete(test(loop))
loop.run_forever()
for x in test(loop):
pass

然后一步一步跟着代码走,首先是连接数据库,然后创建用户实例,再save()保存就行,然而User怎么来?丢参数怎么处理?怎么save()?先看一下类和一些函数的介绍。


User类

我们来看一下这个类:


class User(Model):
__table__ = 'users'

id = StringField(primary_key=True, default=next_id, ddl='varchar(50)')
email = StringField(ddl='varchar(50)')
passwd = StringField(ddl='varchar(50)')
admin = BooleanField()
name = StringField(ddl='varchar(50)')
image = StringField(ddl='varchar(500)')
created_at = FloatField(default=time.time)

似乎把参数这么一丢进去,接收的了吗?

我们看到各个属性都是一个类。

我们还要回到他的父类Model:(省略了大部分)


class Model(dict, metaclass=ModelMetaclass):
def __init__(self, **kw):
super(Model, self).__init__(**kw)
print('kwkw: ',kw)
def __getattr__(self, key):
try:
print('in model getattr: ',key,self[key])
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no attribute '%s'" % key)
..............................
..............................

如果继承知识学得不牢的话,可能还是会看不懂,User继承于Model, Model 继承于dict , 为的就是方便取得各种值,用self[key]就可以取得对应的值,要注意的是要用super加载一遍父类的init,还有把**kw传进去。这样的话就可以用点号运算符,取出其中的属性,如果不存在的话,就会调用 getattr 魔法方法。然后在里面用self[key] 进行提取kw里面的值。

我们可以看到User类里面的变量都是一些类,基于Field的类,所以使用 user.id和user.name 等等的情况下(thml里面会用到),不就是一个类了吗?刚刚传入的kw对应不上啊?

所以这个时候就有了元类,Model还要有一个元类,去修改和增加继承于Model的类(User、Blog、Comment )的一些属性。下面是Model的元类。

class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
if name=='Model':
return type.__new__(cls, name, bases, attrs)
#如果发现使用了该元类的类是Model,则不需要修改,只有User、Blog、Comment 这三个类需要修改诸多的性质
tableName = attrs.get('__table__', None) or name
print('attrs: ',attrs)
print('found model: %s (table: %s)' % (name, tableName))
mappings = dict()
fields = []
primaryKey = None
for k, v in attrs.items():
print('k :',k,'v :',v)
if isinstance(v, Field):
mappings[k] = v
if v.primary_key:
# 找到主键:!!!!!!!!!!!!!!!!!!!!!!主键就是ID!!!!!!!!!!!!!!!!!!!
print('v.primary_key:',v.primary_key)
if primaryKey:
raise StandardError('Duplicate primary key for field: %s' % k)
primaryKey = k
else:
fields.append(k)
print('fields: ',fields)#!!!!!!!!!!!!!!!!!!!!!!
if not primaryKey:
raise StandardError('Primary key not found.')
for k in mappings.keys():
attrs.pop(k)
print('attrs: ', attrs)#!!!!!!!!!!!!!!!!!!!!
escaped_fields = list(map(lambda f: '`%s`' % f, fields))
print('escaped_fields: ',escaped_fields)
attrs['__mappings__'] = mappings # 保存属性和类(ID、name等继承于Field的类)的映射关系
attrs['__table__'] = tableName
attrs['__primary_key__'] = primaryKey # 主键属性名
attrs['__fields__'] = fields # 除主键外的属性名
attrs['__select__'] = 'select `%s`, %s from `%s`' % (primaryKey, ', '.join(escaped_fields), tableName)
attrs['__insert__'] = 'insert into `%s` (%s, `%s`) values (%s)' % (tableName, ', '.join(escaped_fields), primaryKey, create_args_string(len(escaped_fields) + 1))
attrs['__update__'] = 'update `%s` set %s where `%s`=?' % (tableName, ', '.join(map(lambda f: '`%s`=?' % (mappings.get(f).name or f), fields)), primaryKey)
attrs['__delete__'] = 'delete from `%s` where `%s`=?' % (tableName, primaryKey)
print('__mappings__:',attrs['__mappings__'])
print('__fields__: ',attrs['__fields__'])
print('__select__: ',attrs['__select__'] )
print('__insert__: ',attrs['__insert__'] )
print('__update__: ',attrs['__update__'])
print('__delete__: ',attrs['__delete__'])
print('type.__new__(cls, name, bases, attrs): ',type.__new__(cls, name, bases, attrs))

return type.__new__(cls, name, bases, attrs)

运行了一下,就有以下输出。

found model: User (table: users)
k : __module__ v : models
k : __qualname__ v : User
k : __table__ v : users
k : id v :
v.primary_key: True
k : email v :
k : passwd v :
k : admin v :
k : name v :
k : image v :
k : created_at v :
fields: ['email', 'passwd', 'admin', 'name', 'image', 'created_at']
attrs: {'__module__': 'models', '__qualname__': 'User', '__table__': 'users'}
escaped_fields: ['`email`', '`passwd`', '`admin`', '`name`', '`image`', '`created_at`']
__fields__: ['email', 'passwd', 'admin', 'name', 'image', 'created_at']
__mappings__: {'id': ,
'blog_id': ,
'user_id': ,
'user_name': ,
'user_image': ,
'content': ,
'created_at': }
__select__: select `id`, `email`, `passwd`, `admin`, `name`, `image`, `created_at` from `users`
__insert__: insert into `users` (`email`, `passwd`, `admin`, `name`, `image`, `created_at`, `id`) values (?, ?, ?, ?, ?, ?, ?)
__update__: update `users` set `email`=?, `passwd`=?, `admin`=?, `name`=?, `image`=?, `created_at`=? where `id`=?
__delete__: delete from `users` where `id`=?
type.__new__(cls, name, bases, attrs):

然后发现在函数的for k, v in attrs.items(): 部分,把父类是Field的类收在mapping里面,把用户的属性的名称放在列表fields里面,然后再在attrs里面把id、name等类删掉,这样子,user.id和user.name等 就不会返回一个类了,他们是调用不了,因为已经删掉了这些属性,然后就会去调用__getattr__(), 之后就可以返回kw里面的值了!!!理解了这些,之后的一切都很简单了!!!


self.getValueOrDefault()

def getValueOrDefault(self, key):
value = getattr(self, key, None)
if value is None:
field = self.__mappings__[key]
if field.default is not None:
value = field.default() if callable(field.default) else field.default
logging.debug('using default value for %s: %s' % (key, str(value)))
setattr(self, key, value)
return value

这个函数就是用来为save服务的,避免获取失败,就可以使用刚开始设置好的default值。我们看到上面的输出,mapping是一个字典,存放的是从attrs中删掉的属性,是七个类。把这些类拿出来,来获取他的default值。


save()

async def save(self):
args = list(map(self.getValueOrDefault, self.__fields__))
args.append(self.getValueOrDefault(self.__primary_key__))
rows = await execute(self.__insert__, args)
if rows != 1:
logging.warning('failed to insert record: affected rows: %s' % rows)#warn

save就是用来把输入的信息保存到数据库,用map取出fields列表里面的属性对应的值。fields里面是不含ID的,ID放在__primary_key__里面,所以继续append进去。最后再用execute操控mysql保存进去。


findAll

@classmethod
async def findAll(cls, where=None, args=None, **kw):
' find objects by where clause. '
sql = [cls.__select__]#select
#print(cls.__select__)
if where:
sql.append('where')
sql.append(where)
if args is None:
args = []
orderBy = kw.get('orderBy', None)
if orderBy:
sql.append('order by')
sql.append(orderBy)
limit = kw.get('limit', None)
if limit is not None:
sql.append('limit')
if isinstance(limit, int):
sql.append('?')
args.append(limit)
elif isinstance(limit, tuple) and len(limit) == 2:
sql.append('?, ?')
args.extend(limit)
else:
raise ValueError('Invalid limit value: %s' % str(limit))
rs = await select(' '.join(sql), args)
print('in findAll [type(cls(**r)) for r in rs]: ',[type(cls(**r)) for r in rs])
print('in findAll [cls(**r) for r in rs]: ',[cls(**r) for r in rs])
print('rs: ',rs)

return [cls(**r) for r in rs]

先看一下下面的输出,就可以知道findall的返回值是什么。

我的数据库存在两个用户:
in findAll [type(cls(**r)) for r in rs]:
[
,

]
in findAll [cls(**r) for r in rs]:
[
{'id': '00156479459332916458e6d6c144f4f887214c26000cf41000', 'email': 'test1@example.com', 'passwd': '1234567890', 'admin': 0, 'name': 'Test', 'image': 'about:blank', 'created_at': 1564794593.32919},
{'id': '0015648105271120e5ba27b865f4001bc2d1884960050c2000', 'email': 'test@example.com', 'passwd': '12345678', 'admin': 0, 'name': 'Test', 'image': 'about:blank', 'created_at': 1564810527.11229},
]

[cls(**r) for r in rs] 是一个存放着N个类的列表,有N个用户。能明白 cls(**r) 的话就好理解。

举个例子就能明白:

在这里插入图片描述

cls是一个类,和函数一样样,有这个传参的用法(把**kw传进去)。

看到这里可能最开始的注册代码都忘了:

import orm
import asyncio
from models import User, Blog, Comment
async def test(loop):
await orm.create_pool(loop=loop, user='www-data', password='kx123456', db='awesome')
u = User(name='Test', email='test5@example.com',
passwd='123456789', image='about:blank')
await u.save()
loop = asyncio.get_event_loop()
loop.run_until_complete(test(loop))
loop.run_forever()
for x in test(loop):
pass

再看一下handler函数

@get('/')
async def index(request):
users = await User.findAll()
return {
'__template__': 'test.html',
'users': users
}

然后在html里面就可以使用user类了








All users
{% for u in users %}

{{ u.name }} / {{ u.email }}


{% endfor %}


然后就可以显示用户信息了。

在这里插入图片描述


总结

服务端、客户端、数据库三者的关系差不多都打通了,就可以天马行空了,自己就可以天就各种各样的功能。


参考

day5

Day5

还是Day5

asyncio

这个是add_routes


thanks!!!

推荐阅读
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
author-avatar
Smitty
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有