06-Python Web开发

Python在Web开发领域生态丰富。Flask轻量灵活,Django功能全面,FastAPI性能卓越。

Flask基础

最小应用

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello, World!'

@app.route('/user/<name>')
def greet(name):
    return f'Hello, {name}!'

if __name__ == '__main__':
    app.run(debug=True, port=5000)

路由和视图

from flask import Flask, request, jsonify, render_template

app = Flask(__name__)

# GET请求
@app.route('/api/users', methods=['GET'])
def get_users():
    users = [{'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'}]
    return jsonify(users)

# POST请求
@app.route('/api/users', methods=['POST'])
def create_user():
    data = request.get_json()
    name = data['name']
    # 保存到数据库...
    return jsonify({'id': 123, 'name': name}), 201

# 路径参数
@app.route('/api/users/<int:user_id>')
def get_user(user_id):
    # 查询数据库...
    return jsonify({'id': user_id, 'name': 'Alice'})

# 查询参数
@app.route('/search')
def search():
    query = request.args.get('q', '')
    page = request.args.get('page', 1, type=int)
    return f'Search: {query}, Page: {page}'

# 表单数据
@app.route('/submit', methods=['POST'])
def submit():
    username = request.form['username']
    password = request.form['password']
    return 'Submitted'

# 模板渲染
@app.route('/profile/<name>')
def profile(name):
    return render_template('profile.html', name=name)

Jinja2模板

<!-- templates/profile.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Profile</title>
</head>
<body>
    <h1>Welcome, {{ name }}!</h1>
    
    {% if age >= 18 %}
        <p>Adult</p>
    {% else %}
        <p>Minor</p>
    {% endif %}
    
    <ul>
    {% for item in items %}
        <li>{{ item }}</li>
    {% endfor %}
    </ul>
    
    <!-- 模板继承 -->
    {% extends "base.html" %}
    {% block content %}
        <p>Content</p>
    {% endblock %}
</body>
</html>

蓝图(Blueprint)

模块化路由,组织大型应用。

# users.py
from flask import Blueprint

users_bp = Blueprint('users', __name__, url_prefix='/users')

@users_bp.route('/')
def list_users():
    return 'User list'

@users_bp.route('/<int:id>')
def get_user(id):
    return f'User {id}'

# app.py
from flask import Flask
from users import users_bp

app = Flask(__name__)
app.register_blueprint(users_bp)

# 访问:/users/ 和 /users/123

数据库集成

# Flask-SQLAlchemy
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
db = SQLAlchemy(app)

# 定义模型
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    
    def __repr__(self):
        return f'<User {self.username}>'

# 创建表
with app.app_context():
    db.create_all()

# CRUD操作
@app.route('/users', methods=['POST'])
def create_user():
    data = request.get_json()
    user = User(username=data['username'], email=data['email'])
    db.session.add(user)
    db.session.commit()
    return jsonify({'id': user.id})

@app.route('/users/<int:id>')
def get_user(id):
    user = User.query.get_or_404(id)
    return jsonify({'id': user.id, 'username': user.username})

@app.route('/users/<int:id>', methods=['PUT'])
def update_user(id):
    user = User.query.get_or_404(id)
    data = request.get_json()
    user.username = data['username']
    db.session.commit()
    return jsonify({'id': user.id})

@app.route('/users/<int:id>', methods=['DELETE'])
def delete_user(id):
    user = User.query.get_or_404(id)
    db.session.delete(user)
    db.session.commit()
    return '', 204

RESTful API设计

from flask import Flask, request, jsonify
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)

class UserList(Resource):
    def get(self):
        # GET /api/users
        return {'users': []}
    
    def post(self):
        # POST /api/users
        data = request.get_json()
        return {'id': 123}, 201

class UserDetail(Resource):
    def get(self, user_id):
        # GET /api/users/:id
        return {'id': user_id, 'name': 'Alice'}
    
    def put(self, user_id):
        # PUT /api/users/:id
        return {'id': user_id}
    
    def delete(self, user_id):
        # DELETE /api/users/:id
        return '', 204

api.add_resource(UserList, '/api/users')
api.add_resource(UserDetail, '/api/users/<int:user_id>')

Django基础

项目结构

# 创建项目
django-admin startproject myproject

# 创建应用
python manage.py startapp myapp

# 结构
myproject/
├── manage.py
├── myproject/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── myapp/
    ├── migrations/
    ├── models.py
    ├── views.py
    ├── urls.py
    └── templates/

模型(ORM)

# models.py
from django.db import models

class User(models.Model):
    username = models.CharField(max_length=80, unique=True)
    email = models.EmailField(unique=True)
    age = models.IntegerField()
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        db_table = 'users'
        ordering = ['-created_at']
    
    def __str__(self):
        return self.username

# 数据库操作
User.objects.create(username='alice', email='alice@example.com', age=25)
User.objects.filter(age__gte=18)
User.objects.get(username='alice')
User.objects.all()
User.objects.count()

视图

# views.py
from django.http import JsonResponse
from django.views import View

class UserView(View):
    def get(self, request, user_id):
        user = User.objects.get(id=user_id)
        return JsonResponse({
            'id': user.id,
            'username': user.username
        })
    
    def post(self, request):
        data = json.loads(request.body)
        user = User.objects.create(**data)
        return JsonResponse({'id': user.id}, status=201)

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('users/<int:user_id>/', views.UserView.as_view()),
]

FastAPI(现代高性能框架)

基本应用

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    id: int
    name: str
    email: str
    age: int

users_db = []

@app.get("/")
async def root():
    return {"message": "Hello World"}

@app.get("/users", response_model=list[User])
async def list_users():
    return users_db

@app.post("/users", response_model=User, status_code=201)
async def create_user(user: User):
    users_db.append(user)
    return user

@app.get("/users/{user_id}", response_model=User)
async def get_user(user_id: int):
    for user in users_db:
        if user.id == user_id:
            return user
    raise HTTPException(status_code=404, detail="User not found")

# 自动生成OpenAPI文档:/docs
# 运行:uvicorn main:app --reload

依赖注入

from fastapi import Depends

def get_db():
    db = Database()
    try:
        yield db
    finally:
        db.close()

@app.get("/items")
async def read_items(db = Depends(get_db)):
    return db.query()

HTTP客户端

requests完整示例

import requests

# 完整请求
response = requests.request(
    method='GET',
    url='https://api.example.com/data',
    params={'key': 'value'},
    headers={'Authorization': 'Bearer token'},
    timeout=10
)

# Session(连接复用)
session = requests.Session()
session.headers.update({'User-Agent': 'MyApp'})
session.get(url1)
session.post(url2, json=data)

# 错误处理
try:
    response = requests.get(url, timeout=5)
    response.raise_for_status()  # 4xx/5xx抛异常
except requests.Timeout:
    print("Timeout")
except requests.HTTPError as e:
    print(f"HTTP Error: {e}")
except requests.RequestException as e:
    print(f"Error: {e}")

# 下载文件
response = requests.get(url, stream=True)
with open('file.zip', 'wb') as f:
    for chunk in response.iter_content(chunk_size=8192):
        f.write(chunk)

aiohttp(异步HTTP)

import aiohttp
import asyncio

async def fetch(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

async def fetch_all(urls):
    tasks = [fetch(url) for url in urls]
    results = await asyncio.gather(*tasks)
    return results

# 运行
urls = ['http://example.com/1', 'http://example.com/2']
results = asyncio.run(fetch_all(urls))

WSGI和ASGI

WSGI(Web Server Gateway Interface)

# WSGI应用
def application(environ, start_response):
    status = '200 OK'
    headers = [('Content-Type', 'text/plain')]
    start_response(status, headers)
    return [b'Hello, WSGI!']

# 运行(使用gunicorn)
# gunicorn app:application

ASGI(Asynchronous SGI)

# ASGI应用(FastAPI/Starlette)
async def application(scope, receive, send):
    await send({
        'type': 'http.response.start',
        'status': 200,
        'headers': [[b'content-type', b'text/plain']],
    })
    await send({
        'type': 'http.response.body',
        'body': b'Hello, ASGI!',
    })

# 运行(使用uvicorn)
# uvicorn app:application

最佳实践

API设计

# RESTful路由设计
GET    /api/users           # 列表
POST   /api/users           # 创建
GET    /api/users/:id       # 详情
PUT    /api/users/:id       # 更新
DELETE /api/users/:id       # 删除

# 版本控制
/api/v1/users
/api/v2/users

# 分页
/api/users?page=1&limit=20

# 过滤
/api/users?age_gte=18&city=Beijing

# 排序
/api/users?sort=-created_at

错误处理

from flask import Flask, jsonify

app = Flask(__name__)

# 自定义错误处理
@app.errorhandler(404)
def not_found(error):
    return jsonify({'error': 'Not found'}), 404

@app.errorhandler(500)
def internal_error(error):
    return jsonify({'error': 'Internal server error'}), 500

# 统一响应格式
def success_response(data, message='Success'):
    return jsonify({
        'success': True,
        'message': message,
        'data': data
    })

def error_response(message, code=400):
    return jsonify({
        'success': False,
        'message': message
    }), code

认证和授权

from flask import Flask, request
from functools import wraps
import jwt

SECRET_KEY = 'your-secret-key'

# JWT生成
def generate_token(user_id):
    payload = {
        'user_id': user_id,
        'exp': datetime.utcnow() + timedelta(hours=24)
    }
    return jwt.encode(payload, SECRET_KEY, algorithm='HS256')

# JWT验证装饰器
def token_required(f):
    @wraps(f)
    def decorator(*args, **kwargs):
        token = request.headers.get('Authorization')
        if not token:
            return jsonify({'message': 'Token missing'}), 401
        
        try:
            data = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
            current_user_id = data['user_id']
        except:
            return jsonify({'message': 'Invalid token'}), 401
        
        return f(current_user_id, *args, **kwargs)
    return decorator

@app.route('/api/protected')
@token_required
def protected(current_user_id):
    return jsonify({'user_id': current_user_id})

数据库操作

SQLAlchemy ORM

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

# 连接数据库
engine = create_engine('sqlite:///app.db')
Session = sessionmaker(bind=engine)
Base = declarative_base()

# 定义模型
class User(Base):
    __tablename__ = 'users'
    
    id = Column(Integer, primary_key=True)
    username = Column(String(80), unique=True, nullable=False)
    email = Column(String(120), unique=True, nullable=False)
    
    def __repr__(self):
        return f'<User {self.username}>'

# 创建表
Base.metadata.create_all(engine)

# 使用
session = Session()

# 创建
user = User(username='alice', email='alice@example.com')
session.add(user)
session.commit()

# 查询
users = session.query(User).all()
user = session.query(User).filter_by(username='alice').first()
user = session.query(User).filter(User.age > 18).all()

# 更新
user.email = 'newemail@example.com'
session.commit()

# 删除
session.delete(user)
session.commit()

# 关闭
session.close()

缓存

Flask-Caching

from flask import Flask
from flask_caching import Cache

app = Flask(__name__)
cache = Cache(app, config={'CACHE_TYPE': 'simple'})

# 缓存视图
@app.route('/expensive')
@cache.cached(timeout=60)  # 缓存60秒
def expensive_operation():
    # 耗时操作
    return result

# 带参数的缓存
@cache.memoize(timeout=60)
def get_user(user_id):
    # 根据参数缓存
    return db.query(user_id)

# 手动缓存
cache.set('key', 'value', timeout=300)
value = cache.get('key')
cache.delete('key')

异步Web框架

aiohttp服务器

from aiohttp import web

# 定义处理器
async def handle(request):
    name = request.match_info.get('name', 'Anonymous')
    return web.json_response({'message': f'Hello, {name}'})

async def handle_post(request):
    data = await request.json()
    return web.json_response(data, status=201)

# 创建应用
app = web.Application()
app.router.add_get('/', handle)
app.router.add_get('/hello/{name}', handle)
app.router.add_post('/data', handle_post)

# 运行
web.run_app(app, port=8080)

WebSocket

Flask-SocketIO

from flask import Flask
from flask_socketio import SocketIO, emit

app = Flask(__name__)
socketio = SocketIO(app)

# 接收消息
@socketio.on('message')
def handle_message(message):
    print(f'Received: {message}')
    emit('response', {'data': 'Message received'})

# 自定义事件
@socketio.on('custom_event')
def handle_custom(data):
    emit('custom_response', data, broadcast=True)  # 广播

# 运行
socketio.run(app, port=5000)

测试

unittest

import unittest

class TestMath(unittest.TestCase):
    def setUp(self):
        # 每个测试前执行
        self.data = [1, 2, 3]
    
    def tearDown(self):
        # 每个测试后执行
        pass
    
    def test_sum(self):
        self.assertEqual(sum(self.data), 6)
    
    def test_len(self):
        self.assertEqual(len(self.data), 3)
    
    def test_contains(self):
        self.assertIn(2, self.data)
    
    def test_raises(self):
        with self.assertRaises(ValueError):
            int('abc')

if __name__ == '__main__':
    unittest.main()

pytest(推荐)

# test_math.py
def test_sum():
    assert sum([1, 2, 3]) == 6

def test_contains():
    assert 2 in [1, 2, 3]

# fixture
import pytest

@pytest.fixture
def sample_data():
    return [1, 2, 3, 4, 5]

def test_with_fixture(sample_data):
    assert len(sample_data) == 5

# 参数化测试
@pytest.mark.parametrize("input,expected", [
    (2, 4),
    (3, 9),
    (4, 16),
])
def test_square(input, expected):
    assert input ** 2 == expected

# 运行
# pytest
# pytest test_math.py
# pytest -v  # 详细输出

部署

Gunicorn(生产WSGI服务器)

# 安装
pip install gunicorn

# 运行Flask应用
gunicorn app:app -w 4 -b 0.0.0.0:8000

# 参数:
# -w 4:4个worker进程
# -b:绑定地址
# --reload:自动重载(开发用)
# --daemon:后台运行

Uvicorn(ASGI服务器)

# 安装
pip install uvicorn


# 运行FastAPI应用
uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4

# 开发模式
uvicorn app:app --reload

Docker部署

# Dockerfile
FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["gunicorn", "app:app", "-w", "4", "-b", "0.0.0.0:8000"]
# 构建
docker build -t myapp .

# 运行
docker run -p 8000:8000 myapp

框架对比

框架

类型

学习曲线

性能

适用场景

Flask

微框架

平缓

小型API、原型

Django

全栈

陡峭

企业应用、CMS

FastAPI

异步

平缓

现代API、微服务

Tornado

异步

中等

长连接、WebSocket

Bottle

微框架

平缓

简单应用

选择:

  • 快速原型:Flask

  • 企业项目:Django

  • 高性能API:FastAPI

  • WebSocket:Tornado或FastAPI

核心: Python Web生态成熟,根据项目需求选择框架。