第20篇:Python 开发进阶:使用Django进行Web开发详解
第20篇:使用Django进行Web开发
内容简介
在上一篇文章中,我们深入探讨了Flask框架的高级功能,并通过构建一个博客系统展示了其实际应用。本篇文章将转向Django,另一个功能强大且广泛使用的Python Web框架。我们将介绍Django的核心概念,包括项目与应用结构、模型与数据库迁移、管理后台、模板与静态文件管理,以及如何将Django应用部署到生产环境。通过详细的代码示例和实战项目,您将掌握使用Django构建复杂且可扩展的Web应用的关键技能。
目录
- Django框架介绍
- Django的历史与特点
- Django的优势
- 安装与设置
- 项目与应用结构
- 创建Django项目
- 理解项目与应用
- 项目目录结构详解
- 模型与数据库迁移
- 定义模型
- 数据库配置
- 执行迁移
- 数据库操作
- 管理后台
- 激活管理后台
- 自定义管理界面
- 权限与用户管理
- 模板与静态文件管理
- Django模板引擎
- 模板继承
- 静态文件管理
- 自定义模板标签与过滤器
- 部署Django应用
- 选择部署平台
- 使用Gunicorn和Nginx
- 配置环境变量与安全设置
- 启用HTTPS
- 示例项目:在线商店
- 项目结构
- 创建应用与定义模型
- 实现用户认证
- 产品管理与购物车功能
- 订单处理与支付集成
- 常见问题及解决方法
- 问题1:如何处理表单的CSRF保护?
- 问题2:如何优化数据库查询性能?
- 问题3:如何实现密码的安全存储?
- 问题4:如何部署Django应用到生产环境?
- 总结
Django框架介绍
Django的历史与特点
Django是一个高级的Python Web框架,旨在快速开发和简化复杂、数据库驱动的网站的构建过程。由Adrian Holovaty和Simon Willison在2003年开发,并于2005年正式发布,Django以其“务实”而闻名,遵循“不要重复自己”(DRY)和“显式优于隐式”的设计原则。
主要特点:
- 快速开发:提供了大量内置功能,减少开发时间。
- 可扩展性:适用于从简单的博客到复杂的企业级应用。
- 安全性:内置多种安全保护机制,防范常见的Web攻击。
- 完善的文档:拥有详尽的官方文档和活跃的社区支持。
- 内置管理后台:自动生成的管理界面,方便数据管理。
Django的优势
- 全栈框架:Django涵盖了从前端到后端的各个方面,无需依赖大量第三方库。
- ORM(对象关系映射):简化数据库操作,支持多种数据库后端。
- 模板系统:强大的模板引擎,支持模板继承和自定义标签。
- 表单处理:自动生成表单,并提供丰富的表单验证功能。
- URL路由:灵活的URL配置,支持正则表达式和命名空间。
- 中间件:支持请求和响应处理的中间件,便于功能扩展。
- 社区与生态系统:拥有大量的第三方包和插件,满足各种需求。
安装与设置
安装Django:
使用pip安装Django是最常见的方法。确保您已经安装了Python和pip。
pip install Django
验证安装:
安装完成后,可以通过以下命令验证Django是否成功安装:
django-admin --version
创建Django项目
创建项目:
使用django-admin工具创建一个新的Django项目。
django-admin startproject mysite
运行开发服务器:
进入项目目录并运行开发服务器。
cd mysite
python manage.py runserver
在浏览器中访问http://127.0.0.1:8000/,您将看到Django的欢迎页面,表明项目已成功创建并运行。
项目与应用结构
创建Django项目
Django项目是一个包含多个应用的集合,负责整体配置和协调。每个项目可以包含一个或多个应用,每个应用负责特定的功能模块。
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的应用。
python manage.py startapp blog
更新项目配置:
在settings.py中添加新创建的应用到INSTALLED_APPS列表。
# mysite/settings.pyINSTALLED_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中定义一个简单的博客文章模型。
# blog/models.py
from django.db import models
from django.contrib.auth.models import Userclass 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:
# mysite/settings.pyDATABASES = {'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 Postadmin.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 Postclass 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类,可以限制用户只能看到和管理自己的数据。
# blog/admin.py
from django.contrib import admin
from .models import Postclass 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 qsreturn qs.filter(author=request.user)def save_model(self, request, obj, form, change):if not obj.pk:obj.author = request.userobj.save()admin.site.register(Post, PostAdmin)
功能说明:
- get_queryset:限制查询集,普通用户只能看到自己的文章。
- save_model:在保存新文章时,自动将当前用户设置为作者。
模板与静态文件管理
Django模板引擎
Django使用自己的模板引擎,允许在HTML中嵌入动态内容。模板引擎支持变量、标签和过滤器,帮助生成动态页面。
基本模板示例:
创建一个简单的模板blog/templates/blog/home.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。
<!-- 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。
<!-- 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中配置静态文件相关设置。
配置静态文件:
# mysite/settings.pySTATIC_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 templateregister = 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 valueif 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 templateregister = template.Library()@register.simple_tag
def current_time(format_string):from django.utils import timezonereturn timezone.now().strftime(format_string)
使用自定义标签:
{% 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:
pip install gunicorn -
运行Gunicorn:
在项目根目录下运行Gunicorn。
gunicorn mysite.wsgi:application -
配置Nginx:
创建一个Nginx配置文件,配置反向代理。
# /etc/nginx/sites-available/mysiteserver {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 configSECRET_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', # 用于静态文件管理... ]
启用HTTPS
使用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 Userclass 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.nameclass 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 UserCreationFormclass UserRegisterForm(UserCreationForm):email = forms.EmailField(required=True)class Meta:model = Userfields = ['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 AuthenticationFormdef 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 viewsurlpatterns = [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, includeurlpatterns = [path('admin/', admin.site.urls),path('', include('store.urls')),
]
创建模板:
创建store/templates/store/register.html和store/templates/store/login.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 %}
<!-- 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中添加导航链接。
<!-- 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 Productdef 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 viewsurlpatterns = [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'),
]
创建模板:
<!-- 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 %}
<!-- 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)管理。
添加购物车视图:
# store/views.py
from django.shortcuts import redirectdef 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)] += 1else:cart[str(pk)] = 1request.session['cart'] = cartmessages.success(request, f'已将 {product.name} 添加到购物车。')return redirect('home')def view_cart(request):cart = request.session.get('cart', {})cart_items = []total = 0for pk, quantity in cart.items():product = get_object_or_404(Product, pk=pk)total += product.price * quantitycart_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/<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'),
]
创建购物车模板:
<!-- 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中添加结账视图。
# 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 -= quantityproduct.save()# 清空购物车request.session['cart'] = {}messages.success(request, '订单已创建成功!')return redirect('home')
更新URL:
# 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库:
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 settingsstripe.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'), ] -
创建支付模板:
<!-- 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'。# 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({ /* 数据 */ }) });
问题2:如何优化数据库查询性能?
原因:在处理大量数据时,未优化的查询可能导致性能瓶颈,影响应用响应速度。
解决方法:
-
使用
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 Paginatordef 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})在模板中显示分页链接:
<!-- 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的缓存框架缓存频繁访问的数据,减少数据库查询次数。
from django.core.cache import cachedef 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 connectiondef 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模型已经实现了密码的哈希存储,确保密码的安全性。from django.contrib.auth.models import Useruser = User.objects.create_user(username='john', email='john@example.com', 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 modelsclass 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',}, ]
问题4:如何部署Django应用到生产环境?
原因:开发环境与生产环境存在差异,直接在生产环境中运行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-slimENV PYTHONUNBUFFERED 1WORKDIR /appCOPY requirements.txt /app/ RUN pip install --no-cache-dir -r requirements.txtCOPY . /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应用所需的关键技能。
学习建议:
- 扩展功能:尝试为在线商店添加更多功能,如用户评价、产品分类、搜索功能等,进一步巩固所学知识。
- 探索Django REST框架:学习如何使用Django REST框架构建RESTful API,扩展应用的功能和可用性。
- 优化性能:研究Django应用的性能优化技巧,如缓存策略、数据库优化、异步任务处理等。
- 增强安全性:深入了解Web应用的安全性,学习防范常见的安全漏洞,如SQL注入、XSS攻击等。
- 持续部署与运维:学习如何实现持续集成和持续部署(CI/CD),提升开发和部署效率。
- 参与社区:通过参与Django相关的开源项目和社区活动,学习业界最佳实践,提升编程和协作能力。
- 深入学习:阅读官方文档和相关书籍,如《Two Scoops of Django》以进一步提升Django开发技能。
如果您有任何问题或需要进一步的帮助,请随时在评论区留言或联系相关技术社区。
相关文章:
第20篇:Python 开发进阶:使用Django进行Web开发详解
第20篇:使用Django进行Web开发 内容简介 在上一篇文章中,我们深入探讨了Flask框架的高级功能,并通过构建一个博客系统展示了其实际应用。本篇文章将转向Django,另一个功能强大且广泛使用的Python Web框架。我们将介绍Django的核…...
文献引用指南ChatGPT提示词分享
文献引用指南 在学术写作中,准确引用是至关重要的环节。它不仅能够为您的研究提供坚实的学术基础,还能确保您尊重并认可他人的学术成果,从而有效避免抄袭的问题。而ChatGPT在这一方面同样能够为您提供有力的支持。借助ChatGPT,您…...
程序代码篇---C++类.c\.h
文章目录 前言第一部分:C中的类1.类的定义2.成员变量(属性)3.成员函数(方法)4.访问修饰符私有受保护公有 5.构造函数和析构函数成员初始化列表方法重载 6.继承7.多态8.友元 第二部分:.c与.h文件头文件&…...
@RabbitListener处理重试机制完成后的异常捕获
application.properties中配置开启手动签收 spring.rabbitmq.listener.direct.acknowledge-modemanual spring.rabbitmq.listener.simple.acknowledge-modemanual定义一个重试器 Slf4j Configuration public class RabbitMQRetryConfing {Bean("customRetry")publi…...
Mac 上管理本地 Go 版本
在 Mac 上修改本地 Go 版本可以通过多种方法实现。以下是几种常见且详细的操作方案: 方法一:使用 goenv 管理多版本(推荐) 适用场景:需要频繁切换不同 Go 版本,适合长期开发者。 步骤: 安装 g…...
低代码系统-产品架构案例介绍、得帆云(八)
产品名称 得帆云DeCode低代码平台-私有化 得帆云DeMDM主数据管理平台 得帆云DeCode低代码平台-公有云 得帆云DePortal企业门户 得帆云DeFusion融合集成平台 得帆云DeHoop数据中台 名词 概念 云原生 指自己搭建的运维平台,区别于阿里云、腾讯云 Dehoop 指…...
免费GPU算力,不花钱部署DeepSeek-R1
在人工智能和大模型技术飞速发展的今天,越来越多的开发者和研究者希望能够亲自体验和微调大模型,以便更好地理解和应用这些先进的技术。然而,高昂的GPU算力成本往往成为了阻碍大家探索的瓶颈。幸运的是,腾讯云Cloud Studio提供了免…...
JavaEE:多线程进阶
JavaEE:多线程进阶 一、对比不同锁策略之间的应用场景及其区别1. 悲观锁 和 乐观锁1.1 定义和原理1.2 应用场景1.3 示例代码 2. 重量级锁 和 轻量级锁2.1 定义和原理2.2 应用场景2.3 示例代码 3. 挂起等待锁 和 自旋锁3.1 定义和原理3.2 应用场景3.3 示例代码 4. 几…...
不只是mini-react第二节:实现最简fiber
省流|总结 首先,我们编写JSX文件,并通过Babel等转换工具将其转化为createElement()函数的调用,最终生成虚拟 DOM(Vdom)格式。举个例子: // 原始 JSX const App <div>hi-mini-react</div>;//…...
C++实现设计模式---命令模式 (Command)
命令模式 (Command) 命令模式 是一种行为型设计模式,它将请求封装为一个对象,从而使得可以用不同的请求对客户端进行参数化、对请求排队或记录日志,以及支持可撤销的操作。 意图 将操作的调用者与接收者分离,通过将请求封装为独…...
设计模式的艺术-享元模式
结构性模式的名称、定义、学习难度和使用频率如下表所示: 1.如何理解享元模式 当一个软件系统在运行时产生的对象数量太多,将导致运行代价过高,带来系统性能下降等问题。 在享元模式中,存储这些共享实例对象的地方称为享元池&…...
Linux的权限和一些shell原理
目录 shell的原理 Linux权限 sudo命令提权 权限 文件的属性 ⽂件类型: 基本权限: chmod改权限 umask chown 该拥有者 chgrp 改所属组 最后: 目录权限 粘滞位 shell的原理 我们广义上的Linux系统 Linux内核Linux外壳 Linux严格…...
【Postgres_Python】使用python脚本批量创建和导入多个PG数据库
之前批量创建和导入数据库分为2个python脚本进行,现整合优化代码合并为一个python脚本,可同步实现数据库的创建和数据导入。之前的文章链接: 【Postgres_Python】使用python脚本批量创建PG数据库 【Postgres_Python】使用python脚本将多个.S…...
Ubuntu安装GitLab
在 Ubuntu 上安装 GitLab 的步骤如下。这里以 GitLab Community Edition(CE)为例: 前提条件 确保你的 Ubuntu 系统是 20.04 或更高版本。确保你的系统满足 GitLab 的硬件要求。 步骤 更新系统包: sudo apt update sudo apt upg…...
网络知识小科普--5
81、什么是组播路由? 组播路由是一种有针对性的广播形式,将消息发送到所选择的用户组,而不是将其发送到子网上的所有用户。 82、加密在网络上的重要性是什么? 加密是将信息转换成用户不可读的代码的过程。然后使用秘密密钥或密码将其翻译或解密回其…...
JavaScript学习记录23
第十一节 JSON对象 1. JSON 格式 JSON 格式(JavaScript Object Notation 的缩写)是一种用于数据交换的文本格式,2001年由 Douglas Crockford 提出,目的是取代繁琐笨重的 XML 格式。 相比 XML 格式,JSON 格式有两个显…...
VScode 开发 Springboot 程序
1. 通过maven创建springboot程序 输入 mvn archetype:generate 选择模板,一般默认选择为第 7 种方式; 选择之后,一般要你填写如下内容: groupId: 组织名称;artifactId: 项目名称;version: 版本࿰…...
.git/hooks/post-merge 文件的作用
.git/hooks/post-merge 文件是 Git 版本控制系统中的一个钩子(hook)脚本,其作用是在合并(merge)操作完成后自动执行一些特定的操作。以下是关于 .git/hooks/post-merge 文件作用的详细解释: 作用 自动化任…...
Kafak 单例生产者实现-C#操作
前面写了一篇入门操作的文章,因为工作需要,简单修改了下如何实现单例生产者。 Kafka入门-C#操作_c# kafka-CSDN博客文章浏览阅读1.6k次,点赞20次,收藏9次。2).报错:“kafka.zookeeper.ZooKeeperClientTimeoutException: Timed out waiting for connection while in state…...
Cursor开发前端的详细过程
以下是使用 Cursor 开发前端的详细过程: 一、创建项目 打开 Cursor 并新建项目: 启动 Cursor 编辑器。点击 “File” 菜单,选择 “New Project”。在弹出的对话框中,输入项目名称,如 “MyFrontendProject”࿰…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
