flask项目1实战:5、房源信息相关操作(待完善)

在这里插入图片描述

(根据居然老师直播课内容整理)

一、所在城区信息

  • 本项目城区未进行省市县三级联动,仅考虑一个固定区域
  • 城区信息有多个地方使用,如:首页、房屋信息等页面都会使用
  • 所在城区调用时,不一定要登录,如首页时,未登录,就有可能访问所在城区信息
  • 城区信息基本不变动,并且频繁需要使用,建议将此数据存放到redis中,设置失效时间,失效后,再到数据库中查询

1、城区信息接口设计

1.1 请求方式

选项 方案
请求方法 GET
请求地址 /areas

1.2 请求参数:路径参数

参数名 类型 是否必传 说明
title 字符串 标题
price 字符串 单价,单位:分
area_id 字符串 归属地的区域编号
address 字符串 地址
room_count 字符串 房间数目
acreage 字符串 房屋面积
unit 字符串 房屋单元, 如几室几厅
capacity 字符串 房屋容纳的人数
beds 字符串 房屋床铺的配置
deposit 字符串 房屋押金
min_days 字符串 最少入住天数
max_days 字符串 最多入住天数,0表示不限制
facility 列表 房屋的设施

1.3 响应结果:JSON

名字 类型 是否必须 说明
errno 字符串 错误代码
errmsg 字符串 错误内容
data 列表 每个列表元素都一个字典:{}

2、城区信息实现

  • 查询 redis里面是否有城区信息 ,如果有,将redis中保存的数据返回
    • redis中保存的是json格式字符串
      • {data:[{aid:area_id,anmae:area_name},{aid:area_id,anmae:area_name},…]}
  • 从数据库中获取所有城区信息
  • 生成城区信息列表:列表元素
    • 为方便存取,在Area类中定义to_dict 方法
    • 对取出来的所有记录,用to_dict生成字典,再添加到列表中
  • 将数据保存redis中
    • 生成字典{data:[{aid:area_id,anmae:area_name},{aid:area_id,anmae:area_name},…]}
    • 将字典用json.dumps()转换成字符串
    • 将字符串保存到redis中
  • 返回数据
@api.route("/areas",methods=["GET"])
def get_area_info():
    """获取城区信息"""

    # 用redis中读取数据
    try:
        response_json = redis_store.get("area_info")
    except Exception as e:
        logging.error(e)
    else:
        # redis有缓存数据
        if response_json is not None:
            response_json = json.loads(response_json)
            # print(response_json)
            logging.info('redis cache')
            # return response_json, 200, {"Content-Type": "application/json"}
            return jsonify(errno=RET.OK, errmsg='OK', data=response_json['data'])

    # 查询数据库,读取城区信息
    try:
        area_li = Area.query.all()
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库异常')

    area_dict_li = []
    for area in area_li:
        area_dict_li.append(area.to_dict())

    # 将数据转成json字符串  {key:value} <= dict(key=value)
    # response_dict = dict(errno=RET.OK, errmsg='OK', data=area_dict_li)
    response_dict = dict(data=area_dict_li)
    response_json = json.dumps(response_dict)

    try:
        redis_store.setex("area_info", constants.AREA_INFO_REDIS_CACHE_EXPIRES, response_json)
    except Exception as e:
        logging.error(e)

    # return response_json, 200, {"Content-Type": "application/json"}
    return jsonify(errno=RET.OK, errmsg='OK', data=area_dict_li)

在这里插入图片描述

二、保存房屋的基本信息

  • 保存房屋信息,必须是登录后才能操作,且是身份认证过的客户才能发布信息

1、保存房屋基本信息接口设计

1.1 请求方式

选项 方案
请求方法 POST
请求地址 /houses/info

1.2 请求参数:路径参数

参数名 类型 是否必传 说明

1.3 响应结果:JSON

响应结果 响应内容
errno 错误编号
errmsg 错误信息
data 房屋信息

2、保存房屋基本信息实现

  • 首先应用登录装饰器
  • 如果没有登录,直接返回 sessionerr,跳转到登录界面
  • 进入函数后,首先获取g对象中的user_id
  • 获取参数
  • 参数校验
    • 房屋名称标题:应该防止敏感字符
    • 单价:(数据库一般保存为分)
    • 区域编号:判断是否存在
    • 房间数:>0 <最大数 (根据需求来)
    • 房屋面积:大于最小值
    • 房屋布局:(根据需求来)
    • 容纳人数:(根据需求来)
    • 卧床数:(根据需求来)
    • 押金: 同单价
    • 最小入住天数:
    • 最大入住天数:>=最小入住天数 <=(根据需求来)
  • 创建house实例,并赋值
  • 校验房屋设施列表:判断设施编号是否存在
    • select * from h_facility_info where id in “1,3,5”
    • 将有效设施列表赋值给house实例中
  • 保存数据

三、保存房屋的图片

1、保存房屋图片接口设计

1.1 请求方式

选项 方案
请求方法 POST
请求地址 /houses/image

1.2 请求参数:路径参数

参数名 类型 是否必传 说明
house_image files 房屋图片
house_id string 房屋ID

1.3 响应结果:JSON

响应结果 响应内容
errno 错误编号
errmsg 错误信息
data 房屋图片URL地址

2、保存房屋图片实现

  • 首先应用登录装饰器
  • 如果没有登录,直接返回 sessionerr,跳转到登录界面
  • 获取参数
  • 参数校验
    • 从数据库中检查房屋编号是否在
    • 图片信息(本项目未校验)
  • 保存图片到七牛云平台,获得保存图片的名称
  • 保存数据
    • 创建房屋图片对象,并赋值
    • 判断房屋是否有首页图片
      • 如果没有,将图片名称赋值给房屋信息的首页图片
    • 提交保存数据,失败回滚
  • 返回
    • 返回信息及组合的图片url地址

四、获取房东发布的房源信息

1、获取房东发布的房源信息接口设计

1.1 请求方式

选项 方案
请求方法 GET
请求地址 /user/houses

1.2 请求参数:g对象中user_id

参数名 类型 是否必传 说明
user_id string 用户ID

1.3 响应结果:JSON

响应结果 响应内容
errno 错误编号
errmsg 错误信息
data 房屋列表信息

2、获取房东发布的房源信息实现

@api.route("user/houses",methods=["GET"])
@login_required
def get_user_houses():
    """
    获取用户发布的房源
    :return: 发布的房源信息
    """
    # 获取当前的用户
    user_id = g.user_id

    try:
        user = User.query.get(user_id)
        houses = user.houses
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='获取数据失败')

    # 转成字典存放到列表中
    houses_list = []
    if houses:
        for house in houses:
            houses_list.append(house.to_basic_dict())

    return jsonify(errno=RET.OK, errmsg="OK", data={
    
    "houses": houses_list})

五、获取主页展示的房屋基本信息(轮播图)

  • 首页以轮播图的方式,从房源信息中筛选的部分房屋基本信息(图片)进行轮播展示
    • 数量有限制,不会太多
    • 具有一定排序规则,如竞价规则等(本项目采用定单多的顺序从高到低筛选)

1、获取主页展示的房屋基本信息接口设计

1.1 请求方式

选项 方案
请求方法 GET
请求地址 /houses/index

1.2 请求参数:无

1.3 响应结果:JSON

|响应结果|响应内容|
|errno|错误编号|
|errmsg|错误信息|
|data|房屋列表信息|

2、获取主页展示的房屋基本信息实现

  • 查询数据库
    • 按订单数量排序查询房屋信息,根据最大数据限制记录数
  • 将记录插入到空列表中,
  • 返回数据
@api.route("/houses/index",methods=["GET"])
def get_user_info():
    """
        获取首页房屋信息
        :return: 排序后的房屋信息
        """
    try:
        # 查询数据库,房屋订单最多的5条
        houses = House.query.order_by(House.order_count.desc()).limit(constants.HOME_PAGE_MAX_NUMS).all()
    except Exception as e:
        return jsonify(errno=RET.DBERR, errmsg="查询数据库失败")

    if not houses:
        return jsonify(errno=RET.NODATA, errmsg="查询没有数据")

    houses_list = []
    for house in houses:
        houses_list.append(house.to_basic_dict())

    house_dict = dict(errno=RET.OK, errmsg="OK", data=houses_list)
    json_houses = json.dumps(house_dict)

    # [<House 1>, <House 2>]
    return json_houses, 200, {
    
    "Content-Type": "application/json"}
    # return jsonify(errno=RET.OK, errmsg="OK", data=houses_list)

3、获取主页展示的房屋基本信息优化

  • 首页轮播图数据变动不用太频繁
  • 每次重新生成对系统资源消耗较大
  • 建议将轮播图存在到缓存中,在有效期内,直接使用

3.1优化实现

  • 首先查询缓存数据
  • 如果存在,即非None,直接返回
  • 如果不存在,
    • 查询数据库
      • 按订单数量排序查询房屋信息,根据最大数据限制记录数
    • 将记录插入到空列表中
    • 生成返回数据字典
    • 将数据转换成json格式
    • 将数据保存到redis中
    • 返回数据
@api.route("/houses/index",methods=["GET"])
def get_user_info():
    """
        获取首页房屋信息
        :return: 排序后的房屋信息
        """

    # 先查询缓存数据
    try:
        result=redis_store.get("home_page_data")
    except Exception as e:
        logging.error(e)
        result=None
    if result:
        # print("redis")
        return result.decode(), 200, {
    
    "Content-Type": "application/json"}
        return jsonify(errno=RET.OK, errmsg="OK", data=houses_list)
    else:
        try:
            # 查询数据库,房屋订单最多的5条
            houses = House.query.order_by(House.order_count.desc()).limit(constants.HOME_PAGE_MAX_NUMS).all()
        except Exception as e:
            return jsonify(errno=RET.DBERR, errmsg="查询数据库失败")

        if not houses:
            return jsonify(errno=RET.NODATA, errmsg="查询没有数据")

        houses_list = []
        for house in houses:
            houses_list.append(house.to_basic_dict())

        house_dict = dict(errno=RET.OK, errmsg="OK", data=houses_list)
        json_houses = json.dumps(house_dict)

        # try:
        #     redis_store.setex("home_page_data", constants.HOME_PAGE_DATA_REDIS_EXPIRES, json_houses)
        # except Exception as e:
        #     logging.error(e)

        # [<House 1>, <House 2>]
        return json_houses, 200, {
    
    "Content-Type": "application/json"}
        # return jsonify(errno=RET.OK, errmsg="OK", data=houses_list)

六、获取房屋详情信息

1、获取房屋详细信息接口设计

1.1 请求方式

选项 方案
请求方法 GET
请求地址 /user/houses

1.2 请求参数:g对象中user_id

参数名 类型 是否必传 说明
house_id 整形 房屋ID

1.3 响应结果:JSON

响应结果 响应内容
errno 错误编号
errmsg 错误信息
data {“house”:房屋详细信息,“user_id”:房东_id}

2、获取房屋详细信息实现

  • 获取登录用户id

    • 可能没有登录,所以不能g对象中取值
      • 从session中取user_id,如果不存在赋为-1,不让他为空出错
  • 验证参数

  • 根据house_id查询房屋信息,

  • 如果房屋信息不存在,返回提示错误

  • 将生成房东详情信息字典(在house类中创建 to_full_dict()方法)

    • 信息类型:信息值,但需要注意几个特殊的值
    • 用户图像user_avatar:需要判断是否存在(是否上传了图像),如果没有需要置空,如果有,需要拼接图像url
    • 房屋图片images:是外键,存在多个值,需要拼接成图片url
      • 取出图片信息(图片名称)后,拼接成图片url
      • 将图片url插入到列表中
      • 再将列表赋值图片img_urls,做房屋图片信息
    • 房屋设施facilities:是外键
  • 返回

  • 注意:

    • 返回值里包括user_id,目的是让前端判断是不是房东显示自己发布的房屋
      • 如果是user_id=house.user_id,表示登录用户为房东,正在看自己的房子,不显示预订按钮
      • 如果不是,则可以显示预订按钮
@api.route("/houses/<int:house_id>",methods=["GET"])
def get_house_detail(house_id):
    """
        获取房屋详情
        :param house_id: 房屋的ID
        :return: 房屋的详细信息
        """
    # 当前用户
    # session中user_id
    user_id=session.get("user_id","-1")

    if not house_id:
        return jsonify(errno=RET.PARAMERR,errmsg="参数错误")

    try:
        house=House.query.get(house_id)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR,errmsg="查询数据库失败")
    
    if not house:
        return jsonify(errno=RET.NODATA,errmsg="房屋信息不存在")
        
    house_data=house.to_full_dict()

    return jsonify(errno=RET.OK,errmsg="ok",data={
    
    "house":house_data,"user_id":user_id})

3、获取房屋详细信息优化

  • 将图片数据直接存放入缓存,(首页轮播图一般点击率较高,并且数据变动不大)

3.1 优化实现

  • 首先判断缓存是否存在图片
  • 获取到图片信息后,存入缓存后再返回
    # 先从缓存中查询数据
    try:
        result=redis_store.get("house_info_%s"% house_id)
    except Exception as e:
        logging.error(e)
        result=None

    if result:
        # result_dict=dict(errno=RET.OK,errmsg="ok",data={"house":result,"user_id":user_id})
        # return json.dumps(result_dict)
        # dict(name=12)  {"name": 12}
        return '{"errno":%s, "errmsg":"OK", "data":{"house": %s, "user_id": %s}}' % (RET.OK, result.decode(), user_id), 200, {
    
    "Content-Type": "application/json"}

    # 存入redis
    json_house = json.dumps(house_data)
    try:
        redis_store.setex("house_info_%s"% house_id,constants.HOUSE_DETAIL_REDIS_EXPIRE, json_house)
    except Exception as e:
        logging.error(e)
        print(e)

七、检索房源

  • 首页显示房屋轮播图,并提供房源检索功能
  • 检索条件:区域、入住时间
  • 显示有排序选择
  • 检索房源可以不登录,也可以登录

1、检索房屋信息接口设计

1.1 请求方式

选项 方案
请求方法 GET
请求地址 /houses

1.2 请求参数:g对象中user_id

参数名 类型 是否必传 说明
aid 整形 区域的id
sd string 开始时间
ed string 结束时间
sk 整形 排序
p 整形 页码

1.3 响应结果:JSON

响应结果 响应内容
errno 错误编号
errmsg 错误信息
data {“total_page”:total_page,“house”:房屋信息}

2、检索房屋信息实现

  • 接收参数
  • 校验参数
    • 如果sd、ed不为空,要能转换日期,否则返回参数错
    • 如果sd、ed都不为空,ed>=sd,否则返回参数错
    • 如果 aid 不为空,aid要存在,否则返回参数错
    • 如果sk不为空,sk取值必须在约定类型中,如果不在,赋值最常用的一个
    • 如果 p 不为空,p必须为整形,如果不是,直接赋为1
  • 设定检索起止时间内的房屋条件
    • 房屋信息表里没有订房的时间,所以要从定单表中找到可以入住的房间,即排除已经预订出去房间号即可在这里插入图片描述
    • 从订单表中取出冲突的房间号
    • 房屋表中排除冲突的房间号
      • 如果开始和结束时间都有,按上面条件处理
      • 如果仅有开始时间:order.end_date>=start_date
      • 如果仅有结束时间:order.begin_date<=end_data
  • 设定检索区域的房屋条件
  • 将条件放到列中,对房屋表进行组合查询
  • 按排序参数,进行设定排序
  • 创建分页实例:利用BaseQuery.paginate方法进行创建
  • 获得总页码
  • 获取分页的房屋数据
  • 将每一个房屋信息转换成字典,插入到列表中,形成房屋数组
  • 组合成返回数据,返回

3、检索房屋信息优化

  • 搜索数据放到redis中,以提高处理速度
  • 因为条件较多,按页用hash保存

3.1 优化实现

  • 数据校验完后,直接查询缓存
  • 如果有,直接返回
  • 检索出来后,保存到缓存
@api.route("/houses", methods=["GET"])
def get_house_list():
    """
    房屋的搜索页面
    :param: aid   区域的id   sd    开始时间 ed    结束时间 sk    排序  p  页码
    :return: 符合条件的房屋
    """
    # 接收参数
    start_date = request.args.get("sd")
    end_date = request.args.get("ed")
    area_id = request.args.get("aid")
    sort_key = request.args.get("sk")
    page = request.args.get("p")

    # 校验参数
    try:
        if start_date:
            start_date = datetime.strptime(start_date, "%Y-%m-%d")

        if end_date:
            end_date = datetime.strptime(end_date, "%Y-%m-%d")

        if start_date and end_date:
            assert start_date <= end_date  # 断言: 如果条件不满足,触发异常
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg="日期参数有误")

    if area_id:
        try:
            area = Area.query.get(area_id)
        except Exception as e:
            logging.error(e)
            return jsonify(errno=RET.PARAMERR, errmsg="区域参数有误")

    if sort_key not in ["new", "booking", "price-inc", "price-des"]:
        sort_key = "new"

    try:
        page = int(page)
    except Exception as e:
        logging.error(e)
        page = 1

    # 搜索的数据放到redis中
    # 选择什么数据类型
    # 字符串 : house_开始_结束_区域ID_排序_页数
    # hash:
    #     house_开始_结束_区域ID_排序 key
    #     value
    #     {
    
    
    #       "1": {1, 2, 3},
    #       "2": {1, 3}
    #     }

    # 查询缓存数据
    redis_key = "house_%s_%s_%s_%s" % (start_date, end_date, area_id, sort_key)
    try:
        resp_json = redis_store.hget(redis_key, page)
    except Exception as e:
        logging.error(e)
    else:
        if resp_json:
            return resp_json.decode(), 200, {
    
    "Content-Type": "application/json"}

    # 查询数据库
    # 过滤条件
    filter_params = []

    conflict_orders = None  # 冲突订单
    try:
        if start_date and end_date:
            # 查询冲突的订单
            # order.begin_data  <= end_date and order.end_date >= start_date
            conflict_orders = Order.query.filter(Order.begin_date <= end_date, Order.end_date >= start_date).all()
        elif start_date:
            conflict_orders = Order.query.filter(Order.end_date >= start_date).all()
        elif end_date:
            conflict_orders = Order.query.filter(Order.begin_date <= end_date).all()
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库异常')

    if conflict_orders:
        # 从订单中获取冲突的房屋ID
        conflict_house_id = [order.house_id for order in conflict_orders]  # 列表循环推导式

        if conflict_house_id:
            # house = House.query.filter(House.id.notin_(conflict_house_id))
            filter_params.append(House.id.notin_(conflict_house_id))

    if area_id:
        # 查询的条件
        filter_params.append(House.area_id == area_id)

    # 排序
    if sort_key == "booking":
        house_query = House.query.filter(*filter_params).order_by(House.order_count.desc())  # *filter_params?解包
    elif sort_key == "price-inc":
        house_query = House.query.filter(*filter_params).order_by(House.price.asc())
    elif sort_key == "price-des":
        house_query = House.query.filter(*filter_params).order_by(House.price.desc())
    else:  # 缺省 new
        house_query = House.query.filter(*filter_params).order_by(House.create_time.desc())

    # 处理分页  :  当前页码,每页最大记录,是否自动输出错误
    page_obj = house_query.paginate(page=page, per_page=constants.HOUSE_LIST_PAGE_NUMS, error_out=False)

    # 总页数
    total_page = page_obj.pages

    # 获取数据
    house_li = page_obj.items

    houses = []
    for house in house_li:
        houses.append(house.to_basic_dict())

    resp_dict = dict(errno=RET.OK, errmsg="OK", data={
    
    "total_page": total_page, "houses": houses})
    resp_json = json.dumps(resp_dict)

    # 将数据保存到redis中
    redis_key = "house_%s_%s_%s_%s" % (start_date, end_date, area_id, sort_key)
    try:
        # redis管道
        pipeline = redis_store.pipeline()

        pipeline.hset(redis_key, page, resp_json)
        pipeline.expire(redis_key, constants.HOUSE_LIST_PAGE_REDIS_CACHE_EXPIRES)
        pipeline.execute()
    except Exception as e:
        logging.error(e)

    return jsonify(errno=RET.OK, errmsg="OK", data={
    
    "total_page": total_page, "houses": houses})

猜你喜欢

转载自blog.csdn.net/laoluobo76/article/details/110729510
今日推荐