Flask项目之手机端租房网站的实战开发(九)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41782425/article/details/86488529

说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!

接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/86302733

目录

一丶区县信息后端编写

二丶为区县信息补充缓存机制

三丶缓存数据同步问题

四丶完善后端用户模块

五丶完善前端用户模块

六丶测试用户模块中个人信息栏,我的爱家,实名认证

七丶效果动图展示


一丶区县信息后端编写

1.定义视图函数

@api.route("/areas")
def get_area_info():
    """
    获取区县信息
    :return: 
    """
    pass

2.逻辑编写

  • step1 从数据库中获取所有区县信息
try:
    # 从数据库中获取所有区县信息
    area_li = Area.query.all()
except Exception as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg="数据库异常")
  • step2 在Area类中构建一个to_dict方法,将对象转换成字典,因为我们给前端传输数据是需要传输json格式数据的
def to_dict(self):
    """将对象转换为字典"""
    data = {
        "aid": self.id,
        "aname": self.name
    }
    return data
  • step3 遍历对象列表,调用每个对象中的to_dict方法,将返回的值添加到area_dict_li列表中
# 将对象转换为字典
area_dict_li = []

for area in area_li:
    area_dict_li.append(area.to_dict())
  • step4 返回正确响应数据
return jsonify(errno=RET.OK, errmsg="OK", data=area_dict_li)

3.测试接口

  • step2 返回接口正确响应数据,因为数据库Area表没有数据,所以返回的响应数据为空

4.向数据库中ih_area_info以及ih_facility_info两张表插入测试数据

  • step1 执行如下SQL语句
INSERT INTO `ih_area_info`(`name`) VALUES ('锦江区'),('青羊区'),('金牛区'),('武侯区'),('成华区'),('龙泉驿区'),('青白江区'),('新都区'),('温江区'),('郫都区'),('双流区'),('高新区'),('天府新区'),('新津县'),('大邑县'),('金堂县');
INSERT INTO `ih_facility_info`(`name`) VALUES('无线网络'),('热水淋浴'),('空调'),('暖气'),('允许吸烟'),('饮水设备'),('牙具'),('香皂'),('拖鞋'),('手纸'),('毛巾'),('沐浴露、洗发露'),('冰箱'),('洗衣机'),('电梯'),('允许做饭'),('允许带宠物'),('允许聚会'),('门禁系统'),('停车位'),('有线网络'),('电视'),('浴缸');

ih_area_info表

ih_facility_info表

 

  • step2 再回到Postman中Send发送请求,显示出正确响应数据

 二丶为区县信息补充缓存机制

1.为什么要补充缓存机制

  • step1 在网站首页,需要频繁获取区域数据

  •  step2 在我的爱家中,发布房源也需要频繁获取区域数据

  • step3 以及在房源列表,搜索栏也要频繁获取区域数据

2.分析,因为区县一般都是固定的,不会经常变化,搜索页面和主页是用户经常访问的地方,那么即我们后端接口以及数据库就会被大量访问,而且是在很短时间内,这样可能会出现异常问题,导致访问速度缓慢,所以完美解决方法就是使用缓存

3.具体实现流程

  • step1 当用户访问区县信息时,就会调用后端我们写的get_area_info接口,之前写的代码是,每次访问都会从数据库中拿去数据,现在将这一块改变一下,先从redis数据中拿去数据库(这里选择redis数据库,因为是内存级的数据库)
  • step2 如果redis数据库中有区域数据,直接返回给前端
  • step3 当redis数据库中没有区域数据时,此时再去mysql数据库中进行数据获取,拿到的区域数据不会马上返回给前端,而是将拿到的数据保存到redis数据库中,最后才返回给前端

4.逻辑代码编写

思考:将数据保存到redis数据库中,此时需要思考,以怎么形式保存数据,保存哪些数据到redis数据库

实现:是将整个返回给前端正确响应数据全部以json格式的字符串保存

  • step1 将数据转换为json字符串
resp_dict = dict(errno=RET.OK, errmsg="OK", data=area_dict_li)
resp_json_str = json.dumps(resp_dict)
  • step2 将构建的json格式的字符串响应数据保存到redis数据库中, 并设置有效期
try:
    redis_store.setex("area_info",constants.AREA_INFO_REDIS_CACHE_EXPIRES, resp_json_str)
except Exception as e:
    current_app.logger.error(e)
  • step3 返回构造响应头数据,设置Content-Type为json,默认的为html
return resp_json_str, 200, {"Content-Type":"application/json"}
  • step4 调用接口一开始从redis数据库中拿去区域数据
try:
    resp_json_str = redis_store.get("area_info")
except Exception as e:
    current_app.logger.error(e)
else:
    if resp_json_str is not None:
        return resp_json_str, 200, {"Content-Type": "application/json"}
  • step5 当redis数据库中获取的数据不为空时,往日志info中记录一句话,方便测试是从mysql 还是 redis 获取的区域数据
current_app.logger.info("Area data from redis")

5.测试redis缓存是否成功

  • step1 运行项目

  • step2 在Postman测试工具中向后端接口

第一次Send发送请求,查看程序运行日志,并没有显示我们定义日志信息

第二次Send发送请求,则显示我们定义的info日志信息,说明区域数据是从redis数据库中获取的

  •  step3 查看redis数据库,并获取area_info键的值

三丶缓存数据同步问题

:怎么保证redis缓存的数据和mysql数据保持一致性?

第一种方式:mysql数据库维护人员在修改区域表数据时,将redis数据库key删除

第二种方式:就是我们这种简单暴力有效的方式,设置数据的有效期,让redis数据库来帮我们把数据进行删除

四丶完善后端用户模块

1.对profile模块中的update_name进行修改

  • step1 修改路由请求方式
@api.route("/users/name", methods=["PUT"])
  •  step2 从g对象中获取用户id
user_id = g.user_id
  • step3 获取前端请求中的参数
req_dict = request.get_json()
  • step4 判断参数是否为空
if not req_dict:
    return jsonify(errno=RET.PARAMERR, errmsg="参数不完整")
  • step5 获取用户修改的名字
name = req_dict.get("name")
  • step6 判断用户是否输入用户名
if name is None:
    return jsonify(errno=RET.PARAMERR, errmsg="名字不能为空")
  • step7 将用户修改后的名字保存到数据库,需要注意的是这里不需要判断名字name是否重复,因为当初在创建数据库表字段name的时候设置了唯一索引
try:
    User.query.filter_by(id=user_id).update({"name": name})
    db.session.commit()
except Exception as e:
    db.session.rollback()
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg="保存用户名字失败")
  • step8 因为在登录和注册接口编写时候最后都将name保存到session中,所以当用户修改name后,也需要将新的name进行session保存
 session["name"] = name
  • step9 最后返回正确响应
return jsonify(errno=RET.OK, errmsg="修改成功", data={"name": name})

2.获取个人信息在我的爱家中进行显示

  • step1 定义视图函数
@api.route("/user", methods=["GET"])
@login_required
def get_user_profile():
  • step2 获取用户id
user_id = g.user_id
  • step3 根据用户id在数据库中查询个人信息
try:
    user = User.query.get(user_id)
except Exception as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg="获取用户信息失败")
  • step4 判断用户对象是否为空
if User is None:
    return jsonify(errno=RET.NODATA, errmsg="无效操作")
  • step5 在models模块中构建一个to_dict方法,用于构建我们返回给前端的数据
def to_dict(self):
    """将对象转换为字典数据"""
    user_dict = {
        "user_id": self.id,
        "name": self.name,
        "mobile": self.mobile,
        "avatar": constants.QINIU_URL_DOMAIN + self.avatar_url if self.avatar_url else "",
        "create_time": self.create_time.strftime("%Y-%m-%d %H:%M:%S")
    }
    return user_dict    
  •  step6 回到profile模块中,返回正确响应数据给前端
return jsonify(errno=RET.OK, errmsg="OK", data=user.to_dict())

3.获取用户的实名认证信息

  • step1 定义视图函数
@api.route("/users/auth", methods=["GET"])
@login_required
def get_user_auth():
  • step2 获取用户id
user_id = g.user_id
  • step3 根据用户id在数据库中进行查询, 获取用户对象
try:
    user = User.query.get(user_id)
except Exception as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg="获取用户实名认证信息失败")
  • step4 判断用户对象是否为空
if user is None:
    return jsonify(errno=RET.NODATA, errmsg="无效操作") 
  • step5 在models模块中构建一个auth_to_dict方法,用于构建我们返回给前端的数据
def auth_to_dict(self):
    """将用户实名信息转换为字典数据"""
    auth_dict = {
        "user_id": self.id,
        "real_name": self.real_name,
        "id_card": self.id_card
    }
    return auth_dict
  • step6 回到profile模块中,返回正确响应数据给前端
return jsonify(errno=RET.OK, errmsg="OK", data=user.auth_to_dict())

4.向数据库中保存用户实名认证信息

  • step1 定义视图函数
@api.route("/users/auth", methods=["POST"])
@login_required
def set_user_auth():
  • step2 获取用户id
user_id = g.user_id
  • step3 获取前端发送请求中的参数
req_data = request.get_json()
  • step4 判断参数是否为空
if not req_data:
    return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
  • step5 获取用户输入的真实姓名和身份证号
real_name = req_data.get("real_name")
id_card = req_data.get("id_card")
  • step6 校验参数完整性
if not all([real_name,id_card]):
    return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
  • step7 将用户填写的真实姓名和身份证号保存到数据库中,这里在数据库进行数据更新是为什么将real_name和id_card默认为空,因为在后端来说,用户只能设置一次实名认证,当real_name和id_card为None时,才代用户是第一次进行认证,当获取的real_name和id_card不为空时,说明用户之前已经认证过了
try:
    User.query.filter_by(id=user_id, real_name=None, id_card=None).update({"real_name":real_name, "id_card":id_card})
    db.session.commit()
except Exception as e:
    current_app.logger.error(e)
    db.session.rollback()
    return jsonify(errno=RET.DBERR, errmsg="保存用户实名信息失败")
  • step8 向前端返回正确响应
return jsonify(errno=RET.OK, errmsg="OK")

五丶完善前端用户模块

1.在profile.html文件中进行如下修改

<form id="form-name" action="/api/profile/name" method="post">

2.在profile.js中进行如下编写

  • step1 在个人信息栏显示用户信息
// 在页面加载是向后端查询用户的信息
    $.get("/api/v1.0/user", function(resp){
        // 当后端接口检验登录装饰器返回4101状态码,表示用户未登录,即跳转到登录页面
        if (resp.errno == "4101") {
            location.href = "/login.html";
        }
        // 后端返回正确响应码,将响应数据中的name填写到id = user-name的标签内容上
        else if (resp.errno == "0") {
            $("#user-name").val(resp.data.name);
            // 头像链接存在则显示到src下
            if (resp.data.avatar) {
                $("#user-avatar").attr("src", resp.data.avatar);
            }
        }
    }, "json");
  • step2 将用户修改后的名字保存到数据库中
 // 将用户修改后名字向后端进行发送
    $("#form-name").submit(function(e){
        e.preventDefault();
        // 获取用户输入的名字
        var name = $("#user-name").val();

        if (!name) {
            alert("请填写用户名!");
            return;
        }
        $.ajax({
            url:"/api/v1.0/users/name",
            type:"PUT",
            data: JSON.stringify({name: name}),
            contentType: "application/json",
            dataType: "json",
            headers:{
                "X-CSRFTOKEN":getCookie("csrf_token")
            },
            success: function (data) {
                if (data.errno == "0") {
                    $(".error-msg").hide();
                    showSuccessMsg();
                } else if (data.errno == "4001") {
                    $(".error-msg").show();
                } else if (data.errno == "4101") {
                    location.href = "/login.html";
                }
            }
        });
    })

3.在我的爱家my.js中进行如下编写

$(document).ready(function(){
    $.get("/api/v1.0/user", function(resp){
        // 用户未登录
        if (resp.errno == "4101") {
            location.href = "/login.html";
        }
        // 查询到了用户的信息
        else if (resp.errno == "0") {
            $("#user-name").html(resp.data.name);
            $("#user-mobile").html(resp.data.mobile);
            if (resp.data.avatar) {
                $("#user-avatar").attr("src", resp.data.avatar);
            }
        }
    }, "json");

});

4.在实名认证auth.js中进行如下编写

  • step1 当页面加载完毕时,想后端接口发送请求来获取用户的姓名和身份证
$(document).ready(function(){
    // 查询用户的实名认证信息
    $.get("/api/v1.0/users/auth", function(resp){
        // 4101代表用户未登录
        if (resp.errno == "4101") {
            location.href = "/login.html";
        } else if (resp.errno == "0") {
            // 如果返回的数据中real_name与id_card不为null,表示用户有填写实名信息
            if (resp.data.real_name && resp.data.id_card) {
                $("#real-name").val(resp.data.real_name);
                $("#id-card").val(resp.data.id_card);
                // 给input添加disabled属性,禁止用户修改
                $("#real-name").prop("disabled", true);
                $("#id-card").prop("disabled", true);
                // 隐藏提交保存按钮
                $("#form-auth>input[type=submit]").hide();
            }
        } else {
            alert(resp.errmsg);
        }
    }, "json");
  • step2 当页面加载完毕时,如果用户已经进行了实名认证,那么就不显示保存按钮以及对输入框进行禁止修改
// 管理实名信息表单的提交行为
    $("#form-auth").submit(function(e){
        e.preventDefault();
        // 如果用户没有填写完整,展示错误信息
        var realName = $("#real-name").val();
        var idCard = $("#id-card").val();
        if (realName == "" ||  idCard == "") {
            $(".error-msg").show();
        }

        // 将表单的数据转换为json字符串
        var data = {
            real_name: realName,
            id_card: idCard
        };
        var jsonData = JSON.stringify(data);

        // 向后端发送请求
        $.ajax({
            url:"/api/v1.0/users/auth",
            type:"post",
            data: jsonData,
            contentType: "application/json",
            dataType: "json",
            headers: {
                "X-CSRFTOKEN": getCookie("csrf_token")
            },
            success: function (resp) {
                if (resp.errno == "0") {
                    $(".error-msg").hide();
                    // 显示保存成功的提示信息
                    showSuccessMsg();
                    $("#real-name").prop("disabled", true);
                    $("#id-card").prop("disabled", true);
                    $("#form-auth>input[type=submit]").hide();
                }
            }
        });
    })

5.当用户进行实名制后,那么在我的房源页面,就不应该显示去实名认证按钮

  • step1 实名制之前显示界面

 

  • step2 在myhouse.js中进行判断验证,如果用户已经实名制了,那么就不显示此按钮,否则才会显示
$(document).ready(function(){
    // 对于发布房源,只有认证后的用户才可以,所以先判断用户的实名认证状态
    $.get("/api/v1.0/users/auth", function(resp){
        if ("4101" == resp.errno) {
            // 用户未登录
            location.href = "/login.html";
        } else if ("0" == resp.errno) {
            // 未认证的用户,在页面中展示 "去认证"的按钮
            if (!(resp.data.real_name && resp.data.id_card)) {
                $(".auth-warn").show();
                return;
            }
        }
    })
});
  • step3 实名制之后显示页面

六丶测试用户模块中个人信息栏,我的爱家,实名认证

1.个人信息栏测试

  • step1 登录网站,进入个人信息栏,显示如下

  • step2 当修改用户名后,点击保存,显示保存成功提示

 

 2.我的爱家栏测试,成功显示出用户头像,用户名以及手机号

3.用户实名认证栏测试

  • step1 首次进行实名认证,界面显示如下

  • step2 当未填写姓名和身份证号时,出现如下显示

  • step3 当输入完整信息后,会显示保存成功,出现如下界面

  • step4 查看数据库信息

  • step5 此号码已经实名认证成功,再次进入实名认证,则显示如下界面

 七丶效果动图展示

猜你喜欢

转载自blog.csdn.net/qq_41782425/article/details/86488529