• Flask-爱家租房项目ihome-07-我的房源列表页和房源详情页


    我的房源列表页

    用户进入我的房源页面时, 应该展示该用户发布的房源列表信息

    image-20200827225636224

    房源列表后端逻辑

    需要给前端返回房屋ID/标题/城区/价格/发布时间/默认图片的数据, 可以在接口中一个一个查询返回, 也可以在房屋的模型类Houses中添加一个方法get_list_info用来汇总维护这些字段信息.

    # ihome/models.py
    class Houses(BasicModel):
        """房屋模型类"""
        __tablename__ = 'ih_houses'
        ......
        # 房屋列表页的信息
        def get_list_info(self):
            return {
                'house_id': self.id,
                'title': self.title,
                'area_name': self.area.name,
                'price': self.price,
                'created_date': datetime.strftime(self.created_date, '%Y-%m-%d %H:%M:%S'),
                'img_url': self.default_image_url,
            }
    

    注:

    1. 创建时间需要从datetime类型转化为字符串类型

    在房屋视图文件ihome/api_1_0/houses.py中, 添加返回房屋列表信息的后端接口, url为: api/v1.0/user/houses

    @api.route('/user/houses')
    @login_required
    def get_user_houses():
        """返回我的房源列表信息"""
        user = g.user
        # 获取该用户下的房屋对象列表
        try:
            house_objs = user.houses
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg='获取房屋信息异常')
        # 获取房屋列表页需要展示的信息
        houses = [obj.get_list_info() for obj in house_objs]
        return jsonify(errno=RET.OK, data=houses)
    

    房屋列表前端逻辑

    后端返回的json格式为[{"房屋1": xxxx},{"房屋2": xxxx},{"房屋3": xxxx}], 所以前端需要遍历整个列表, 把每个房屋的信息提取出来, 并展示. 目前最好的方法还是使用前端模板, 因为在模板中可以循环数据并展示. 继续使用art-template模板插件

    编辑html文件

    首先编辑对应的html文件myhouse.html, 在展示房屋列表处添加模板代码

    <ul id="houses-list" class="houses-list">
        <li>
            <div class="new-house">
                <a href="/newhouse.html">发布新房源</a>
            </div>
        </li>
        <script type="text/html" id="list-house-info">
            {{ each houses as house}}
                <li>
                    <a href="/detail.html?id={{house.house_id}}">
                        <div class="house-title">
                            <h3>房屋ID:{{ house.house_id }} —— {{ house.title }}</h3>
                        </div>
                        <div class="house-content">
                            <img src="{{ house.img_url }}">
                            <div class="house-text">
                                <ul>
                                    <li>位于:{{ house.area_name }}</li>
                                    <li>价格:¥{{ house.price }}/晚</li>
                                    <li>发布时间:{{ house.created_date }}</li>
                                </ul>
                            </div>
                        </div>
                    </a>
                </li>
            {{ /each }}
        </script>
    </ul>
    

    注:

    1. 模板代码需要使用<script type="text/html" id=xxxx>的标签包裹, 整段script脚本可以放在任意位置, 不过最好是哪里需要编写模板就放在哪里

    2. art-template的语法中使用each可以遍历列表和js对象, houses为js逻辑处理返回给模板的参数, 使用as关键字可以给里面的元素重命名, 使用起来比较方便, 也可以使用{{$index}}表示下标索引 {{$value}}表示值

      {{each target}}
          {{$index}} {{$value}}
      {{/each}}
      

    编辑js文件

    编辑对应的js文件myhouse.js, 添加发送请求和处理模板的代码

    //发送ajax请求获取我的房屋信息
    $.get('api/v1.0/user/houses', function (resp) {
        if (resp.errno == '0'){
            //使用art-template模板发送房屋信息, 获取html文本
            var html = template('list-house-info', {houses:resp.data})
            //将html文本放到合适的位置
            $('.houses-list').append(html)
        }
    }, 'json')
    

    注:

    1. template函数的第一个参数为html模板中script标签的ID属性, 第二个参数必须是一个js对象, key为给html模板传入的参数名, val为传入的参数值
    2. template函数返回的是一个html文本, 因此需要通过jQuery把该文本设置到模板想要放入的位置

    房源详情页

    在我的房源列表也中点击具体的房源标题, 即可进入房源详情页, 对应的url为: /detail.html?id=房屋ID

    image-20200827170652568

    房屋详情页后端逻辑

    模型类中添加统一返回信息的逻辑

    与我的房源列表页类似, 将前端需要展示的房屋信息发送过去就好了, 同样在房屋的模型类Houses中添加统一返回详情的方法.

    # ihome/models.py
    class Houses(BasicModel):
        """房屋模型类"""
        __tablename__ = 'ih_houses'
        ......
        # 房屋详细信息
        def get_detail_info(self):
            """获取房屋详细信息"""
            return {
                'img_urls': [image.image_url for image in self.images],
                'title': self.title,
                'price': self.price,
                'owner_id': self.user.id,
                'owner_img_url': self.user.image_url,
                'owner_name': self.user.name,
                'address': self.address,
                'room_count': self.room_count,
                'acreage': self.acreage,
                'unit': self.unit,
                'capacity': self.capacity,
                'beds': self.beds,
                'deposit': self.deposit,
                'min_days': self.min_days,
                'max_days': self.max_days,
                'facilities': [facility.id for facility in self.facilities],
                'comments': [order.get_comment() for order in self.get_comment_orders()],
            }
        
        def get_comment_orders(self):
            """获取房屋评论过的订单"""
            # 房屋ID当前房屋的/订单状态为已完成的/评论内容不为空的/根据最后更新时间倒叙排序/获取前10条评论展示
            orders = Orders.query.filter(Orders.house_id == self.id, Orders.status == 'COMPLETED',
                                         Orders.comment is not None).order_by(Orders.updated_date.desc()).limit(
                COMMENT_DISPLAY_COUNTS).all()
            return orders
    
    # 订单模型类
    class Orders(BasicModel):
        """订单模型类"""
        __tablename__ = 'ih_orders'
        ......
        # 获取评论信息
        def get_comment(self):
            """获取评论信息"""
            return {
                'user_name': self.user.name if self.user.name != self.user.phone else '匿名用户',
                'comment_date': datetime.strftime(self.updated_date, '%Y-%m-%d %H:%M:%S'),
                'comment': self.comment
            }
    

    注:

    1. 一个房源中允许存在多条图片/设施/评论信息, 因此使用列表生成式遍历反向关系对象, 再获取相应的属性
    2. 在获取评论时, 需要从该房屋关联的订单中获取, 且需要获取评论人/时间/评论内容, 所以把这个过程分为两步:
      • 首先获取到相关的订单, 需要限制订单的状态和评论是否为空, 并且一般都是把最新的评论展示在前面, 因此需要按最后更新日期倒序, 最终获取的是订单对象列表.
      • 通过列表生成式遍历订单对象列表, 获取订单的评论相关的信息, 再生成一个新的评论内容列表.

    编写详情页接口

    在房屋视图文件house.py中添加返回详情页信息的接口, url为: api/v1.0/houses/房屋ID

    @api.route('/houses/<int:house_id>')
    def get_house_info(house_id):
        # 获取当前登录用户, 为-1则说明未登录
        user_id = session.get('user_id', '-1')
        # 查询缓存中是否存在数据
        redis_key = f'house_info_{house_id}'
        try:
            info_json = redis_connect.get(redis_key).decode()
        except Exception as e:
            current_app.logger.error(e)
            info_json = None
        # 缓存中不存在则查询房屋信息
        if not info_json:
            try:
                house = Houses.query.get(house_id)
            except Exception as e:
                current_app.logger.error(e)
                return jsonify(errno=RET.DBERR, errmsg='获取房屋信息异常')
            if not house:
                return jsonify(errno=RET.NODATA, errmsg='房屋ID不存在')
            # 获取房屋详情
            info = house.get_detail_info()
            # 将字典转为json
            info_json = json.dumps(info)
            # 存入缓存中
            redis_connect.setex(redis_key, constants.HOUSE_REDIS_EXPIRES, info_json)
    
        return f'{{"errno": 0, "data": {{"user_id": {user_id}, "house": {info_json}}}}}', 200, {'Content-Type': 'application/json'}
    

    注:

    1. 由于房屋详细内容很少改变且访问频率比较高, 所以使用redis缓存这些信息, 存入的数据就是转换后的json字符串, 可能是由于内容中包含中文, 存入的是编码后的数据, 所以取出时需要手动解码一下.
    2. 这里将当前登录用户user_id和房东owner_id都传给了前端, 前端根据这两个值判断是否显示预定按钮
    3. 由于info_json本身就已经是字符串类型了, 因此最终返回时采用元组的形式返回, 第一个是返回的json字符串, 第二个是http状态码, 第三个是headers(这里需要指定返回的类型为application/json)
    4. 其中第一个值需要再加上errnodata键值对, 因此需要格式化拼接字符串, 在f-string的格式化中, 一对大括号表示引用变量, 两对大括号{{ }}表示一对大括号.

    房屋详情页前端逻辑

    由于需要展示的房屋信息很多, 不好去用jQuery一个一个获取标签然后设置值, 因此还是使用了前端模板art-template

    修改html文件, 添加模板

    编辑对应的html文件detail.html, 在展示房屋图片和房屋具体信息处添加模板代码

    <div class="swiper-container">
        <script type="text/html" id="house-images">
            <div class="swiper-pagination"></div>
            <ul class="swiper-wrapper">
                {{ each houseImages as image }}
                <li class="swiper-slide"><img src="{{ image }}"></li>
                {{ /each }}
            </ul>
            <div class="house-price">¥<span>{{ price }}</span>/晚</div>
        </script>
    </div>
    ......
    <div class="detail-con">
        <script type="text/html" id="house-info">
            <div class="detail-header layout-style">
                <h2 class="house-title">{{ house.title }}</h2>
                <div class="landlord-pic"><img src="{{ house.owner_img_url }}"></div>
                <h2 class="landlord-name">房东: <span>{{ house.owner_name }}</span></h2>
            </div>
            <div class="house-info layout-style">
               <h3>房屋地址</h3>
               <ul class="house-info-list text-center">
                    <li>{{ house.address }}</li>
               </ul>
            </div>
            <ul class="house-type layout-style">
                <li>
                    <span class="icon-house"></span>
                    <div class="icon-text">
                        <h3>出租{{ house.room_count }}间</h3>
                        <p>房屋面积:{{ house.acreage }}平米</p>
                        <p>房屋户型:{{ house.unit }}</p>
                    </div>
                </li>
                <li>
                    <span class="icon-user"></span>
                    <div class="icon-text">
                        <h3>宜住{{ house.capacity }}人</h3>
                    </div>
                </li>
                <li>
                    <span class="icon-bed"></span>
                    <div class="icon-text">
                        <h3>卧床配置</h3>
                        <p>{{ house.beds }}</p>
                    </div>
                </li>
            </ul>
            <div class="house-info layout-style">
                <h3>房间详情</h3>
                <ul class="house-info-list">
                    <li>收取押金<span>{{ house.deposit }}</span></li>
                    <li>最少入住天数<span>{{ house.min_days }}</span></li>
                    <li>最多入住天数<span>{{ if house.max_days>=0 }}{{ house.max_days }}{{ else }}无限制{{ /if }}</span></li>
                </ul>
            </div>
            <div class="house-facility layout-style">
                <h3>配套设施</h3>
                <ul class="house-facility-list clearfix">
                    <li><span class="{{ if house.facilities.indexOf(1)>=0 }}wirelessnetwork-ico {{ else }}jinzhi-ico{{ /if }}"></span>无线网络</li>
                    <li><span class="{{ if house.facilities.indexOf(2)>=0 }}shower-ico {{ else }}jinzhi-ico{{ /if }}"></span>热水淋浴</li>
    .........
                </ul>
            </div>
            {{ if house.comments.length != 0 }}
            <div class="house-info layout-style">
                <h3>评价信息</h3>
                <ul class="house-comment-list">
                    {{ each house.comments as comment }}
                    <li>
                        <p>用户: {{ comment.user_name }}<span class="fr">{{ comment.comment_date }}</span></p>
                        <p>评论内容: {{ comment.comment }}</p>
                    </li>
                    {{ /each }}
                </ul>
            </div>
            {{ /if }}
        </script>
    </div>
    

    修改js文件, 添加调用模板代码

    编辑对应的js文件detail.js, 添加发送请求和处理模板的代码

    $(document).ready(function(){
        // 获取url的参数, 房屋id
        var url_param = decodeQuery()
        // url中存在房屋ID则发送ajax请求获取房屋数据
        if (url_param !== undefined) {
            $.get('api/v1.0/houses/' + url_param.id, function (resp) {
                if (resp.errno == '0') {
                    //获取成功
                    var data = resp.data;
                    //使用template设置图片
                    $('.swiper-container').html(template('house-images', {houseImages: data.house.img_urls, price: data.house.price}))
                    //展示其他信息
                    $('.detail-con').html(template('house-info', {house: data.house}))
                    //不是当前用户不是房东则展示即刻预定按钮
                    if (data.user_id != data.house.owner_id) {
                        $(".book-house").show();
                    };
                    
                    //设置预定按钮的跳转url
                    if (data.user_id != -1){
                        //登录了则进入预定界面
                        var url = '/booking.html?id=' + url_param.id;
                    }else{
                        //未登录则进入登录界面
                        var url = '/login.html';
                    }
                    $('.book-house').attr('href', url);
    
                    //Swiper轮播图插件
                    var mySwiper = new Swiper ('.swiper-container', {
                        loop: true,
                        autoplay: 2000,
                        autoplayDisableOnInteraction: false,
                        pagination: '.swiper-pagination',
                        paginationType: 'fraction'
                    });
                } else {
                    //获取失败
                    alert(resp.errmsg)
                }
            }, 'json');
        };
    })
    

    注:

    1. template的第二个参数为js对象, 其中可以包含多个键值对

    2. 轮播图插件Swiper和模板插件art-template一起使用时, 注意两点

      • html中导入插件时, 最好都放在底部导入, 且先导入轮播图插件js再导入模板js和业务代码js

        <script src="/static/js/jquery.min.js"></script>
        <script src="/static/plugins/swiper/js/swiper.jquery.min.js"></script>
        <script src="/static/js/template.js"></script>
        <script src="/static/js/ihome/detail.js"></script>
        
      • 在业务代码js文件detail.js中, 创建的Swiper对象必须和调用模板设置的逻辑放在同一个回调函数success中, 如果把创建mySwiper的语句放到ajax请求外部, 就发现没有轮播的效果了. 因为放到请求外部的话, 那么执行完发送ajax请求后(还没有来得及执行回调函数)就会执行下面的创建mySwiper的语句, 而此时回调函数还未执行, html文本也并没有生成, 所以轮播效果就没有了.

  • 相关阅读:
    三菱Q系列PLC MC协议通讯
    相机常用属性配置简介[转]---Labview IMAQ 修改相机曝光等参数的方法
    数码显微镜的实际放大倍数的正确计算方法【转载】
    VS2012 C# 配置log4net
    CHM格式帮助文档无法打开的问题
    win10 下安装win7虚拟机
    杂记:使用RawCap和Wireshark对 127.0.0.1或localhost 进行抓包
    杂记:01
    linux应用编程一:文件IO和目录操作
    QTableWidget常用函数及注意事项
  • 原文地址:https://www.cnblogs.com/gcxblogs/p/13574524.html
Copyright © 2020-2023  润新知