从零开始用 Flask 搭建一个网站(二)
从零开始用 Flask 搭建一个网站(一) 介绍了如何搭建 Python 环境,以及 Flask 应用基本项目结构。我们要搭建的网站是管理第三方集成的控制台,类似于 Slack。 本篇主要讲解数据如何在 Flask 应用中流动,其它的框架基本上也是大同小异。
数据库
既然是数据的流动,首先要建立起存取数据的地方,也就是数据库了(这里是指关系型数据库,NoSQL 不在这讨论)。第一节中我们使用了 Flask-SQLAlchemy 管理数据库,在 Flask-SQLAlchemy 中,数据库使用 URL 指定,最流行的数据库 URL 格式如下:
| 数据库引擎 | URL |
|---|---|
| MySQL | mysql://username:password@hostname/database |
| Postgres | postgresql://username:password@hostname/database |
| SQLite(Unix) | sqlite:////absolute/path/to/database |
| SQLite(Windows) | sqlite:///c:/absolute/path/to/database |
在 config.py 中我们已经指定了数据库 URL,如果使用云平台部署程序,直接将生成的数据库 URL 写到 config.py 中 SQLALCHEMY_DATABASE_URI 即可。这里我们使用的是 SQLite 数据库。Flask-SQLAlchemy 采用数据库抽象层来操作数据库,也称为对象关系映射(Object-Relational Mapper, ORM),在用户不知不觉的情况下把高层的面向对象操作转换成低层的数据库指令,因此易用性好。我们已经在 app/__init__.py 中实例化了 SQLAlchemy 类:
app/__init__.py
from flask_sqlalchemy import SQLAlchemy
...
db = SQLAlchemy()
...
定义模型
模型类可以理解为数据库中的一张表,Flask-SQLAlchemy 提供了一个基类和一系列辅助类和函数来让我们定义模型的结构。我们直接在 app 文件夹下创建一个 models.py 文件。鉴于每个网站需求都不一样,所存的数据也不同,但本质上是大同小异的。这里以笔者网站需求为例,需要创建 Developer,Integration 和 Channel 三个表。
app/models.py 部分代码
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 都有一个一对多的关系,一个用户可以有多个集成及多个频道。 看到这两句:
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__() 方法返回一个具有可读性的字符串表示模型,可以在调试和测试时使用。下面我们就在命令行中操作一下数据库。
首先执行:
//创建 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() 就可以根据模型类创建表。如图:

使用 db.drop_all() 方法就可以删除所有的表,但是这种方式比较粗暴,所有的数据也一同销毁了。
插入行
以下命令在数据库表中插入了一条数据:

通过数据库会话 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 拥有的哪些集成,运行截图如下:

修改行
在提交数据库会话之前,改变对象的某个属性然后再提交即可更新行数据。如:
>>> developer.username = 'lisi'
>>> db.session.add(developer)
>>> db.session.commit()
运行截图:

删除行
调用 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来管理数据库,并且示范了数据从网页储存到数据库的过程,这只是最基础的部分,下一节我们将探索如何在网页上发送请求并且得到数据,以及如何在页面之间传递数据。
作者:KenChoi - 极光
原文:从零开始用 Flask 搭建一个网站(二)
知乎专栏:极光日报
从零开始用 Flask 搭建一个网站(二)的更多相关文章
- 从零开始用 Flask 搭建一个网站(三)
从零开始用 Flask 搭建一个网站(二) 介绍了有关于数据库的运用,接下来我们在完善一下数据在前端以及前端到后端之间的交互.本节涉及到前端,因此也会讲解一下 jinja2 模板.jQuery.aja ...
- 从零开始用 Flask 搭建一个网站(一)
前言 笔者之前未接触过 Python,只是略懂一点前端,所以说从零开始也相差无几吧.Flask 是一个轻量级的基于 Python 的框架,但是扩展性非常良好(Github 上 22000 多个 sta ...
- 从零开始用 Flask 搭建一个网站(四)
前言 从零开始用 Flask 搭建一个网站(三) 介绍了网页前端与后端.前端与前端之间数据的交流.本节主要介绍一下如何应用 Flask-OAuthlib, 使用 Flask-OAuthlib 就可以轻 ...
- Python Flask搭建一个视频网站实战视频教程
点击了解更多Python课程>>> Python Flask搭建一个视频网站实战视频教程 第1章 课程介绍 第2章 预备开发环境 第3章 项目分析.建立目录及模型规划 第4章 建立前 ...
- 05、(通过nat123软件) 实现用自己电脑搭建一个网站
(通过nat123软件) 实现用自己电脑搭建一个网站 准备: Tomcat:这个是web容器,其实有了这个就已经让电脑成为服务器了,在自己电脑上可以通过 localhost:8080/xxx 来访问自 ...
- Python - 利用flask搭建一个共享服务器
零.概述 我利用flask搭建了一个简易的共享服务器,分享给大家 一.python代码 import os import time from flask import Flask,render_tem ...
- 3分钟搭建一个网站?腾讯云Serverless开发体验
作为一个开发者,应该都能理解一个网站从开发到上线,要经过很多繁琐的步骤. 编写代码,部署应用,部署数据库,申请域名,申请SSL证书,域名备案,到最终上线起码要几天时间. 作为一个不精通代码的业务玩家, ...
- [译]简单得不得了的教程-一步一步用 NODE.JS, EXPRESS, JADE, MONGODB 搭建一个网站
原文: http://cwbuecheler.com/web/tutorials/2013/node-express-mongo/ 原文的源代码在此 太多的教程教你些一个Hello, World!了, ...
- 教程:动手用自己电脑搭建一个网站 (nat123 花生壳 动态域名 个人电脑做服务器)
先ps一下..今晚试了N种方法,终于找到一个靠谱 免费 好用的方法,来改装自己电脑成为服务器,在外网也能通过域名访问了!!! 需要准备的东西: Tomcat:这个是web容器,其实有了这个就已经让电脑 ...
随机推荐
- 对JavaScript中变量类型的重新理解
<JavaScript启示录>这本书中提出:JavaScript中,对象为“王”(JavaScript里的几乎所有东西都是对象或者用起来像对象). 飞燕草对JavaScript最深刻的理解 ...
- Java基础之路(一)下--引用数据类型之数组
上次我们说了java的基础数据类型,今天我们就来说一下引用数据类型中的数组. 什么是数组 数组:存储在一个连续的内存块中的相同数据类型(引用数据类型)的元素集合. 数组中的每一个数据称之为数组元素,数 ...
- C#自动弹出窗口并定时自动关闭
最近做个小项目,用到一个小功能:后台线程定时查询数据库,不符合条件的记录弹出消息提醒(在窗口最前面),并且过几秒钟再自动关闭弹出的窗口. 所以从网上找来资料,如下: WinForm 下实现一个自动关闭 ...
- iOS 推送问题全解答《十万个为啥吖?》
Q 1:为啥收不到推送(1)? 如果收到推送时,App 在前台运行,那么: iOS 10 before 顶部横幅不会弹出.没有任何展示,你以为「没有收到推送」. iOS 10 after 如果没有实现 ...
- java装箱跟拆箱解析
/** * 在jdk1.5之后,java为基本数据类型到对应的应用数据类型提供了自动拆箱装箱操作 * 不管是自动拆箱还是自动装箱都是应用数据类型有的方法,基本数据类型是没有任何方法可调用的 *从概念上 ...
- wemall app商城源码android开发MD5加密工具类
wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享android开发MD5加密工具类主要代码,供 ...
- 1619: [Usaco2008 Nov]Guarding the Farm 保卫牧场
1619: [Usaco2008 Nov]Guarding the Farm 保卫牧场 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 498 Solve ...
- 《Django By Example》第九章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者@ucag 注:哈哈哈,第九章终于来啦 ...
- 关于极光推送在手机系统低于iOS10的手机上闪退的问题。
最近项目中用到了极光推送,升级到了最新的SDK 2.1.9版本,发现只能在iOS10 上运行,其他测试的时候真机闪退.贴上一个可能的原因:
- JNDI常见配置方式
JNDI(Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API.命名服务将名称和对象联系起来,使得我们可以用 ...