目录
1. 前言
在众多的 Python Web 开发框架中,Flask 像一颗闪耀的明星,备受开发者的青睐。无论是小型的个人项目,还是大型的商业应用,Flask 都能展现出其独特的优势和魅力。作为一名 Python 开发者,深入学习和掌握 Flask 库是提升开发技能、拓展项目选择范围的关键一步。本文将带大家全面了解 Flask,从它的基本概念、适应场景,到实际代码示例,助大家搭建自己的UI界面和Web。
因为本片文件搭建了一个完整的博客网站实例,所以需要有HTML知识、CSS知识及JS知识,可以去阅读:
《一文看懂网页开发中的CSS(Cascading Style Sheets,层叠样式表)》
2. Flask 基本概念
Flask 是一个用 Python 编写的轻量级 Web 应用框架。它基于 Werkzeug WSGI 工具箱和 Jinja2 模板引擎。所谓轻量级,意味着 Flask 核心功能简洁,没有许多大型框架那样复杂的默认配置和模块依赖,这使得开发者可以根据项目需求灵活地扩展和定制功能。
其中核心组件如下:
应用实例(app) :是 Flask 应用的核心。通过创建 Flask 类的实例来启动应用。例如:
from flask import Flask
app = Flask(__name__)
__name__
表示当前模块的名称,Flask 使用它来确定应用的根目录,以便正确地处理静态文件、模板等资源。
路由(Route) :用于将 URL 映射到对应的处理函数。例如:
@app.route('/')
def home():
return 'Hello, Flask!'
当用户访问网站的根 URL(即 “/”)时,Flask 会调用 home 函数,并返回字符串 “Hello, Flask!” 作为响应。
视图函数(View Function) :与路由关联的函数,负责处理客户端请求并返回响应。如上述的 home 函数就是视图函数。
模板(Template) :用于分离 Python 代码和 HTML 页面,使页面更具可读性和可维护性。Flask 默认使用 Jinja2 模板引擎。例如,在 templates 文件夹下创建一个 index.html 模板文件:
<!DOCTYPE html>
<html>
<head>
<title>Flask 模板示例</title>
</head>
<body>
<h1>{{ message }}</h1>
</body>
</html>
在视图函数中可以通过 render_template 函数将变量传递给模板并渲染:
from flask import render_template
@app.route('/template')
def template_demo():
return render_template('index.html', message='Hello, Template!')
访问 “/template” URL 时,页面会显示 “Hello, Template!”。
其中render_template(template_name, **context)将模板文件中的动态内容与实际数据结合,生成最终的 HTML 页面:
-
template_name :这是一个字符串,表示要渲染的模板文件的名称。Flask 会自动在应用的
templates
文件夹中查找这个模板文件。 -
context :这是一个关键字参数列表,用于将变量传递给模板。这些变量可以在模板中使用,以便生成动态内容。例如,
render_template('index.html', name='John')
会将变量name
的值设置为'John'
,然后在模板中可以通过{{ name }}
来引用这个变量。
请求对象(Request Object) :用于获取客户端请求的数据,如表单数据、查询参数等。例如:
from flask import request
@app.route('/form', methods=['POST'])
def form_example():
name = request.form['name']
return f'Hello, {name}!'
@app.route('/query')
def query_example():
name = request.args.get('name', 'Guest')
return f'Hello, {name}!'
当以 POST 方法提交表单到 “/form” URL 时,可以从 request.form 中获取表单字段 “name” 的值;访问 “/query?name=John” URL 时,可以从 request.args 中获取查询参数 “name” 的值。
3. Flask 适应场景
-
小型网站和应用 :Flask 的轻量级特性使其非常适合快速开发小型网站,如个人博客、简历网站、小工具应用等。不需要复杂的配置和大量依赖,开发者可以迅速搭建起基本的 Web 功能。
-
原型开发和 MVP(最小可行产品)测试 :对于初创团队或新项目,在开发初期需要快速验证产品概念和用户需求时,Flask 能够以较少的代码和开发时间构建出可运行的原型系统,方便展示给用户和投资者进行反馈收集。
-
微服务架构中的服务 :在构建微服务系统时,Flask 作为一个独立的服务组件,易于与其他服务进行集成和通信。其简洁的结构便于实现特定的业务功能,并且可以方便地进行扩展和维护。
-
API 服务 :可以使用 Flask 构建 RESTful API 服务,为前端应用或其他系统提供数据接口。通过定义路由和视图函数来处理不同的 HTTP 请求方法(如 GET、POST、PUT、DELETE 等),返回 JSON 格式的数据响应。
4. Flask 实操:个人博客系统
下面将以一个简单的博客系统为例,展示 Flask 的实际应用。
4.1 项目结构
my-blog/
├── app.py
├── data/
│ └── posts.json
├── templates/
│ ├── base.html
│ ├── index.html
│ ├── post.html
│ ├── login.html
│ └── create.html
└── static/
└── css/
└── style.css
4.2 app.py
from flask import Flask, render_template, request, redirect, url_for, jsonify, session
import json
import os
app = Flask(__name__)
app.secret_key = 'your-secret-key-123' # 生产环境中应使用更复杂的密钥
def load_posts():
try:
with open('data/posts.json', 'r') as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return []
def save_posts(posts):
os.makedirs('data', exist_ok=True)
with open('data/posts.json', 'w') as f:
json.dump(posts, f, indent=4)
@app.route('/')
def index():
posts = load_posts()
return render_template('base.html', posts=posts)
@app.route('/mypostlist')
def mypostlist():
posts = load_posts()
return render_template('index.html', posts=posts)
@app.route('/post/<int:post_id>')
def post(post_id):
posts = load_posts()
post = next((p for p in posts if p['id'] == post_id), None)
return render_template('post.html', post=post) if post else ('Post not found', 404)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
if request.form['username'] == 'admin' and request.form['password'] == 'secret':
session['logged_in'] = True
return redirect(url_for('index'))
return 'Invalid credentials', 401
return render_template('login.html')
@app.route('/logout')
def logout():
session.pop('logged_in', None)
return redirect(url_for('index'))
@app.route('/create')
def create_post():
if not session.get('logged_in'):
return redirect(url_for('login'))
return render_template('create.html')
@app.route('/api/add_post', methods=['POST'])
def api_add_post():
if not session.get('logged_in'):
return jsonify({'error': 'Unauthorized'}), 401
data = request.get_json()
if not (data.get('title') and data.get('content')):
return jsonify({'error': 'Missing title or content'}), 400
posts = load_posts()
new_post = {
'id': len(posts) + 1,
'title': data['title'],
'content': data['content']
}
posts.append(new_post)
save_posts(posts)
return jsonify(new_post), 201
if __name__ == '__main__':
app.run(debug=True)
4.3 模板文件兼主页(base.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Blog - {% block title %}{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<nav>
<a href="{{ url_for('index') }}">Home</a>
{% if session.logged_in %}
<a href="{{ url_for('create_post') }}">New Post</a>
<a href="{{ url_for('logout') }}">Logout</a>
<a href="{{ url_for('mypostlist') }}">my-post</a>
{% else %}
<a href="{{ url_for('login') }}">Login</a>
{% endif %}
</nav>
<div class="container">
{% block content %}{% endblock %}
</div>
</body>
</html>
4.4 我的博客列表:index.html
{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block content %}
<h1>Latest Posts</h1>
{% for post in posts %}
<article class="post">
<h2><a href="{{ url_for('post', post_id=post.id) }}">{{ post.title }}</a></h2>
<p>{{ post.content[:150] }}{% if post.content|length > 150 %}...{% endif %}</p>
</article>
{% endfor %}
<a href="{{ url_for('index') }}">← Back to base</a>
{% endblock %}
4.5 文章详细页:post.html
{% extends "base.html" %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
<article class="post-detail">
<h1>{{ post.title }}</h1>
<p>{{ post.content }}</p>
<a href="{{ url_for('mypostlist') }}">← Back to all posts</a>
</article>
{% endblock %}
4.6 登录页:login.html
{% extends "base.html" %}
{% block title %}Login{% endblock %}
{% block content %}
<h1>Login</h1>
<form method="POST">
<div class="form-group">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit">Login</button>
</form>
{% endblock %}
4.7 新建博客页:create.html
{% extends "base.html" %}
{% block title %}New Post{% endblock %}
{% block content %}
<h1>Create New Post</h1>
<form id="new-post-form">
<div class="form-group">
<label for="title">Title:</label>
<input type="text" id="title" name="title" required>
</div>
<div class="form-group">
<label for="content">Content:</label>
<textarea id="content" name="content" rows="6" required></textarea>
</div>
<button type="submit">Publish</button>
</form>
<script>
document.getElementById('new-post-form').addEventListener('submit', async (e) => {
e.preventDefault();
const response = await fetch('/api/add_post', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
title: document.getElementById('title').value,
content: document.getElementById('content').value
})
});
if (response.ok) {
window.location.href = '/';
} else {
alert('Error creating post');
}
});
</script>
{% endblock %}
4.8 CSS样式
/* Basic reset */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f9f9f9;
}
nav {
background: #333;
padding: 1rem;
}
nav a {
color: white;
text-decoration: none;
margin-right: 1.5rem;
}
nav a:hover {
text-decoration: underline;
}
.container {
max-width: 800px;
margin: 2rem auto;
padding: 0 1rem;
}
.post {
background: white;
padding: 1.5rem;
margin-bottom: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.post h2 {
margin-bottom: 0.5rem;
color: #222;
}
.post p {
color: #666;
}
.form-group {
margin-bottom: 1rem;
}
input, textarea {
width: 100%;
padding: 0.5rem;
margin-top: 0.25rem;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
background: #333;
color: white;
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #555;
}
.post-detail {
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.post-detail h1 {
margin-bottom: 1rem;
}
最后,在命令行中,进入项目目录,运行以下命令启动 Flask 应用:
python app.py
然后在浏览器中访问 “http://127.0.0.1:5000/” 即可查看博客首页,访问 “http://127.0.0.1:5000/login” 可以尝试登录(用户名为 “admin”,密码为 “secret”),登录成功后在新建博客页通过发送 POST 请求到 “http://127.0.0.1:5000/api/add_post” 可以添加新的博客文章,提交后保存到data文件中,通过mypostlist逐个查询访问具体博客。
5. 总结
Flask 以其简洁、灵活的特点在 Python Web 开发领域占据重要地位。它不仅为开发者提供了快速构建 Web 应用的能力,还通过丰富的扩展插件满足各种复杂功能需求。无论是小型项目还是大型系统,Flask 都能大显身手。通过对 Flask 基本概念的理解、适应场景的把握以及实际案例的操作,你已经迈出了成为熟练 Flask 开发者的坚实步伐。在后续的学习和实践中,不断探索 Flask 的更多功能和优化技巧,相信你将能够开发出更加出色的 Web 应用。我是橙色小博,关注我,一起在人工智能领域学习进步!