mkdir myproject
cd myproject
python3 -m venv venv
. venv/bin/activate
pip install Flask
将下列代码保存为 hello.py:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
运行上述代码:
export FLASK_APP=hello
flask run
这样访问:http://127.0.0.1:5000 会看到 Hello, World!
这里有 flask 的基本知识(非常重要的基础,大家可以自己看:链接
HTML Escaping (利用 Jinja,参考:链接
Routing (下面几个例子)
@app.route('/')
def index():
return 'Index Page'
@app.route('/hello')
def hello():
return 'Hello, World'
@app.route('/user/<username>')
def show_user_profile(username):
# show the user profile for that user
return f'User {escape(username)}'
@app.route('/post/<int:post_id>')
def show_post(post_id):
# show the post with the given id, the id is an integer
return f'Post {post_id}'
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
# show the subpath after /path/
return f'Subpath {escape(subpath)}'
HTTP Methods
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
else:
Static Files (url_for('static', filename='style.css'))
Rendering Templates (这个参考之前的 Jinja)
File Uploads、Cookies、Redirects and Errors、About Responses、APIs with JSON、Sessions、Message Flashing、Logging 这些等我们实际用到时再过来看
第 1 节教大家如何利用 python 虚拟环境,快速构建 flask 环境;第 2 节带着大家简单熟悉了 flask 的编程规则(或风格)。
大家在着手本节时,务必将第 2 节中的基础的代码跟着官网敲一下!因为,这一节我们不是由简到难一步步搭建 flask 服务器,而是直接拿搭建好的反过来分析。
$ git clone https://github.com/pallets/flask
$ cd flask
$ cd examples/tutorial
代码目录结构如下:
➜ tutorial git:(main) tree -L 4
.
├── flaskr
│ ├── __init__.py
│ ├── db.py
│ ├── schema.sql
│ ├── auth.py
│ ├── blog.py
│ │
│ ├── templates
│ │ ├── base.html
│ │ ├── auth
│ │ │ ├── login.html
│ │ │ └── register.html
│ │ └── blog
│ │ ├── create.html
│ │ ├── index.html
│ │ └── update.html
│ │
│ └── static
│ └── style.css
│
├── MANIFEST.in
└── setup.py
def create_app(test_config=None):
"""Create and configure an instance of the Flask application."""
# 1-创建一个 Flask 实例
# 并设置一些 APP 需要用到的参数
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
# a default secret that should be overridden by instance config
SECRET_KEY="dev",
# store the database in the instance folder
DATABASE=os.path.join(app.instance_path, "flaskr.sqlite"),
)
# 2-测试用的
if test_config is None:
# load the instance config, if it exists, when not testing
app.config.from_pyfile("config.py", silent=True)
else:
# load the test config if passed in
app.config.update(test_config)
# 3-创建一个文件夹,用来存 DB 运行时的产生的文件
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass
@app.route("/hello")
def hello():
return "Hello, World!"
# register the database commands
# 3.3 数据库设置(为 flask 新增一个 init_db 命令,这样直接敲 flask init_db 就能生成表)
from flaskr import db
db.init_app(app)
# apply the blueprints to the app
# #### 3.4 蓝图和视图(基于蓝图来管理组织视图,视图注册到蓝图,蓝图注册到应用)
from flaskr import auth, blog
app.register_blueprint(auth.bp)
app.register_blueprint(blog.bp)
# make url_for('index') == url_for('blog.index')
# in another app, you might define a separate main index here with
# app.route, while giving the blog blueprint a url_prefix, but for
# the tutorial the blog will be the main index
app.add_url_rule("/", endpoint="index")
return app
该项目采用了 SQLite 作为数据库(Python 内置了,免去安装和配置工作)。
SQLite 的数据存储在表格中,在向表格增删改查数据前,需要先建表。该项目中的 schema.sql 编写了建表的 SQL 语句。分别创建了一个 user 表和 post 表。
DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS post;
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
CREATE TABLE post (
id INTEGER PRIMARY KEY AUTOINCREMENT,
author_id INTEGER NOT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
title TEXT NOT NULL,
body TEXT NOT NULL,
FOREIGN KEY (author_id) REFERENCES user (id)
);
2)与数据库建立连接与断开
def get_db():
"""Connect to the application's configured database. The connection
is unique for each request and will be reused if this is called
again.
"""
if "db" not in g:
g.db = sqlite3.connect(
current_app.config["DATABASE"], detect_types=sqlite3.PARSE_DECLTYPES
)
g.db.row_factory = sqlite3.Row
return g.db
def close_db(e=None):
"""If this request connected to the database, close the
connection.
"""
db = g.pop("db", None)
if db is not None:
db.close()
g 是一个特殊结构,对于每次请求,会产生一个。
3)数据库初始化(生成表)
第 1 节的 schema.sql 用于建表,那么如何执行其中的建表命令呢? db.py 中的 init_db 就是干这个事情的。
def init_db():
"""Clear existing data and create new tables."""
db = get_db() # 获取数据库(如果没有则创建)
# 读取 schema.sql 中的 SQL 命令,并用 db.executescript 执行 SQL 命令
with current_app.open_resource("schema.sql") as f:
db.executescript(f.read().decode("utf8"))
4)将 init_db 注册为 flask 命令
由于数据库初始化并不需要每次启动数据库时运行(不属于运行时需要执行的函数),我们需要将注册成 flask 一个指令,只要在命令行中敲 flask init-db 就能够执行 init_db,其实现方法如下:
@click.command("init-db")
@with_appcontext
def init_db_command():
"""Clear existing data and create new tables."""
init_db()
click.echo("Initialized the database.")
def init_app(app):
"""Register database functions with the Flask app. This is called by
the application factory.
"""
app.teardown_appcontext(close_db) # 在返回响应后进行清理时调用该函数
app.cli.add_command(init_db_command) # 添加一个可以用flask命令调用的新命令
这样,执行完之后,flask.sqlite 文件将会出现在 instance 文件夹。
蓝图是一种组织一组相关视图和其他代码的方法。它们不是直接向应用程序注册视图和其他代码,而是向蓝图注册。然后,当蓝图在factory函数中可用时,它将在应用程序中注册。
该项目中有两个蓝图:auth 和 blog
bp = Blueprint("auth", __name__, url_prefix="/auth") # in auth.py
bp = Blueprint("blog", __name__) # in blog.py
参数分别是:蓝图的名字,import_name(一般为 __name__),url 前缀
1)auth 视图
这里主要有三个路由:
@bp.route("/register", methods=("GET", "POST"))
def register():
...
@bp.route("/login", methods=("GET", "POST"))
def login():
...
@bp.route("/logout")
def logout():
2)blog 视图
这里主要有四个路由:
@bp.route("/")
def index():
...
@bp.route("/create", methods=("GET", "POST"))
@login_required
def create():
...
@bp.route("/<int:id>/update", methods=("GET", "POST"))
@login_required
def update(id):
...
@bp.route("/<int:id>/delete", methods=("POST",))
@login_required
def delete(id):
...
3)注册视图中各个功能实现介绍
注册
注册逻辑为:首先从 POST 中获取 username 和 password,然后调用数据库插入操作:
username = request.form["username"]password = request.form["password"]db.execute("INSERT INTO user (username, password) VALUES (?, ?)", (username, generate_password_hash(password)),)登录
登录逻辑为:首先从 POST 中获取 username 和 password,然后调用数据库查询操作,获取该用户的密码,然后进行密码匹配:
user = db.execute("SELECT * FROM user WHERE username = ?",username,)).fetchone()check_password_hash(user["password"], password)密码匹配后,需要创建 session:
if error is None:
# store the user id in a new session and return to the index
session.clear()
session["user_id"] = user["id"]
return redirect(url_for("index"))
注销
注销需要清空 session:
session.clear()Session
Session 逻辑如下:注册一个方法,让其在任何 URL 请求之前执行,在其中做 Session 管理:
@bp.before_app_request
def load_logged_in_user():
user_id = session.get('user_id')
if user_id is None:
g.user = None
else:
g.user = get_db().execute(
'SELECT * FROM user WHERE id = ?', (user_id,)
).fetchone()
其他 View 使用认证
其他 View 也想使用认证该如何做?在 auth.py 中实现 login_required 函数,判断 user 是否为空,如果为空,则跳转到登录页面:
def login_required(view):
@functools.wraps(view)
def wrapped_view(**kwargs):
if g.user is None:
return redirect(url_for('auth.login'))
return view(**kwargs)
return wrapped_view
4)博客视图中各个功能实现介绍
展示所有博客
逻辑如下:执行数据库查询操作,获取所有博客,然后加载:
@bp.route("/")
def index():
"""Show all the posts, most recent first."""
db = get_db()
posts = db.execute(
"SELECT p.id, title, body, created, author_id, username"
" FROM post p JOIN user u ON p.author_id = u.id"
" ORDER BY created DESC"
).fetchall()
return render_template("blog/index.html", posts=posts)
创建博客
逻辑如下:函数前加上 @login_required 前缀,这样就能自动判断是否已经登录,否则跳到登录页面;创建博客就是获取标题和内容,然后调用插入命令,进行插入:
@bp.route("/create", methods=("GET", "POST"))
@login_required
def create():
"""Create a new post for the current user."""
if request.method == "POST":
title = request.form["title"]
body = request.form["body"]
error = None
if not title:
error = "Title is required."
if error is not None:
flash(error)
else:
db = get_db()
db.execute(
"INSERT INTO post (title, body, author_id) VALUES (?, ?, ?)",
(title, body, g.user["id"]),
)
db.commit()
return redirect(url_for("blog.index"))
return render_template("blog/create.html")
更新和删除博客
更新和删除博客,需要传入一个 id,然后有一个内部函数用于判断该 id 是否存在:
def get_post(id, check_author=True):
"""Get a post and its author by id.
Checks that the id exists and optionally that the current user is
the author.
:param id: id of post to get
:param check_author: require the current user to be the author
:return: the post with author information
:raise 404: if a post with the given id doesn't exist
:raise 403: if the current user isn't the author
"""
post = (
get_db()
.execute(
"SELECT p.id, title, body, created, author_id, username"
" FROM post p JOIN user u ON p.author_id = u.id"
" WHERE p.id = ?",
(id,),
)
.fetchone()
)
if post is None:
abort(404, f"Post id {id} doesn't exist.")
if check_author and post["author_id"] != g.user["id"]:
abort(403)
return post
因此,更新的逻辑如下:
@bp.route("/<int:id>/update", methods=("GET", "POST"))
@login_required
def update(id):
"""Update a post if the current user is the author."""
post = get_post(id)
if request.method == "POST":
title = request.form["title"]
body = request.form["body"]
error = None
if not title:
error = "Title is required."
if error is not None:
flash(error)
else:
db = get_db()
db.execute(
"UPDATE post SET title = ?, body = ? WHERE id = ?", (title, body, id)
)
db.commit()
return redirect(url_for("blog.index"))
return render_template("blog/update.html", post=post)
删除的逻辑如下:
@bp.route("/<int:id>/delete", methods=("POST",))
@login_required
def delete(id):
"""Delete a post.
Ensures that the post exists and that the logged in user is the
author of the post.
"""
get_post(id)
db = get_db()
db.execute("DELETE FROM post WHERE id = ?", (id,))
db.commit()
return redirect(url_for("blog.index"))
其他还有一些,是大家玩熟了之后才需要看的:
最后,我们跑起 Demo 看看效果:
1)在 tutorial 目录下,创建虚拟环境,并安装 Flask:
python3 -m venv venv
. venv/bin/activate
pip install Flask
2)以开发者方式运行:
export FLASK_APP=flaskr
export FLASK_ENV=development
flask init-db
flask run
效果如下:

[1]. 本文源码
[2]. Flask 文档主页
[3]. tutorial 主页
[4]. Jinja 模板入门
[5]. python django web 开发
[6]. 真正搞明白Python中Django和Flask框架的区别
: 这篇是在大家熟悉基本的 flaskr 之后,进行稍微大一点的项目开发的指导,里面涉及到数据库、蓝图等...
如果觉得不错,帮忙点个支持哈~

关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?
我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的rubyyaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir
我的问题的一个例子是体育游戏。一场体育比赛有两支球队,一支主队和一支客队。我的事件记录模型如下:classTeam"Team"has_one:away_team,:class_name=>"Team"end我希望能够通过游戏访问一个团队,例如:Game.find(1).home_team但我收到一个单元化常量错误:Game::team。谁能告诉我我做错了什么?谢谢, 最佳答案 如果Gamehas_one:team那么Rails假设您的teams表有一个game_id列。不过,您想要的是games表有一个team_id列,在这种情况下