今日目标
o 理解Web开发的基本概念
o 掌握Flask框架的使用
o 学会创建路由和视图函数
o 了解模板渲染和表单处理
o 掌握Flask应用的基本结构
Web开发概述
Web开发是创建网站和Web应用程序的过程:
o 前端:用户界面(HTML、CSS、JavaScript)
o 后端:服务器端逻辑(Python、数据库)
o HTTP协议:客户端和服务器之间的通信协议
o Web框架:简化Web开发的工具集
Web应用的基本流程
# 简化的Web应用流程
def web_application_flow():
"""
1. 客户端发送HTTP请求
2. 服务器接收请求
3. 路由系统找到对应的处理函数
4. 处理函数执行业务逻辑
5. 返回HTTP响应(HTML、JSON等)
"""
pass
Flask框架介绍
Flask是一个轻量级的Python Web框架,具有以下特点:
o 微框架:核心简单,易于扩展
o 灵活性:不强制特定的项目结构
o 易学易用:学习曲线平缓
o 丰富的扩展:支持各种功能扩展
安装Flask
pip install flask
Flask基础
1. 第一个Flask应用
from flask import Flask
# 创建Flask应用实例
app = Flask(__name__)
# 定义路由和视图函数
@app.route('/')
def hello_world():
return 'Hello, World!'
@app.route('/about')
def about():
return '这是关于页面'
@app.route('/user/<username>')
def show_user_profile(username):
return f'用户: {username}'
if __name__ == '__main__':
app.run(debug=True)
2. 路由系统
from flask import Flask, request
app = Flask(__name__)
# 基本路由
@app.route('/')
def index():
return '首页'
# 带参数的路由
@app.route('/user/<username>')
def user_profile(username):
return f'用户资料: {username}'
# 指定参数类型
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f'文章ID: {post_id} (类型: {type(post_id)})'
# 多个参数
@app.route('/user/<username>/posts/<int:post_id>')
def user_post(username, post_id):
return f'用户 {username} 的文章 {post_id}'
# HTTP方法
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
return '处理登录请求'
else:
return '显示登录表单'
# 默认值
@app.route('/page/')
@app.route('/page/<int:page_num>')
def show_page(page_num=1):
return f'显示第 {page_num} 页'
if __name__ == '__main__':
app.run(debug=True)
3. 请求和响应
from flask import Flask, request, jsonify, make_response
app = Flask(__name__)
@app.route('/api/data', methods=['GET', 'POST'])
def handle_data():
if request.method == 'GET':
# 获取查询参数
name = request.args.get('name', '默认名称')
age = request.args.get('age', 0, type=int)
# 返回JSON响应
return jsonify({
'name': name,
'age': age,
'message': 'GET请求成功'
})
elif request.method == 'POST':
# 获取表单数据
form_data = request.form
name = form_data.get('name', '')
email = form_data.get('email', '')
# 获取JSON数据
json_data = request.get_json()
# 获取文件
file = request.files.get('file')
return jsonify({
'form_data': dict(form_data),
'json_data': json_data,
'file_name': file.filename if file else None,
'message': 'POST请求成功'
})
@app.route('/custom-response')
def custom_response():
# 创建自定义响应
response = make_response('自定义响应内容')
response.headers['Content-Type'] = 'text/plain'
response.headers['X-Custom-Header'] = '自定义头部'
return response
@app.route('/redirect-example')
def redirect_example():
# 重定向
from flask import redirect, url_for
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)
模板系统
1. 基本模板渲染
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
# 传递数据到模板
user = {
'name': '张三',
'age': 25,
'email': 'zhangsan@example.com'
}
posts = [
{'title': '第一篇文章', 'content': '这是第一篇文章的内容'},
{'title': '第二篇文章', 'content': '这是第二篇文章的内容'},
{'title': '第三篇文章', 'content': '这是第三篇文章的内容'}
]
return render_template('index.html', user=user, posts=posts)
@app.route('/user/<username>')
def user_profile(username):
return render_template('user.html', username=username)
if __name__ == '__main__':
app.run(debug=True)
2. 模板文件示例
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<header>
<nav>
<a href="{{ url_for('index') }}">首页</a>
<a href="{{ url_for('about') }}">关于</a>
</nav>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>(c) 2024 Flask应用</p>
</footer>
</body>
</html>
<!-- templates/index.html -->
{% extends "base.html" %}
{% block title %}首页{% endblock %}
{% block content %}
<div class="container">
<h1>欢迎, {{ user.name }}!</h1>
<div class="user-info">
<p>年龄: {{ user.age }}</p>
<p>邮箱: {{ user.email }}</p>
</div>
<h2>文章列表</h2>
<div class="posts">
{% for post in posts %}
<article class="post">
<h3>{{ post.title }}</h3>
<p>{{ post.content }}</p>
</article>
{% endfor %}
</div>
{% if posts|length > 2 %}
<p>共有 {{ posts|length }} 篇文章</p>
{% endif %}
</div>
{% endblock %}
<!-- templates/user.html -->
{% extends "base.html" %}
{% block title %}{{ username }}的页面{% endblock %}
{% block content %}
<div class="container">
<h1>{{ username }}的个人页面</h1>
<p>这是 {{ username }} 的个人资料页面。</p>
</div>
{% endblock %}
3. 模板语法
<!-- 模板语法示例 -->
{% comment %}
Jinja2模板语法
{% endcomment %}
<!-- 变量输出 -->
<p>{{ user.name }}</p>
<p>{{ user['age'] }}</p>
<!-- 条件语句 -->
{% if user.age >= 18 %}
<p>成年人</p>
{% elif user.age >= 12 %}
<p>青少年</p>
{% else %}
<p>儿童</p>
{% endif %}
<!-- 循环语句 -->
{% for item in items %}
<li>{{ item }}</li>
{% else %}
<li>没有项目</li>
{% endfor %}
<!-- 过滤器 -->
<p>{{ user.name|upper }}</p>
<p>{{ user.name|length }}</p>
<p>{{ user.name|default('匿名用户') }}</p>
<!-- 宏定义 -->
{% macro input(name, value='', type='text') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
<!-- 使用宏 -->
{{ input('username') }}
{{ input('password', type='password') }}
<!-- 包含其他模板 -->
{% include 'header.html' %}
<!-- 设置变量 -->
{% set title = '页面标题' %}
<h1>{{ title }}</h1>
表单处理
1. 基本表单
from flask import Flask, render_template, request, redirect, url_for, flash
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'
# 定义表单类
class LoginForm(FlaskForm):
username = StringField('用户名', validators=[DataRequired(), Length(min=3, max=20)])
password = PasswordField('密码', validators=[DataRequired(), Length(min=6)])
submit = SubmitField('登录')
class RegistrationForm(FlaskForm):
username = StringField('用户名', validators=[DataRequired(), Length(min=3, max=20)])
email = StringField('邮箱', validators=[DataRequired(), Email()])
password = PasswordField('密码', validators=[DataRequired(), Length(min=6)])
confirm_password = PasswordField('确认密码', validators=[DataRequired()])
submit = SubmitField('注册')
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
# 处理登录逻辑
username = form.username.data
password = form.password.data
# 这里应该验证用户名和密码
if username == 'admin' and password == 'password':
flash('登录成功!', 'success')
return redirect(url_for('index'))
else:
flash('用户名或密码错误!', 'error')
return render_template('login.html', form=form)
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
# 验证密码确认
if form.password.data != form.confirm_password.data:
flash('密码不匹配!', 'error')
return render_template('register.html', form=form)
# 处理注册逻辑
username = form.username.data
email = form.email.data
password = form.password.data
# 这里应该保存用户数据到数据库
flash(f'用户 {username} 注册成功!', 'success')
return redirect(url_for('login'))
return render_template('register.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
2. 表单模板
<!-- templates/login.html -->
{% extends "base.html" %}
{% block title %}登录{% endblock %}
{% block content %}
<div class="container">
<h1>用户登录</h1>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.username.label }}
{{ form.username(class="form-control") }}
{% if form.username.errors %}
{% for error in form.username.errors %}
<span class="error">{{ error }}</span>
{% endfor %}
{% endif %}
</div>
<div class="form-group">
{{ form.password.label }}
{{ form.password(class="form-control") }}
{% if form.password.errors %}
{% for error in form.password.errors %}
<span class="error">{{ error }}</span>
{% endfor %}
{% endif %}
</div>
{{ form.submit(class="btn btn-primary") }}
</form>
<p>还没有账号?<a href="{{ url_for('register') }}">立即注册</a></p>
</div>
{% endblock %}
静态文件
1. 静态文件结构
your_app/
├── static/
│ ├── css/
│ │ └── style.css
│ ├── js/
│ │ └── script.js
│ └── images/
│ └── logo.png
├── templates/
│ └── ...
└── app.py
2. CSS样式示例
/* static/css/style.css */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
background-color: #333;
color: white;
padding: 1rem;
}
nav a {
color: white;
text-decoration: none;
margin-right: 20px;
}
nav a:hover {
color: #ddd;
}
.form-group {
margin-bottom: 15px;
}
.form-control {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-primary {
background-color: #007bff;
color: white;
}
.alert {
padding: 10px;
margin-bottom: 15px;
border-radius: 4px;
}
.alert-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.post {
background-color: white;
padding: 15px;
margin-bottom: 15px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
错误处理
from flask import Flask, render_template
app = Flask(__name__)
@app.errorhandler(404)
def not_found_error(error):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_error(error):
return render_template('500.html'), 500
@app.errorhandler(403)
def forbidden_error(error):
return render_template('403.html'), 403
# 自定义错误页面模板
<!-- templates/404.html -->
{% extends "base.html" %}
{% block title %}页面未找到{% endblock %}
{% block content %}
<div class="container">
<h1>404 - 页面未找到</h1>
<p>抱歉,您访问的页面不存在。</p>
<a href="{{ url_for('index') }}">返回首页</a>
</div>
{% endblock %}
应用配置
from flask import Flask
app = Flask(__name__)
# 配置选项
app.config['SECRET_KEY'] = 'your-secret-key-here'
app.config['DEBUG'] = True
app.config['TESTING'] = False
app.config['DATABASE'] = 'path/to/database.db'
# 或者使用配置文件
class Config:
SECRET_KEY = 'your-secret-key-here'
DEBUG = True
DATABASE = 'path/to/database.db'
class DevelopmentConfig(Config):
DEBUG = True
class ProductionConfig(Config):
DEBUG = False
# 根据环境选择配置
import os
if os.environ.get('FLASK_ENV') == 'production':
app.config.from_object(ProductionConfig)
else:
app.config.from_object(DevelopmentConfig)
项目结构
my_flask_app/
├── app/
│ ├── __init__.py
│ ├── models.py
│ ├── views.py
│ ├── forms.py
│ └── utils.py
├── static/
│ ├── css/
│ ├── js/
│ └── images/
├── templates/
│ ├── base.html
│ ├── index.html
│ └── user.html
├── config.py
├── requirements.txt
└── run.py
今日总结
今天我们学习了Flask Web开发的基础知识:
1. Flask框架基础:应用创建、路由系统、请求响应
2. 模板系统:Jinja2模板、模板继承、模板语法
3. 表单处理:WTForms、表单验证、CSRF保护
4. 静态文件:CSS、JavaScript、图片管理
5. 错误处理:自定义错误页面
6. 应用配置:环境配置、项目结构
Flask是一个优秀的Web开发入门框架,掌握这些基础知识可以开始构建简单的Web应用。