Flask-愛家租房專案ihome-07-我的房源列表頁和房源詳情頁
我的房源列表頁
使用者進入我的房源頁面時, 應該展示該使用者釋出的房源列表資訊
房源列表後端邏輯
需要給前端返回房屋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, }
注:
- 建立時間需要從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>
注:
-
模板程式碼需要使用
<script type="text/html" id=
xxxx>
的標籤包裹, 整段script
指令碼可以放在任意位置, 不過最好是哪裡需要編寫模板就放在哪裡 -
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')
注:
template
函式的第一個引數為html模板中script
標籤的ID屬性, 第二個引數必須是一個js物件, key為給html模板傳入的引數名, val為傳入的引數值template
函式返回的是一個html文字, 因此需要通過jQuery把該文字設定到模板想要放入的位置
房源詳情頁
在我的房源列表也中點選具體的房源標題, 即可進入房源詳情頁, 對應的url為: /detail.html?id=房屋ID
房屋詳情頁後端邏輯
模型類中新增統一返回資訊的邏輯
與我的房源列表頁類似, 將前端需要展示的房屋資訊傳送過去就好了, 同樣在房屋的模型類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
}
注:
- 一個房源中允許存在多條圖片/設施/評論資訊, 因此使用列表生成式遍歷反向關係物件, 再獲取相應的屬性
- 在獲取評論時, 需要從該房屋關聯的訂單中獲取, 且需要獲取評論人/時間/評論內容, 所以把這個過程分為兩步:
- 首先獲取到相關的訂單, 需要限制訂單的狀態和評論是否為空, 並且一般都是把最新的評論展示在前面, 因此需要按最後更新日期倒序, 最終獲取的是訂單物件列表.
- 通過列表生成式遍歷訂單物件列表, 獲取訂單的評論相關的資訊, 再生成一個新的評論內容列表.
編寫詳情頁介面
在房屋檢視檔案house.py
中新增返回詳情頁資訊的介面, url為: api/v1.0/houses/房屋ID
@api.route('/houses/<int:house_id>')
def get_house_info(house_id):
# 查詢快取中是否存在資料
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()
# 獲取當前登入使用者, 為空則說明未登入
user_id = session.get('user_id', '-1')
# 將當前使用者加入房屋詳情中
info['user_id'] = user_id
# 將字典轉為json
info_json = json.dumps(info)
# 存入快取中
redis_connect.setex(redis_key, constants.HOUSE_REDIS_EXPIRES, info_json)
return f'{{"errno": 0, "data": {info_json}}}', 200, {'Content-Type': 'application/json'}
注:
- 由於房屋詳細內容很少改變且訪問頻率比較高, 所以使用redis快取這些資訊, 存入的資料就是轉換後的json字串, 可能是由於內容中包含中文, 存入的是編碼後的資料, 所以取出時需要手動解碼一下.
- 這裡將當前登入使用者user_id和房東owner_id都傳給了前端, 前端根據這兩個值判斷是否顯示預定按鈕
- 由於
info_json
本身就已經是字串型別了, 因此最終返回時採用元組的形式返回, 第一個是返回的json字串, 第二個是http狀態碼, 第三個是headers(這裡需要指定返回的型別為application/json
) - 其中第一個值需要再加上
errno
和data
鍵值對, 因此需要格式化拼接字串, 在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 house = resp.data;
//使用template設定圖片
$('.swiper-container').html(template('house-images', {houseImages: house.img_urls, price: house.price}))
//使用template設定其他資訊
$('.detail-con').html(template('house-info', {house: house}))
//不是當前使用者不是房東則展示即刻預定按鈕
if (house.user_id != house.owner_id) {
$(".book-house").show();
}
//Swiper輪播圖外掛
var mySwiper = new Swiper ('.swiper-container', {
loop: true,
autoplay: 2000,
autoplayDisableOnInteraction: false,
pagination: '.swiper-pagination',
paginationType: 'fraction'
});
} else {
//獲取失敗
alert(resp.errmsg)
}
}, 'json');
};
})
注:
-
template
的第二個引數為js物件, 其中可以包含多個鍵值對 -
輪播圖外掛
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
請求外部, 就發現沒有輪播的效果了.
-