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

第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列表。

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

  1. 创建迁移文件

    python manage.py makemigrations
    

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

  2. 应用迁移

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

  1. 创建超级用户

    python manage.py createsuperuser
    

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

  2. 注册模型到管理后台

    blog/admin.py中注册模型。

    # 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模型的管理界面

# 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类,可以限制用户只能看到和管理自己的数据。

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

配置静态文件

# 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. 定义过滤器

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

创建自定义标签

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

# 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

    # 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

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

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

  4. 配置安全中间件

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

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

# 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.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中添加首页和产品详情视图。

# 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的简要示例。

  1. 安装Stripe库

    pip install stripe
    
  2. 配置Stripe

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

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

    # 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

    # 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'

    # 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

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

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

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

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

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

    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的缓存框架缓存频繁访问的数据,减少数据库查询次数。

    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. 优化查询集

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

    products = Product.objects.only('name', 'price')
    
  6. 使用原生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:如何实现密码的安全存储?

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

解决方法

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

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

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

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

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

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

    # 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设置自定义哈希器。

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

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

    # 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. 日志管理

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

    # 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.mzph.cn/bicheng/69103.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

操作无法完成,因为文件已经在Electronic Team Virtual Serial Port Driver Service中打开

报错 操作无法完成,因为文件已经在Electronic Team Virtual Serial Port Driver Service中打开 现象 这个exe文件无法删除 解决办法 按下WinR, 找到Electronic Team Virtual Serial Port Driver Service,右击停止. 再次尝试删除,发现这个exe文件成功删除!

单值二叉树(C语言详解版)

一、摘要 今天要讲的是leetcode单值二叉树&#xff0c;这里用到的C语言&#xff0c;主要提供的是思路&#xff0c;大家看了我的思路之后可以点击链接自己试一下。 二、题目简介 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。 只有给定的树是单…

【多表查询】

目录 一. 一对多二. 一对一 and 多对多三. 多表设计案例四. 多表查询4.1 概述4.2 内连接与外连接4.3 子查询4.4 案例 \quad 一. 一对多 \quad 删除外键 \quad 二. 一对一 and 多对多 \quad \quad 三. 多表设计案例 \quad 一个员工对应多个工作经历 \quad 四. 多表查询 \quad \q…

CentOS 7 搭建lsyncd实现文件实时同步 —— 筑梦之路

在 CentOS 7 上搭建 lsyncd&#xff08;Live Syncing Daemon&#xff09;以实现文件的实时同步&#xff0c;可以按照以下步骤进行操作。lsyncd 是一个基于 inotify 的轻量级实时同步工具&#xff0c;支持本地和远程同步。以下是详细的安装和配置步骤&#xff1a; 1. 系统准备 …

[Dialog屏幕开发] Table Control 列数据操作

阅读该篇文章之前&#xff0c;可先阅读下述资料 [Dialog屏幕开发] 屏幕绘制(Table Control控件)https://blog.csdn.net/Hudas/article/details/145314623?spm1001.2014.3001.5501https://blog.csdn.net/Hudas/article/details/145314623?spm1001.2014.3001.5501上篇文章我们…

Arduino大师练成手册 -- 读取DHT11

要在 Arduino 上控制 DHT11 温湿度传感器&#xff0c;你可以按照以下步骤进行&#xff1a; 硬件连接&#xff1a; 将 DHT11 的 VCC 引脚连接到 Arduino 的 5V 引脚。 将 DHT11 的 GND 引脚连接到 Arduino 的 GND 引脚。 将 DHT11 的 DATA 引脚连接到 Arduino 的数字引脚&am…

leetcode刷题记录(八十九)——35. 搜索插入位置

&#xff08;一&#xff09;问题描述 35. 搜索插入位置 - 力扣&#xff08;LeetCode&#xff09;35. 搜索插入位置 - 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位…

-bash: ./uninstall.command: /bin/sh^M: 坏的解释器: 没有那个文件或目录

终端报错&#xff1a; -bash: ./uninstall.command: /bin/sh^M: 坏的解释器: 没有那个文件或目录原因&#xff1a;由于文件行尾符不匹配导致的。当脚本文件在Windows环境中创建或编辑后&#xff0c;行尾符为CRLF&#xff08;即回车和换行&#xff0c;\r\n&#xff09;&#xf…

渐变颜色怎么调?

渐变颜色的调整是设计中非常重要的一部分&#xff0c;尤其是在创建具有视觉吸引力和深度感的设计作品时。以下是一些在不同设计软件中调整渐变颜色的详细步骤和技巧&#xff1a; 一、Adobe Photoshop 1. 创建渐变 打开渐变工具&#xff1a; 选择工具栏中的“渐变工具”&#x…

安装wxFormBuilder

1. 网址&#xff1a;GitHub - wxFormBuilder/wxFormBuilder: A wxWidgets GUI Builder 2. 安装MSYS2 MSYS2可以在GitHub的内容中找到&#xff0c;这个版本是32位64位的 3. 在程序中打开MINGW64 shell 4. 在MSYS2 MINGW64 shell中输入 pacman -Syu pacman -S ${MINGW_PACKAGE…

在 Windows 系统上,将 Ubuntu 从 C 盘 迁移到 D 盘

在 Windows 系统上&#xff0c;如果你使用的是 WSL&#xff08;Windows Subsystem for Linux&#xff09;并安装了 Ubuntu&#xff0c;你可以将 Ubuntu 从 C 盘 迁移到 D 盘。迁移过程涉及导出当前的 Ubuntu 发行版&#xff0c;然后将其导入到 D 盘的目标目录。以下是详细的步骤…

【知识】可视化理解git中的cherry-pick、merge、rebase

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 这三个确实非常像&#xff0c;以至于对于初学者来说比较难理解。 总结对比 先给出对比&#xff1a; 特性git mergegit rebasegit cherry-pick功能合并…

DBO优化最近邻分类预测matlab

蜣螂优化算法&#xff08;Dung Beetle Optimizer&#xff0c;简称 DBO&#xff09;作为一种新兴的群智能优化算法&#xff0c;于 2022 年末被提出&#xff0c;其灵感主要来源于蜣螂的滚球、跳舞、觅食、偷窃以及繁殖等行为。 本次使用的数据为 Excel 格式的分类数据集。该数据…

vulnhub靶场【kioptrix-3】靶机

前言 靶机&#xff1a;kioptrix-3&#xff0c;IP地址为192.168.1.74 攻击&#xff1a;kali&#xff0c;IP地址为192.168.1.16 都采用虚拟机&#xff0c;网卡为桥接模式 文章中涉及的靶机&#xff0c;来源于vulnhub官网&#xff0c;想要下载&#xff0c;可自行访问官网下载&a…

springboot使用ssl连接elasticsearch

使用es时ssl证书报错 unable to find valid certification path to requested target 1.依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency>2…

高并发处理 --- 超卖问题+一人一单解决方案

在高并发场景下&#xff0c;超卖和一人一单是两个典型的并发问题。为了解决这两个问题&#xff0c;我们可以使用乐观锁&#xff08;CAS&#xff09;和悲观锁&#xff0c;这两者分别有不同的实现方式和适用场景。下面我们详细介绍如何通过 乐观锁&#xff08;CAS&#xff09; 和…

前端react后端java实现提交antd form表单成功即导出压缩包

前端&#xff08;React Ant Design&#xff09; 1. 创建表单&#xff1a;使用<Form>组件来创建你的表单。 2. 处理表单提交&#xff1a;在onFinish回调中发起请求到后端API&#xff0c;并处理响应。 import React from react; import { Form, Input, Button } from ant…

C++ 标准模板库 (STL, Standard Template Library)

声明&#xff1a;大佬们~这是Tubishu在追寻stl过程中偶然得到了“颢天”大佬的笔记&#xff0c;shushu感觉非常有帮助&#x1f525;又颢天佬未曾来过CSDN&#xff0c;索性在此传达颢天大佬的功德&#x1f9ce; 传送门在此➡️颢天笔记✨✨ C 标准模板库 (STL, Standard Templa…

iOS 集成ffmpeg

前言 本来打算用flutter去实现一个ffmpeg的项目的&#xff0c;不过仔细分析了一下&#xff0c;我后期需要集成OpenGL ES做视频渲染处理&#xff0c;OpenGL ES的使用目前在flutter上面还不是很成熟&#xff0c;所以最后还是选择用原生来开发 ffmpeg集成到iOS工程 iOS对于ffmp…

java读取设置pdf属性信息

pom <dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.24</version> </dependency>读取属性 import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmod…