在上一篇文章中,我们深入探讨了Flask框架的高级功能,并通过构建一个博客系统展示了其实际应用。本篇文章将转向Django,另一个功能强大且广泛使用的Python Web框架。我们将介绍Django的核心概念,包括项目与应用结构、模型与数据库迁移、管理后台、模板与静态文件管理,以及如何将Django应用部署到生产环境。通过详细的代码示例和实战项目,您将掌握使用Django构建复杂且可扩展的Web应用的关键技能。
Django是一个高级的Python Web框架,旨在快速开发和简化复杂、数据库驱动的网站的构建过程。由Adrian Holovaty和Simon Willison在2003年开发,并于2005年正式发布,Django以其“务实”而闻名,遵循“不要重复自己”(DRY)和“显式优于隐式”的设计原则。
主要特点:
安装Django:
使用pip
安装Django是最常见的方法。确保您已经安装了Python和pip
。
pip install Django
验证安装:
安装完成后,可以通过以下命令验证Django是否成功安装:
django-admin --version
创建项目:
使用django-admin
工具创建一个新的Django项目。
django-admin startproject mysite
运行开发服务器:
进入项目目录并运行开发服务器。
cd mysite
python manage.py runserver
在浏览器中访问http://127.0.0.1:8000/
,您将看到Django的欢迎页面,表明项目已成功创建并运行。
Django项目是一个包含多个应用的集合,负责整体配置和协调。每个项目可以包含一个或多个应用,每个应用负责特定的功能模块。
django-admin startproject mysite
cd mysite
创建项目后,您将看到以下目录结构:
mysite/
├── manage.py
├── mysite/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
文件说明:
创建应用:
使用manage.py
创建一个新的应用,例如创建一个名为blog
的应用。
python manage.py startapp blog
更新项目配置:
在settings.py
中添加新创建的应用到INSTALLED_APPS
列表。
# mysite/settings.py
INSTALLED_APPS = [
...
'blog',
]
创建应用后,项目目录结构如下:
mysite/
├── manage.py
├── mysite/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── blog/
├── __init__.py
├── admin.py
├── apps.py
├── migrations/
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
文件说明:
Django的**模型(Models)**是用Python类定义的,代表数据库中的数据结构。每个模型类继承自django.db.models.Model
。
示例模型:
在blog/models.py
中定义一个简单的博客文章模型。
# blog/models.py
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
date_posted = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
字段说明:
默认情况下,Django使用SQLite作为数据库。可以在settings.py
中更改数据库配置,以使用其他数据库如PostgreSQL、MySQL等。
示例:配置PostgreSQL:
# mysite/settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydatabase',
'USER': 'mydatabaseuser',
'PASSWORD': 'mypassword',
'HOST': 'localhost',
'PORT': '5432',
}
}
安装相应的数据库驱动:
对于PostgreSQL,需要安装psycopg2
。
pip install psycopg2
定义模型后,需要创建数据库表。这通过Django的迁移系统完成。
创建迁移文件:
python manage.py makemigrations
该命令会根据模型的变化生成迁移文件,记录数据库结构的变化。
应用迁移:
python manage.py migrate
该命令会将迁移应用到数据库,创建或修改相应的表。
Django提供了强大的ORM(对象关系映射)工具,简化了数据库操作。
创建新记录:
# 在Django shell中操作
python manage.py shell
from blog.models import Post
from django.contrib.auth.models import User
# 获取用户
user = User.objects.get(username='john')
# 创建新文章
post = Post(title='我的第一篇博客', content='这是博客内容。', author=user)
post.save()
查询数据:
# 获取所有文章
posts = Post.objects.all()
# 过滤查询
john_posts = Post.objects.filter(author__username='john')
# 获取单个对象
post = Post.objects.get(id=1)
更新数据:
post = Post.objects.get(id=1)
post.title = '更新后的标题'
post.save()
删除数据:
post = Post.objects.get(id=1)
post.delete()
Django自带一个功能强大的管理后台,用于管理数据库中的数据。要激活管理后台,需要进行以下步骤。
创建超级用户:
python manage.py createsuperuser
按提示输入用户名、电子邮件和密码,创建一个超级用户。
注册模型到管理后台:
在blog/admin.py
中注册模型。
# blog/admin.py
from django.contrib import admin
from .models import Post
admin.site.register(Post)
运行开发服务器并访问管理后台:
python manage.py runserver
在浏览器中访问http://127.0.0.1:8000/admin/
,使用超级用户的凭据登录。您将看到已注册的Post
模型,可以在管理后台中添加、编辑和删除文章。
可以自定义管理界面以更好地展示和管理数据。
示例:自定义Post模型的管理界面:
# blog/admin.py
from django.contrib import admin
from .models import Post
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'date_posted')
search_fields = ('title', 'content')
list_filter = ('date_posted', 'author')
admin.site.register(Post, PostAdmin)
功能说明:
Django的管理后台不仅用于数据管理,还支持权限和用户管理。
用户和组管理:
在管理后台,可以创建和管理用户和组,分配不同的权限。
示例:限制用户只能管理自己的文章:
通过自定义ModelAdmin
类,可以限制用户只能看到和管理自己的数据。
# blog/admin.py
from django.contrib import admin
from .models import Post
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'date_posted')
search_fields = ('title', 'content')
list_filter = ('date_posted', 'author')
def get_queryset(self, request):
qs = super().get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(author=request.user)
def save_model(self, request, obj, form, change):
if not obj.pk:
obj.author = request.user
obj.save()
admin.site.register(Post, PostAdmin)
功能说明:
Django使用自己的模板引擎,允许在HTML中嵌入动态内容。模板引擎支持变量、标签和过滤器,帮助生成动态页面。
基本模板示例:
创建一个简单的模板blog/templates/blog/home.html
。
DOCTYPE html>
<html>
<head>
<title>博客首页title>
head>
<body>
<h1>欢迎来到我的博客h1>
<ul>
{% for post in posts %}
<li><a href="{% url 'blog:post_detail' post.id %}">{{ post.title }}a> by {{ post.author.username }}li>
{% empty %}
<li>暂无文章。li>
{% endfor %}
ul>
body>
html>
模板继承允许定义一个基础模板,并在此基础上创建子模板,避免重复代码。
创建基础模板:
创建templates/base.html
。
DOCTYPE html>
<html>
<head>
<title>{% block title %}我的网站{% endblock %}title>
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
head>
<body>
<header>
<h1>我的网站h1>
<nav>
<a href="{% url 'blog:home' %}">首页a> |
<a href="{% url 'admin:index' %}">管理后台a>
nav>
header>
<main>
{% block content %}{% endblock %}
main>
<footer>
<p>© 2025 我的公司p>
footer>
body>
html>
创建子模板:
修改blog/templates/blog/home.html
,继承自base.html
。
{% extends 'base.html' %}
{% block title %}博客首页{% endblock %}
{% block content %}
<h2>博客文章h2>
<ul>
{% for post in posts %}
<li><a href="{% url 'blog:post_detail' post.id %}">{{ post.title }}a> by {{ post.author.username }}li>
{% empty %}
<li>暂无文章。li>
{% endfor %}
ul>
{% endblock %}
Django管理静态文件(如CSS、JavaScript、图片)通过STATICFILES
系统处理。需要在settings.py
中配置静态文件相关设置。
配置静态文件:
# mysite/settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = [BASE_DIR / "static"]
使用静态文件:
在模板中加载静态文件。
{% load static %}
DOCTYPE html>
<html>
<head>
<title>使用静态文件title>
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
head>
<body>
body>
html>
收集静态文件:
在生产环境中,使用collectstatic
命令将所有静态文件收集到一个目录。
python manage.py collectstatic
可以创建自定义的模板标签和过滤器,扩展模板引擎的功能。
创建自定义过滤器:
创建模板标签目录:
在应用目录下创建templatetags
文件夹,并添加__init__.py
。
blog/
├── templatetags/
│ ├── __init__.py
│ └── blog_extras.py
定义过滤器:
# blog/templatetags/blog_extras.py
from django import template
register = template.Library()
@register.filter(name='truncate')
def truncate(value, arg):
"""Truncate the string after a certain number of characters."""
try:
length = int(arg)
except ValueError:
return value
if len(value) > length:
return value[:length] + '...'
return value
使用过滤器:
在模板中加载并使用自定义过滤器。
{% load blog_extras %}
<p>{{ post.content|truncate:100 }}p>
创建自定义标签:
类似于过滤器,可以创建自定义标签以实现复杂的逻辑。
# blog/templatetags/blog_extras.py
from django import template
register = template.Library()
@register.simple_tag
def current_time(format_string):
from django.utils import timezone
return timezone.now().strftime(format_string)
使用自定义标签:
{% load blog_extras %}
<p>当前时间:{% current_time "%Y-%m-%d %H:%M" %}p>
部署Django应用时,可以选择多种平台,包括:
Gunicorn是一个Python WSGI HTTP服务器,适用于部署Django应用。Nginx作为反向代理服务器,处理客户端请求并转发给Gunicorn。
步骤:
安装Gunicorn:
pip install gunicorn
运行Gunicorn:
在项目根目录下运行Gunicorn。
gunicorn mysite.wsgi:application
配置Nginx:
创建一个Nginx配置文件,配置反向代理。
# /etc/nginx/sites-available/mysite
server {
listen 80;
server_name your_domain.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /path/to/your/mysite;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:8000;
}
}
启用配置并重启Nginx。
sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled
sudo nginx -t
sudo systemctl restart nginx
运行Gunicorn作为后台服务:
使用systemd创建一个服务文件。
# /etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon for Django project
After=network.target
[Service]
User=youruser
Group=www-data
WorkingDirectory=/path/to/your/mysite
ExecStart=/path/to/your/venv/bin/gunicorn mysite.wsgi:application --bind 127.0.0.1:8000
[Install]
WantedBy=multi-user.target
启动并启用Gunicorn服务。
sudo systemctl start gunicorn
sudo systemctl enable gunicorn
配置环境变量:
不要在代码中硬编码敏感信息。使用环境变量管理配置。
安装python-decouple
:
pip install python-decouple
修改settings.py
:
# mysite/settings.py
from decouple import config
SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': config('DB_NAME'),
'USER': config('DB_USER'),
'PASSWORD': config('DB_PASSWORD'),
'HOST': config('DB_HOST'),
'PORT': config('DB_PORT', default='5432'),
}
}
创建.env
文件:
# .env
SECRET_KEY=your_production_secret_key
DEBUG=False
DB_NAME=your_db_name
DB_USER=your_db_user
DB_PASSWORD=your_db_password
DB_HOST=localhost
DB_PORT=5432
注意:确保.env
文件不被版本控制系统(如Git)跟踪。
安全设置:
禁用调试模式:
在生产环境中,确保DEBUG=False
。
设置允许的主机:
在settings.py
中配置ALLOWED_HOSTS
。
ALLOWED_HOSTS = ['your_domain.com', 'www.your_domain.com']
使用HTTPS:
配置SSL证书,启用HTTPS,确保数据传输安全。
配置安全中间件:
确保以下中间件在MIDDLEWARE
中启用,以增强安全性。
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware', # 用于静态文件管理
...
]
使用Let’s Encrypt获取免费的SSL证书,并配置Nginx以启用HTTPS。
安装Certbot:
sudo apt-get update
sudo apt-get install certbot python3-certbot-nginx
获取证书:
sudo certbot --nginx -d your_domain.com -d www.your_domain.com
自动续期:
Certbot自动配置证书续期。可以手动测试续期。
sudo certbot renew --dry-run
更新Nginx配置:
Certbot会自动修改Nginx配置以启用HTTPS。确保配置正确,并重启Nginx。
sudo systemctl restart nginx
为了综合应用上述知识,本节将带您构建一个功能完善的在线商店,包含用户注册与登录、产品管理、购物车功能及订单处理。
online_store/
├── manage.py
├── online_store/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── store/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations/
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── templates/
│ ├── base.html
│ ├── store/
│ │ ├── home.html
│ │ ├── product_detail.html
│ │ └── cart.html
└── static/
└── css/
└── styles.css
创建应用:
python manage.py startapp store
定义模型:
在store/models.py
中定义Product
和Order
模型。
# store/models.py
from django.db import models
from django.contrib.auth.models import User
class Product(models.Model):
name = models.CharField(max_length=200)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.PositiveIntegerField()
image = models.ImageField(upload_to='product_images/', blank=True, null=True)
def __str__(self):
return self.name
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
ordered_date = models.DateTimeField(auto_now_add=True)
is_completed = models.BooleanField(default=False)
def __str__(self):
return f'Order {self.id} by {self.user.username}'
class OrderItem(models.Model):
order = models.ForeignKey(Order, related_name='items', on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
def __str__(self):
return f'{self.quantity} of {self.product.name}'
执行迁移:
python manage.py makemigrations
python manage.py migrate
Django内置了用户认证系统,可以利用其功能实现用户注册与登录。
创建注册表单:
在store/forms.py
中定义用户注册表单。
# store/forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class UserRegisterForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
创建视图:
在store/views.py
中添加注册和登录视图。
# store/views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth import login, authenticate, logout
from .forms import UserRegisterForm
from django.contrib.auth.forms import AuthenticationForm
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if form.is_valid():
user = form.save()
messages.success(request, f'账户 {user.username} 创建成功!请登录。')
return redirect('login')
else:
form = UserRegisterForm()
return render(request, 'store/register.html', {'form': form})
def user_login(request):
if request.method == 'POST':
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
messages.info(request, f'您已登录为 {username}.')
return redirect('home')
else:
messages.error(request, '无效的用户名或密码。')
else:
messages.error(request, '无效的用户名或密码。')
else:
form = AuthenticationForm()
return render(request, 'store/login.html', {'form': form})
def user_logout(request):
logout(request)
messages.info(request, '您已成功注销。')
return redirect('home')
配置URL:
在store/urls.py
中定义应用的URL。
# store/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('register/', views.register, name='register'),
path('login/', views.user_login, name='login'),
path('logout/', views.user_logout, name='logout'),
]
在项目的主URL配置中包含应用的URL。
# online_store/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('store.urls')),
]
创建模板:
创建store/templates/store/register.html
和store/templates/store/login.html
。
{% extends 'base.html' %}
{% load static %}
{% block title %}注册{% endblock %}
{% block content %}
<h2>注册h2>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">注册button>
form>
{% endblock %}
{% extends 'base.html' %}
{% load static %}
{% block title %}登录{% endblock %}
{% block content %}
<h2>登录h2>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">登录button>
form>
{% endblock %}
更新基础模板:
在templates/base.html
中添加导航链接。
DOCTYPE html>
<html>
<head>
<title>{% block title %}在线商店{% endblock %}title>
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
head>
<body>
<header>
<h1>在线商店h1>
<nav>
<a href="{% url 'home' %}">首页a> |
{% if user.is_authenticated %}
<a href="{% url 'logout' %}">注销a>
{% else %}
<a href="{% url 'login' %}">登录a> |
<a href="{% url 'register' %}">注册a>
{% endif %}
nav>
header>
<main>
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}li>
{% endfor %}
ul>
{% endif %}
{% block content %}{% endblock %}
main>
<footer>
<p>© 2025 在线商店p>
footer>
body>
html>
定义视图:
在store/views.py
中添加首页和产品详情视图。
# store/views.py
from django.shortcuts import render, get_object_or_404
from .models import Product
def home(request):
products = Product.objects.all()
return render(request, 'store/home.html', {'products': products})
def product_detail(request, pk):
product = get_object_or_404(Product, pk=pk)
return render(request, 'store/product_detail.html', {'product': product})
更新URL:
# store/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'),
path('product//' , views.product_detail, name='product_detail'),
path('register/', views.register, name='register'),
path('login/', views.user_login, name='login'),
path('logout/', views.user_logout, name='logout'),
]
创建模板:
{% extends 'base.html' %}
{% load static %}
{% block title %}首页{% endblock %}
{% block content %}
<h2>产品列表h2>
<ul>
{% for product in products %}
<li>
<a href="{% url 'product_detail' product.pk %}">{{ product.name }}a> - ${{ product.price }}
li>
{% empty %}
<li>暂无产品。li>
{% endfor %}
ul>
{% endblock %}
{% extends 'base.html' %}
{% load static %}
{% block title %}{{ product.name }}{% endblock %}
{% block content %}
<h2>{{ product.name }}h2>
<p>{{ product.description }}p>
<p>价格:${{ product.price }}p>
<p>库存:{{ product.stock }}p>
{% if user.is_authenticated %}
<form method="POST" action="{% url 'add_to_cart' product.pk %}">
{% csrf_token %}
<button type="submit">加入购物车button>
form>
{% else %}
<p><a href="{% url 'login' %}">登录a>后可添加到购物车。p>
{% endif %}
{% endblock %}
实现购物车功能:
购物车可以通过会话(Session)管理。
添加购物车视图:
# store/views.py
from django.shortcuts import redirect
def add_to_cart(request, pk):
product = get_object_or_404(Product, pk=pk)
cart = request.session.get('cart', {})
if str(pk) in cart:
cart[str(pk)] += 1
else:
cart[str(pk)] = 1
request.session['cart'] = cart
messages.success(request, f'已将 {product.name} 添加到购物车。')
return redirect('home')
def view_cart(request):
cart = request.session.get('cart', {})
cart_items = []
total = 0
for pk, quantity in cart.items():
product = get_object_or_404(Product, pk=pk)
total += product.price * quantity
cart_items.append({
'product': product,
'quantity': quantity,
'total_price': product.price * quantity
})
return render(request, 'store/cart.html', {'cart_items': cart_items, 'total': total})
更新URL:
# store/urls.py
urlpatterns = [
path('', views.home, name='home'),
path('product//' , views.product_detail, name='product_detail'),
path('add_to_cart//' , views.add_to_cart, name='add_to_cart'),
path('cart/', views.view_cart, name='cart'),
path('register/', views.register, name='register'),
path('login/', views.user_login, name='login'),
path('logout/', views.user_logout, name='logout'),
]
创建购物车模板:
{% extends 'base.html' %}
{% load static %}
{% block title %}购物车{% endblock %}
{% block content %}
<h2>购物车h2>
{% if cart_items %}
<ul>
{% for item in cart_items %}
<li>
{{ item.product.name }} - ${{ item.product.price }} x {{ item.quantity }} = ${{ item.total_price }}
li>
{% endfor %}
ul>
<p>总计:${{ total }}p>
<a href="{% url 'checkout' %}">结账a>
{% else %}
<p>购物车为空。p>
{% endif %}
{% endblock %}
定义订单视图:
在store/views.py
中添加结账视图。
# store/views.py
from django.contrib.auth.decorators import login_required
@login_required
def checkout(request):
cart = request.session.get('cart', {})
if not cart:
messages.error(request, '购物车为空。')
return redirect('home')
order = Order.objects.create(user=request.user)
for pk, quantity in cart.items():
product = get_object_or_404(Product, pk=pk)
if product.stock < quantity:
messages.error(request, f'产品 {product.name} 库存不足。')
return redirect('cart')
OrderItem.objects.create(order=order, product=product, quantity=quantity)
product.stock -= quantity
product.save()
# 清空购物车
request.session['cart'] = {}
messages.success(request, '订单已创建成功!')
return redirect('home')
更新URL:
# store/urls.py
urlpatterns = [
path('', views.home, name='home'),
path('product//' , views.product_detail, name='product_detail'),
path('add_to_cart//' , views.add_to_cart, name='add_to_cart'),
path('cart/', views.view_cart, name='cart'),
path('checkout/', views.checkout, name='checkout'),
path('register/', views.register, name='register'),
path('login/', views.user_login, name='login'),
path('logout/', views.user_logout, name='logout'),
]
集成支付网关:
可以集成第三方支付网关(如Stripe、PayPal)处理支付。以下是集成Stripe的简要示例。
安装Stripe库:
pip install stripe
配置Stripe:
在settings.py
中添加Stripe的API密钥。
# mysite/settings.py
STRIPE_PUBLIC_KEY = config('STRIPE_PUBLIC_KEY')
STRIPE_SECRET_KEY = config('STRIPE_SECRET_KEY')
创建支付视图:
# store/views.py
import stripe
from django.conf import settings
stripe.api_key = settings.STRIPE_SECRET_KEY
@login_required
def payment(request):
if request.method == 'POST':
token = request.POST.get('stripeToken')
try:
charge = stripe.Charge.create(
amount=int(request.POST['amount']) * 100, # 转换为分
currency='usd',
description='在线商店订单',
source=token,
)
messages.success(request, '支付成功!')
return redirect('home')
except stripe.error.CardError as e:
messages.error(request, '支付失败:{}'.format(e))
return render(request, 'store/payment.html', {'stripe_public_key': settings.STRIPE_PUBLIC_KEY})
更新URL:
# store/urls.py
urlpatterns = [
...
path('payment/', views.payment, name='payment'),
]
创建支付模板:
{% extends 'base.html' %}
{% load static %}
{% block title %}支付{% endblock %}
{% block content %}
<h2>支付h2>
<form action="{% url 'payment' %}" method="POST">
{% csrf_token %}
<script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ stripe_public_key }}"
data-amount="5000"
data-name="在线商店"
data-description="订单支付"
data-currency="usd"
data-email="{{ user.email }}">
script>
form>
{% endblock %}
注意:此示例仅为基本集成,实际应用中需要处理更复杂的支付流程和安全性措施。
原因:跨站请求伪造(CSRF)是一种常见的Web攻击,Django通过生成和验证CSRF令牌来防止此类攻击。
解决方法:
启用CSRF保护:
Django默认启用了CSRF中间件。确保MIDDLEWARE
中包含'django.middleware.csrf.CsrfViewMiddleware'
。
# mysite/settings.py
MIDDLEWARE = [
...
'django.middleware.csrf.CsrfViewMiddleware',
...
]
在表单中包含CSRF令牌:
在模板中的表单标签内添加{% csrf_token %}
。
<form method="POST" action="{% url 'some_view' %}">
{% csrf_token %}
form>
处理Ajax请求的CSRF:
对于Ajax请求,需要在请求头中包含CSRF令牌。可以在JavaScript中通过Cookie获取CSRF令牌并设置请求头。
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
fetch('/some_url/', {
method: 'POST',
headers: {
'X-CSRFToken': csrftoken,
'Content-Type': 'application/json'
},
body: JSON.stringify({ /* 数据 */ })
});
原因:在处理大量数据时,未优化的查询可能导致性能瓶颈,影响应用响应速度。
解决方法:
使用select_related
和prefetch_related
:
这些方法可以减少数据库查询次数,优化关联对象的获取。
# 使用select_related获取关联的外键对象
posts = Post.objects.select_related('author').all()
# 使用prefetch_related获取多对多或反向外键对象
orders = Order.objects.prefetch_related('items__product').all()
添加数据库索引:
为频繁查询的字段添加索引,提高查询速度。
# store/models.py
class Product(models.Model):
name = models.CharField(max_length=200, db_index=True)
# 其他字段
分页查询:
对大量数据进行分页展示,减少单次查询的数据量。
from django.core.paginator import Paginator
def home(request):
products_list = Product.objects.all()
paginator = Paginator(products_list, 10) # 每页10个
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'store/home.html', {'page_obj': page_obj})
在模板中显示分页链接:
<ul>
{% for product in page_obj %}
<li>{{ product.name }} - ${{ product.price }}li>
{% endfor %}
ul>
<div class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
<a href="?page=1">« 第一页a>
<a href="?page={{ page_obj.previous_page_number }}">上一页a>
{% endif %}
<span class="current">
第 {{ page_obj.number }} 页,共 {{ page_obj.paginator.num_pages }} 页
span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">下一页a>
<a href="?page={{ page_obj.paginator.num_pages }}">最后一页 »a>
{% endif %}
span>
div>
使用缓存:
利用Django的缓存框架缓存频繁访问的数据,减少数据库查询次数。
from django.core.cache import cache
def home(request):
products = cache.get('all_products')
if not products:
products = Product.objects.all()
cache.set('all_products', products, 300) # 缓存5分钟
return render(request, 'store/home.html', {'products': products})
优化查询集:
仅获取需要的字段,减少数据传输量。
products = Product.objects.only('name', 'price')
使用原生SQL查询:
在复杂查询场景下,使用原生SQL语句可能比ORM查询更高效。
from django.db import connection
def get_custom_data():
with connection.cursor() as cursor:
cursor.execute("SELECT name, price FROM store_product WHERE stock > %s", [10])
row = cursor.fetchall()
return row
原因:用户密码的安全存储对于保护用户隐私和防止数据泄露至关重要。
解决方法:
使用Django内置的用户模型:
Django的User
模型已经实现了密码的哈希存储,确保密码的安全性。
from django.contrib.auth.models import User
user = User.objects.create_user(username='john', email='[email protected]', password='password123')
密码哈希算法:
Django使用强大的哈希算法(如PBKDF2)和盐(Salt)自动处理密码的加密和存储。
# 验证密码
user = User.objects.get(username='john')
user.check_password('password123') # 返回True或False
自定义用户模型(可选):
如果需要扩展用户模型,可以创建自定义的用户模型,并确保继承自AbstractBaseUser
和PermissionsMixin
,以保持安全性。
# accounts/models.py
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.db import models
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
# 其他字段
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
注意:在创建自定义用户模型时,应尽早进行,以避免迁移和兼容性问题。
定期更新密码哈希算法:
随着技术的发展,Django会定期更新默认的密码哈希算法。可以通过PASSWORD_HASHERS
设置自定义哈希器。
# mysite/settings.py
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]
强密码策略:
通过配置密码验证器,强制用户设置强密码。
# mysite/settings.py
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {
'min_length': 8,
}
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
原因:开发环境与生产环境存在差异,直接在生产环境中运行Django开发服务器不安全且不高效。
解决方法:
使用WSGI服务器:
部署Django应用时,应使用专业的WSGI服务器,如Gunicorn或uWSGI。
# 使用Gunicorn
pip install gunicorn
gunicorn online_store.wsgi:application
配置反向代理:
使用Nginx或Apache作为反向代理服务器,处理客户端请求并转发给WSGI服务器。
Nginx示例配置:
server {
listen 80;
server_name your_domain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /static/ {
alias /path/to/your/online_store/static/;
}
location /media/ {
alias /path/to/your/online_store/media/;
}
}
启用HTTPS:
使用SSL证书为您的网站启用HTTPS,提高数据传输的安全性。可以使用Let’s Encrypt免费获取SSL证书。
配置环境变量:
不要将敏感信息(如SECRET_KEY
、数据库URI)硬编码在代码中,而应通过环境变量配置。
export DJANGO_SECRET_KEY='your_production_secret_key'
export DJANGO_DEBUG=False
export DB_NAME='your_db_name'
export DB_USER='your_db_user'
export DB_PASSWORD='your_db_password'
export DB_HOST='your_db_host'
export DB_PORT='your_db_port'
并在settings.py
中使用这些变量。
日志管理:
配置日志记录,监控应用的运行状态和错误信息。
# mysite/settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': '/path/to/your/logs/debug.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}
使用容器化:
使用Docker等容器技术,简化部署过程,提高环境一致性。
Dockerfile示例:
FROM python:3.9-slim
ENV PYTHONUNBUFFERED 1
WORKDIR /app
COPY requirements.txt /app/
RUN pip install --no-cache-dir -r requirements.txt
COPY . /app/
CMD ["gunicorn", "online_store.wsgi:application", "--bind", "0.0.0.0:8000"]
构建与运行容器:
docker build -t online_store .
docker run -d -p 8000:8000 online_store
定期备份:
定期备份数据库和重要数据,确保数据安全。
监控与维护:
使用监控工具(如Prometheus、Grafana)监控应用性能,及时发现并解决问题。
在本篇文章中,我们深入探讨了Django框架的核心概念和高级功能,包括项目与应用结构、模型与数据库迁移、管理后台、模板与静态文件管理,以及如何将Django应用部署到生产环境。通过构建一个在线商店的实战项目,您已经掌握了使用Django构建复杂且可扩展的Web应用所需的关键技能。
学习建议:
如果您有任何问题或需要进一步的帮助,请随时在评论区留言或联系相关技术社区。