V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
jpush
V2EX  ›  Python

从零开始用 Flask 搭建一个网站(三)

  •  
  •   jpush ·
    jpush · 2017-04-14 13:23:08 +08:00 · 1846 次点击
    这是一个创建于 2817 天前的主题,其中的信息可能已经有所发展或是发生改变。

    从零开始用 Flask 搭建一个网站(二) 介绍了有关于数据库的运用,接下来我们在完善一下数据在前端以及前端到后端之间的交互。本节涉及到前端,因此也会讲解一下 jinja2 模板、 jQuery 、 ajax 等用法。

    下面我们来创建两个界面,一个可以新建 channel ,并显示,另一个可以创建 integration ,并且可以绑定一个之前创建的 channel 。

    post2channel.html

    {% extends "base.html" %}
    {% import "bootstrap/wtf.html" as wtf %}
    
    {% block head %}    
    {{ super() }}    
    <link rel="stylesheet" href="{{ url_for('static', filename='css/post2channel.css') }}">    
    <link rel="stylesheet" href="{{ url_for('static', filename='css/toastr.css') }}">    
    <script type="text/javascript" src="{{ url_for('static', filename= 'js/jquery.min.js') }}"></script>    
    <script type="text/javascript" src="{{ url_for('static', filename='js/toastr.js') }}"></script>    
    <script type="text/javascript" src="{{ url_for ('static', filename='js/')}}"></script>
    {% endblock %}
    
    {% block title %}极光宝盒-创建集成{% endblock %}
    {% block navbar %}    
    <div id="navigationbar"></div>    
    {{ super() }}
    {% endblock %}
    
    ...
    

    我们从第一行开始来讲解一下这个模板,第一句

    {% extends "base.html" %}
    

    从字面上可以明白这个模板继承了 base.html 模板,使用 {% %} 是 jinja2 模板的语法,表示语句块,还有一种分隔符 {{ }} ,表示变量,通常用来在模板上显示。接下来是

    {% block head %}
    

    可以看到也是比较容易理解的语法, block 后接一个 block name ,表示一个块开始的声明,结束的时候用 {% end %} 即可。在块中可以使用普通的 HTML 语法。{{ super() }} 表示继承父模板的块声明,这里指继承 base.html 中声明的 head 块。接下来是 css 和 js 文件的引用。此页面使用了 toastr 来显示通知,类似于 Android 中的 Toast ,有兴趣的可以了解一下它的详细用法。

    接下来是 HTML 代码,有一个下拉输入框和按钮。

    <ul id="content" class="dropdown-menu dropdown-menu-right">    
      {% for channel in channels %}        
        <li id="{{ channel }}" class="channel_class"><a>{{ channel }}</a></li>    
      {% endfor %}
    </ul>
    

    以上代码就是下拉输入框中的数据来源,用了一个 for 循环来遍历 channels 列表,并且用 li 包裹每个元素,这也是模板常见的用法。 channels 是在视图函数中传过来的。

    auth/views.py

    @auth.route('/new/post_to_channel', methods=['GET'])
    def post_to_channel():    
      developer = get_developer()    
      dev_key = developer.dev_key    
      channels = get_channel_list()    
      return render_template('auth/new/post2channel.html', **locals())
    

    remder_template 的第二个参数表明,渲染这个 post2channel.html 页面的时候,把以上所有的变量都传递到页面中,这样就可以使用 {{ }} 模板语法拿到。接下来回到 post2channel.html , 看到<script></script>部分:

    $('#create_integration_btn').click(function () {    
      var channel = $('#selected_channel').val();   
      console.log('create integration, channel: ' + channel);    
      if (channel != '') {        
        $.ajax({            
        type: "POST",            
        contentType: "application/json; charset=utf-8",            
        url: "../../v1/developers/{{ dev_key }}/integrations",            
        data: JSON.stringify({channel: channel}),            
        success: function (data) {                
          if (data != undefined) {                    
            if (data["token"]) {                       
              window.location.href = '/auth/manage/create_integration/' + data["integration_id"] + '/' + data["token"] + '/' + channel                    
            }                
          } else {                    
            toastr.error("服务器异常");                
          }            
        },            
          error: function (error) {                
            console.log(error);                
            toastr.error("创建失败");            
          },            
          dataType: "json"        
        })    
      }
    })
    

    这是创建集成按钮的逻辑,用 jQuery 的 ajax 发送 post 请求,以 json 格式将输入框中的 channel 值传到 url 表明的视图函数。这里的 url 是相对路径。在 ajax 中有 success 和 error 两个函数,分别是请求成功和失败的回调函数。下面看到这个请求的视图函数,我们来看看视图函数是如何处理从页面传过来的数据。

    api_1_0/developers.py

    # 添加一个集成,并返回 integration_id ,如果 channel 已存在,直接绑定该 channel , 否则新建一个 [email protected]('/developers/<dev_key>/integrations', methods=['POST'])
    def create_integrations(dev_key):
    # 先判断一下传过来的 json 数据是否符合要求
      if not request.json or not 'channel' in request.json:    
        print("request json error")    
        abort(400)
    #从数据库得到 developer
      developer = Developer.query.filter_by(dev_key=dev_key).first()
      if developer is None:    
        print("developer not found")    
        abort(400)
      #以下省略
      ...
      #最后返回相关数据
      return jsonify({'integration_id': new_integration_id,                
                      'token': token.decode('utf-8')}), 201
    

    以上代码创建成功后也返回了一个 json ,这样在 success 的回调函数中就能得到这个数据,用于在跳转到其他界面的时候做一些初始化操作。即以下代码:

    if (data != undefined) { 
      if (data["token"]) { 
        window.location.href = '/auth/manage/create_integration/' + data["integration_id"] + '/' + data["token"] + '/' + channel 
      } 
    }
    

    这里将参数放到 url 中,调用了对应的视图函数:

    auth/views.py

    @auth.route('/manage/create_integration/<string:integration_id>/<string:token>/<string:channel>', methods=['GET', 'POST'])
    def create_integration(integration_id, token, channel):    
      integration = Integration.query.filter_by(integration_id=integration_id).first()    
      channels = get_channel_list()    
      developer = get_developer()    
      dev_key = developer.dev_key    
      return render_template('auth/create.html', **locals())
    

    可以看到上面的参数就是从 post2channel 页面传过来的,并且还从数据库中查询到对应的 integration ,然后将相关数据传到 create.html ,让后者渲染页面。

    我们通过一个例子看到数据在前端和后端、前端之间的交互。总结一下,无非就是在前端页面中发送请求,然后在视图函数中操作数据库,并且返回相关数据,回调到前端页面中,最后在回调中调用另一个视图函数,在跳转页面时利用得到的数据渲染页面。一切就是这么简单,没什么黑魔法!源码在 github 上。下一节介绍一下 flask_oauthlib 的用法,学习一下如何使用 oath2 第三方授权登录以及调用提供方的相关 API 。


    作者: KenChoi - 极光( JPush 为极光团队账号,欢迎关注)

    原文:从零开始用 Flask 搭建一个网站(三)

    知乎专栏:极光日报

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1106 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 313ms · UTC 18:57 · PVG 02:57 · LAX 10:57 · JFK 13:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.