Python Flask從前端到后臺,嘗試一個簡單的web項目

前言:最近為了了解前端和服務(wù)器之間的通訊是如何工作的,所以做了一個簡單的web項目,包括web界面、服務(wù)器接口、數(shù)據(jù)庫等,以本機作為服務(wù)器來開發(fā)了一個相對完整的demo,最終可以正常運行。因為本人是做iOS開發(fā)的,所以這里用到的很多知識我也僅僅是了解的比較淺,因為重點只是熟悉一下前端和服務(wù)器之間的交流流程,中間使用到的語言也只是為這次demo開發(fā)準備。

中間用到的知識包括:

前端:html、css、js、jQuery、ajax。

服務(wù)器:Flask、python3。

數(shù)據(jù)庫:MongoDB、pymongo。

用到的工具:PyCharm、HBuilder、Robo 3T

環(huán)境:Mac OS

ps:以上用到的都是免費的。

本文也算是做完之后的筆記吧,由于重點在于整個的流程,所以中間很多知識的細節(jié)欠缺,歡迎大家補充相互學(xué)習(xí)。

一、環(huán)境配置

1. 使用Python3進行服務(wù)器開發(fā),簡單說下python3的安裝:

安裝homebrew,終端命令:ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安裝python3: brew install python

完成后使用:python3 --version 查看版本。(系統(tǒng)自帶python2.7,如果使用python --version查看的是2.7的版本,同理直接使用python進入的也是2.7版本,如果你想在終端使用python進入python3的環(huán)境,可以在.base_profile中用alias命令來實現(xiàn),具體可自行百度)

2. 虛擬環(huán)境,用python開發(fā)做項目,那虛擬環(huán)境是很有必要的,為什么要使用虛擬環(huán)境?首先我們用python做項目,很多東西都是別人封裝好的庫,不必我們自己實現(xiàn),例如ios、android開發(fā)也一樣,而python不同的是安裝的庫都會安裝在python目錄下,例如你有三個python項目那么這三個python項目各自安裝使用的庫都在一個地方放著,如果項目1使用庫A的1.0版本,而項目2使用庫A的2.0版本,那么就可能庫A的2.0版本把1.0版本覆蓋了,從而導(dǎo)致項目1不能正常工作,所以虛擬環(huán)境對于開發(fā)python項目來說很有必要!每個項目對應(yīng)各自的虛擬環(huán)境,其中其實就是把python環(huán)境復(fù)制了一份,使你每個項目安裝的庫都在各自的環(huán)境里,從而互不影響(說的比較淺顯,望見諒??)。

使用pip來安裝:

安裝virtualenv: sudo pip install virtualenv

安裝virtualenvwrapper:sudo pip install virtualenvwrapper(這是安裝一個虛擬環(huán)境的操作插件,使用起來更方便,如果提示失敗可嘗試:sudo pip install virtualenvwrapper --upgrade --ignore-installed six)

安裝成功后新建一個放置虛擬環(huán)境的目錄,我是在hoom下建了一個virtualenv-wokespaces:

mkdir ~/virtualenv-wokespaces

cd ~/virtualenv-wokespaces

設(shè)置環(huán)境變量,在.base_profile中添加:

export WORKON_HOME=~/virtualenv-wokespaces

source /usr/local/bin/virtualenvwrapper.sh

然后就可以在終端根目錄下使用命令了:

新建虛擬環(huán)境:mkvirtualenv xxx

列出所有虛擬環(huán)境:workon

切換到某個虛擬環(huán)境:workon xxx

退出虛擬環(huán)境:deactivate

刪除虛擬環(huán)境:rmvirtualenv xxx

配置好虛擬環(huán)境之后,以后寫項目就建立一個對應(yīng)的虛擬環(huán)境,需要安裝庫時就進入對應(yīng)的虛擬環(huán)境后再安裝。用PyCharm創(chuàng)建一個項目后可以再偏好設(shè)置的Project:xx -> Project Interpreter中連接自己的虛擬環(huán)境。

IDE推薦:PyCharm的community版本 https://www.jetbrains.com/pycharm/ 需要vpn

3. 數(shù)據(jù)庫使用MongoDB

如何安裝以及我遇到的一些問題請參考我的另一篇文章:http://www.itdecent.cn/p/9c3e37e02f75。

這里是詳細步驟:https://www.runoob.com/mongodb/mongodb-osx-install.html (這里有關(guān)于mongodb的各種操作,如:創(chuàng)建數(shù)據(jù)庫、刪除數(shù)據(jù)庫、往數(shù)據(jù)庫寫數(shù)據(jù)、等??梢栽谶@里面學(xué)習(xí),這里不詳細說了)

一定要注意其中的/data/db的連接,這個目錄就是你數(shù)據(jù)庫數(shù)據(jù)存放的地方,你可以自定義,在你可創(chuàng)建文件夾的任意地方,也可以是其他名字,創(chuàng)建好后使用sudo mongod --dbpath=/data/db連接上就可以,注意:mongodb啟動時需要指定path也就是指定data/db路徑,啟動mongo就是啟動了一個進程,如果這個進程關(guān)閉了,再次啟動都需要指定路徑,所以,如果你關(guān)機了,下次開機啟動mongodb之前記得先指定路徑哦。

ps:如果mongodb啟動成功后在瀏覽器打開:http://localhost:27017,會顯示“It looks like you are trying to access MongoDB over HTTP on the native driver port.”

mogodb啟動成功

4.數(shù)據(jù)庫可視化GUI推薦:Robo 3T

下載鏈接:https://robomongo.org/download

使用教程隨便貼一個:https://www.bbsmax.com/A/obzb7V2BJE/ ,可自行百度。

弄好之后連上你的數(shù)據(jù)庫你就可以在這里看到你的數(shù)據(jù)庫中的數(shù)據(jù)了。

Robo 3T界面

5.編寫前端界面我使用的HBuilder,其實用PyCharm也可以寫h5、css、js等,但是,它不會自動補全,沒有代碼提示??,特別是對于我這樣的新手簡直不知道該怎么寫代碼,其實也能讓他有自動補全功能,但是得付費~ 所以這里我推薦新手的話可以使用HBuilder,下載標準版也就一二十兆,h5、css、js都有自動補全,而且有內(nèi)置瀏覽器,可以實時的看效果,對于新手還是很不錯的(老手忽略),寫完把代碼復(fù)制到PyCharm項目中去。

下載鏈接:https://www.dcloud.io/hbuilderx.html (選擇標準版就好,使用過程中需要安裝的插件按提示安裝就好)

HBuilder界面

關(guān)于環(huán)境先說到這里把,說的比較亂大家見諒 ~ 因為中間配置的時候可能會遇到各種各樣的問題,這里沒詳細列舉,如有遇到歡迎評論留言。

二、項目開發(fā)

建議:對于要學(xué)習(xí)某種語言或者類似這樣要去做一個整套的流程的項目,中間要用到不同的語言不同的知識,建議不要在某個點上死磕,非要去學(xué)全套的,沒必要,學(xué)習(xí)要搞清楚學(xué)習(xí)的重點在哪里,比如我們這個項目,要用到html我要從頭到尾去學(xué)html么?不需要!因為我們還要用到j(luò)s、css、python等等。我們沒有那個必要把每個語言學(xué)的很精通,我們做這個項目的重點也不在此。對于這種情況,我的建議是:先把項目文件建起來,然后從界面入手,先寫個界面,有了界面就要有操作,然后寫事件,這樣一步一步往下,每一步用到什么知識再去看你用到的知識,這樣最有效。

項目簡介:簡單說下這次做的項目功能,兩個界面的web,一個登錄界面,一個搜索界面,其中登錄界面只需要輸入賬號點擊登錄就進入搜索界面,因為牽涉到密碼比較復(fù)雜所以這里只用賬號登錄,輸入賬號后點擊登錄進入搜索界面。搜索界面可以根據(jù)輸入的經(jīng)緯度搜索對應(yīng)的地址信息,并顯示這個用戶的搜索記錄。功能部分就這些,下面來一步一步實現(xiàn)這個功能。

1.建立項目

用PyCharm新建一個項目,項目位置可自定義。PyCharm新建項目會有一個venv文件,這個是自帶的虛擬環(huán)境,可以把這個文件刪掉然后連接上面我們說的自定義的虛擬環(huán)境(當然,你也可以用這個自帶的,用法自己研究下,我沒有用自帶的)。

(1)創(chuàng)建 web_location.py 文件,用來寫服務(wù)器代碼,提供接口。

(2)創(chuàng)建兩個文件夾 static 和 templates。 因為此項目使用flask框架,所以根據(jù)需求需要建立這兩個文件夾,其中static中放靜態(tài)文件,比如css、js、圖片資源等。templates中放html文件。必須這么放,不然后面會出錯。

(3)在static中創(chuàng)建一個.css文件用來寫css代碼,創(chuàng)建個js文件用來寫js代碼,名字自定義。在templates中創(chuàng)建兩個html文件,代表兩個h5界面。這些文件可以先建好放著就行。

image

2.web界面相關(guān)

上面我們說過了,由于用PyCharm寫html代碼沒有自動補全,所以我們在HBuilder中寫界面相關(guān)的,寫完把代碼復(fù)制到我們上面創(chuàng)建好的對應(yīng)的文件中就行了。關(guān)于前端界面的實現(xiàn)不詳細說了只貼部分代碼,css代碼比較多所以不貼了,需要的可以留言。關(guān)于html、css、js的學(xué)習(xí)可以去找一些教程,這里是菜鳥教程的鏈接:https://www.runoob.com/html/html-tutorial.html 感興趣的可以看看。

下面這個是登錄界面代碼:


<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>登錄</title>
        <script type="text/javascript" src="static/jquery.min.js"></script>
        <link rel="stylesheet" href="static/LoginPage.css" type="text/css" />
        <script src="static/map.js"></script>
    </head>
    <body>
        <div class="box_header"></div>

        <div class="box_body">
            <div class="div_login_bg">
                <input id="login_input_view" class="login_input_view" type="text" placeholder="請輸入賬號"/>
                <button class="login_button" onclick="login()">登 錄</button>
            </div>
        </div>
    </body>
</html>

里面引入了css文件和js文件,還有一個jquery文件,關(guān)于為什么引入jquery后面會說到。這個界面的效果如下:


登錄頁效果

下面是搜索頁html代碼:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title id="page_title">搜索</title>
        <link rel="stylesheet" href="static/LoginPage.css" type="text/css" />
        <script type="text/javascript" src="static/jquery.min.js"></script>
        <script type="text/javascript" src="static/map.js"></script>
        <script type="text/javascript" src="https://api.map.baidu.com/api?v=1.0&type=webgl&ak=mEKm2VjHEdOF6RGX3kulObKmgGkyP2Gy"></script>
    </head>
    <body onload="initSearchPage()">
        <div class="result_body_bg">
            <div class="result_body_bg" id="map_container"></div>

            <div class="box_header">
                <input id="lat_input_view" class="search_input_view" type="text" placeholder="請輸入經(jīng)度" />
                <input id="lng_input_view" class="search_input_view" type="text" placeholder="請輸入緯度" />
                <button class="search_button" onclick="search()" >搜索</button>
            </div>

            <p id="address_text" class="search_result_text"></p>

            <div class="search_result_list_bg">
                <ul id="search_history_ul" class="result_list_ul"></ul>
            </div>
        </div>
    </body>
</html>

這個界面我是引用了百度地圖sdk,用地圖作為背景。效果如下:


搜索頁效果

css代碼比較多就不貼了。這樣兩個web界面就有了。

3.Flask框架使用
關(guān)于Flask請看這里:https://flask.palletsprojects.com/en/1.1.x/quickstart/#quickstart

通過Flask框架可以簡單的開發(fā)后臺,提供接口。
添加Flask庫(記得前面說的!切換到這個項目對應(yīng)的虛擬環(huán)境,然后再安裝Flask) 終端: sudo pip install flask
安裝成功后就可以在虛擬環(huán)境中看到了,如下圖:

安裝了Flask1.1.2

接下來在我們之前建好的web_location.py中寫如下代碼:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

然后在終端中執(zhí)行:

export FLASK_APP=hello.py
flask run
就會提示你:
Running on http://127.0.0.1:5000/

這是什么意思呢?其中@app.route('/')就是代表一個路徑,看這里指的是:127.0.0.1:5000也就是就是你本機,這個路徑指向了下面緊接著的hello_world()方法,你在瀏覽器中輸入 http://127.0.0.1:5000/那么就會執(zhí)行hello_world()方法,return 'Hello, World!'就是在網(wǎng)頁上顯示了Hello, World!字樣. 網(wǎng)頁效果如下:

hello

到這里,你應(yīng)該明白了這是怎么回事了吧??,這就是我們平常打開網(wǎng)頁鏈接的樣子。當然,這是最最基本的。

你也可以這樣寫:

app = Flask(__name__)


@app.route('/', methods=['GET', 'POST'])
def hello_world():
    return 'Hello, World!'


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)
  • 這樣寫后可以直接在終端中直接執(zhí)行這個py文件:python3 web_location.py 就運行了。
  • 其中@app.route('/', methods=['GET', 'POST'])這里可以指定GET、POST支持,這里不細說了,請看Flask文檔。
  • 主要的是這里可以指定host和port,之前你只能在自己電腦上通過 http://127.0.0.1:5000來訪問,假如你想在同個局域網(wǎng)的其他設(shè)備上也能訪問你寫的這個接口,那么你可以把host改成0.0.0.0,port自定義,然后就可以,只要在同一個局域網(wǎng),其他電腦或者手機就可以通過你的電腦當前的局域網(wǎng)IP地址來訪問(在終端中通過ifconfig來查看你當前的局域網(wǎng)ip,注意:這個ip在你每次重新連接局域網(wǎng)的時候是會變得?。劣?.0.0.0 是什么!請看這里:https://cloud.tencent.com/developer/article/1643083 這里面說的比較詳細了,相信你看完也能明白了??!

當然,我們的網(wǎng)頁不可能就顯示個hello world!我們之前已經(jīng)寫好了兩個網(wǎng)頁了,那么怎么把我們寫好的html文件和flask關(guān)聯(lián)起來呢?如果你看flask文檔的話就會知道,下面直接看代碼:

@app.route('/login', methods=['GET', 'POST'])
def login():
    return render_template('LoginPage.html')

沒錯,就這么簡單!Flask渲染模板使用Jinja2
(Flask已經(jīng)為你自動配置了,至于什么是Jinja2,自己學(xué)習(xí)下吧??,特別注意我們之前說到的html文件要放在templates文件夾中,否則這里不會生效)。
然后你就可以用http://0.0.0.0:8080/login在瀏覽器打開你的登錄頁面了。


同理,寫一個搜索頁的鏈接:

@app.route('/search', methods=['GET', 'POST'])
def search():
    return render_template('SearchPage.html')

此時你就可以用 http://0.0.0.0:8080/login訪問登錄頁面,用http://0.0.0.0:8080/search訪問搜索頁面,那么直接用http://0.0.0.0:8080呢? 還是只會顯示一個’hello world!‘,那么如果我想默認首頁就是登錄頁呢? 用重定向:

@app.route('/', methods=['GET', 'POST'])
def hello_world():
    return redirect('/login')

這樣就ok了。
當然這里面牽涉到很多知識沒有細說,比如:Jinja2、GET、POST、重定向、等等。大家有興趣可以慢慢研究。


4. 事件

上面我們可以顯示我們的網(wǎng)頁了,但是正常的網(wǎng)頁不僅能看,還得能操作不是!下面我們就給我們的網(wǎng)頁加上交互吧!

上面的登錄頁h5代碼里面其實已經(jīng)調(diào)用了js方法了,登錄按鈕,調(diào)用了login()方法,我們在js文件中實現(xiàn)這個方法:

function login(){
    var name = document.getElementById("login_input_view").value;
    if (name.length <= 0) {
        alert("賬號不能為空!趕緊滴!");
        return;
    }

    window.location.href='/search';
    alert("歡迎"+name+"登錄成功!");
}

這樣,當我們輸入賬戶點擊了登錄按鈕,進進入搜索界面了!
當然,這只是一個簡單的跳轉(zhuǎn),重要的是點擊的是點擊登錄后要做的后臺操作。
我們的目標是:

  • 點擊登錄調(diào)用后臺提供的登錄接口
  • 后臺的登錄接口中會檢測輸入的賬號是否存在
  • 如果存在則更新數(shù)據(jù)庫中的用戶信息,如果不存在則生成用戶信息并存入數(shù)據(jù)庫,然后接口返回成功或失敗信息
  • 前端根據(jù)登錄接口返回的結(jié)果進行處理,或進入搜索頁,或提示登錄失敗!

先上代碼:

@app.route('/web/login', methods=['GET'])
def web_login():
    name = request.args.get("name")
    db = client["web_location"]
    collection = db["user"]

    query = {"name": name}
    token = get_token()
    current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    exp_time = int(time.time()) + 60
    if collection.count_documents(query) > 0:
        new_values = {"$set": {"token": token, "last_login_time": current_time, "exp_time": exp_time}}
        collection.update_one(query, new_values)
    else:
        data = {"name": name, "token": token, "last_login_time": current_time, "exp_time": exp_time}
        collection.insert_one(data)
    return token

對應(yīng)的調(diào)用代碼:

function login(){
    var name = document.getElementById("login_input_view").value;
    if (name.length <= 0) {
        alert("賬號不能為空!趕緊滴!");
        return;
    }

    $.ajax({
        type:"GET",
        url:"/web/login"+"?name="+name,
        data:{},
        success:function(res){
            document.cookie = "token=" + res;
            window.location.href='/search';
            alert("歡迎"+name+"登錄成功!");
        }
    });
}

這里我們在js中調(diào)用服務(wù)器提供的接口時使用了ajax,ajax了解請看這里:https://www.runoob.com/ajax/ajax-tutorial.html
且使用ajax的代碼我們要引入一個庫:jQuery,自行百度jQuery的用途。這就是一個js文件,下載下來拖進項目里就可以了,并在有用到的html文件中引入(參考上面html代碼)。下載鏈接:https://jquery.com (我用的min版本,比較?。?/p>

且這里我們使用cookie來保存token。

這里我們定義了一個GET方法,前端調(diào)用/web/login時會給傳過來賬號的參數(shù),也就是輸入的用戶名name。這里的GET傳參我簡單總結(jié)一下吧:

GET方法傳參數(shù)我試了三種,都可以:

  1. 在Flask文檔中提到的:
@app.route('/web/login/<name>', methods=['GET'])
def web_login(name):

然后在js的ajax請求中的url用:

url:"/web/login/"+name

這樣就可以在web_login(name):方法中直接使用name參數(shù)


  1. 使用”? =“path拼接的方式
    就是我們上面代碼中使用的方式,這里不寫了

  1. 用data傳json的方式
    接口不變,還是:
@app.route('/web/login', methods=['GET'])
def web_login():

傳遞方式改為:

   var data = {
      data: JSON.stringify({
          'name': name
      })
  };

  $.ajax({
      type:"GET",
      url:"/web/login",
      data:data,
      success:function(res){
          document.cookie = "token=" + res;
          window.location.href='/search';
          alert("歡迎"+name+"登錄成功!");
      }
  });

接收方式改為:

 data = json.loads(request.args.get("data"))
 name = data["name"]

我試了這三種方式都可以,如果還有其他方式希望大家多多交流。這是數(shù)據(jù)的傳遞。


POST方式傳值
傳遞:

var data = {
      data: JSON.stringify({
          'lat': lat,
          'lng': lng,
          'result': address
      })
  };

   $.ajax({
       type:"POST",
      url:"/save/search_result",
      data:data,
      success:function(res){
          if (res["success"]) {
              updateList();
          } else {
              window.location.href='/login';
              alert(res["msg"]);
          }
      }
  });

接收:

data = json.loads(request.form.get('data'))
result = data['result']
lat = data['lat']
lng = data['lng']

數(shù)據(jù)庫: 相信大家也看到了在/web/login接口的實現(xiàn)方法中,我們進行了數(shù)據(jù)庫的操作,我們簡單說一下,在python中操作mongodb,我們需要用到pymongo!

這里是教程:https://www.runoob.com/python3/python-mongodb.html
里面講了如何安裝pymongo,以及如何利用pymongo來進行數(shù)據(jù)庫的增刪改查等操作,在這里就不贅述了!

上面代碼中使用的client是我定義的一個全局變量:
client = pymongo.MongoClient('mongodb://localhost:27017/')
(最后我會貼出web_location.py的全部代碼),當然你也可以在使用的時候每次都創(chuàng)建一個client連接。上面操作數(shù)據(jù)庫的代碼就不詳細解釋了,相信大家看過pymongo后也都看得懂。

數(shù)據(jù)庫的設(shè)計我是建了兩張表,一張用來存放用戶信息,一張用來存放用戶的搜索記錄。

  • 用戶信息表:每當生成一個新的用戶信息的時候我會為其生成一個對應(yīng)的token(用sha1生成),并設(shè)置過期時間'exp_time'(我設(shè)置的登錄后60秒失效)。如下:

    用戶信息表

    每次進行需要跟用戶相關(guān)的操作時,都要進行token是否有效的驗證,如果無效,則提示重新登錄!
    注意:其中的'_id'是mongodb自動給生成的,這并不是一個字符串,其中包含了一些信息,詳細解釋在上面的mongodb文檔中會有!

  • 搜索記錄表:所有用戶成功搜索后都會生成一條搜索記錄并放在這張表中,并把用戶信息表中的’_id‘作為這張表中的’user_id‘進行關(guān)聯(lián),結(jié)構(gòu)如下:

    搜索記錄表


保存搜索記錄接口實現(xiàn)

@app.route('/save/search_result', methods=['GET', 'POST'])
def save():
    data = json.loads(request.form.get('data'))
    result = data['result']
    lat = data['lat']
    lng = data['lng']
    token = request.cookies.get('token')
    current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    if verify_token(token):
        db = client["web_location"]
        collection = db["search_history"]
        user_id = get_user_id(token)
        history_dic = {"user_id": user_id, "lat": lat, "lng": lng, "address": result, "search_time": current_time}
        collection.insert_one(history_dic)
        return {"success": True, "msg": "Save success!!!"}
    else:
        return {"success": False, "msg": "Save failed! Token is invalid!!!"}

這個沒什么好說的。


獲取搜索記錄接口實現(xiàn)

@app.route('/get/search_history', methods=['GET', 'POST'])
def get_search_history():
    token = request.cookies.get('token')
    if verify_token(token):
        user_id = get_user_id(token)
        db = client["web_location"]
        collection = db["search_history"]
        query = {"user_id": user_id}
        cursor = collection.find(query, {"_id": 0, "user_id": 0})
        doc = [item for item in cursor]
        doc = sorted(doc, key=lambda e: e.__getitem__('search_time'), reverse=True)
        print(doc)
        return {"success": True, "msg": "Success!!!", "data": doc}
    else:
        return {"success": False, "msg": "Get history failed! Token is invalid!!!", "data": ""}

這里有一點需要注意:查找數(shù)據(jù)時,由于mongodb自動給生成的'_id'比較特殊,不能轉(zhuǎn)化為json,所以獲取的時候把這個相關(guān)的給去掉
cursor = collection.find(query, {"_id": 0, "user_id": 0})
如果看了pymongo的文檔,這個應(yīng)該能明白。當然,這個’_id‘參數(shù)你也可以設(shè)置成自己的數(shù)據(jù),比如電話號,這樣的話你就可以獲取了。自己可以研究下這個。


到這里我們的項目基本就算完成了,點擊登錄按鈕調(diào)用登錄接口,點擊搜索按鈕搜索到數(shù)據(jù)后調(diào)用保存數(shù)據(jù)接口,然后調(diào)用獲取搜索記錄接口獲取數(shù)據(jù),然后在js中動態(tài)刷新UI界面:

function updateList(){
    $.ajax({
        type:"GET",
        url:"/get/search_history",
        data:{},
        success:function(res){
            if (res["success"]) {
                var data = res["data"];
                var li_html = ''
                for (var i=0; i<data.length; i++){
                    dic = data[I];
                    li_html += '<li class="result_list_li">';
                    li_html += '<div class="result_list_li_div">';
                    li_html += '<div class="li_row">' + dic["lat"] + ',' + dic["lng"] + '</div>';
                    li_html += '<div class="li_row">' + dic["address"] + '</div>';
                    li_html += '<div class="li_row">' + dic["search_time"] + '</div>';
                    li_html += '</div>';
                    li_html += '</li>';
                }
                $("#search_history_ul").html(li_html);
            } else {
                window.location.href='/login';
                alert(res["msg"]);
            }
        }
    });
}

這樣,就算完成了!流程算是完整了。
最終效果圖:


最終效果圖

web_location.py代碼:

import json
import os
from _sha1 import sha1
import time
import pymongo

from flask import Flask, render_template
from flask import request
from werkzeug.utils import redirect

app = Flask(__name__)
client = pymongo.MongoClient('mongodb://localhost:27017/')


@app.route('/', methods=['GET', 'POST'])
def hello_world():
    return redirect('/login')


@app.route('/login', methods=['GET', 'POST'])
def login():
    return render_template('LoginPage.html')


@app.route('/search', methods=['GET', 'POST'])
def search():
    return render_template('SearchPage.html')


@app.route('/web/login', methods=['GET'])
def web_login():
    # data = json.loads(request.args.get("data"))
    # name = data["name"]

    name = request.args.get("name")
    db = client["web_location"]
    collection = db["user"]

    query = {"name": name}
    token = get_token()
    current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    exp_time = int(time.time()) + 60
    if collection.count_documents(query) > 0:
        new_values = {"$set": {"token": token, "last_login_time": current_time, "exp_time": exp_time}}
        collection.update_one(query, new_values)
    else:
        data = {"name": name, "token": token, "last_login_time": current_time, "exp_time": exp_time}
        collection.insert_one(data)
    return token


def get_token():
    token = sha1(os.urandom(24)).hexdigest()
    return token


@app.route('/save/search_result', methods=['GET', 'POST'])
def save():
    data = json.loads(request.form.get('data'))
    result = data['result']
    lat = data['lat']
    lng = data['lng']
    token = request.cookies.get('token')
    current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    if verify_token(token):
        db = client["web_location"]
        collection = db["search_history"]
        user_id = get_user_id(token)
        history_dic = {"user_id": user_id, "lat": lat, "lng": lng, "address": result, "search_time": current_time}
        collection.insert_one(history_dic)
        return {"success": True, "msg": "Save success!!!"}
    else:
        return {"success": False, "msg": "Save failed! Token is invalid!!!"}


@app.route('/get/search_history', methods=['GET', 'POST'])
def get_search_history():
    token = request.cookies.get('token')
    if verify_token(token):
        user_id = get_user_id(token)
        db = client["web_location"]
        collection = db["search_history"]
        query = {"user_id": user_id}
        cursor = collection.find(query, {"_id": 0, "user_id": 0})
        doc = [item for item in cursor]
        doc = sorted(doc, key=lambda e: e.__getitem__('search_time'), reverse=True)
        print(doc)
        return {"success": True, "msg": "Success!!!", "data": doc}
    else:
        return {"success": False, "msg": "Get history failed! Token is invalid!!!", "data": ""}


def verify_token(token):
    if token:
        doc = get_user_info(token)
        if doc:
            time_ts = int(time.time())
            exp_time = doc["exp_time"]
            return time_ts < exp_time
        else:
            return False
    else:
        return False


def get_user_id(token):
    if token:
        doc = get_user_info(token)
        if doc:
            return doc["_id"]
        else:
            return ""
    else:
        return ""


def get_user_info(token):
    if token:
        db = client["web_location"]
        collection = db["user"]
        query = {"token": token}
        return collection.find_one(query)
    else:
        return None


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

js代碼:

var map;
var icon_marker;
var address_text;

function login(){
    var name = document.getElementById("login_input_view").value;
    if (name.length <= 0) {
        alert("賬號不能為空!趕緊滴!");
        return;
    }

//    var data = {
//      data: JSON.stringify({
//          'name': name
//      })
//  };
//
//    $.ajax({
//      type:"GET",
//      url:"/web/login",
//      data:data,
//      success:function(res){
//          document.cookie = "token=" + res;
//          window.location.href='/search';
//          alert("歡迎"+name+"登錄成功!");
//      }
//  });

    $.ajax({
        type:"GET",
        url:"/web/login"+"?name="+name,
        data:{},
        success:function(res){
            document.cookie = "token=" + res;
            window.location.href='/search';
            alert("歡迎"+name+"登錄成功!");
        }
    });
}

function initSearchPage(){
    address_text = document.getElementById("address_text");
    address_text.style.visibility = "hidden";
    initMap();
}

function initMap(){
    address_text = document.getElementById("address_text");
    address_text.style.visibility = "hidden";

    map = new BMapGL.Map("map_container");
    var point = new BMapGL.Point(116.404, 39.915);

    map.centerAndZoom(point, 15);
    map.enableScrollWheelZoom(true)
    var scaleCtrl = new BMapGL.ScaleControl();
    map.addControl(scaleCtrl);
    var zoomCtrl = new BMapGL.ZoomControl();
    map.addControl(zoomCtrl);
    map.setMapStyleV2({styleId: 'dffd8b79b3fb4f384b5deb371abb9722'});

    var myIcon = new BMapGL.Icon("/static/img/Maker.png", new BMapGL.Size(40, 50), {
        anchor: new BMapGL.Size(20, 45)
    });
    icon_marker = new BMapGL.Marker(point, {
        icon: myIcon
    });
    map.addOverlay(icon_marker);
    searchWithCoor(116.404, 39.915, true);
}

function search(){
    var lat = document.getElementById("lat_input_view").value;
    var lng = document.getElementById("lng_input_view").value;
    if (lat.length <= 0 || lng.length <= 0) {
        alert("請輸入完整的經(jīng)緯度!");
        return;
    }
    if (lat < 0 || lat > 180) {
        alert("請輸入正確的經(jīng)度!");
        return;
    } else if (lng < 0 || lng > 90) {
        alert("請輸入正確的緯度!");
        return;
    }
    searchWithCoor(lat, lng, false);
}

function searchWithCoor(lat, lng, auto) {
    var point = new BMapGL.Point(lat, lng);
    map.centerAndZoom(point, 15);
    icon_marker.setPosition(point);

    address_text.style.visibility = "hidden";
    var geoc = new BMapGL.Geocoder({extensions_town: true});
    geoc.getLocation(point, function(result){
        address_text.style.visibility = "visible";
        if (result.address) {
            address_text.innerText = result.address;
            if (auto) {
                updateList();
            } else {
                saveResult(lat, lng, result.address);
            }
        } else {
            address_text.innerText = "暫無結(jié)果";
        }
    });
}

function saveResult(lat, lng, address){
    var data = {
        data: JSON.stringify({
            'lat': lat,
            'lng': lng,
            'result': address
        })
    };

    $.ajax({
        type:"POST",
        url:"/save/search_result",
        data:data,
        success:function(res){
            if (res["success"]) {
                updateList();
            } else {
                window.location.href='/login';
                alert(res["msg"]);
            }
        }
    });
}

function updateList(){
    $.ajax({
        type:"GET",
        url:"/get/search_history",
        data:{},
        success:function(res){
            if (res["success"]) {
                var data = res["data"];
                var li_html = ''
                for (var i=0; i<data.length; i++){
                    dic = data[i];
                    li_html += '<li class="result_list_li">';
                    li_html += '<div class="result_list_li_div">';
                    li_html += '<div class="li_row">' + dic["lat"] + ',' + dic["lng"] + '</div>';
                    li_html += '<div class="li_row">' + dic["address"] + '</div>';
                    li_html += '<div class="li_row">' + dic["search_time"] + '</div>';
                    li_html += '</div>';
                    li_html += '</li>';
                }
                $("#search_history_ul").html(li_html);
            } else {
                window.location.href='/login';
                alert(res["msg"]);
            }
        }
    });
}

新手,第一次寫python、js,代碼寫的比較垃圾,還請各路大神指點,謝謝!


  • 全部完成后就可以在桶局域網(wǎng)的任何一臺機器上通過你的ip來訪問你的web了! 如果你想讓你的網(wǎng)站被外網(wǎng)訪問那你就得把項目部署在遠程服務(wù)器上了。關(guān)于項目部署大家可以自行研究!
  • 如果你想免費的,可以試一下pythonanywhere,可以參考一下這里:http://www.itdecent.cn/p/70a0c097e537 后半部分有教程如何在pythonanywhere托管!但是我們這個項目托管后你只能訪問登錄頁,點擊登錄按鈕是沒有用的,因為我們使用了數(shù)據(jù)庫??,參考這里:https://help.pythonanywhere.com/pages/MongoDB/ 如果你愿意付費,你可以實現(xiàn)的!??

后面寫的比較亂,大家見諒,先寫到這里吧。沒怎么寫過文章,格式有些渣大家將就著看 ~
因為我也是第一次搞這些,所以寫這個文章主要也不是為了讓大家學(xué)習(xí),更多的是自己總結(jié)和拋磚引玉,希望大家指出我做的不對的地方和欠缺需要學(xué)習(xí)的地方,謝謝!??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容