还有,我在这里面定义了3个函数,关于pictures的显示和设置,这个稍微熟悉一点python的都知道,只不过在普通的类中,直接@property。而在sqlalchemy里面,用@hybrid_property即可。还有就是专门为返回json格式数据做准备的函数to_dict(),获取每个对象,然后执行这个函数就可以返回字典方式。这个大家可以用别的库,也可以用自己写的to_dict函数,我在很多地方强调,写代码一定要灵活,千万不能被框架限定死了。如果哪天觉得不好,也可以用flask别的扩展库实现串行化对象。
model.py方面添加好,下面就在view.py中添加如下函数:
view.py
1 @api.route('/get-multi-qiniu-token')
2 @login_check
3 def get_multi_qiniu_token():
4 count = request.args.get('count')
5
6 if not 0 < int(count) < 10:
7 return jsonify({'code': 0, 'message': '一次只能获取1到9个'})
8
9 key_token_s = []
10 for x in range(int(count)):
11 key = uuid.uuid1()
12 token = current_app.q.upload_token(current_app.bucket_name, key, 3600)
13 key_token_s.append((key, token))
14 return jsonify({'code': 1, 'key_token_s': key_token_s})
15
16
17 @api.route('/post-blog', methods=['POST'])
18 @login_check
19 def post_blog():
20 user = g.current_user
21
22 title = request.get_json().get('title')
23 text_content = request.get_json().get('text_content')
24 pictures = request.get_json().get('pictures')
25
26 newblog = SmallBlog(title=title, text_content=text_content, post_user=user)
27
28 newblog.pictures = pictures
29 db_session.add(newblog)
30 try:
31 db_session.commit()
32 except Exception as e:
33 print e
34 db_session.rollback()
35 return jsonify({'code': 0, 'message': '上传不成功'})
36 return jsonify({'code': 1, 'message': '上传成功'})
37
38
39 @api.route('/get-blogs')
40 @login_check
41 def get_blogs():
42 last_id = request.args.get('last_id')
43 if not int(last_id):
44 blogs = db_session.query(SmallBlog).order_by(desc(SmallBlog.id)).limit(10)
45 else:
46 blogs = db_session.query(SmallBlog).filter(SmallBlog.id < int(last_id)).order_by(desc(SmallBlog.id)).limit(10)
47 return jsonify({'code': 1, 'blogs': [blog.to_dict() for blog in blogs]})
多了3个函数,第一个函数get_multi_qiniu_token,看名字也可以看出来,这个就是获取七牛token的方法,只不过获取多个,这边限制在1到9个之间,自己上传一个数目,然后返回key和token。
第二个函数 post_blog就是一个提交新blog的过程,唯一要注意的就是pictures,在客户端直接上传数组,然后赋值就可以了。
第三个函数 get_blogs有点意思,其实就是根据last_id得到上面10条数据,如果第一次没有last_id,就填0就可以了。最主要是给客户端使用,你总不能一下子把所有数据都给客户端,通常都是一定的条目,然后下拉,根据最上面的id,来获取其上10条数据,这样可以无限下拉刷新,只要一个接口就好了。就跟微博一样。如果你有其他好办法,欢迎一起探讨。
服务器端写好了,我们就写客户端吧,客户端代码也很简单,针对这3个函数,写3个接口而已。
client.py
def get_multi_qiniu_token(self, count, path='/get-multi-qiniu-token'):
self.headers = {'token': self.token}
payload = {'count': count}
response = requests.get(url=self.base_url + path, params=payload, headers=self.headers)
response_data = json.loads(response.content)
key_token_s = response_data.get('key_token_s')
return key_token_s
def post_blog(self, title, text_content, picture_files, path='/post-blog'):
self.headers = {'token': self.token}
count = len(picture_files)
key_token_s = self.get_multi_qiniu_token(count=count)
pictures = []
for x in range(count):
put_file(key_token_s[x][1], key_token_s[x][0], picture_files[x])
pictures.append(self.qiniu_base_url + key_token_s[x][0])
payload = {'title': title, 'text_content': text_content, 'pictures': pictures}
self.headers = {'content-type': 'application/json', 'token': self.token}
response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)
response_data = json.loads(response.content)
print response_data.get('code')
return response_data
def get_blogs(self, last_id, path='/get-blogs'):
self.headers = {'token': self.token}
payload = {'last_id': last_id}
response = requests.get(url=self.base_url + path, params=payload, headers=self.headers)
response_data = json.loads(response.content)
return response_data
测试一下吧,
if __name__ == '__main__':
api = API_1_1()
u = api.login('13565208554', '123456')
# key_token_s = api.get_multi_qiniu_token(4)
api.post_blog(title=u'dsfsdfrerg434', text_content=u'是打发撒dsfsdgsdfsgs法上得分未违法',
picture_files=['./img/4.png', './img/2.png', './img/3.png'])
blogs = api.get_blogs(0)
print blogs
api.logout()
随便上传几个,打印一下,就可以看到效果了,好了,添加完毕。
下面我们就介绍一下结构,想象一下,我们目前添加了最最基本的功能,就要添加3个接口。其实一个功能慢慢做下去,几十个接口都是少的,如果都放在view.py里面,大家想象一个文件有多少接口,不要说看起来非常难看,就是以后查找bug,也不是一般的复杂。在传统python中,通常都是根据功能划分文件,当然也有其他的,就像我们上面所说的,只要找到适合你自己的,就去改变,不要被框架限定死。
我们目前在view.py里面,其实有很多功能了,有装饰器模块,验证模块,注册模块,七牛模块,博客模块等,其实现在接口少,就少分一点文件,这个只要灵活即可,没有必要限定自己,我们就划分auth.py, main.py, decorators.py,blogs.py这4个模块,其中auth.py里面包含验证和注册;main.py里面包含一些公用,包括运行接口前后以及七牛token和key的获取;decorators.py就是装饰器;blogs.py就是博客接口。大致先这么分,以后遇到其他接口,再动态添加即可。
转移一下之前的代码,把view.py的代码分别复制到各个py文件下,然后删除view.py文件,现在的文件结构如下:

是不是清晰多了?这里再把各个py文件的代码复制一下,最最重要的就是__init__.py里面的代码,一定要覆盖到所有接口。
# coding:utf-8
from flask import Blueprint
api = Blueprint('api1_1', __name__)
from . import auth, blogs, decorators, main
看清楚from . import auth, blogs, decorators, main这行代码,就是程序启动的时候,覆盖所有接口。
下面分别是auth.py, blogs.py, decorators.py, main.py文件,这里就不用一一介绍了吧。
auth.py
![]()
# coding:utf-8
from flask import Flask, request, jsonify, g, render_template, redirect, url_for, session, current_app
from app.model import User, db_session, SmallBlog, desc
import hashlib
import time
from app.util import message_validate
import random
from .decorators import login_check
from . import api
@api.route('/login', methods=['POST'])
def login():
phone_number = request.get_json().get('phone_number')
encryption_str = request.get_json().get('encryption_str')
random_str = request.get_json().get('random_str')
time_stamp = request.get_json().get('time_stamp')
user = User.query.filter_by(phone_number=phone_number).first()
if not user:
return jsonify({'code': 0, 'message': '没有此用户'})
password_in_sql = user.password
s = hashlib.sha256()
s.update(password_in_sql)
s.update(random_str)
s.update(time_stamp)
server_encryption_str = s.hexdigest()
if server_encryption_str != encryption_str:
return jsonify({'code': 0, 'message': '密码错误'})
m = hashlib.md5()
m.update(phone_number)
m.update(user.password)
m.update(str(int(time.time())))
token = m.hexdigest()
pipeline = current_app.redis.pipeline()
pipeline.hmset('user:%s' % user.phone_number, {'token': token, 'nickname': user.nickname, 'app_online': 1})
pipeline.set('token:%s' % token, user.phone_number)
pipeline.expire('token:%s' % token, 3600*24*30)
pipeline.execute()
return jsonify({'code': 1, 'message': '成功登录', 'nickname': user.nickname, 'token': token})
@api.route('/user')
@login_check
def user():
user = g.current_user
nickname = current_app.redis.hget('user:%s' % user.phone_number, 'nickname')
return jsonify({'code': 1, 'nickname': nickname, 'phone_number': user.phone_number})
@api.route('/logout')
@login_check
def logout():
user = g.current_user
pipeline = current_app.redis.pipeline()
pipeline.delete('token:%s' % g.token)
pipeline.hmset('user:%s' % user.phone_number, {'app_online': 0})
pipeline.execute()
return jsonify({'code': 1, 'message': '成功注销'})
@api.route('/set-head-picture', methods=['POST'])
@login_check
def set_head_picture():
head_picture = request.get_json().get('head_picture')
user = g.current_user
user.head_picture = head_picture
try:
db_session.commit()
except Exception as e:
print e
db_session.rollback()
return jsonify({'code': 0, 'message': '未能成功上传'})
current_app.redis.hset('user:%s' % user.phone_number, 'head_picture', head_picture)
return jsonify({'code': 1, 'message': '成功上传'})
@api.route('/register-step-1', methods=['POST'])
def register_step_1():
"""
接受phone_number,发送短信
"""
phone_number = request.get_json().get('phone_number')
user = User.query.filter_by(phone_number=phone_number).first()
if user:
return jsonify({'code': 0, 'message': '该用户已经存在,注册失败'})
validate_number = str(random.randint(100000, 1000000))
result, err_message = message_validate(phone_number, validate_number)
if not result:
return jsonify({'code': 0, 'message': err_message})
pipeline = current_app.redis.pipeline()
pipeline.set('validate:%s' % phone_number, validate_number)
pipeline.expire('validate:%s' % phone_number, 60)
pipeline.execute()
return jsonify({'code': 1, 'message': '发送成功'})
@api.route('/register-step-2', methods=['POST'])
def register_step_2():
"""
验证短信接口
"""
phone_number = request.get_json().get('phone_number')
validate_number = request.get_json().get('validate_number')
validate_number_in_redis = current_app.redis.get('validate:%s' % phone_number)
if validate_number != validate_number_in_redis:
return jsonify({'code': 0, 'message': '验证没有通过'})
pipe_line = current_app.redis.pipeline()
pipe_line.set('is_validate:%s' % phone_number, '1')
pipe_line.expire('is_validate:%s' % phone_number, 120)
pipe_line.execute()
return jsonify({'code': 1, 'message': '短信验证通过'})
@api.route('/register-step-3', methods=['POST'])
def register_step_3():
"""
密码提交
"""
phone_number = request.get_json().get('phone_number')
password = request.get_json().get('password')
password_confirm = request.get_json().get('password_confirm')
if len(password) < 7 or len(password) > 30:
# 这边可以自己拓展条件
return jsonify({'code': 0, 'message': '密码长度不符合要求'})
if password != password_confirm:
return jsonify({'code': 0, 'message': '密码和密码确认不一致'})
is_validate = current_app.redis.get('is_validate:%s' % phone_number)
if is_validate != '1':
return jsonify({'code': 0, 'message': '验证码没有通过'})
pipeline = current_app.redis.pipeline()
pipeline.hset('register:%s' % phone_number, 'password', password)
pipeline.expire('register:%s' % phone_number, 120)
pipeline.execute()
return jsonify({'code': 1, 'message': '提交密码成功'})
@api.route('/register-step-4', methods=['POST'])
def register_step_4():
"""
基本资料提交
"""
phone_number = request.get_json().get('phone_number')
nickname = request.get_json().get('nickname')
is_validate = current_app.redis.get('is_validate:%s' % phone_number)
if is_validate != '1':
return jsonify({'code': 0, 'message': '验证码没有通过'})
password = current_app.redis.hget('register:%s' % phone_number, 'password')
new_user = User(phone_number=phone_number, password=password, nickname=nickname)
db_session.add(new_user)
try:
db_session.commit()
except Exception as e:
print e
db_session.rollback()
return jsonify({'code': 0, 'message': '注册失败'})
finally:
current_app.redis.delete('is_validate:%s' % phone_number)
current_app.redis.delete('register:%s' % phone_number)
return jsonify({'code': 1, 'message': '注册成功'})
View Code