热门标签 | 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!!!

推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 本文介绍了django中视图函数的使用方法,包括如何接收Web请求并返回Web响应,以及如何处理GET请求和POST请求。同时还介绍了urls.py和views.py文件的配置方式。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
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社区 版权所有