第20篇:使用Django进行Web开发
内容简介
在上一篇文章中,我们深入探讨了Flask 框架的高级功能,并通过构建一个博客系统展示了其实际应用。本篇文章将转向Django,另一个功能强大且广泛使用的Python Web框架。我们将介绍Django的核心概念,包括项目与应用结构、模型与数据库迁移、管理后台、模板与静态文件管理,以及如何将Django应用部署到生产环境。通过详细的代码示例和实战项目,您将掌握使用Django构建复杂且可扩展的Web应用的关键技能。
目录
Django框架介绍
Django的历史与特点
Django 是一个高级的Python Web框架,旨在快速开发和简化复杂、数据库驱动的网站的构建过程。由Adrian Holovaty 和Simon Willison在2003年开发,并于2005年正式发布,Django以其"务实"而闻名,遵循"不要重复自己"(DRY)和"显式优于隐式"的设计原则。
主要特点:
- 快速开发:提供了大量内置功能,减少开发时间。
- 可扩展性:适用于从简单的博客到复杂的企业级应用。
- 安全性:内置多种安全保护机制,防范常见的Web攻击。
- 完善的文档:拥有详尽的官方文档和活跃的社区支持。
- 内置管理后台:自动生成的管理界面,方便数据管理。
Django的优势
- 全栈框架:Django涵盖了从前端到后端的各个方面,无需依赖大量第三方库。
- ORM(对象关系映射):简化数据库操作,支持多种数据库后端。
- 模板系统:强大的模板引擎,支持模板继承和自定义标签。
- 表单处理:自动生成表单,并提供丰富的表单验证功能。
- URL路由:灵活的URL配置,支持正则表达式和命名空间。
- 中间件:支持请求和响应处理的中间件,便于功能扩展。
- 社区与生态系统:拥有大量的第三方包和插件,满足各种需求。
安装与设置
安装Django:
使用pip
安装Django是最常见的方法。确保您已经安装了Python和pip
。
bash
pip install Django
验证安装:
安装完成后,可以通过以下命令验证Django是否成功安装:
bash
django-admin --version
创建Django项目
创建项目:
使用django-admin
工具创建一个新的Django项目。
bash
django-admin startproject mysite
运行开发服务器:
进入项目目录并运行开发服务器。
bash
cd mysite
python manage.py runserver
在浏览器中访问http://127.0.0.1:8000/
,您将看到Django的欢迎页面,表明项目已成功创建并运行。
项目与应用结构
创建Django项目
Django项目 是一个包含多个应用的集合,负责整体配置和协调。每个项目可以包含一个或多个应用,每个应用负责特定的功能模块。
bash
django-admin startproject mysite
cd mysite
理解项目与应用
- 项目(Project):整个Web应用的容器,包含全局配置、URL路由和应用的集合。
- 应用(App):项目中的一个独立模块,负责特定功能,如用户管理、博客、商店等。
项目目录结构详解
创建项目后,您将看到以下目录结构:
mysite/
├── manage.py
├── mysite/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
文件说明:
- manage.py:Django的命令行工具,用于执行各种任务,如运行开发服务器、数据库迁移等。
- mysite/ :项目的核心目录,包含全局配置文件。
- init.py:将该目录标识为Python包。
- settings.py:项目的配置文件,包含数据库配置、已安装的应用、静态文件配置等。
- urls.py:全局URL路由配置,定义URL与视图的对应关系。
- wsgi.py:WSGI兼容的Web服务器网关接口,用于部署。
创建应用:
使用manage.py
创建一个新的应用,例如创建一个名为blog
的应用。
bash
python manage.py startapp blog
更新项目配置:
在settings.py
中添加新创建的应用到INSTALLED_APPS
列表。
python
# 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
文件说明:
- admin.py:用于注册模型到Django管理后台。
- apps.py:应用的配置文件。
- migrations/:数据库迁移文件夹,用于记录模型的变化。
- models.py:定义应用的数据模型。
- tests.py:编写测试用例。
- views.py:定义视图函数或类视图,处理请求和返回响应。
模型与数据库迁移
定义模型
Django的**模型(Models)**是用Python类定义的,代表数据库中的数据结构。每个模型类继承自django.db.models.Model
。
示例模型:
在blog/models.py
中定义一个简单的博客文章模型。
python
# 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
字段说明:
- title:文章标题,字符型,最大长度200。
- content:文章内容,文本型。
- date_posted:文章发布时间,自动设置为创建时的时间。
- author:文章作者,外键关联到Django内置的用户模型。
数据库配置
默认情况下,Django使用SQLite作为数据库。可以在settings.py
中更改数据库配置,以使用其他数据库如PostgreSQL、MySQL等。
示例:配置PostgreSQL:
python
# mysite/settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydatabase',
'USER': 'mydatabaseuser',
'PASSWORD': 'mypassword',
'HOST': 'localhost',
'PORT': '5432',
}
}
安装相应的数据库驱动:
对于PostgreSQL,需要安装psycopg2
。
bash
pip install psycopg2
执行迁移
定义模型后,需要创建数据库表。这通过Django的迁移系统完成。
-
创建迁移文件:
bashpython manage.py makemigrations
该命令会根据模型的变化生成迁移文件,记录数据库结构的变化。
-
应用迁移:
bashpython manage.py migrate
该命令会将迁移应用到数据库,创建或修改相应的表。
数据库操作
Django提供了强大的ORM(对象关系映射)工具,简化了数据库操作。
创建新记录:
python
# 在Django shell中操作
python manage.py shell
python
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()
查询数据:
python
# 获取所有文章
posts = Post.objects.all()
# 过滤查询
john_posts = Post.objects.filter(author__username='john')
# 获取单个对象
post = Post.objects.get(id=1)
更新数据:
python
post = Post.objects.get(id=1)
post.title = '更新后的标题'
post.save()
删除数据:
python
post = Post.objects.get(id=1)
post.delete()
管理后台
激活管理后台
Django自带一个功能强大的管理后台,用于管理数据库中的数据。要激活管理后台,需要进行以下步骤。
-
创建超级用户:
bashpython manage.py createsuperuser
按提示输入用户名、电子邮件和密码,创建一个超级用户。
-
注册模型到管理后台:
在
blog/admin.py
中注册模型。python# blog/admin.py from django.contrib import admin from .models import Post admin.site.register(Post)
-
运行开发服务器并访问管理后台:
bashpython manage.py runserver
在浏览器中访问
http://127.0.0.1:8000/admin/
,使用超级用户的凭据登录。您将看到已注册的Post
模型,可以在管理后台中添加、编辑和删除文章。
自定义管理界面
可以自定义管理界面以更好地展示和管理数据。
示例:自定义Post模型的管理界面:
python
# 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)
功能说明:
- list_display:在列表视图中显示的字段。
- search_fields:启用搜索功能的字段。
- list_filter:侧边栏的过滤选项。
权限与用户管理
Django的管理后台不仅用于数据管理,还支持权限和用户管理。
用户和组管理:
在管理后台,可以创建和管理用户和组,分配不同的权限。
- 创建用户:在管理后台的"Users"部分创建新用户。
- 分配权限:为用户分配特定的权限,如添加、修改或删除某些模型。
- 创建组:将权限分配给组,然后将用户添加到组,简化权限管理。
示例:限制用户只能管理自己的文章:
通过自定义ModelAdmin
类,可以限制用户只能看到和管理自己的数据。
python
# 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)
功能说明:
- get_queryset:限制查询集,普通用户只能看到自己的文章。
- save_model:在保存新文章时,自动将当前用户设置为作者。
模板与静态文件管理
Django模板引擎
Django使用自己的模板引擎,允许在HTML中嵌入动态内容。模板引擎支持变量、标签和过滤器,帮助生成动态页面。
基本模板示例:
创建一个简单的模板blog/templates/blog/home.html
。
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
。
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
。
html
<!-- blog/templates/blog/home.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
中配置静态文件相关设置。
配置静态文件:
python
# mysite/settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = [BASE_DIR / "static"]
使用静态文件:
在模板中加载静态文件。
html
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>使用静态文件</title>
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
</head>
<body>
<!-- 页面内容 -->
</body>
</html>
收集静态文件:
在生产环境中,使用collectstatic
命令将所有静态文件收集到一个目录。
bash
python manage.py collectstatic
自定义模板标签与过滤器
可以创建自定义的模板标签和过滤器,扩展模板引擎的功能。
创建自定义过滤器:
-
创建模板标签目录:
在应用目录下创建
templatetags
文件夹,并添加__init__.py
。blog/ ├── templatetags/ │ ├── __init__.py │ └── blog_extras.py
-
定义过滤器:
python# 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
-
使用过滤器:
在模板中加载并使用自定义过滤器。
html{% load blog_extras %} <p>{{ post.content|truncate:100 }}</p>
创建自定义标签:
类似于过滤器,可以创建自定义标签以实现复杂的逻辑。
python
# 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)
使用自定义标签:
html
{% load blog_extras %}
<p>当前时间:{% current_time "%Y-%m-%d %H:%M" %}</p>
部署Django应用
选择部署平台
部署Django应用时,可以选择多种平台,包括:
- 虚拟私有服务器(VPS):如DigitalOcean、Linode、AWS EC2。
- 平台即服务(PaaS):如Heroku、PythonAnywhere、Google App Engine。
- 容器化平台:如Docker、Kubernetes。
使用Gunicorn和Nginx
Gunicorn 是一个Python WSGI HTTP服务器,适用于部署Django应用。Nginx作为反向代理服务器,处理客户端请求并转发给Gunicorn。
步骤:
-
安装Gunicorn:
bashpip install gunicorn
-
运行Gunicorn:
在项目根目录下运行Gunicorn。
bashgunicorn mysite.wsgi:application
-
配置Nginx:
创建一个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。
bashsudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled sudo nginx -t sudo systemctl restart nginx
-
运行Gunicorn作为后台服务:
使用systemd创建一个服务文件。
ini# /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服务。
bashsudo systemctl start gunicorn sudo systemctl enable gunicorn
配置环境变量与安全设置
配置环境变量:
不要在代码中硬编码敏感信息。使用环境变量管理配置。
-
安装
python-decouple
:bashpip install python-decouple
-
修改
settings.py
:python# 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# .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
。pythonALLOWED_HOSTS = ['your_domain.com', 'www.your_domain.com']
-
使用HTTPS:
配置SSL证书,启用HTTPS,确保数据传输安全。
-
配置安全中间件:
确保以下中间件在
MIDDLEWARE
中启用,以增强安全性。pythonMIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', # 用于静态文件管理 ... ]
启用HTTPS
使用Let's Encrypt获取免费的SSL证书,并配置Nginx以启用HTTPS。
-
安装Certbot:
bashsudo apt-get update sudo apt-get install certbot python3-certbot-nginx
-
获取证书:
bashsudo certbot --nginx -d your_domain.com -d www.your_domain.com
-
自动续期:
Certbot自动配置证书续期。可以手动测试续期。
bashsudo certbot renew --dry-run
更新Nginx配置:
Certbot会自动修改Nginx配置以启用HTTPS。确保配置正确,并重启Nginx。
bash
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
创建应用与定义模型
创建应用:
bash
python manage.py startapp store
定义模型:
在store/models.py
中定义Product
和Order
模型。
python
# 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}'
执行迁移:
bash
python manage.py makemigrations
python manage.py migrate
实现用户认证
Django内置了用户认证系统,可以利用其功能实现用户注册与登录。
创建注册表单:
在store/forms.py
中定义用户注册表单。
python
# 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
中添加注册和登录视图。
python
# 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。
python
# 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。
python
# 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
。
html
<!-- store/templates/store/register.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 %}
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 %}
更新基础模板:
在templates/base.html
中添加导航链接。
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 '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
中添加首页和产品详情视图。
python
# 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:
python
# store/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'),
path('product/<int:pk>/', 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'),
]
创建模板:
html
<!-- store/templates/store/home.html -->
{% 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 %}
html
<!-- store/templates/store/product_detail.html -->
{% 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)管理。
添加购物车视图:
python
# 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:
python
# store/urls.py
urlpatterns = [
path('', views.home, name='home'),
path('product/<int:pk>/', views.product_detail, name='product_detail'),
path('add_to_cart/<int:pk>/', 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'),
]
创建购物车模板:
html
<!-- store/templates/store/cart.html -->
{% 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
中添加结账视图。
python
# 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:
python
# store/urls.py
urlpatterns = [
path('', views.home, name='home'),
path('product/<int:pk>/', views.product_detail, name='product_detail'),
path('add_to_cart/<int:pk>/', 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库:
bashpip install stripe
-
配置Stripe:
在
settings.py
中添加Stripe的API密钥。python# mysite/settings.py STRIPE_PUBLIC_KEY = config('STRIPE_PUBLIC_KEY') STRIPE_SECRET_KEY = config('STRIPE_SECRET_KEY')
-
创建支付视图:
python# 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:
python# store/urls.py urlpatterns = [ ... path('payment/', views.payment, name='payment'), ]
-
创建支付模板:
html<!-- store/templates/store/payment.html --> {% 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 %}
注意:此示例仅为基本集成,实际应用中需要处理更复杂的支付流程和安全性措施。
常见问题及解决方法
问题1:如何处理表单的CSRF保护?
原因:跨站请求伪造(CSRF)是一种常见的Web攻击,Django通过生成和验证CSRF令牌来防止此类攻击。
解决方法:
-
启用CSRF保护:
Django默认启用了CSRF中间件。确保
MIDDLEWARE
中包含'django.middleware.csrf.CsrfViewMiddleware'
。python# mysite/settings.py MIDDLEWARE = [ ... 'django.middleware.csrf.CsrfViewMiddleware', ... ]
-
在表单中包含CSRF令牌:
在模板中的表单标签内添加
{% csrf_token %}
。html<form method="POST" action="{% url 'some_view' %}"> {% csrf_token %} <!-- 表单字段 --> </form>
-
处理Ajax请求的CSRF:
对于Ajax请求,需要在请求头中包含CSRF令牌。可以在JavaScript中通过Cookie获取CSRF令牌并设置请求头。
javascriptfunction 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({ /* 数据 */ }) });
问题2:如何优化数据库查询性能?
原因:在处理大量数据时,未优化的查询可能导致性能瓶颈,影响应用响应速度。
解决方法:
-
使用
select_related
和prefetch_related
:这些方法可以减少数据库查询次数,优化关联对象的获取。
python# 使用select_related获取关联的外键对象 posts = Post.objects.select_related('author').all() # 使用prefetch_related获取多对多或反向外键对象 orders = Order.objects.prefetch_related('items__product').all()
-
添加数据库索引:
为频繁查询的字段添加索引,提高查询速度。
python# store/models.py class Product(models.Model): name = models.CharField(max_length=200, db_index=True) # 其他字段
-
分页查询:
对大量数据进行分页展示,减少单次查询的数据量。
pythonfrom 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})
在模板中显示分页链接:
html<!-- store/templates/store/home.html --> <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的缓存框架缓存频繁访问的数据,减少数据库查询次数。
pythonfrom 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})
-
优化查询集:
仅获取需要的字段,减少数据传输量。
pythonproducts = Product.objects.only('name', 'price')
-
使用原生SQL查询:
在复杂查询场景下,使用原生SQL语句可能比ORM查询更高效。
pythonfrom 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
问题3:如何实现密码的安全存储?
原因:用户密码的安全存储对于保护用户隐私和防止数据泄露至关重要。
解决方法:
-
使用Django内置的用户模型:
Django的
User
模型已经实现了密码的哈希存储,确保密码的安全性。pythonfrom django.contrib.auth.models import User user = User.objects.create_user(username='john', email='john@example.com', password='password123')
-
密码哈希算法:
Django使用强大的哈希算法(如PBKDF2)和盐(Salt)自动处理密码的加密和存储。
python# 验证密码 user = User.objects.get(username='john') user.check_password('password123') # 返回True或False
-
自定义用户模型(可选):
如果需要扩展用户模型,可以创建自定义的用户模型,并确保继承自
AbstractBaseUser
和PermissionsMixin
,以保持安全性。python# 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
设置自定义哈希器。python# mysite/settings.py PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.Argon2PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', ]
-
强密码策略:
通过配置密码验证器,强制用户设置强密码。
python# 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', }, ]
问题4:如何部署Django应用到生产环境?
原因:开发环境与生产环境存在差异,直接在生产环境中运行Django开发服务器不安全且不高效。
解决方法:
-
使用WSGI服务器:
部署Django应用时,应使用专业的WSGI服务器,如Gunicorn或uWSGI。
bash# 使用Gunicorn pip install gunicorn gunicorn online_store.wsgi:application
-
配置反向代理:
使用Nginx或Apache作为反向代理服务器,处理客户端请求并转发给WSGI服务器。
Nginx示例配置:
nginxserver { 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)硬编码在代码中,而应通过环境变量配置。bashexport 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
中使用这些变量。 -
日志管理:
配置日志记录,监控应用的运行状态和错误信息。
python# 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示例:
dockerfileFROM 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"]
构建与运行容器:
bashdocker build -t online_store . docker run -d -p 8000:8000 online_store
-
定期备份:
定期备份数据库和重要数据,确保数据安全。
-
监控与维护:
使用监控工具(如Prometheus、Grafana)监控应用性能,及时发现并解决问题。
总结
在本篇文章中,我们深入探讨了Django框架的核心概念和高级功能,包括项目与应用结构、模型与数据库迁移、管理后台、模板与静态文件管理,以及如何将Django应用部署到生产环境。通过构建一个在线商店的实战项目,您已经掌握了使用Django构建复杂且可扩展的Web应用所需的关键技能。
学习建议:
- 扩展功能:尝试为在线商店添加更多功能,如用户评价、产品分类、搜索功能等,进一步巩固所学知识。
- 探索Django REST框架:学习如何使用Django REST框架构建RESTful API,扩展应用的功能和可用性。
- 优化性能:研究Django应用的性能优化技巧,如缓存策略、数据库优化、异步任务处理等。
- 增强安全性:深入了解Web应用的安全性,学习防范常见的安全漏洞,如SQL注入、XSS攻击等。
- 持续部署与运维:学习如何实现持续集成和持续部署(CI/CD),提升开发和部署效率。
- 参与社区:通过参与Django相关的开源项目和社区活动,学习业界最佳实践,提升编程和协作能力。
- 深入学习:阅读官方文档和相关书籍,如《Two Scoops of Django》以进一步提升Django开发技能。
如果您有任何问题或需要进一步的帮助,请随时在评论区留言或联系相关技术社区。