第20篇:Python 开发进阶:使用Django进行Web开发详解

news/2025/1/31 11:20:09/

第20篇:使用Django进行Web开发

内容简介

在上一篇文章中,我们深入探讨了Flask框架的高级功能,并通过构建一个博客系统展示了其实际应用。本篇文章将转向Django,另一个功能强大且广泛使用的Python Web框架。我们将介绍Django的核心概念,包括项目与应用结构、模型与数据库迁移、管理后台、模板与静态文件管理,以及如何将Django应用部署到生产环境。通过详细的代码示例和实战项目,您将掌握使用Django构建复杂且可扩展的Web应用的关键技能。


目录

  1. Django框架介绍
    • Django的历史与特点
    • Django的优势
    • 安装与设置
  2. 项目与应用结构
    • 创建Django项目
    • 理解项目与应用
    • 项目目录结构详解
  3. 模型与数据库迁移
    • 定义模型
    • 数据库配置
    • 执行迁移
    • 数据库操作
  4. 管理后台
    • 激活管理后台
    • 自定义管理界面
    • 权限与用户管理
  5. 模板与静态文件管理
    • Django模板引擎
    • 模板继承
    • 静态文件管理
    • 自定义模板标签与过滤器
  6. 部署Django应用
    • 选择部署平台
    • 使用Gunicorn和Nginx
    • 配置环境变量与安全设置
    • 启用HTTPS
  7. 示例项目:在线商店
    • 项目结构
    • 创建应用与定义模型
    • 实现用户认证
    • 产品管理与购物车功能
    • 订单处理与支付集成
  8. 常见问题及解决方法
    • 问题1:如何处理表单的CSRF保护?
    • 问题2:如何优化数据库查询性能?
    • 问题3:如何实现密码的安全存储?
    • 问题4:如何部署Django应用到生产环境?
  9. 总结

Django框架介绍

Django的历史与特点

Django是一个高级的Python Web框架,旨在快速开发和简化复杂、数据库驱动的网站的构建过程。由Adrian HolovatySimon Willison在2003年开发,并于2005年正式发布,Django以其“务实”而闻名,遵循“不要重复自己”(DRY)和“显式优于隐式”的设计原则。

主要特点

  • 快速开发:提供了大量内置功能,减少开发时间。
  • 可扩展性:适用于从简单的博客到复杂的企业级应用。
  • 安全性:内置多种安全保护机制,防范常见的Web攻击。
  • 完善的文档:拥有详尽的官方文档和活跃的社区支持。
  • 内置管理后台:自动生成的管理界面,方便数据管理。

Django的优势

  1. 全栈框架:Django涵盖了从前端到后端的各个方面,无需依赖大量第三方库。
  2. ORM(对象关系映射):简化数据库操作,支持多种数据库后端。
  3. 模板系统:强大的模板引擎,支持模板继承和自定义标签。
  4. 表单处理:自动生成表单,并提供丰富的表单验证功能。
  5. URL路由:灵活的URL配置,支持正则表达式和命名空间。
  6. 中间件:支持请求和响应处理的中间件,便于功能扩展。
  7. 社区与生态系统:拥有大量的第三方包和插件,满足各种需求。

安装与设置

安装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列表。

python"># 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中定义一个简单的博客文章模型。

python"># 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

python"># 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的迁移系统完成。

  1. 创建迁移文件

    python manage.py makemigrations
    

    该命令会根据模型的变化生成迁移文件,记录数据库结构的变化。

  2. 应用迁移

    python 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自带一个功能强大的管理后台,用于管理数据库中的数据。要激活管理后台,需要进行以下步骤。

  1. 创建超级用户

    python manage.py createsuperuser
    

    按提示输入用户名、电子邮件和密码,创建一个超级用户。

  2. 注册模型到管理后台

    blog/admin.py中注册模型。

    python"># blog/admin.py
    from django.contrib import admin
    from .models import Postadmin.site.register(Post)
    
  3. 运行开发服务器并访问管理后台

    python manage.py runserver
    

    在浏览器中访问http://127.0.0.1:8000/admin/,使用超级用户的凭据登录。您将看到已注册的Post模型,可以在管理后台中添加、编辑和删除文章。

自定义管理界面

可以自定义管理界面以更好地展示和管理数据。

示例:自定义Post模型的管理界面

python"># 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的管理后台不仅用于数据管理,还支持权限和用户管理。

用户和组管理

在管理后台,可以创建和管理用户和组,分配不同的权限。

  1. 创建用户:在管理后台的“Users”部分创建新用户。
  2. 分配权限:为用户分配特定的权限,如添加、修改或删除某些模型。
  3. 创建组:将权限分配给组,然后将用户添加到组,简化权限管理。

示例:限制用户只能管理自己的文章

通过自定义ModelAdmin类,可以限制用户只能看到和管理自己的数据。

python"># 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>&copy; 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中配置静态文件相关设置。

配置静态文件

python"># 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

自定义模板标签与过滤器

可以创建自定义的模板标签和过滤器,扩展模板引擎的功能。

创建自定义过滤器

  1. 创建模板标签目录

    在应用目录下创建templatetags文件夹,并添加__init__.py

    blog/
    ├── templatetags/
    │   ├── __init__.py
    │   └── blog_extras.py
    
  2. 定义过滤器

    python"># 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
    
  3. 使用过滤器

    在模板中加载并使用自定义过滤器。

    {% load blog_extras %}<p>{{ post.content|truncate:100 }}</p>
    

创建自定义标签

类似于过滤器,可以创建自定义标签以实现复杂的逻辑。

python"># 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。

步骤

  1. 安装Gunicorn

    pip install gunicorn
    
  2. 运行Gunicorn

    在项目根目录下运行Gunicorn。

    gunicorn mysite.wsgi:application
    
  3. 配置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
    
  4. 运行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
    

配置环境变量与安全设置

配置环境变量

不要在代码中硬编码敏感信息。使用环境变量管理配置。

  1. 安装python-decouple

    pip install python-decouple
    
  2. 修改settings.py

    python"># 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'),}
    }
    
  3. 创建.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)跟踪。

安全设置

  1. 禁用调试模式

    在生产环境中,确保DEBUG=False

  2. 设置允许的主机

    settings.py中配置ALLOWED_HOSTS

    python">ALLOWED_HOSTS = ['your_domain.com', 'www.your_domain.com']
    
  3. 使用HTTPS

    配置SSL证书,启用HTTPS,确保数据传输安全。

  4. 配置安全中间件

    确保以下中间件在MIDDLEWARE中启用,以增强安全性。

    python">MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','whitenoise.middleware.WhiteNoiseMiddleware',  # 用于静态文件管理...
    ]
    

启用HTTPS

使用Let’s Encrypt获取免费的SSL证书,并配置Nginx以启用HTTPS。

  1. 安装Certbot

    sudo apt-get update
    sudo apt-get install certbot python3-certbot-nginx
    
  2. 获取证书

    sudo certbot --nginx -d your_domain.com -d www.your_domain.com
    
  3. 自动续期

    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中定义ProductOrder模型。

python"># 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中定义用户注册表单。

python"># 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中添加注册和登录视图。

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 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。

python"># 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。

python"># 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.htmlstore/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>&copy; 2025 在线商店</p></footer>
</body>
</html>

产品管理与购物车功能

定义视图

store/views.py中添加首页和产品详情视图。

python"># 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

python"># 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)管理。

添加购物车视图

python"># 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

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'),
]

创建购物车模板

<!-- 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 -= quantityproduct.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的简要示例。

  1. 安装Stripe库

    pip install stripe
    
  2. 配置Stripe

    settings.py中添加Stripe的API密钥。

    python"># mysite/settings.py
    STRIPE_PUBLIC_KEY = config('STRIPE_PUBLIC_KEY')
    STRIPE_SECRET_KEY = config('STRIPE_SECRET_KEY')
    
  3. 创建支付视图

    python"># 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})
    
  4. 更新URL

    python"># store/urls.py
    urlpatterns = [...path('payment/', views.payment, name='payment'),
    ]
    
  5. 创建支付模板

    <!-- 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令牌来防止此类攻击。

解决方法

  1. 启用CSRF保护

    Django默认启用了CSRF中间件。确保MIDDLEWARE中包含'django.middleware.csrf.CsrfViewMiddleware'

    python"># mysite/settings.py
    MIDDLEWARE = [...'django.middleware.csrf.CsrfViewMiddleware',...
    ]
    
  2. 在表单中包含CSRF令牌

    在模板中的表单标签内添加{% csrf_token %}

    <form method="POST" action="{% url 'some_view' %}">{% csrf_token %}<!-- 表单字段 -->
    </form>
    
  3. 处理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:如何优化数据库查询性能?

原因:在处理大量数据时,未优化的查询可能导致性能瓶颈,影响应用响应速度。

解决方法

  1. 使用select_relatedprefetch_related

    这些方法可以减少数据库查询次数,优化关联对象的获取。

    python"># 使用select_related获取关联的外键对象
    posts = Post.objects.select_related('author').all()# 使用prefetch_related获取多对多或反向外键对象
    orders = Order.objects.prefetch_related('items__product').all()
    
  2. 添加数据库索引

    为频繁查询的字段添加索引,提高查询速度。

    python"># store/models.py
    class Product(models.Model):name = models.CharField(max_length=200, db_index=True)# 其他字段
    
  3. 分页查询

    对大量数据进行分页展示,减少单次查询的数据量。

    python">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">&laquo; 第一页</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 }}">最后一页 &raquo;</a>{% endif %}</span>
    </div>
    
  4. 使用缓存

    利用Django的缓存框架缓存频繁访问的数据,减少数据库查询次数。

    python">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})
    
  5. 优化查询集

    仅获取需要的字段,减少数据传输量。

    python">products = Product.objects.only('name', 'price')
    
  6. 使用原生SQL查询

    在复杂查询场景下,使用原生SQL语句可能比ORM查询更高效。

    python">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:如何实现密码的安全存储?

原因:用户密码的安全存储对于保护用户隐私和防止数据泄露至关重要。

解决方法

  1. 使用Django内置的用户模型

    Django的User模型已经实现了密码的哈希存储,确保密码的安全性。

    python">from django.contrib.auth.models import Useruser = User.objects.create_user(username='john', email='john@example.com', password='password123')
    
  2. 密码哈希算法

    Django使用强大的哈希算法(如PBKDF2)和盐(Salt)自动处理密码的加密和存储。

    python"># 验证密码
    user = User.objects.get(username='john')
    user.check_password('password123')  # 返回True或False
    
  3. 自定义用户模型(可选):

    如果需要扩展用户模型,可以创建自定义的用户模型,并确保继承自AbstractBaseUserPermissionsMixin,以保持安全性。

    python"># 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 = []
    

    注意:在创建自定义用户模型时,应尽早进行,以避免迁移和兼容性问题。

  4. 定期更新密码哈希算法

    随着技术的发展,Django会定期更新默认的密码哈希算法。可以通过PASSWORD_HASHERS设置自定义哈希器。

    python"># mysite/settings.py
    PASSWORD_HASHERS = ['django.contrib.auth.hashers.PBKDF2PasswordHasher','django.contrib.auth.hashers.Argon2PasswordHasher','django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
    ]
    
  5. 强密码策略

    通过配置密码验证器,强制用户设置强密码。

    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开发服务器不安全且不高效。

解决方法

  1. 使用WSGI服务器

    部署Django应用时,应使用专业的WSGI服务器,如Gunicorn或uWSGI。

    # 使用Gunicorn
    pip install gunicorn
    gunicorn online_store.wsgi:application
    
  2. 配置反向代理

    使用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/;}
    }
    
  3. 启用HTTPS

    使用SSL证书为您的网站启用HTTPS,提高数据传输的安全性。可以使用Let’s Encrypt免费获取SSL证书。

  4. 配置环境变量

    不要将敏感信息(如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中使用这些变量。

  5. 日志管理

    配置日志记录,监控应用的运行状态和错误信息。

    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,},},
    }
    
  6. 使用容器化

    使用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
    
  7. 定期备份

    定期备份数据库和重要数据,确保数据安全。

  8. 监控与维护

    使用监控工具(如Prometheus、Grafana)监控应用性能,及时发现并解决问题。


总结

在本篇文章中,我们深入探讨了Django框架的核心概念和高级功能,包括项目与应用结构、模型与数据库迁移、管理后台、模板与静态文件管理,以及如何将Django应用部署到生产环境。通过构建一个在线商店的实战项目,您已经掌握了使用Django构建复杂且可扩展的Web应用所需的关键技能。

学习建议

  1. 扩展功能:尝试为在线商店添加更多功能,如用户评价、产品分类、搜索功能等,进一步巩固所学知识。
  2. 探索Django REST框架:学习如何使用Django REST框架构建RESTful API,扩展应用的功能和可用性。
  3. 优化性能:研究Django应用的性能优化技巧,如缓存策略、数据库优化、异步任务处理等。
  4. 增强安全性:深入了解Web应用的安全性,学习防范常见的安全漏洞,如SQL注入、XSS攻击等。
  5. 持续部署与运维:学习如何实现持续集成和持续部署(CI/CD),提升开发和部署效率。
  6. 参与社区:通过参与Django相关的开源项目和社区活动,学习业界最佳实践,提升编程和协作能力。
  7. 深入学习:阅读官方文档和相关书籍,如《Two Scoops of Django》以进一步提升Django开发技能。

如果您有任何问题或需要进一步的帮助,请随时在评论区留言或联系相关技术社区。


http://www.ppmy.cn/news/1567585.html

相关文章

【环境搭建】Metersphere v2.x 容器部署教程踩坑总结

前言 Metersphere部署过程中遇到的问题有点多&#xff0c;原因是其容器的架构蛮复杂的&#xff0c;比较容易踩坑&#xff0c;所以记录一下。 介绍 MeterSphere 是开源持续测试平台&#xff0c;遵循 GPL v3 开源许可协议&#xff0c;涵盖测试管理、接口测试、UI 测试和性能测…

C++ 学习:深入理解 Linux 系统中的冯诺依曼架构

一、引言 冯诺依曼架构是现代计算机系统的基础&#xff0c;它的提出为计算机的发展奠定了理论基础。在学习 C 和 Linux 系统时&#xff0c;理解冯诺依曼架构有助于我们更好地理解程序是如何在计算机中运行的&#xff0c;包括程序的存储、执行和资源管理。这对于编写高效、可靠的…

二级C语言题解:孤独数、找最长子串、返回两数组交集

目录 一、程序填空 --- 孤独数 题目 分析 二、程序修改 --- 找最长子串 题目 分析 三、程序设计题 --- 返回两数组交集 题目 分析 前言&#xff1a; 本文开始讲解二级C语言的程序填空题和程序改错题以及程序设计题 旨在帮助大家更好的理解这些题目 希望能够帮助到大家(…

使用八爪鱼爬虫和Web Scraper抓取数据实战案例,附详细教程

最近有不少小伙伴咨询怎么抓取抖音视频或者评论的数据&#xff0c;他们多是自媒体或者商家&#xff0c;想要模仿爆火视频或者分析视频评论区的舆情信息&#xff0c;确实呀&#xff0c;现在抖音是流量高地&#xff0c;淘金的地方&#xff0c;真的是一个值得挖掘的宝藏。当然我一…

机器学习:支持向量机

支持向量机&#xff08;Support Vector Machine&#xff09;是一种二类分类模型&#xff0c;其基本模型定义为特征空间上的间隔最大的广义线性分类器&#xff0c;其学习策略便是间隔最大化&#xff0c;最终可转化为一个凸二次规划问题的求解。 假设两类数据可以被 H x : w T x…

08.OSPF 特殊区域及其他特性

OSPF 特殊区域及其他特性 一. 前言OSPF的四个特殊区域Stub末梢区域Totally Stub完全末梢区域NSSATotally NSSA完全的NSSA二.Stub 区域和 Totally Stub 区域(1)网络规模变大引发的问题(2)传输区域和末端区域(3)Stub 区域(4)Totally Stub 区域三.NSSA 区域和 Totally NSS…

运行虚幻引擎UE设置Visual Studio

运行虚幻引擎UE设置Visual Studio 1. 枚举转换为字符串2. 修改 解决方案配置 下拉框宽度3. 调试较慢4. 如何修复GPU驱动程序崩溃 1. 枚举转换为字符串 -Desc: 从静态 Uenum 调用 GetNameStringByValue 并为其提供你要获取其名称的值&#xff0c;可以将枚举转换为字符串。初始化…

【2024年终总结】深圳工作生活评测

距离上次写年终总结已经过了一年半了&#xff0c;这一年半中哪怕经历了很多的事情&#xff0c;但是感觉又没发生什么。想写一些骚话&#xff0c;却总觉得自己无法完全表达&#xff0c;便也就这样&#xff0c;静静地记录下这一段时光。 现在是2025年&#xff0c;春节前的时光&am…