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

从零开始用 Flask 搭建一个网站(一) 介绍了如何搭建 Python 环境,以及 Flask 应用基本项目结构。咱们要搭建的网站是管理第三方集成的控制台,相似于 Slack。 本篇主要讲解数据如何在 Flask 应用中流动,其它的框架基本上也是大同小异。html

数据库python

既然是数据的流动,首先要创建起存取数据的地方,也就是数据库了(这里是指关系型数据库,NoSQL 不在这讨论)。第一节中咱们使用了 Flask-SQLAlchemy 管理数据库,在 Flask-SQLAlchemy 中,数据库使用 URL 指定,最流行的数据库 URL 格式以下:git

clipboard.png

在 config.py 中咱们已经指定了数据库 URL,若是使用云平台部署程序,直接将生成的数据库 URL 写到 config.py 中 SQLALCHEMY_DATABASE_URI 便可。这里咱们使用的是 SQLite 数据库。Flask-SQLAlchemy 采用数据库抽象层来操做数据库,也称为对象关系映射(Object-Relational Mapper, ORM),在用户不知不觉的状况下把高层的面向对象操做转换成低层的数据库指令,所以易用性好。咱们已经在 app/__init__.py 中实例化了 SQLAlchemy 类:github

app/__init__.pysql

from flask_sqlalchemy import SQLAlchemy
...
db = SQLAlchemy()
...

定义模型shell

模型类能够理解为数据库中的一张表,Flask-SQLAlchemy 提供了一个基类和一系列辅助类和函数来让咱们定义模型的结构。咱们直接在 app 文件夹下建立一个 models.py 文件。鉴于每一个网站需求都不同,所存的数据也不一样,但本质上是大同小异的。这里以笔者网站需求为例,须要建立 Developer,Integration 和 Channel 三个表。数据库

app/models.py 部分代码flask

from flask import current_app
from app import db

class Developer(db.Model):    
    __tablename__ = 'developers'    
    id = db.Column(db.Integer, primary_key=True)    
    dev_key = db.Column(db.String(40), unique=True, index=True)    
    platform = db.Column(db.String(50))    
    platform_id = db.Column(db.String(40), unique=True)    
    username = db.Column(db.String(150), index=True)    
    integrations = db.relationship('Integration', backref='developer')    
    channels = db.relationship('Channel', backref='developer')

class Integration(db.Model):    
    __tablename__ = 'integrations'    
    id = db.Column(db.Integer, primary_key=True)    
    integration_id = db.Column(db.String(40), unique=True)    
    name = db.Column(db.String(100))    
    description = db.Column(db.String(150))    
    icon = db.Column(db.String(150))    
    channel = db.Column(db.String(150))    
    token = db.Column(db.String(150))    
    developer_id = db.Column(db.Integer, db.ForeignKey('developers.id'))

class Channel(db.Model):    
    __tablename__ = 'channels'    
    id = db.Column(db.Integer, primary_key=True)    
    developer_id = db.Column(db.Integer, db.ForeignKey('developers.id'))    
    channel = db.Column(db.String(150))    

    def __repr__(self):        
        return '<Channel %r>' % self.channel

上面的每一个 Class 都继承了 Model 类,所以每一个类在数据库中都体现为一张表,表名用 tablename 表示,通常用复数形式。这里主要讲一下一对多的关系。能够看到上面 Developer 和 Integration 及 Channel 都有一个一对多的关系,一个用户能够有多个集成及多个频道。 看到这两句:bootstrap

integrations = db.relationship('Integration', backref='developer')
developer_id = db.Column(db.Integer, db.ForeignKey('developers.id'))

第一句代表添加到 Developer 表的 integrations 属性表示这个关系的面向对象视角,对于一个 Developer 实例,integrations 属性将返回与 Developer 相关的全部 Integration,db.relationship() 第一个参数代表关系的另外一端是哪一个模型,backref 参数向 Integration 添加一个 developer 属性,从而定义反向关系。第二句是在 Integration 表中建立一个 developer_id 字段,这个字段被定义为外键,就是这个外键创建起了关系。传给 db.ForeignKey() 的参数 'developers.id' 代表这列的值是 developers 表中行的 id 值。另外,__repr__() 方法返回一个具备可读性的字符串表示模型,能够在调加粗文字试和测试时使用。下面咱们就在命令行中操做一下数据库。
首先执行:segmentfault

//建立 migrations 文件夹及相关文件
python manage.py db init

而后执行 :

//自动建立迁移脚本
python manage.py db migrate
//建立数据表或者升级到最新版本
python manage.py db upgrade

之后模型类有改动,好比删除或添加列,都要执行这两个命令,更新数据库表。如今在项目目录下应该自动生成了名为 dev 的数据库。
接下来执行以下命令进入 Python Shell:

python manage.py shell

建立表

使用 db.create_all() 就能够根据模型类建立表。如图:

clipboard.png

使用 db.drop_all() 方法就能够删除全部的表,可是这种方式比较粗暴,全部的数据也一同销毁了。

插入行

如下命令在数据库表中插入了一条数据:

clipboard.png

经过数据库会话 db.session 来管理对数据库所作的改动,在准备把对象写入数据库以前,首先要添加到会话中,而后用 commit() 方法提交会话。接下来咱们继续插入一条 Integration 数据:

>>>from app.models import Integration,Channel
>>>integration = Integration(integration_id='i0001',name='github',description='first >>>application',channel='github_event',developer=developer)
>>> db.session.add(integration)
>>> db.session.commit()

注意上面的 developer 属性,正是咱们在 models.py 中 Developer 模型中定义的一对多关系 integrations 属性的 backref 值, 所谓的反向关系即指在 Integration 表中每条数据都有一个 developer 属性指向 Developer 表中的某条数据,这是一对多关系的高级表示。如今能够用 developer.integrations 来查看该 developer 拥有的哪些集成,运行截图以下:

clipboard.png

修改行

在提交数据库会话以前,改变对象的某个属性而后再提交便可更新行数据。如:

>>> developer.username = 'lisi'
>>> db.session.add(developer)
>>> db.session.commit()

运行截图:

clipboard.png

删除行

调用 db.session.delete() 方法便可删除行:

>>>db.session.delete(developer)
>>>db.session.commit()

查询行

Flask-SQLAlchemy 为每一个模型提供了 query 对象,最基本的查询是返回表中的全部记录:

>>>Developer.query.all()

使用过滤器能够进行更精确的查询:

>>>Developer.query.filter_by(platform='qq').all()

若是你退出了 shell 会话,前面建立的对象就不会以 Python 对象的形式存在,而是做为各自数据库表中的行。这时须要重数据库中读取行,再从新建立 Python 对象,如:

>>> new_developer = Developer.query.filter_by(dev_key=12345).first()

注意上面的 first() 方法,若是不加上,将返回一个 BaseQuery 对象,这样就不能直接用 new_developer 来访问它的属性值。

在视图函数中操做数据库

上面介绍的全部数据库操做能够直接在视图函数中进行。假设咱们要作一个简陋的注册功能,下面咱们就来看看如何从网页中获取数据并保存在数据库中。咱们先定义一个用户模型:

app/models.py

class User(db.model):
    __tablename__ = 'users'
    id = db.column(db.Integer, primary_key=True)
    username = db.column(db.String(50), unique=True)
    password = db.column(db.String(100))

    def __repr__(self):
        return '<User %r>'  % self.username

而后在 main 文件夹下建立一个 forms.py 文件。

app/main/forms.py

from flask_wtf import Form
from wtforms import StringField, SubmitField, PasswordField
from wtforms.validators import DataRequired

class UserForm(Form):      
    username = StringField('Input your username', validators=[DataRequired()])    
    password = PasswordField('Input your password', validators=[DataRequired()])    
    submit = SubmitField('Submit')

咱们使用了 flask-wtf 扩展(pip install flask-wtf)来处理表单。
而后咱们用在 index 页面中显示这个表单。

index.html

{% extends "bootstrap/base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

{% block title %}注册{% endblock %}

{% block content %}
    {% for message in get_flashed_messages() %}    
        <div class="alert alert-warning">        
            <button type="button" class="close" data-dismiss="alert">×</button>        
            {{ message }}    
        </div>
    {% endfor %}
    <div id="form">
        <div class="col-md-4">
            {{ wtf.quick_form(form) }}
        </div>
    </div>
{% endblock %}

如今回到 views.py 中,在咱们的视图函数中做以下改动:

app/main/views.py

...
from .forms import UserForm
from ..models import User
from app import db


@main.route('/', methods=['GET', 'POST'])
def signin():
    form = UserForm()
    if form is None:
        flash('Should input username and password')
    elif  form.validate_on_submit():
        user = User(username=form.username.data, password=form.password.data)
        db.session.add(user)
        try:
            db.session.commit()
            flash('User created !')
        except:
            db.session.rollback()
            flash('User create failed')
    return render_template('index.html', form=form)

接下来咱们运行一下这个小试验:

python manage.py runserver

总结

本节咱们介绍了在 Flask 中是如何使用 Flask-SQLAlchemy 、Flask-Migrate来管理数据库,而且示范了数据从网页储存到数据库的过程,这只是最基础的部分,下一节咱们将探索如何在网页上发送请求而且获得数据,以及如何在页面之间传递数据。

相关文章
相关标签/搜索