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

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

目录

一丶区县信息前端编写

二丶发布新房源后端接口编写

三丶发布新房源前端编写

四丶测试接口

五丶celery的使用

六丶测试celery

七丶celery以目录形式创建


一丶区县信息前端编写

1.分析,当用户进入发布新房源,页面加载完毕即向后端发送请求拿取数据库中的区域信息数据,显示在页面上所在区县栏

2.所以需在此页面newhouse.js中进行如下编写

  1. //当页面加载完成时,想后端获取区县信息
  2. $.get("/api/v1.0/areas", function (resp) {
  3. if (resp.errno == "0"){
  4. var areas = resp.data;
  5. for (i=0; i<areas.length; i++){
  6. var area = areas[i]
  7. $("#area-id").append('<option value="'+area.aid+'">'+area.aname+'</option>')
  8. }
  9. }else {
  10. alert(resp.errmsg)
  11. }
  12. }, "json")

3.当通过js向后端获取区域信息全部对象,通过循环遍历获取每个对象,将每个对象中的aid以及aname通过append方式追加到id为area_id的select标签下,所以最后需要在newhouse.html中将原有的option标签删除

  1. <select class="form-control" id="area-id" name="area_id">
  2. <!--<option value="1">锦江区</option>-->
  3. <!--<option value="2">青羊区</option>-->
  4. <!--<option value="3">金牛区</option>-->
  5. <!--<option value="4">武侯区</option>-->
  6. <!--<option value="5">成华区</option>-->
  7. <!--<option value="6">龙泉驿区</option>-->
  8. <!--<option value="7">青白江区</option>-->
  9. <!--<option value="8">新都区</option>-->
  10. <!--<option value="9">温江区</option>-->
  11. <!--<option value="10">郫都区</option>-->
  12. <!--<option value="11">双流区</option>-->
  13. <!--<option value="12">高新区</option>-->
  14. <!--<option value="13">天府新区</option>-->
  15. <!--<option value="14">新津县</option>-->
  16. <!--<option value="15">大邑县</option>-->
  17. <!--<option value="16">金堂县</option>-->
  18. </select>

4.测试,重新启动程序,清除页面缓存,查看发布房源页面中的所有区县栏是否有数据,此时显示的区县数据,就是从后端数据库获取的

5.查看网页Network,从api/v1.0/area接口获取的数据

6.当前端页面需要从后端拿取很多数据时候,如果用上面的方式通过jquery想后端获取数据,再填充到标签中,这样就太麻烦了,所以这里需要在前端去引入一个js模板,来帮助我们更快的完成页面数据的填充

  • step1 比如在房间信息详情页面,需要很多数据

7.将发布新房源页面中的所在区县栏使用前端js模板进行改写

  • step1 在newhouse.html中引入template.js
  1. <script src="/static/js/template.js"></script>
  • step2 在newhouse.html中定义模板
  1. <script type="text/html" id="areas-template">
  2. {{ each areas as area }}
  3. <option value="{{area.aid}}">{{area.aname}}</option>
  4. {{ /each }}
  5. </script>
  • step3 在newhouse.js中使用js模板
  1. var html_text =template("areas-template", {areas:areas});
  2. $("#area-id").html(html_text)
  • step4 测试,清除缓存,刷新网页,查看结果使用js模板成功

二丶发布新房源后端接口编写

1.流程分析,在发布房源前端文件newhouse.html中将该页面下的上传图片栏隐藏了#form-house-image,当用户完成先关填写后,该上传房屋图片表单才会显示出来

  • step1 在浏览器开发端进行显示

  • step2 查看发布房源界面

2.在house.py中进行房屋信息接口编写

  • step1 创建视图
  1. @api.route("/houses/info", methods=["POST"])
  2. @login_required
  3. def save_house_info():
  4. """
  5. 保存房屋信息
  6. :return:
  7. """
  8. pass
  • step2 获取数据
  1. user_id = g.user_id
  2. house_data = request.get_json()
  • step3 从获取的data数据中拿取必填字段的值,facility配套设施可能啥都没有,前端就会传个空列表
  1. title = house_data.get("title") # 房屋名称标题
  2. price = house_data.get("price") # 房屋单价
  3. area_id = house_data.get("area_id") # 房屋所属城区的编号
  4. address = house_data.get("address") # 房屋地址
  5. room_count = house_data.get("room_count") # 房屋包含的房间数目
  6. acreage = house_data.get("acreage") # 房屋面积
  7. unit = house_data.get("unit") # 房屋布局(几室几厅)
  8. capacity = house_data.get("capacity") # 房屋容纳人数
  9. beds = house_data.get("beds") # 房屋卧床数目
  10. deposit = house_data.get("deposit") # 押金
  11. min_days = house_data.get("min_days") # 最小入住天数
  12. max_days = house_data.get("max_days") # 最大入住天数
  • step4 校验参数
  1. if not all([title, price, area_id, address, room_count, acreage, unit,capacity, beds, deposit, min_days, max_days]):
  2. return jsonify(errno=RET.PARAMERR, errmsg="参数不完整")
  • step5 判断用户输入的房屋单价和押金是否为正确参数,通过存入数据库字段单位分,如果用户输入的值不能转换为float和int类型,说明参数错误
  1. try:
  2. price = int(float(price)*100)
  3. deposit = int(float(deposit)*100)
  4. except Exception as e:
  5. current_app.logger.error(e)
  6. return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
  • step6 判断区县id是否存在
  1. try:
  2. area = Area.query.get(area_id)
  3. except Exception as e:
  4. current_app.logger.error(e)
  5. return jsonify(errno=RET.DBERR, errmsg="数据库异常")
  • step7 如果在数据库中查询不到区域信息,表示区县信息有误
  1. if area is None:
  2. return jsonify(errno=RET.NODATA, errmsg="区县信息有误")
  • step8 保存房屋信息到数据库
  1. house = House(
  2. user_id = user_id,
  3. title = title,
  4. price = price,
  5. area_id = area_id,
  6. address = address,
  7. room_count = room_count,
  8. acreage = acreage,
  9. unit = unit,
  10. capacity = capacity,
  11. beds = beds,
  12. deposit = deposit,
  13. min_days = min_days,
  14. max_days = max_days
  15. )
  • step9 获取设备设施数据id值
  1. facility_ids = house_data.get("facility")
  • step10 对获取设备设施字段的值进行判断,下判断这个值存不存在,当用户勾选设备设施时,举例facility_ids值为[2,4]
  1. if facility_ids:
  2. # 通过Facility类中的id值使用in_方法查询其中的id
  3. # select * from ih_facility_info where id in facility_ids;
  4. try:
  5. facilities = Facility.query.filter(Facility.id.in_(facility_ids)).all()
  6. except Exception as e:
  7. current_app.logger.error(e)
  8. return jsonify(errno=RET.DBERR, errmsg="数据库异常")
  • step11 判断查询的每个facility对象是否存在,存在则保存设施数据
  1. if facilities:
  2. house.facilities = facilities
  3. try:
  4. db.session.add(house)
  5. db.session.commit()
  6. except Exception as e:
  7. current_app.logger.error(e)
  8. db.session.rollback()
  9. return jsonify(errno=RET.DBERR, errmsg="保存数据异常")
  • step12 返回正确响应内容
  1. return jsonify(errno=RET.OK, errmsg="OK", data={"house_id":house.id})

3.上传房屋图片接口编写

  • step1 定义视图
  1. @api.route("/houses/image", methods=["POST"])
  2. @login_required
  3. def save_house_image():
  4. """保存房屋图片"""
  5. pass
  • step2 获取图片
  1. image_file = request.files.get("house_image")
  • step3 获取图片id
  1. house_id = request.form.get("house_id")
  • step4 校验参数
  1. if not all([image_file, house_id]):
  2. return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
  • step5 判断房屋是否在,存在才上传到七牛,如果不存在就不上传
  1. try:
  2. house = House.query.get(house_id)
  3. except Exception as e:
  4. current_app.logger.error(e)
  5. return jsonify(errno=RET.DBERR, errmsg="数据库异常")
  • step6 house对象不存在则返回错误信息
  1. if house is None:
  2. return jsonify(errno=RET.NODATA, errmsg="房屋不存在")
  • step7 获取图片二进制数据
  1. image_data = image_file.read()
  • step8 将图片保存到七牛中
  1. try:
  2. file_name = storage(image_data)
  3. except Exception as e:
  4. current_app.logger.error(e)
  5. return jsonify(errno=RET.THIRDERR, errmsg="上传图片失败")
  • step9 保存图片信息到数据库中
  1. house_image = HouseImage(house_id=house_id, url=file_name)
  2. db.session.add(house_image)
  • step10 当house对象中的index_image_url不存在时,设置网站主页房屋图片
  1. if not house.index_image_url:
  2. house.index_image_url = file_name
  3. db.session.add(house)
  • step11 提交到数据库
  1. try:
  2. db.session.commit()
  3. except Exception as e:
  4. current_app.logger.error(e)
  5. db.session.rollback()
  6. return jsonify(errno=RET.DBERR, errmsg="保存图片异常")
  • step12 拼接图片完整url,最后返回正确响应数据
  1. image_url = constants.QINIU_URL_DOMAIN + file_name
  2. return jsonify(errno=RET.OK, errmsg="OK", data={"image_url":image_url})

三丶发布新房源前端编写

1.在newhouse.js中进行提交房屋信息表单事件编写

  1. $("#form-house-info").submit(function (e) {
  2. })
  • step1 阻止表单默认行为
  1. e.preventDefault();
  • step2 处理表单数据
  1. var data = {};
  2. $("#form-house-info").serializeArray().map(function (x) { data[x.name] = x.value });
  • step3 收集用户勾选的设备id
  1. var facility = [];
  2. $(":checked[name=facility]").each(function (index, x) {facility[index] = $(x).val()});
  • step4 将收集到的用户勾选id存到data数据库里的facility属性中
  1. data.facility = facility;
  • step5 通过ajax方式向后端接口发送请求
  1. $.ajax({
  2. url:"/api/v1.0/houses/info",
  3. type:"post",
  4. contentType:"application/json",
  5. data:JSON.stringify(data),
  6. dataType:"json",
  7. headers:{
  8. "X-CSRFToken": getCookie("csrf_token")
  9. },
  10. success: function (resp) {
  11. if (resp.errno == "4101"){
  12. location.href = "/login.html"
  13. } else if (resp.errno == "0"){
  14. // 将设备设施表单隐藏
  15. $("#form-house-info").hide();
  16. // 将上传房屋图片表单显示
  17. $("#form-house-image").show();
  18. // 设置上传房屋图片表单中的house_id
  19. $("#house-id").val(resp.data.house_id);
  20. }else {
  21. alert(resp.errmsg)
  22. }
  23. }
  24. },"json")

2.在newhouse.js中进行上传房屋图片表单事件编写

  1. $("#form-house-image").submit(function (e) {
  2. })
  • step1 阻止表单默认行为
  1. e.preventDefault();
  • step2 利用jquery.form.min.js插件通过的ajaxSubmit对表单进行异步提交,当后端返回正确响应时, 往class 为 house-image-cons div标签下去添加img标签,该div下的img标签为用户上传图片的标签,上传一个图片则在此div下追加一个img标签
  1. $(this).ajaxSubmit({
  2. url:"/api/v1.0/houses/image",
  3. type:"post",
  4. dataType:"json",
  5. headers:{
  6. "X-CSRFToken": getCookie("csrf_token")
  7. },
  8. success:function (resp) {
  9. if(resp.errno == "4101"){
  10. location.href = "/login.html"
  11. }else if (resp.errno == "0"){
  12. // 往class 为 house-image-cons div标签下去添加img标签
  13. $(".house-image-cons").append('<img src="' + resp.data.image_url+'">')
  14. }else {
  15. alert(resp.errmsg);
  16. }
  17. }
  18. })

四丶测试接口

1.运行项目,清除浏览器缓存,刷新网页,进入发布房源,博主这里使用的FS截图工具进行滚动窗口截图

2. 填写房屋信息

3.填写完数据后,点击发布房源信息,逻辑成功则显示上传房屋图片界面

查看数据库ih_house_info房屋信息表,数据显示为刚填写的表单勾选数据,此时并没有上传图片所以index_image_url为空

4.点击选择文件,选择桌面上的图片后,再点击上传,则显示在页面中

5.依次上传三张图片后

查看此时数据库ih_house_image表信息,在上图上传的三张图片是house_id为6的房屋例子,所以在下标显示house_id为6的图片url链接为三个

6.同时查看七牛云上的存储空间

五丶celery的使用

1.问题,我们在做网站后端程序开发时,会碰到这样的需求:用户需要在我们的网站填写注册信息,我们发给用户一封注册激活邮件到用户邮箱,如果由于各种原因,这封邮件发送所需时间较长,那么客户端将会等待很久,造成不好的用户体验.

2.解决,celery适用异步处理问题,当发送邮件、或者文件上传, 图像处理等等一些比较耗时的操作,我们可将其异步执行,这样用户不需要等待很久,提高用户体验。 celery的特点是:

  • 简单,易于使用和维护,有丰富的文档。
  • 高效,单个celery进程每分钟可以处理数百万个任务。
  • 灵活,celery中几乎每个部分都可以自定义扩展。

3.Task Queue

celery通过消息进行通信,通常使用一个叫Broker(中间人)来协client(任务的发出者)和worker(任务的处理者). clients发出消息到队列中,broker将队列中的信息派发给worker来处理。

  一个celery系统可以包含很多的worker和broker,可增强横向扩展性和高可用性能

4.在项目ihome目录下创建一个tasks包,用于处理项目中的需要的异步任务,在这个包下创建单一文件来处理对应的异步任务,这种方式是对于所有的web框架来说,都是万能的,像django中使用的djcelery包是别人针对于django框架封装好的包

  • step1 在task包下创建一个task_sms.py文件,在这个文件中去编写对于发送短信的任务

  • step2 定义celery对象
  1. app = Celery("ihome", broker="redis://127.0.0.1:6379/1")
  • step3 定义短信的异步任务
  1. @app.task
  2. def send_sms():
  3. """发送短信的异步任务"""
  4. pass
  • step4 导入之前定义好云通讯中封装的CCP类,并创建CCP的对象ccp
  1. from ihome.libs.yuntongxun.SendSMS import CCP
  • step5 通过ccp对象调用sendTemplateSMS方法,同时调用send_sms方法时传递三个参数,从这三个参数拿sendTemplateSMS方法中即可
  1. @app.task
  2. def send_sms(to, datas, tempId):
  3. """发送短信的异步任务"""
  4. ccp = CCP()
  5. ccp.sendTemplateSMS(to, datas, tempId)
  • step6 回到api_1_0目录下的verify_code中,将之前写的get_sms_code方法中发送短信逻辑代码进行重新编写,跟之前写的逻辑不一样,之前是发送成功则返回发送成功msg,失败则返回发送失败msg,大多网站都是现在以下这种逻辑
  1. # 使用celery异步发送短信
  2. send_sms.delay(mobile, [sms_code, int(constants.SMS_CODE_REDIS_EXPIRES/60)], 1)
  3. # 发送成功与否,用户自己去查看手机,大多网站都是这样做的
  4. return jsonify(errno=RET.OK, errmsg="发送短信成功")

六丶测试celery

1.在终端中运行项目

2.另开一个终端执行celery -A ihome.tasks.task_sms worker -l info,-l代表开启日志 info级别,但是出现了报错

3.在百度上查询此报错信息,明白原来是celery4.0以上版本不支持windows

4.所以博主这里进行 pip install celery==3.1.7 进行3.x版本安装

5.在新开启的终端上,重新执行celery -A ihome.tasks.task_sms worker -l info,报错问题解决

6.进入网站注册界面http://127.0.0.1:5000/register.html,后进行注册,输入博主本人手机号,输入正确验证码,然后点击获取短信验证码后,回头查看celery终端,结果又报错了

7.这次报的错好像是跟redis有关,查看redis版本为3.0的

8.将redis版本缓存2.0的,再看看是否报错,pip install redis==2.10.5

9.先运行项目,再开启另一终端执行celery -A ihome.tasks.task_sms worker -l info

  • step1 在注册页面进行注册

  • step2 查看celery终端日志,显示七牛SDK发送短信成功日志

  • step3 查看博主手机上短信验证码为987427

  • step4 查看redis数据库1中的keys

七丶celery以目录形式创建

1.在ihome/tasks目录下创建main.py用于启动文件,再创建一个config.py用于作配置文件,然后在tasks目录下创建一个sms包,再该包下创建一个tasks.py文件或者是celery.py文件用于创建worker

2.拆分task_sms.py文件

  • step1 在启动文件main.py中编写以下代码
  1. app = Celery("ihome", broker="redis://127.0.0.1:6379/1")
  • step2 将如下代码拷贝到tasks.py中
  1. @app.task
  2. def send_sms(to, datas, tempId):
  3. """发送短信的异步任务"""
  4. ccp = CCP()
  5. ccp.sendTemplateSMS(to, datas, tempId)
  • step3 将main.py中创建app对象中的配置文件拿到config.py中
  1. BROKER_URL = "redis://127.0.0.1:6379/1"
  2. CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/2'
  • step4 回到main.py中进入如下编写
  1. app = Celery("ihome")
  2. # 引入配置信息
  3. app.config_from_object(config)
  4. # 自动搜索任务
  5. app.autodiscover_tasks(["ihome.tasks.sms"])
  • step5 回到ihome/api_1_0/verify_code.py中,将之前写的代码进行修改,将不在从from ihome.tasks.task_sms去导入send_sms,而是从我们刚定义好的sms.tasks中去导入send_sms
  1. from ihome.tasks.sms.tasks import send_sms

3.测试

  • step1 首先启动项目,再另起终端启动worker,结果又特么报错了

  • step2 这个错是因为操作系统原因导致的,在linux系统上运行则不会保错

原因:因为windows操作系统的原因,在Windows中,多进程multiprocessing使用的是序列化pickle来在多进程之间转移数据,而socket对象是不能被序列化的,但是在linux操作系统上却没问题,因为在linux上多进程multiprocessing使用的是fork,所以在windows上可以改用多线程,而celery是使用多进程以及携程方式进行异步工作,博主又是在windows环境进行测试开发,所以这里先暂时这样,等开发完成部署在linux服务器即可

Flask项目之手机端租房网站的实战开发(十)的更多相关文章

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

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...

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

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 一丶项目介绍 产品:关于手机移动端的租房网站 角色:在这个产品中用户包括房东与房客 功能:房东可以在这个平台发布自己的房屋,房客可 ...

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

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...

  4. Flask项目之手机端租房网站的实战开发(十四)

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...

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

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...

  6. Flask项目之手机端租房网站的实战开发(十一)

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...

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

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...

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

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...

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

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...

随机推荐

  1. Ubunut PPA源概述

    Ubuntu 自带的“软件”应用,可以安装海量软件,既包括发行者支持的软件.社区支持的软件,也包括专有驱动和版权软件.有时,我们需要的软件通过这些渠道仍然无法找到.这时,可以到 PPA 软件源中查找. ...

  2. Lightroom 学习笔记

    16.8.28  白平衡:      夕阳照片,色温高大

  3. vue踩坑- 报错npm ERR! cb() never called!

    在vue项目中引入饿了么elementUI组件的步骤之中,出现以下的错误: D:\my-project-first>npm i element-ui -S Unhandled rejection ...

  4. Spring MVC源码——Servlet WebApplicationContext

    上一篇笔记(Spring MVC源码——Root WebApplicationContext)中记录了下 Root WebApplicationContext 的初始化代码.这一篇来看 Servlet ...

  5. from disk cache 与 from memory cache

    webkit资源的分类 webkit的资源分类主要分为两大类:主资源和派生资 http状态码 200 from memory cache 不访问服务器,直接读缓存,从内存中读取缓存.此时的数据时缓存到 ...

  6. Reuse Is About People and Education, Not Just Architecture

     Reuse Is About People and Education, Not Just Architecture Jeremy Meyer you MigHT AdopT THE AppRoA ...

  7. hdoj-1421-搬寝室【DP】

    搬寝室 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submis ...

  8. android 图片特效处理之图片叠加

    这篇将讲到图片特效处理的图片叠加效果.跟前面一样是对像素点进行处理,可参照前面的android图像处理系列之七--图片涂鸦,水印-图片叠加和android图像处理系列之六--给图片添加边框(下)-图片 ...

  9. 智课雅思词汇---二、词根acu和acr

    智课雅思词汇---二.词根acu和acr 一.总结 一句话总结:acu和acr:sharp锋利的,敏捷的: acuteacutelyacuity sharp锋利的,敏捷的 1.词根acr表示什么意思? ...

  10. es6 --- var const let

    var  const   let  区别 今天第一次遇到const定义的变量,查阅了相关资料整理了这篇文章.主要内容是:js中三种定义变量的方式const, var, let的区别. 1. const ...