1. 程式人生 > 實用技巧 >Flask-愛家租房專案ihome-05-釋出房源

Flask-愛家租房專案ihome-05-釋出房源

獲取地區介面

在釋出房源頁面/主頁/搜尋頁都需要獲取城市的地區資訊, 而主頁和搜尋頁都是訪問頻率比較高的, 所以這裡需要將獲取到的資訊儲存至redis快取中

後端邏輯

ihome/api_1_0下新增房屋模組的檢視檔案houses.py

# ihome/api_1_0/houses.py
import json
from flask import jsonify, current_app
from . import api
from ihome import redis_connect
from ihome.models import Areas
from ihome.utils.response_codes import RET
from ihome.utils import constants

@api.route('/areas')
def get_areas():
    # 從快取中獲取城區資訊
    try:
        areas = redis_connect.get('areas')
    except Exception as e:
        current_app.logger.error(e)
        areas = None

    # 快取中不存在則查詢資料庫
    if not areas:
        try:
            areas_obj_list = Areas.query.all()
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg='獲取城區資訊失敗')
        # 將城區物件列表轉換為字典
        areas_dict = {area.id: area.name for area in areas_obj_list}
        # 將字典轉換為json字串
        areas = json.dumps(areas_dict)
        # 將資料存入快取
        try:
            redis_connect.setex('areas', constants.AREA_REDIS_EXPIRES, areas)
            print('設定areas快取')
        except Exception as e:
            current_app.logger.error(e)

    return areas, 200, {'content-Type': 'application/json'}

注:

  1. Areas.query.all()返回的是模型物件列表, 最終我們要提取到城區的id和名字
  2. 因為最終返回的是json, 因此直接將json資料存入redis快取中, 方便傳送給前端, 且一定要設定快取到期時間
  3. 列表通過字典生成式轉換為字典, 再通過json.dumps轉化為json字串
  4. 通過 (內容, http狀態碼, response頭資訊) 也可以設定response返回

釋出房源-選擇城區的前臺邏輯

後端返回的是一個js物件, 類似於:

{1: "東城區", 2: "西城區", 3: "朝陽區", 4: "海淀區", 5: "昌平區", 6: "豐臺區", 7: "房山區", 8: "通州區", 9: "順義區", 10: "大興區", 11: "懷柔區", 12: "平谷區", 13: "密雲區", 14: "延慶區", 15: "石景山區", 16: "門頭溝區"}

因此前端需要通過select+option動態獲取資料並展示成列表選擇, 有兩種方式可以動態設定html

使用jQuery設定html標籤

這一種方式也是之前程式碼常用的, 即獲取到後端的json資料後, 通過jQuery設定標籤的屬性, 編輯前端釋出房源頁面對應的js檔案newhouse.js, 頁面一載入就傳送ajax請求獲取資料

// newhouse.js
$(document).ready(function(){
    // $('.popup_con').fadeIn('fast');
    // $('.popup_con').fadeOut('fast');
    //獲取房屋資訊
    $.get("api/v1.0/areas", function (resp) {
        if (!resp.errno){
            //獲取到城區資料
            var areas = resp;
            // 方法一:普通迴圈, 在select下面新增option標籤
            for (var key in areas){
                //<option value="1">東區</option>
                $("#area-id").append('<option value="'+key+'">' + areas[key] + '</option>')
            }
        }else {
            //存在錯誤
            alert(resp.errmsg);
        }
    }, 'json')
})

注:

  1. 正常返回時resp就是城區的json資料, 當後臺返回報錯時resp.errno才有值
  2. js中可以使用for迴圈遍歷js物件, key為索引, areas[key] 為值
  3. 使用.append()select標籤新增option標籤, 傳入的值為需要option標籤的html文字字串

使用前段模板: art-template

前端中也有像django或者flask模板那樣模板外掛, art-template就是一種模板外掛, 它需要基於jQuery.

匯入template.js原始碼

先在官網下載js檔案, 再在static/js下匯入剛下載的js模板檔案template.js

在html檔案中使用模板

編輯房屋釋出頁面的html檔案newhouse.html

<div class="form-group">
    <label for="area-id">所在城區</label>
    <select class="form-control" id="area-id" name="area_id">
        <script type="text/html" id="area-option">
         {{ each areas }}
             <option value = "{{ $index }}">{{ $value }}</option>
         {{ /each }}
        </script>
    </select>
</div>
....
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/jquery.form.min.js"></script>
<script src="/static/js/template.js"></script>

注:

  1. 先在html檔案中匯入template.js的路徑
  2. 把需要生成動態html文字的模板程式碼放在<script type="text/html" id="area-option"></script>標籤中(id可以自定義), 並且這個script標籤可以放在該html檔案的任意位置, 不一定就要放在select標籤中
  3. 這點和django或flask的模板語言不一樣, django或flask模板語言需要放在具體需要替換的地方, 說明art-template這個外掛只是用來生成html文字字串的, 具體生成的html文字到底放在哪裡, 還需要自己再指定
  4. 後面的js程式碼給html模板傳入的變數引數名為areas, 這是地區的js物件, 我們需要遍歷這個js物件獲取其中的key和value, 並生成option文字.
  5. art-template中可以使用each遍歷物件, 使用{{$index}}獲取key值, {{$value}}獲取value, {{$data}}獲取物件本身, 或者使用{{each areas val key}}遍歷物件, 獲取到val和key

在js中設定模板

在對應的js檔案newhouse.js

$(document).ready(function(){
    // $('.popup_con').fadeIn('fast');
    // $('.popup_con').fadeOut('fast');
    //獲取房屋資訊
    $.get("api/v1.0/areas", function (resp) {
        if (!resp.errno){
            //獲取到城區資料
            var areas = resp;
            // 方法一:通過jQuery迴圈, 在select下面新增option標籤
            // for (var key in areas){
            //     //<option value="1">東區</option>
            //     $("#area-id").append('<option value="'+key+'">' + areas[key] + '</option>')
            // }
            // 方法二:使用art-template前端模板
            // 給模板傳值, 返回html文字
            html = template('area-option', {areas: areas});
            // 設定select中的文字物件
            $('#area-id').html(html);
        }else {
            //存在錯誤
            alert(resp.errmsg);
        }
    }, 'json')
})

注:

  1. 使用template()函式返回模板程式碼生成的html文字, 第一個引數為html模板中script標籤的id屬性值, 第二個引數為js物件格式, 表示傳給模板的資料, key為引數名, val為傳入的值
  2. 通過jQuery將得到的一連串option標籤的html檔案, 放到select的html文字中, 就指定了html的具體位置

釋出房源介面

使用者在我的房源頁面點選釋出新房源按鈕, 可以進入釋出新房源頁面

該頁面有兩個表單, 一個表單是用來發布房源的資訊, 另一個是用來上傳圖片的, 這個表單是隱藏的, 需要成功釋出房源之後才會顯示出來.

釋出房源後端邏輯

在房屋模組的檢視檔案houses.py中添加發布介面邏輯, url為/api/v1.0/houses

@api.route('/houses', methods=['POST'])
@login_required
def create_house():
    # 接收資料
    data_dict = request.get_json()
    if not data_dict:
        return parameter_error()
    # 提取資料
    title = data_dict.get('title')
    price = data_dict.get('price')
    area_id = data_dict.get('area_id')
    address = data_dict.get('address')
    room_count = data_dict.get('room_count')
    acreage = data_dict.get('acreage')
    unit = data_dict.get('unit')
    capacity = data_dict.get('capacity')
    beds = data_dict.get('beds')
    deposit = data_dict.get('deposit')
    min_days = data_dict.get('min_days')
    max_days = data_dict.get('max_days')
    facility_ids = data_dict.get('facilities')

    # 校驗資料
    if not all(
            [title, price, area_id, address, room_count, acreage, unit, capacity, beds, deposit, min_days, facility_ids]):
        return parameter_error()

    # 校驗金額格式, 轉化為兩位小數
    try:
        price = round(float(price), 2)
        deposit = round(float(deposit), 2)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg='金額格式有誤')
    
    # 校驗數字型別
    try:
        room_count = int(room_count)
        acreage = int(acreage)
        capacity = int(capacity)
        min_days = int(min_days)
        max_days = int(max_days)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg='房屋面積/房屋數目/宜住人數/最少最多天數必須為數字')
    
    # area_id是否存在
    try:
        area = Areas.query.get(area_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='獲取城區資訊異常')
    if not area:
        return jsonify(errno=RET.PARAMERR, errmsg='城區ID不存在')

    # 最小最大入住日期
    if min_days > max_days | max_days >= 0:
        return jsonify(errno=RET.PARAMERR, errmsg='最小日期不能超過最大日期')

    # 建立房屋物件
    house = Houses(user_id=g.user.id, area_id=area_id, title=title, price=price, address=address,
                   room_count=room_count, acreage=acreage, unit=unit, capacity=capacity, beds=beds, deposit=deposit,
                   min_days=min_days, max_days=max_days)

    # 給房屋新增設施資訊

    # 第一種: 迴圈校驗每個設施, 然後通過append新增
    # for facility_id in facility_ids:
    #     # 迴圈校驗設施ID
    #     try:
    #         facility = Facilities.query.get(facility_id)
    #     except Exception as e:
    #         current_app.logger.error(e)
    #         return jsonify(errno=RET.DBERR, errmsg='獲取設施資訊異常')
    #     if not facility:
    #         return jsonify(errno=RET.DBERR, errmsg=f'設施ID"{facility_id}"不存在')
    #     # 建立房屋設定資料
    #     house.facilities.append(facility)

    # 第二種: 不迴圈, 通過in_(list)查詢設施, 但是這樣不會處理錯誤的設施id
    try:
        facilities = Facilities.query.filter(Facilities.id.in_(facility_ids)).all()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='獲取設施資訊異常')
    if facilities:
        # 存在正確的設施ID, 則給房屋物件新增設施資訊
        house.facilities = facilities
	# 提交session
    try:
        db.session.add(house)
        db.session.commit()
    except IntegrityError as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg='該房屋標題已存在')
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='儲存房屋資訊異常')

    return jsonify(errno=RET.OK, data={'house_id': house.id})

注:

  1. 提交的json資料格式為:

    {
    	"title": "test3",
    	"price": "110",
    	"area_id": "3",
    	"address": "上海小區",
    	"room_count": "3",
    	"acreage": "120",
    	"unit": "三室一廳",
    	"capacity": "4",
    	"beds": "2雙人床",
    	"deposit": "100",
    	"min_days": "2",
    	"max_days": "-1",
    	"facilities": ["1","2","4"]
    }
    
  2. 房屋設施資訊通過設施ID組成的列表facilities提交, 這裡涉及到了房屋表和設施表的一對多關係, 在models檔案中定義了多對多關係的中間表, 以及邏輯關係

# 房屋和設定表屬於多對多關係, 官方推薦db.table的方式建立多對多關係
house_facilities = db.Table('ih_house_facilities',
                            db.Column('house_id', db.Integer, db.ForeignKey('ih_houses.id'), nullable=False),
                            db.Column('facility_id', db.Integer, db.ForeignKey('ih_facilities.id'), nullable=False))
# 房屋模型類
class Houses(BasicModel):
	__tablename__ = 'ih_houses'
    .....
    # 在模型類中定義關係, 注意secondary引數傳入中間表變數
    facilities = db.relationship('Facilities', secondary=house_facilities, backref='houses')
  1. 建立房屋資料時, 也要建立房屋設施中間表的資料, 不過不需要我們手動賦值, 只需要給house物件的facilities屬性賦值就可以了, 型別為Facility物件組成的list, 有兩種方法得到這個list
  • 第一種: 迴圈前端傳送的設施ID列表facilities, 校驗每個ID是否正確, 獲取到ID對應的facility物件, 再把facility物件追加到house物件的facilities屬性中:

    for facility_id in facility_ids:
        # 迴圈校驗設施ID
        try:
            facility = Facilities.query.get(facility_id)
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg='獲取設施資訊異常')
        if not facility:
            return jsonify(errno=RET.DBERR, errmsg=f'設施ID"{facility_id}"不存在')
        # 建立房屋設定資料
        house.facilities.append(facility)
    
  • 第二種: 不迴圈ID列表, 直接使用in_方法查詢ID列表中對應的facility, 獲取到facility物件的列表, 再賦值給house物件的facilities屬性

    try:
        facilities = Facilities.query.filter(Facilities.id.in_(facility_ids)).all()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='獲取設施資訊異常')
    if facilities:
        # 存在正確的設施ID, 則給房屋物件新增設施資訊
        house.facilities = facilitie
    

釋出房源前段邏輯

在房屋釋出頁面對應的js檔案newhouse.js中新增以下邏輯

//釋出房源表單的提交
    $('#form-house-info').submit(function (e) {
        //阻止預設
        e.preventDefault();
        //自定義提交
        //獲取資料
        var data = {};
        //獲取form表單中的所有input提交項, 使用map迴圈每個input獲取name和value
        $(this).serializeArray().map(function (item) {
            data[item.name] = item.value;
        })
        //獲取複選框中勾選的設施項
        var facility_ids = [];
        $('input:checkbox:checked').each(function (index, item) {
            facility_ids[index] = $(item).val();
        })
        var data["facilities"] = facility_ids;
        //轉為json
        var data = JSON.stringify(data);
		//傳送請求
        $.ajax({
            url: 'api/v1.0/houses',
            type: 'POST',
            contentType: 'application/json',
            data: data,
            headers: {'X-CSRFToken': getCookie('csrf_token')},
            dataType: 'json',
            success: function (resp) {
                if (resp.errno == '0'){
                    //釋出成功
                    //設定house_id
                    $('#house-id').val(resp.data.house_id);
                    //展示上傳圖片表單
                    $('#form-house-image').show();
                    alert('儲存成功, 請上傳房屋圖片');
                }else {
                    //釋出失敗
                    alert(resp.errmsg)
                }
            }
        })
    })

注:

  1. 該頁面的表單中有很多input框需要提交, 顯然不適合一個一個獲取, 可以通過$('form選擇器').serializeArray()獲取form下所有的輸入框, 返回的是所有輸入框的name和value組成的物件列表, 如:

    > $('#form-house-info').serializeArray()
    < (16) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
    0: {name: "house_id", value: ""}
    1: {name: "title", value: "test"}
    2: {name: "price", value: "100"}
    3: {name: "area_id", value: "1"}
    4: {name: "address", value: "上海a小區"}
    5: {name: "room_count", value: "1"}
    6: {name: "acreage", value: "50"}
    7: {name: "unit", value: "一室一廳"}
    8: {name: "capacity", value: "2"}
    9: {name: "beds", value: "雙人床:2x1.8x1張"}
    10: {name: "deposit", value: "100"}
    11: {name: "min_days", value: "1"}
    12: {name: "max_days", value: "2"}
    13: {name: "facility", value: "1"}
    14: {name: "facility", value: "2"}
    15: {name: "facility", value: "3"}
    length: 16
    __proto__: Array(0)
    

    我們最終需要的是列表中每個輸入框的namevalue屬性組成的鍵值對, 如:{title": "test", "price": "100"}

    因此先建立一個空js物件data, 然後使用map函式遍歷列表, 取出每個item的name和value, 再新增到data物件中, map函式的引數item就是列表中的每個js物件

  2. 配套設施使用的是多個複選框的形式, 因此可以通過選擇器$('input:checkbox:checked')獲取到選中狀態的checkbox, 其value值設計的就是對應資料庫中設施表ih_facilities中的ID.

    同樣獲取到的是一個checkbox的列表, 但是列表中的元素是整個input框物件, 我們同樣只需要每個input框的value屬性值.

    因為最終想要的是每個checkboxvalue屬性組成的陣列, 如:["3", "4", "7"], 所以也預先定義一個空陣列facility_ids.

    這裡使用的是each遍歷checkbox陣列, each接收有兩個引數: index是列表的索引, item是陣列的元素值, 因此item代表的是整個checkbox元素物件, 所以想要使用jQuery的方式獲取其value屬性: $(item).val(), 然後將indexvalue追加到facility_ids中, 最後賦值給data的facilities屬性

  3. 房源釋出成功後, 展示上傳房屋圖片的表單, 同時接收新房源的ID並賦值給隱藏的輸入框house-id中, 是為了在上傳房屋圖片介面判斷上傳的圖片是屬於哪一個房源.

上傳房屋圖片介面

房屋成功釋出後, 就會展示上傳圖片的表單介面

上傳房屋圖片後端邏輯

在房屋模組的檢視檔案houses.py中新增上傳房屋圖片介面邏輯, url為/api/v1.0/house/images

@api.route('house/images', methods=['POST'])
@login_required
def create_house_images():
    # 獲取資料, 該圖片資料由瀏覽器提交, 不是json格式的
    image_data = request.files.get('house_image')
    house_id = request.form.get('house_id')

    # 校驗資料
    if not all([image_data, house_id]):
        return parameter_error()
    # 校驗房屋Id
    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.PARAMERR, errmsg='房屋ID不存在')

    # 呼叫上傳圖片介面
    ret = storage(image_data)
    print(ret)
    if ret['status'] != 200:
        # 上傳失敗
        return jsonify(errno=RET.THIRDERR, errmsg=f'上傳失敗:{ret["errmsg"]}')

    # 上傳成功, 建立圖片資料
    try:
        house_image = HouseImages(house=house, image_url=ret['url'])
        db.session.add(house_image)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='儲存圖片資訊異常')

    return jsonify(errno=RET.OK, data={'url': ret['url']})

注:

  1. 和之前的上傳使用者頭像介面類似, 前端資料不是通過json格式傳入, 而是使用瀏覽器表單提交的方式傳輸資料, 除了request.files.get('house_image')獲取檔案資料外, 還需要獲取表單中的house_id = request.form.get('house_id')

  2. 將七牛上傳圖片介面返回的url存入資料庫中, 並返回給前端

上傳房屋圖片前端邏輯

在房屋釋出頁面對應的js檔案newhouse.js中新增以下邏輯, 與之前的上傳使用者頭像介面類似

//上傳房屋圖片的表單
$('#form-house-image').submit(function (e) {
    //阻止表單預設提交行為
    e.preventDefault()
    //自定義提交行為
    $(this).ajaxSubmit({
        url: 'api/v1.0/house/images',
        type: 'POST',
        headers: {'X-CSRFToken': getCookie('csrf_token')},
        dataType: 'json',
        success: function (resp) {
            if (resp.errno == '0'){
                //上傳成功, 展示圖片
                $('.house-image-cons').append('<img src="' + resp.data.url + '"></img>')
            }else {
                //上傳失敗
                alert(resp.errmsg)
            }
        }
    })
})
  1. 阻止表單的完整提交行為, 因為傳送的是檔案資料, 用普通的ajax請求不好處理, 而使用.ajaxSubmit方法可以將傳送的功能交給瀏覽器預設的表單提交, 而回調函式依舊可以自定義處理
  2. 上傳成功後, 將圖片展示出來, 使用appenddiv中新增img標籤, 設定src屬性為返回的圖片url

更新房屋資訊介面

這裡做一個改進, 就是在成功釋出房源資訊之後, 將原來的按鈕文字釋出房源資訊變成更新房源資訊, 可以繼續修改上面的房屋資料, 再點選更新按鈕即可更新房屋資料.

在釋出新房源資訊的html檔案newhouse.html中的釋出房源的form中也新增一個隱藏的input輸入框, 用來儲存第一次成功釋出房源資訊後的house_id, 用於第二次的更新操作

<form id="form-house-info">
    ......
    <div class="form-group">
        <input type="hidden" name="house_id" id="new-house-id" value="">
        <label for="house-title">房屋標題</label>
        <input type="text" class="form-control" name="title" id="house-title" value="test" required>
    </div>
    ......

後端邏輯修改

@api.route('/houses', methods=['POST', 'PUT'])
@login_required
def create_or_update_house():
    # 接收資料
    data_dict = request.get_json()
    if not data_dict:
        return parameter_error()
    # 提取資料
    title = data_dict.get('title')
    price = data_dict.get('price')
    area_id = data_dict.get('area_id')
    address = data_dict.get('address')
    room_count = data_dict.get('room_count')
    acreage = data_dict.get('acreage')
    unit = data_dict.get('unit')
    capacity = data_dict.get('capacity')
    beds = data_dict.get('beds')
    deposit = data_dict.get('deposit')
    min_days = data_dict.get('min_days')
    max_days = data_dict.get('max_days')
    facility_ids = data_dict.get('facilities')
    house_id = data_dict.get('house_id')

    # 校驗資料
    if not all(
            [title, price, area_id, address, room_count, acreage, unit, capacity, beds, deposit, min_days, facility_ids]):
        return parameter_error()

    # 校驗金額格式, 轉化為兩位小數
    try:
        price = round(float(price), 2)
        deposit = round(float(deposit), 2)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg='金額格式有誤')

    # 校驗數字型別
    try:
        room_count = int(room_count)
        acreage = int(acreage)
        capacity = int(capacity)
        min_days = int(min_days)
        max_days = int(max_days)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg='房屋面積/房屋數目/宜住人數/最少最多天數必須為數字')

    # area_id是否存在
    try:
        area = Areas.query.get(area_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='獲取城區資訊異常')
    if not area:
        return jsonify(errno=RET.PARAMERR, errmsg='城區ID不存在')

    # 最小最大入住日期
    if min_days > max_days | max_days >= 0:
        return jsonify(errno=RET.PARAMERR, errmsg='最小日期不能超過最大日期')

    # 給房屋新增設施資訊

    # 第一種: 迴圈校驗每個設施, 然後通過append新增
    # for facility_id in facility_ids:
    #     # 迴圈校驗設施ID
    #     try:
    #         facility = Facilities.query.get(facility_id)
    #     except Exception as e:
    #         current_app.logger.error(e)
    #         return jsonify(errno=RET.DBERR, errmsg='獲取設施資訊異常')
    #     if not facility:
    #         return jsonify(errno=RET.DBERR, errmsg=f'設施ID"{facility_id}"不存在')
    #     # 建立房屋設定資料
    #     house.facilities.append(facility)

    # 第二種: 不迴圈, 通過in_(list)查詢設施, 但是這樣不會處理錯誤的設施id
    try:
        facilities = Facilities.query.filter(Facilities.id.in_(facility_ids)).all()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='獲取設施資訊異常')
    if not facilities:
        return jsonify(errno=RET.PARAMERR, errmsg='無有效的設施ID')

    # POST則建立新房屋物件, PUT則更新房屋
    if request.method == 'POST':
        # 建立房屋物件
        house = Houses(user_id=g.user.id, area_id=area_id, title=title, price=price, address=address,
                       room_count=room_count, acreage=acreage, unit=unit, capacity=capacity, beds=beds, deposit=deposit,
                       min_days=min_days, max_days=max_days, facilities=facilities)
    else:
        # 獲取房屋物件
        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.PARAMERR, errmsg='房屋ID不存在')
        # 重新設定房屋屬性
        house.area_id = area_id
        house.title = title
        house.price = price
        house.address = address
        house.room_count = room_count
        house.acreage = acreage
        house.unit = unit
        house.capacity = capacity
        house.beds = beds
        house.deposit = deposit
        house.min_days = min_days
        house.max_days = max_days
        house.facilities = facilities

    # 提交房屋資訊
    try:
        db.session.add(house)
        db.session.commit()
    except IntegrityError as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg='該房屋標題已存在')
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='儲存房屋資訊異常')

    return jsonify(errno=RET.OK, data={'house_id': house.id}

注:

  1. 請求方法中新增PUT方法
  2. PUTPOST前面接收/校驗資料邏輯都一樣, 只是把之前的建立房屋house物件移到了設定facilities物件的後面, 再根據請求方式判斷是建立house物件還是根據house_id查詢並更新house物件

前端邏輯修改

    //釋出房源表單的提交
    $('#form-house-info').submit(function (e) {
        //阻止預設
        e.preventDefault();
        //自定義提交
        //獲取資料
        var data = {};
        //獲取form表單中的所有input提交項, 使用map迴圈每個input獲取name和value
        $(this).serializeArray().map(function (item) {
            data[item.name] = item.value;
        })
        //獲取複選框中勾選的設施項
        var facility_ids = [];
        $('input:checkbox:checked').each(function (index, item) {
            facility_ids[index] = $(item).val();
        })
        data["facilities"] = facility_ids;
        //轉為json
        var data = JSON.stringify(data);

        //根據new_house_id判斷是更新還是建立
        var newHouseID = $('#new-house-id').val();
        if (newHouseID){
            var type = 'PUT';
        }
        else{
            var type = 'POST';
        }
        $.ajax({
            url: 'api/v1.0/houses',
            type: type,
            contentType: 'application/json',
            data: data,
            headers: {'X-CSRFToken': getCookie('csrf_token')},
            dataType: 'json',
            success: function (resp) {
                if (resp.errno == '0'){
                    //釋出成功
                    //設定house_id
                    $('#house-id').val(resp.data.house_id);
                    $('#new-house-id').val(resp.data.house_id);
                    //修改按鈕提示
                    $('.btn-commit').val('修改房源資訊');
                    //展示上傳圖片表單
                    $('#form-house-image').show();
                    alert('儲存成功, 請上傳房屋圖片');
                }else {
                    //釋出失敗
                    alert(resp.errmsg)
                }
            }
        })
    })

注:

  1. 成功釋出房源之後也要再設定一下new-house-id的值,

  2. 先獲取new-house-id的值, 存在則說明需要更新, 請求方式為PUT, 不存在說明需要建立, 請求方式為POST