美文网首页
Python Flask从前端到后台,尝试一个简单的web项目

Python Flask从前端到后台,尝试一个简单的web项目

作者: DonfexCui | 来源:发表于2020-07-04 16:50 被阅读0次

    前言:最近为了了解前端和服务器之间的通讯是如何工作的,所以做了一个简单的web项目,包括web界面、服务器接口、数据库等,以本机作为服务器来开发了一个相对完整的demo,最终可以正常运行。因为本人是做iOS开发的,所以这里用到的很多知识我也仅仅是了解的比较浅,因为重点只是熟悉一下前端和服务器之间的交流流程,中间使用到的语言也只是为这次demo开发准备。

    中间用到的知识包括:

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

    服务器:Flask、python3。

    数据库:MongoDB、pymongo。

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

    环境:Mac OS

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

    本文也算是做完之后的笔记吧,由于重点在于整个的流程,所以中间很多知识的细节欠缺,欢迎大家补充相互学习。

    一、环境配置

    1. 使用Python3进行服务器开发,简单说下python3的安装:

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

    安装python3: brew install python

    完成后使用:python3 --version 查看版本。(系统自带python2.7,如果使用python --version查看的是2.7的版本,同理直接使用python进入的也是2.7版本,如果你想在终端使用python进入python3的环境,可以在.base_profile中用alias命令来实现,具体可自行百度)

    2. 虚拟环境,用python开发做项目,那虚拟环境是很有必要的,为什么要使用虚拟环境?首先我们用python做项目,很多东西都是别人封装好的库,不必我们自己实现,例如ios、android开发也一样,而python不同的是安装的库都会安装在python目录下,例如你有三个python项目那么这三个python项目各自安装使用的库都在一个地方放着,如果项目1使用库A的1.0版本,而项目2使用库A的2.0版本,那么就可能库A的2.0版本把1.0版本覆盖了,从而导致项目1不能正常工作,所以虚拟环境对于开发python项目来说很有必要!每个项目对应各自的虚拟环境,其中其实就是把python环境复制了一份,使你每个项目安装的库都在各自的环境里,从而互不影响(说的比较浅显,望见谅😺)。

    使用pip来安装:

    安装virtualenv: sudo pip install virtualenv

    安装virtualenvwrapper:sudo pip install virtualenvwrapper(这是安装一个虚拟环境的操作插件,使用起来更方便,如果提示失败可尝试:sudo pip install virtualenvwrapper --upgrade --ignore-installed six)

    安装成功后新建一个放置虚拟环境的目录,我是在hoom下建了一个virtualenv-wokespaces:

    mkdir ~/virtualenv-wokespaces

    cd ~/virtualenv-wokespaces

    设置环境变量,在.base_profile中添加:

    export WORKON_HOME=~/virtualenv-wokespaces

    source /usr/local/bin/virtualenvwrapper.sh

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

    新建虚拟环境:mkvirtualenv xxx

    列出所有虚拟环境:workon

    切换到某个虚拟环境:workon xxx

    退出虚拟环境:deactivate

    删除虚拟环境:rmvirtualenv xxx

    配置好虚拟环境之后,以后写项目就建立一个对应的虚拟环境,需要安装库时就进入对应的虚拟环境后再安装。用PyCharm创建一个项目后可以再偏好设置的Project:xx -> Project Interpreter中连接自己的虚拟环境。

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

    3. 数据库使用MongoDB

    如何安装以及我遇到的一些问题请参考我的另一篇文章:https://www.jianshu.com/p/9c3e37e02f75

    这里是详细步骤:https://www.runoob.com/mongodb/mongodb-osx-install.html (这里有关于mongodb的各种操作,如:创建数据库、删除数据库、往数据库写数据、等。可以在这里面学习,这里不详细说了)

    一定要注意其中的/data/db的连接,这个目录就是你数据库数据存放的地方,你可以自定义,在你可创建文件夹的任意地方,也可以是其他名字,创建好后使用sudo mongod --dbpath=/data/db连接上就可以,注意:mongodb启动时需要指定path也就是指定data/db路径,启动mongo就是启动了一个进程,如果这个进程关闭了,再次启动都需要指定路径,所以,如果你关机了,下次开机启动mongodb之前记得先指定路径哦。

    ps:如果mongodb启动成功后在浏览器打开:http://localhost:27017,会显示“It looks like you are trying to access MongoDB over HTTP on the native driver port.”

    mogodb启动成功

    4.数据库可视化GUI推荐:Robo 3T

    下载链接:https://robomongo.org/download

    使用教程随便贴一个:https://www.bbsmax.com/A/obzb7V2BJE/ ,可自行百度。

    弄好之后连上你的数据库你就可以在这里看到你的数据库中的数据了。

    Robo 3T界面

    5.编写前端界面我使用的HBuilder,其实用PyCharm也可以写h5、css、js等,但是,它不会自动补全,没有代码提示😅,特别是对于我这样的新手简直不知道该怎么写代码,其实也能让他有自动补全功能,但是得付费~ 所以这里我推荐新手的话可以使用HBuilder,下载标准版也就一二十兆,h5、css、js都有自动补全,而且有内置浏览器,可以实时的看效果,对于新手还是很不错的(老手忽略),写完把代码复制到PyCharm项目中去。

    下载链接:https://www.dcloud.io/hbuilderx.html (选择标准版就好,使用过程中需要安装的插件按提示安装就好)

    HBuilder界面

    关于环境先说到这里把,说的比较乱大家见谅 ~ 因为中间配置的时候可能会遇到各种各样的问题,这里没详细列举,如有遇到欢迎评论留言。

    二、项目开发

    建议:对于要学习某种语言或者类似这样要去做一个整套的流程的项目,中间要用到不同的语言不同的知识,建议不要在某个点上死磕,非要去学全套的,没必要,学习要搞清楚学习的重点在哪里,比如我们这个项目,要用到html我要从头到尾去学html么?不需要!因为我们还要用到js、css、python等等。我们没有那个必要把每个语言学的很精通,我们做这个项目的重点也不在此。对于这种情况,我的建议是:先把项目文件建起来,然后从界面入手,先写个界面,有了界面就要有操作,然后写事件,这样一步一步往下,每一步用到什么知识再去看你用到的知识,这样最有效。

    项目简介:简单说下这次做的项目功能,两个界面的web,一个登录界面,一个搜索界面,其中登录界面只需要输入账号点击登录就进入搜索界面,因为牵涉到密码比较复杂所以这里只用账号登录,输入账号后点击登录进入搜索界面。搜索界面可以根据输入的经纬度搜索对应的地址信息,并显示这个用户的搜索记录。功能部分就这些,下面来一步一步实现这个功能。

    1.建立项目

    用PyCharm新建一个项目,项目位置可自定义。PyCharm新建项目会有一个venv文件,这个是自带的虚拟环境,可以把这个文件删掉然后连接上面我们说的自定义的虚拟环境(当然,你也可以用这个自带的,用法自己研究下,我没有用自带的)。

    (1)创建 web_location.py 文件,用来写服务器代码,提供接口。

    (2)创建两个文件夹 static 和 templates。 因为此项目使用flask框架,所以根据需求需要建立这两个文件夹,其中static中放静态文件,比如css、js、图片资源等。templates中放html文件。必须这么放,不然后面会出错。

    (3)在static中创建一个.css文件用来写css代码,创建个js文件用来写js代码,名字自定义。在templates中创建两个html文件,代表两个h5界面。这些文件可以先建好放着就行。

    image

    2.web界面相关

    上面我们说过了,由于用PyCharm写html代码没有自动补全,所以我们在HBuilder中写界面相关的,写完把代码复制到我们上面创建好的对应的文件中就行了。关于前端界面的实现不详细说了只贴部分代码,css代码比较多所以不贴了,需要的可以留言。关于html、css、js的学习可以去找一些教程,这里是菜鸟教程的链接: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文件,关于为什么引入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="请输入经度" />
                    <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框架使用
    关于Flask请看这里:https://flask.palletsprojects.com/en/1.1.x/quickstart/#quickstart

    通过Flask框架可以简单的开发后台,提供接口。
    添加Flask库(记得前面说的!切换到这个项目对应的虚拟环境,然后再安装Flask) 终端: sudo pip install flask
    安装成功后就可以在虚拟环境中看到了,如下图:

    安装了Flask1.1.2

    接下来在我们之前建好的web_location.py中写如下代码:

    from flask import Flask
    app = Flask(__name__)
    
    @app.route('/')
    def hello_world():
        return 'Hello, World!'
    

    然后在终端中执行:

    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/那么就会执行hello_world()方法,return 'Hello, World!'就是在网页上显示了Hello, World!字样. 网页效果如下:

    hello
    到这里,你应该明白了这是怎么回事了吧😺,这就是我们平常打开网页链接的样子。当然,这是最最基本的。

    你也可以这样写:

    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)
    
    • 这样写后可以直接在终端中直接执行这个py文件:python3 web_location.py 就运行了。
    • 其中@app.route('/', methods=['GET', 'POST'])这里可以指定GET、POST支持,这里不细说了,请看Flask文档。
    • 主要的是这里可以指定host和port,之前你只能在自己电脑上通过 http://127.0.0.1:5000来访问,假如你想在同个局域网的其他设备上也能访问你写的这个接口,那么你可以把host改成0.0.0.0,port自定义,然后就可以,只要在同一个局域网,其他电脑或者手机就可以通过你的电脑当前的局域网IP地址来访问(在终端中通过ifconfig来查看你当前的局域网ip,注意:这个ip在你每次重新连接局域网的时候是会变得!),至于0.0.0.0 是什么!请看这里:https://cloud.tencent.com/developer/article/1643083 这里面说的比较详细了,相信你看完也能明白了👍!

    当然,我们的网页不可能就显示个hello world!我们之前已经写好了两个网页了,那么怎么把我们写好的html文件和flask关联起来呢?如果你看flask文档的话就会知道,下面直接看代码:

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

    没错,就这么简单!Flask渲染模板使用Jinja2
    (Flask已经为你自动配置了,至于什么是Jinja2,自己学习下吧💪,特别注意我们之前说到的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. 事件

    上面我们可以显示我们的网页了,但是正常的网页不仅能看,还得能操作不是!下面我们就给我们的网页加上交互吧!

    上面的登录页h5代码里面其实已经调用了js方法了,登录按钮,调用了login()方法,我们在js文件中实现这个方法:

    function login(){
        var name = document.getElementById("login_input_view").value;
        if (name.length <= 0) {
            alert("账号不能为空!赶紧滴!");
            return;
        }
    
        window.location.href='/search';
        alert("欢迎"+name+"登录成功!");
    }
    

    这样,当我们输入账户点击了登录按钮,进进入搜索界面了!
    当然,这只是一个简单的跳转,重要的是点击的是点击登录后要做的后台操作。
    我们的目标是:

    • 点击登录调用后台提供的登录接口
    • 后台的登录接口中会检测输入的账号是否存在
    • 如果存在则更新数据库中的用户信息,如果不存在则生成用户信息并存入数据库,然后接口返回成功或失败信息
    • 前端根据登录接口返回的结果进行处理,或进入搜索页,或提示登录失败!

    先上代码:

    @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
    

    对应的调用代码:

    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中调用服务器提供的接口时使用了ajax,ajax了解请看这里:https://www.runoob.com/ajax/ajax-tutorial.html
    且使用ajax的代码我们要引入一个库:jQuery,自行百度jQuery的用途。这就是一个js文件,下载下来拖进项目里就可以了,并在有用到的html文件中引入(参考上面html代码)。下载链接:https://jquery.com (我用的min版本,比较小)

    且这里我们使用cookie来保存token。

    这里我们定义了一个GET方法,前端调用/web/login时会给传过来账号的参数,也就是输入的用户名name。这里的GET传参我简单总结一下吧:

    GET方法传参数我试了三种,都可以:

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

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

    url:"/web/login/"+name
    

    这样就可以在web_login(name):方法中直接使用name参数


    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"]
    

    我试了这三种方式都可以,如果还有其他方式希望大家多多交流。这是数据的传递。


    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']
    

    数据库: 相信大家也看到了在/web/login接口的实现方法中,我们进行了数据库的操作,我们简单说一下,在python中操作mongodb,我们需要用到pymongo!

    这里是教程:https://www.runoob.com/python3/python-mongodb.html
    里面讲了如何安装pymongo,以及如何利用pymongo来进行数据库的增删改查等操作,在这里就不赘述了!

    上面代码中使用的client是我定义的一个全局变量:
    client = pymongo.MongoClient('mongodb://localhost:27017/')
    (最后我会贴出web_location.py的全部代码),当然你也可以在使用的时候每次都创建一个client连接。上面操作数据库的代码就不详细解释了,相信大家看过pymongo后也都看得懂。

    数据库的设计我是建了两张表,一张用来存放用户信息,一张用来存放用户的搜索记录。

    • 用户信息表:每当生成一个新的用户信息的时候我会为其生成一个对应的token(用sha1生成),并设置过期时间'exp_time'(我设置的登录后60秒失效)。如下:

      用户信息表
      每次进行需要跟用户相关的操作时,都要进行token是否有效的验证,如果无效,则提示重新登录!
      注意:其中的'_id'是mongodb自动给生成的,这并不是一个字符串,其中包含了一些信息,详细解释在上面的mongodb文档中会有!
    • 搜索记录表:所有用户成功搜索后都会生成一条搜索记录并放在这张表中,并把用户信息表中的’_id‘作为这张表中的’user_id‘进行关联,结构如下:

      搜索记录表

    保存搜索记录接口实现

    @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": ""}
    

    这里有一点需要注意:查找数据时,由于mongodb自动给生成的'_id'比较特殊,不能转化为json,所以获取的时候把这个相关的给去掉
    cursor = collection.find(query, {"_id": 0, "user_id": 0})
    如果看了pymongo的文档,这个应该能明白。当然,这个’_id‘参数你也可以设置成自己的数据,比如电话号,这样的话你就可以获取了。自己可以研究下这个。


    到这里我们的项目基本就算完成了,点击登录按钮调用登录接口,点击搜索按钮搜索到数据后调用保存数据接口,然后调用获取搜索记录接口获取数据,然后在js中动态刷新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("请输入完整的经纬度!");
            return;
        }
        if (lat < 0 || lat > 180) {
            alert("请输入正确的经度!");
            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 = "暂无结果";
            }
        });
    }
    
    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,代码写的比较垃圾,还请各路大神指点,谢谢!


    • 全部完成后就可以在桶局域网的任何一台机器上通过你的ip来访问你的web了! 如果你想让你的网站被外网访问那你就得把项目部署在远程服务器上了。关于项目部署大家可以自行研究!
    • 如果你想免费的,可以试一下pythonanywhere,可以参考一下这里:https://www.jianshu.com/p/70a0c097e537 后半部分有教程如何在pythonanywhere托管!但是我们这个项目托管后你只能访问登录页,点击登录按钮是没有用的,因为我们使用了数据库😭,参考这里:https://help.pythonanywhere.com/pages/MongoDB/ 如果你愿意付费,你可以实现的!💪

    后面写的比较乱,大家见谅,先写到这里吧。没怎么写过文章,格式有些渣大家将就着看 ~
    因为我也是第一次搞这些,所以写这个文章主要也不是为了让大家学习,更多的是自己总结和抛砖引玉,希望大家指出我做的不对的地方和欠缺需要学习的地方,谢谢!🙂

    相关文章

      网友评论

          本文标题:Python Flask从前端到后台,尝试一个简单的web项目

          本文链接:https://www.haomeiwen.com/subject/wiruqktx.html