python简易博客(七)–编写主体代码

  • by

正文:

装饰器:

讲的很通俗易懂:http://python.jobbole.com/86632
再来个一步一步分析的    http://python.jobbole.com/81683/
还有类装饰器等。前面的生成器,迭代器有什么具体作用,自己还有研究下

获取属性值:

https://blog.csdn.net/weixin_35955795/article/details/53053762

几大板块:

1.编写api     (有10来个api函数吧)

把url进行处理的函数,然后根据处理后的信息分发到不同的前端页面

@asyncio.coroutine
def auth_factory(app, handler):#利用middle在处理URL之前,把cookie解析出来,并将登录用户绑定到request对象上,这样,后续的URL处理函数就可以直接拿到登录用户
    @asyncio.coroutine
    def auth(request):
        logging.info('check user: %s %s' % (request.method, request.path))
        request.__user__ = None
        cookie_str = request.cookies.get(COOKIE_NAME)
        if cookie_str:
            user = yield from cookie2user(cookie_str)
            if user:
                logging.info('set current user: %s' % user.email)
                request.__user__ = user
        if request.path.startswith('/manage/') and (request.__user__ is None or not request.__user__.admin):
            return web.HTTPFound('/signin')
        return (yield from handler(request))
    return auth

2.用户注册和登录 (模板文件和api函数)

注册:api/user(也有判断)

@post('/api/users')
def api_register_user(*, email, name, passwd):#用户注册功能
    if not name or not name.strip():
        raise APIValueError('name')
    if not email or not _RE_EMAIL.match(email):
        raise APIValueError('email')
    if not passwd or not _RE_SHA1.match(passwd):
        raise APIValueError('passwd')
    users = yield from User.findAll('email=?', [email])
    if len(users) > 0:
        raise APIError('register:failed', 'email', 'Email is already in use.')
    uid = next_id()
    sha1_passwd = '%s:%s' % (uid, passwd)
    user = User(id=uid, name=name.strip(), email=email, passwd=hashlib.sha1(sha1_passwd.encode('utf-8')).hexdigest(), image='http://www.gravatar.com/avatar/%s?d=mm&s=120' % hashlib.md5(email.encode('utf-8')).hexdigest())
    yield from user.save()
    # make session cookie:
    r = web.Response()
    r.set_cookie(COOKIE_NAME, user2cookie(user, 86400), max_age=86400, httponly=True)
    user.passwd = '******'
    r.content_type = 'application/json'
    r.body = json.dumps(user, ensure_ascii=False).encode('utf-8')
    return r
用户口令是客户端传递的经过SHA1计算后的40位Hash字符串,所以服务器端并不知道用户的原始口令
页面的js函数判断(注册页面)
大多数Web框架提供了Session功能来封装保存用户状态的cookie,Session的缺点是服务器需要在内存中维护一个映射表来存储用户登录信息,如果有两台以上服务器,就需要对Session做集群,因此,使用Session的Web App很难扩展
这里:
直接读取cookie的方式来验证用户登录,每次用户访问任意URL,都会对cookie进行验证,这种方式的好处是保证服务器处理任意的URL都是无状态的,可以扩展到多台服务器
登陆:api/authenticate
@post('/api/authenticate') #登陆api的处理函数
def authenticate(*, email, passwd):
    if not email:
        raise APIValueError('email', 'Invalid email.')
    if not passwd:
        raise APIValueError('passwd', 'Invalid password.')
    users = yield from User.findAll('email=?', [email])
    if len(users) == 0:
        raise APIValueError('email', 'Email not exist.')
    user = users[0]
    # check passwd:
    sha1 = hashlib.sha1()
    sha1.update(user.id.encode('utf-8'))
    sha1.update(b':')
    sha1.update(passwd.encode('utf-8'))
    if user.passwd != sha1.hexdigest():
        raise APIValueError('passwd', 'Invalid password.')
    # authenticate ok, set cookie:
    r = web.Response()
    r.set_cookie(COOKIE_NAME, user2cookie(user, 86400), max_age=86400, httponly=True)
    user.passwd = '******'
    r.content_type = 'application/json'
    r.body = json.dumps(user, ensure_ascii=False).encode('utf-8')
    return r
计算加密cookie
def user2cookie(user, max_age): #计算加密的cookie
    '''
    Generate cookie str by user.
    '''
    # build cookie string by: id-expires-sha1
    expires = str(int(time.time() + max_age))
    s = '%s-%s-%s-%s' % (user.id, user.passwd, expires, _COOKIE_KEY)
    L = [user.id, expires, hashlib.sha1(s.encode('utf-8')).hexdigest()]
    return '-'.join(L)
解密cookie
@asyncio.coroutine
def cookie2user(cookie_str): #解密cookie
    '''
    Parse cookie and load user if cookie is valid.
    '''
    if not cookie_str:
        return None
    try:
        L = cookie_str.split('-')
        if len(L) != 3:
            return None
        uid, expires, sha1 = L
        if int(expires) < time.time():
            return None
        user = yield from User.find(uid)
        if user is None:
            return None
        s = '%s-%s-%s-%s' % (uid, user.passwd, expires, _COOKIE_KEY)
        if sha1 != hashlib.sha1(s.encode('utf-8')).hexdigest():
            logging.info('invalid sha1')
            return None
        user.passwd = '******'
        return user
    except Exception as e:
        logging.exception(e)
        return None
利用middle在处理URL之前,把cookie解析出来
解析cookie,并将登陆用户绑定到request对象上
@asyncio.coroutine
def auth_factory(app, handler):#利用middle在处理URL之前,把cookie解析出来,并将登录用户绑定到request对象上,这样,后续的URL处理函数就可以直接拿到登录用户
    @asyncio.coroutine
    def auth(request):
        logging.info('check user: %s %s' % (request.method, request.path))
        request.__user__ = None
        cookie_str = request.cookies.get(COOKIE_NAME)
        if cookie_str:
            user = yield from cookie2user(cookie_str)
            if user:
                logging.info('set current user: %s' % user.email)
                request.__user__ = user
        if request.path.startswith('/manage/') and (request.__user__ is None or not request.__user__.admin):
            return web.HTTPFound('/signin')
        return (yield from handler(request))
    return auth

利用middle在处理url之前,把cookie解析出来,并将登陆用户绑定到request对象上,这样,后续的url处理函数就可以直接拿到登陆用户(auth_factory(app,handler))

3.编写日志创建页  (mvvm,很重要)

REST API
创建blog
@post('/api/blogs')
def api_create_blog(request, *, name, summary, content):
    check_admin(request)
    if not name or not name.strip():
        raise APIValueError('name', 'name cannot be empty.')
    if not summary or not summary.strip():
        raise APIValueError('summary', 'summary cannot be empty.')
    if not content or not content.strip():
        raise APIValueError('content', 'content cannot be empty.')
    blog = Blog(user_id=request.__user__.id, user_name=request.__user__.name, user_image=request.__user__.image, name=name.strip(), summary=summary.strip(), content=content.strip())
    yield from blog.save()
    return blog

model用纯js对象表示(数据),view是纯html(显示)。angularjs,knockoutjs。简单易用的mvvm框架来实现blog的页面
el:绑定的view,即<div>标签
data,js对象表示的model
methods:view可以触发的js函数
接下来,我们在<form>标签里,用几个简单的v-model,就可以让vue和view关联起来
<input v-model=”name”,class=”uk-width-1-1″>

4.编写日志列表页   (api函数)

在apis里创建分页代码
class Page(object):
    '''
    Page object for display pages.
    '''
    def __init__(self, item_count, page_index=1, page_size=10):
        '''
        Init Pagination by item_count, page_index and page_size.
        >>> p1 = Page(100, 1)
        >>> p1.page_count
        10
        >>> p1.offset
        0
        >>> p1.limit
        10
        >>> p2 = Page(90, 9, 10)
        >>> p2.page_count
        9
        >>> p2.offset
        80
        >>> p2.limit
        10
        >>> p3 = Page(91, 10, 10)
        >>> p3.page_count
        10
        >>> p3.offset
        90
        >>> p3.limit
        10
        '''
        self.item_count = item_count
        self.page_size = page_size
        self.page_count = item_count // page_size + (1 if item_count % page_size > 0 else 0)
        if (item_count == 0) or (page_index > self.page_count):
            self.offset = 0
            self.limit = 0
            self.page_index = 1
        else:
            self.page_index = page_index
            self.offset = self.page_size * (page_index - 1)
            self.limit = self.page_size
        self.has_next = self.page_index < self.page_count self.has_previous = self.page_index > 1
    def __str__(self):
        return 'item_count: %s, page_count: %s, page_index: %s, page_size: %s, offset: %s, limit: %s' % (self.item_count, self.page_count, self.page_index, self.page_size, self.offset, self.limit)
    __repr__ = __str__
在handler里实现:
@get('/api/blogs')
def api_blogs(*, page='1'):
    page_index = get_page_index(page)
    num = yield from Blog.findNumber('count(id)')
    p = Page(num, page_index)
    if num == 0:
        return dict(page=p, blogs=())
    blogs = yield from Blog.findAll(orderBy='created_at desc', limit=(p.offset, p.limit))
    return dict(page=p, blogs=blogs)
管理界面:
@get('/manage/blogs')
def manage_blogs(*, page='1'):
    return {
        '__template__': 'manage_blogs.html',
        'page_index': get_page_index(page)
    }
模板页面首先通过API:GET /api/blogs?page=?拿到Model 然后,通过Vue初始mvvm View的容器是#vm,包含一个table,我们用v-repeat可以把Model的数组blogs直接变成多行的<tr> <tr v-repeat="blog: blogs" > 往Model的blogs数组中增加一个Blog元素,table就神奇地增加了一行;把blogs数组的某个元素删除,table就神奇地减少了一行。所有复杂的Model-View的映射逻辑全部由MVVM框架完成,我们只需要在HTML中写上v-repeat指令,就什么都不用管了 

配合vue:manage/blog/edit?id=….    /api/blogs/+blog.id+’/delete’

5.提升开发效率      (fab函数,没看)

watchdog库 接受文件变化的通知,如果是.py文件,就自动重启wsgiapp.py进程
subprocess实现进程的启动和终止,并把输入输出重定向到当前进程的输入输出中

6.部署

做python-web的搭建,做到想吐,各种问题
2018.5.19

标签:

发表评论

电子邮件地址不会被公开。 必填项已用*标注