这次项目完全实现了前后端分离,所以在项目我们写了接口文档,以下是其中的一部分
接口文档参考
"""
#这部分文档,为整个项目的文档提供一个目录功能,一般写在主项目文件夹下。
### 爱家接口文档
#### 用户模块
- [注册接口](docs/user/user_register.md)
- [登录接口](docs/user/user_login.md)
- [登出接口](docs/user/user_logout.md)
"""
"""
# 这部分是具体对某个接口具体描述,以便前端可以了解请求类型,参数,以及各种响应状态
# 文件一般写在docs文件夹下
### 注册接口
#### request请求
post /user/register/
##### params参数:
mobile str 电话
password str 密码
password2 str 确认密码
#### response响应
##### 失败响应1:
{
"code": 900,
"msg": "参数错误"
}
##### 失败响应2:
{
"code": 1002,
"msg": "手机号码已注册"
}
##### 失败响应3:
{
"code": 1003,
"msg": "两次密码不一致"
}
##### 失败响应4:
{
"code": 1001,
"msg": "手机号码不符合规则"
}
##### 成功响应:
{
"code": 200,
"msg": "请求成功"
}
"""
"""
#### 我的房源接口
#### request请求
get /house/auth_myhouse/
#### response
##### 成功响应
{
"code": 200,
"hlist_list": [
{
"address": "\u91d1\u878d\u4e2d\u5fc3",
"area": "\u91d1\u725b\u533a",
"create_time": "2018-05-24 18:48:14",
"id": 9,
"image": "/static/upload\\jinrongcheng1.jpg",
"order_count": 0,
"price": 399,
"room": 8,
"title": "\u91d1\u878d\u57ce"
}
]
}
##### params参数:
address str 地址
area str 房屋所在区域
create_time data 创建时间
id int 房屋id
image str 房屋主图片路径
order_count int 订单数
price int 单价 单位分
room int 房屋数
title str 房屋标题
"""
OK = 200
SUCCESS = {'code': 200, 'msg': '请求成功'}
DATABASE_ERROR = {'code': 401, 'msg': '数据库访问失败'}
PARAMS_ERROR = {'code': 900, 'msg': '参数错误'}
# 用户模块
USER_REGISTER_PARAMS_ERROR = {'code': 1000, 'msg': '注册信息参数错误'}
USER_REGISTER_MOBILE_ERROR = {'code': 1001, 'msg': '手机号码不符合规则'}
USER_REGISTER_MOBILE_IS_EXSITS = {'code': 1002, 'msg': '手机号码已存在'}
USER_REGISTER_PASSWORD_IS_ERROR = {'code': 1003, 'msg': '两次密码不一致'}
USER_LOGIN_IS_NOT_EXSIST = {'code': 1004, 'msg': '用户不存在'}
USER_LOGIN_PASSWORD_IS_ERROR = {'code': 1005, 'msg': '密码错误'}
USER_UPLOAD_IMAGE_IS_ERROR = {'code': 1006, 'msg': '上传图片格式错误'}
USER_NAME_IS_EXSIT = {'code': 1007, 'msg': '用户名已存在'}
USER_AUTH_ID_CARD_IS_ERROR = {'code': 1008, 'msg': '身份证格式错误'}
# 房屋模块
MYHOUSE_USER_IS_NOT_AUTH = {'code': 2000, 'msg': '用户没有实名认证'}
MYHOUSE_UPLOAD_IMAGE_IS_ERROR = {'code': 2001, 'msg': '上传图片格式错误'}
MYHOUSE_UPLOAD_IMAGE_IS_EXIST = {'code': 2002, 'msg': '图片已存在'}
# 订单模块
ORDER_START_TIME_GT_END_TIME = {'code': 3000, 'msg': '创建订单时间有误'}
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash
from utils.functions import db
class BaseModel(object):
# 定义基础的模型
create_time = db.Column(db.DATETIME, default=datetime.now())
update_time = db.Column(db.DATETIME, default=datetime.now(), onupdate=datetime.now())
# 不用重复写添加和提交
def add_update(self):
db.session.add(self)
db.session.commit()
def delete(self):
db.session.delete(self)
db.session.commit()
# 多重继承
class User(BaseModel, db.Model):
__tablename__ = 'ihome_user'
id = db.Column(db.Integer, primary_key=True)
phone = db.Column(db.String(11), unique=True)
pwd_hash = db.Column(db.String(200))
name = db.Column(db.String(30), unique=True)
avatar = db.Column(db.String(100)) # 头像
id_name = db.Column(db.String(30)) # 实名认证的姓名
id_card = db.Column(db.String(18), unique=True) # 实名认证的身份证号码
houses = db.relationship('House', backref='user')
orders = db.relationship('Order', backref='user')
# 读
@property
def password(self):
return ''
# 写
# 在保存密码时直接加密
@password.setter
def password(self, pwd):
self.pwd_hash = generate_password_hash(pwd)
# 对比
def check_pwd(self, pwd):
return check_password_hash(self.pwd_hash, pwd)
# 为每个类写了一个转换为Json格式的方法,这样就不用在每次向前端传接口时,在编写额外的代码
def to_basic_dict(self):
return {
'id': self.id,
'avatar': self.avatar if self.avatar else '',
'name': self.name,
'phone': self.phone
}
ihome_house_facility = db.Table(
"ihome_house_facility",
db.Column("house_id", db.Integer, db.ForeignKey("ihome_house.id"), primary_key=True),
db.Column("facility_id", db.Integer, db.ForeignKey("ihome_facility.id"), primary_key=True)
)
import functools
# 定义了一个判断是否登录的装饰器
def is_login(view_fun):
@functools.wraps(view_fun)
# 这句是将要装饰函数的__name__付给装饰它的函数
# 这样可以避免几个函数同时调用这个装饰器,而互相影响
def decorator():
try:
# 验证用户是否登录
# if session['user_id']:
if 'user_id' in session:
return view_fun()
else:
return redirect('/user/login/')
except:
return redirect('/user/login/')
return decorator
@user.route('/register/', methods=['POST'])
def user_register():
register_dict = request.form
mobile = register_dict.get('mobile')
password = register_dict.get('password')
password2 = register_dict.get('password2')
# 检查这个3值是否有空,如果有一个值为空,返回True
if not all([mobile, password, password2]):
return jsonify(status_code.PARAMS_ERROR)
if not re.match(r'^1[345789]\d{9}$', mobile):
return jsonify(status_code.USER_REGISTER_MOBILE_ERROR)
if User.query.filter(User.phone == mobile).count():
return jsonify(status_code.USER_REGISTER_MOBILE_IS_EXSITS)
if password != password2:
return jsonify(status_code.USER_REGISTER_PASSWORD_IS_ERROR)
user = User()
user.phone = mobile
user.name = mobile
user.password = password
try:
user.add_update()
return jsonify(status_code.SUCCESS)
except Exception as e:
return jsonify(status_code.DATABASE_ERROR)
@user.route('/user/', methods=['PUT'])
@is_login
def user_profile():
profile_dict = request.form
file_dict = request.files
user = User.query.filter(User.id == session['user_id']).first()
if 'avatar' in file_dict:
f1 = file_dict['avatar']
# mimetype文件类型
if not re.match(r'^image/.*$', f1.mimetype):
return jsonify(status_code.USER_UPLOAD_IMAGE_IS_ERROR)
# 图片在flask中的存储路径
# 用这种方法,比直接写路径并用字符相加的方法,要节省内存
UPLOAD_DIRS = os.path.join(os.path.join(BASEDIR, 'static'), 'upload')
url = os.path.join(UPLOAD_DIRS, f1.filename)
# 将图片存在flask中
f1.save(url)
image_url = os.path.join('/static/upload', f1.filename)
user.avatar = image_url
try:
db.session.commit()
return jsonify(code=status_code.OK, url=image_url)
except Exception as e:
return jsonify(status_code.DATABASE_ERROR)
# 如果没有传参数,然后参数错误
else:
return jsonify(status_code.PARAMS_ERROR)
facilities_list = []
for fac_id in facilities:
fac = Facility.query.get(fac_id)
house.facilities.append(fac)
facilities_list.append(fac)
db.session.add_all(facilities_list)
#通过这种方法可以实现和上面一样的效果
if facilities:
#可以找到facilities中所有的id
facilitys = Facility.query.filter(Facility.id.in_(facilities)).all()
# 因为house.facilities接收的值是一个列表,所以不用一个一个用append添加
house.facilities = facilitys
house.add_update()
// 下面的两种方法都可以向后台传输文件数据,但明显第一种方法要更简单一点,也推荐用这一种
$(function () {
$("#form-avatar").submit(function(e){
e.preventDefault();
$(this).ajaxSubmit({
url:'/user/user/',
type:'PUT',
dataType:'json',
success:function (data) {
if (data.code == 200) {
$('#user-avatar').attr('src', data.url);
}else if (data.code == 1006){
alert(data.msg)
}
}
})
$.ajax({
url:'/user/user/',
type:'PUT',
data: new FormData($('#form-avatar')[0]),
cache: false,
processData: false,// 告诉jQuery不要去处理发送的数据
contentType: false,// 告诉jQuery不要去设置Content-Type请求头
success:function (data){
if (data.code == 200) {
$('#user-avatar').attr('src', data.url);
}
},
error:function (data) {
alert('请求失败')
}
})
});
//这种是用serialize序列化表单数据,在通过post传给后端
$('#form-house-info').submit(function (e) {
e.preventDefault();
$(this).serialize 可以序列化form表单的数据,在后端可以to.dict()将它转化成字典
$.post('/house/createhouse', $(this).serialize(), function (data) {
});
var newId = $(this).attr('area-id');
var newName = $(this).html();
// 获取当前页面url中的?后面的内容例如?id=1$aname='天'
path = location.search;
// 将字符串以‘=’拆分
oldId = location.search.split('&')[0].split('=')[1];
oldName = location.search.split('&')[1].split('=')[1];
// 替换新的内容
path = path.replace('aid=' + oldId, 'aid=' + newId);
path = path.replace('aname=' + oldName, 'aname=' + newName);
# 只需要问号后面的内容就可以用location.href来跳转页面
location.href = path
$.get('/house/housedetail/' + window.location.search, function (data) {
if (data.code == 200){
// template.js模板引擎
// 第一个参数是页面中要传输数据