Python Web 开发指南
目录
Web 框架
Django
python
from django.shortcuts import render, redirect
from django.views.generic import ListView, DetailView, CreateView, UpdateView
from .models import User
from .forms import UserForm
class UserListView(ListView):
model = User
template_name = 'users/list.html'
context_object_name = 'users'
class UserDetailView(DetailView):
model = User
template_name = 'users/detail.html'
context_object_name = 'user'
class UserCreateView(CreateView):
model = User
form_class = UserForm
template_name = 'users/form.html'
success_url = '/users/'
class UserUpdateView(UpdateView):
model = User
form_class = UserForm
template_name = 'users/form.html'
success_url = '/users/'Flask
python
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
@app.route('/users')
def list_users():
users = User.query.all()
return render_template('users/list.html', users=users)
@app.route('/users/<int:id>')
def get_user(id):
user = User.query.get_or_404(id)
return render_template('users/detail.html', user=user)
@app.route('/users/new', methods=['GET', 'POST'])
def create_user():
if request.method == 'POST':
user = User(
name=request.form['name'],
email=request.form['email']
)
db.session.add(user)
db.session.commit()
return redirect(url_for('list_users'))
return render_template('users/form.html')模板引擎
Jinja2 模板
html
<!DOCTYPE html>
<html>
<head>
<title>用户列表</title>
</head>
<body>
<h1>用户列表</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>邮箱</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
<td>
<a href="{{ url_for('get_user', id=user.id) }}">查看</a>
<a href="{{ url_for('edit_user', id=user.id) }}">编辑</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>静态文件
静态文件处理
python
# Django
STATIC_URL = '/static/'
STATICFILES_DIRS = [
BASE_DIR / "static",
]
# Flask
app = Flask(__name__, static_folder='static')会话管理
Django 会话
python
from django.contrib.sessions.backends.db import SessionStore
def login(request):
if request.method == 'POST':
user = authenticate(
username=request.POST['username'],
password=request.POST['password']
)
if user is not None:
login(request, user)
request.session['user_id'] = user.id
return redirect('home')
return render(request, 'login.html')
def logout(request):
logout(request)
request.session.flush()
return redirect('login')Flask 会话
python
from flask import session
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
user = User.query.filter_by(
username=request.form['username']
).first()
if user and check_password_hash(user.password, request.form['password']):
session['user_id'] = user.id
return redirect(url_for('home'))
return render_template('login.html')
@app.route('/logout')
def logout():
session.pop('user_id', None)
return redirect(url_for('login'))表单处理
Django 表单
python
from django import forms
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ['name', 'email', 'password']
widgets = {
'password': forms.PasswordInput()
}
def clean_email(self):
email = self.cleaned_data.get('email')
if User.objects.filter(email=email).exists():
raise forms.ValidationError('该邮箱已被注册')
return emailFlask 表单
python
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired, Email, Length
class UserForm(FlaskForm):
name = StringField('姓名', validators=[DataRequired()])
email = StringField('邮箱', validators=[DataRequired(), Email()])
password = PasswordField('密码', validators=[
DataRequired(),
Length(min=6, message='密码长度不能小于6位')
])文件上传
Django 文件上传
python
from django.core.files.storage import FileSystemStorage
def upload_file(request):
if request.method == 'POST' and request.FILES['file']:
file = request.FILES['file']
fs = FileSystemStorage()
filename = fs.save(file.name, file)
uploaded_file_url = fs.url(filename)
return render(request, 'upload.html', {
'uploaded_file_url': uploaded_file_url
})
return render(request, 'upload.html')Flask 文件上传
python
import os
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = 'uploads'
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return redirect(request.url)
file = request.files['file']
if file.filename == '':
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('uploaded_file', filename=filename))前端集成
集成 Vue.js
html
<!DOCTYPE html>
<html>
<head>
<title>用户管理</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<h1>用户列表</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>邮箱</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="user in users">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
<td>
<button @click="editUser(user)">编辑</button>
<button @click="deleteUser(user.id)">删除</button>
</td>
</tr>
</tbody>
</table>
</div>
<script>
new Vue({
el: '#app',
data: {
users: []
},
mounted() {
this.loadUsers();
},
methods: {
loadUsers() {
axios.get('/api/users')
.then(response => {
this.users = response.data;
});
},
editUser(user) {
// 处理编辑
},
deleteUser(id) {
axios.delete(`/api/users/${id}`)
.then(() => {
this.loadUsers();
});
}
}
});
</script>
</body>
</html>性能优化
缓存控制
python
# Django
from django.views.decorators.cache import cache_control
@cache_control(max_age=31536000)
def static_file(request, path):
return serve(request, path)
# Flask
@app.after_request
def add_header(response):
response.headers['Cache-Control'] = 'public, max-age=31536000'
return response压缩
python
# Django
MIDDLEWARE = [
'django.middleware.gzip.GZipMiddleware',
]
# Flask
from flask_compress import Compress
compress = Compress()
compress.init_app(app)安全实践
CSRF 保护
python
# Django
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
]
# Flask
from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect()
csrf.init_app(app)XSS 防护
python
# Django
from django.utils.html import escape
def user_profile(request, username):
user = get_object_or_404(User, username=username)
return render(request, 'profile.html', {
'username': escape(user.username)
})
# Flask
from markupsafe import escape
@app.route('/user/<username>')
def user_profile(username):
return render_template('profile.html', username=escape(username))