当前位置: 首页 > article >正文

第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.py

INSTALLED_APPS = [
    ...
    'blog',
]

项目目录结构扩展

创建应用后,项目目录结构如下:

mysite/
├── manage.py
├── mysite/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── blog/
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── migrations/
    │   └── __init__.py
    ├── models.py
    ├── tests.py
    └── views.py

文件说明

  • admin.py:用于注册模型到Django管理后台。
  • apps.py:应用的配置文件。
  • migrations/:数据库迁移文件夹,用于记录模型的变化。
  • models.py:定义应用的数据模型。
  • tests.py:编写测试用例。
  • views.py:定义视图函数或类视图,处理请求和返回响应。

模型与数据库迁移

定义模型

Django的**模型(Models)**是用Python类定义的,代表数据库中的数据结构。每个模型类继承自django.db.models.Model

示例模型

blog/models.py中定义一个简单的博客文章模型。

# blog/models.py
from django.db import models
from django.contrib.auth.models import User

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    date_posted = models.DateTimeField(auto_now_add=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    def __str__(self):
        return self.title

字段说明

  • title:文章标题,字符型,最大长度200。
  • content:文章内容,文本型。
  • date_posted:文章发布时间,自动设置为创建时的时间。
  • author:文章作者,外键关联到Django内置的用户模型。

数据库配置

默认情况下,Django使用SQLite作为数据库。可以在settings.py中更改数据库配置,以使用其他数据库如PostgreSQL、MySQL等。

示例:配置PostgreSQL

# mysite/settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydatabase',
        'USER': 'mydatabaseuser',
        'PASSWORD': 'mypassword',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

安装相应的数据库驱动

对于PostgreSQL,需要安装psycopg2

pip install psycopg2

执行迁移

定义模型后,需要创建数据库表。这通过Django的迁移系统完成。

  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 Post
    
    admin.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 Post

class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'author', 'date_posted')
    search_fields = ('title', 'content')
    list_filter = ('date_posted', 'author')

admin.site.register(Post, PostAdmin)

功能说明

  • list_display:在列表视图中显示的字段。
  • search_fields:启用搜索功能的字段。
  • list_filter:侧边栏的过滤选项。

权限与用户管理

Django的管理后台不仅用于数据管理,还支持权限和用户管理。

用户和组管理

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

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

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

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

# blog/admin.py
from django.contrib import admin
from .models import Post

class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'author', 'date_posted')
    search_fields = ('title', 'content')
    list_filter = ('date_posted', 'author')

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(author=request.user)

    def save_model(self, request, obj, form, change):
        if not obj.pk:
            obj.author = request.user
        obj.save()

admin.site.register(Post, PostAdmin)

功能说明

  • 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.py

STATIC_URL = '/static/'
STATICFILES_DIRS = [BASE_DIR / "static"]

使用静态文件

在模板中加载静态文件。

{% load static %}

<!DOCTYPE html>
<html>
<head>
    <title>使用静态文件</title>
    <link rel="stylesheet" href="{% static 'css/styles.css' %}">
</head>
<body>
    <!-- 页面内容 -->
</body>
</html>

收集静态文件

在生产环境中,使用collectstatic命令将所有静态文件收集到一个目录。

python manage.py collectstatic

自定义模板标签与过滤器

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

创建自定义过滤器

  1. 创建模板标签目录

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

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

    # blog/templatetags/blog_extras.py
    from django import template
    
    register = template.Library()
    
    @register.filter(name='truncate')
    def truncate(value, arg):
        """Truncate the string after a certain number of characters."""
        try:
            length = int(arg)
        except ValueError:
            return value
        if len(value) > length:
            return value[:length] + '...'
        return value
    
  3. 使用过滤器

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

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

创建自定义标签

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

# blog/templatetags/blog_extras.py
from django import template

register = template.Library()

@register.simple_tag
def current_time(format_string):
    from django.utils import timezone
    return timezone.now().strftime(format_string)

使用自定义标签

{% load blog_extras %}

<p>当前时间:{% current_time "%Y-%m-%d %H:%M" %}</p>

部署Django应用

选择部署平台

部署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/mysite
    
    server {
        listen 80;
        server_name your_domain.com;
    
        location = /favicon.ico { access_log off; log_not_found off; }
        location /static/ {
            root /path/to/your/mysite;
        }
    
        location / {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_pass http://127.0.0.1:8000;
        }
    }
    

    启用配置并重启Nginx。

    sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled
    sudo nginx -t
    sudo systemctl restart nginx
    
  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 config
    
    SECRET_KEY = config('SECRET_KEY')
    DEBUG = config('DEBUG', default=False, cast=bool)
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': config('DB_NAME'),
            'USER': config('DB_USER'),
            'PASSWORD': config('DB_PASSWORD'),
            'HOST': config('DB_HOST'),
            'PORT': config('DB_PORT', default='5432'),
        }
    }
    
  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 User

class Product(models.Model):
    name = models.CharField(max_length=200)
    description = models.TextField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    stock = models.PositiveIntegerField()
    image = models.ImageField(upload_to='product_images/', blank=True, null=True)

    def __str__(self):
        return self.name

class Order(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    ordered_date = models.DateTimeField(auto_now_add=True)
    is_completed = models.BooleanField(default=False)

    def __str__(self):
        return f'Order {self.id} by {self.user.username}'

class OrderItem(models.Model):
    order = models.ForeignKey(Order, related_name='items', on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.PositiveIntegerField(default=1)

    def __str__(self):
        return f'{self.quantity} of {self.product.name}'

执行迁移

python manage.py makemigrations
python manage.py migrate

实现用户认证

Django内置了用户认证系统,可以利用其功能实现用户注册与登录。

创建注册表单

store/forms.py中定义用户注册表单。

# store/forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm

class UserRegisterForm(UserCreationForm):
    email = forms.EmailField(required=True)

    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2']

创建视图

store/views.py中添加注册和登录视图。

# store/views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth import login, authenticate, logout
from .forms import UserRegisterForm
from django.contrib.auth.forms import AuthenticationForm

def register(request):
    if request.method == 'POST':
        form = UserRegisterForm(request.POST)
        if form.is_valid():
            user = form.save()
            messages.success(request, f'账户 {user.username} 创建成功!请登录。')
            return redirect('login')
    else:
        form = UserRegisterForm()
    return render(request, 'store/register.html', {'form': form})

def user_login(request):
    if request.method == 'POST':
        form = AuthenticationForm(request, data=request.POST)
        if form.is_valid():
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            user = authenticate(username=username, password=password)
            if user is not None:
                login(request, user)
                messages.info(request, f'您已登录为 {username}.')
                return redirect('home')
            else:
                messages.error(request, '无效的用户名或密码。')
        else:
            messages.error(request, '无效的用户名或密码。')
    else:
        form = AuthenticationForm()
    return render(request, 'store/login.html', {'form': form})

def user_logout(request):
    logout(request)
    messages.info(request, '您已成功注销。')
    return redirect('home')

配置URL

store/urls.py中定义应用的URL。

# store/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('register/', views.register, name='register'),
    path('login/', views.user_login, name='login'),
    path('logout/', views.user_logout, name='logout'),
]

在项目的主URL配置中包含应用的URL。

# online_store/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('store.urls')),
]

创建模板

创建store/templates/store/register.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 Product

def home(request):
    products = Product.objects.all()
    return render(request, 'store/home.html', {'products': products})

def product_detail(request, pk):
    product = get_object_or_404(Product, pk=pk)
    return render(request, 'store/product_detail.html', {'product': product})

更新URL

# store/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='home'),
    path('product/<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 redirect

def add_to_cart(request, pk):
    product = get_object_or_404(Product, pk=pk)
    cart = request.session.get('cart', {})
    if str(pk) in cart:
        cart[str(pk)] += 1
    else:
        cart[str(pk)] = 1
    request.session['cart'] = cart
    messages.success(request, f'已将 {product.name} 添加到购物车。')
    return redirect('home')

def view_cart(request):
    cart = request.session.get('cart', {})
    cart_items = []
    total = 0
    for pk, quantity in cart.items():
        product = get_object_or_404(Product, pk=pk)
        total += product.price * quantity
        cart_items.append({
            'product': product,
            'quantity': quantity,
            'total_price': product.price * quantity
        })
    return render(request, 'store/cart.html', {'cart_items': cart_items, 'total': total})

更新URL

# store/urls.py
urlpatterns = [
    path('', views.home, name='home'),
    path('product/<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 -= quantity
        product.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 settings
    
    stripe.api_key = settings.STRIPE_SECRET_KEY
    
    @login_required
    def payment(request):
        if request.method == 'POST':
            token = request.POST.get('stripeToken')
            try:
                charge = stripe.Charge.create(
                    amount=int(request.POST['amount']) * 100,  # 转换为分
                    currency='usd',
                    description='在线商店订单',
                    source=token,
                )
                messages.success(request, '支付成功!')
                return redirect('home')
            except stripe.error.CardError as e:
                messages.error(request, '支付失败:{}'.format(e))
        return render(request, 'store/payment.html', {'stripe_public_key': settings.STRIPE_PUBLIC_KEY})
    
  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 Paginator
    
    def home(request):
        products_list = Product.objects.all()
        paginator = Paginator(products_list, 10)  # 每页10个
        page_number = request.GET.get('page')
        page_obj = paginator.get_page(page_number)
        return render(request, 'store/home.html', {'page_obj': page_obj})
    

    在模板中显示分页链接:

    <!-- 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 cache
    
    def home(request):
        products = cache.get('all_products')
        if not products:
            products = Product.objects.all()
            cache.set('all_products', products, 300)  # 缓存5分钟
        return render(request, 'store/home.html', {'products': products})
    
  5. 优化查询集

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

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

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

    from django.db import connection
    
    def get_custom_data():
        with connection.cursor() as cursor:
            cursor.execute("SELECT name, price FROM store_product WHERE stock > %s", [10])
            row = cursor.fetchall()
        return row
    

问题3:如何实现密码的安全存储?

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

解决方法

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

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

    from django.contrib.auth.models import User
    
    user = 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 models
    
    class 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-slim
    
    ENV PYTHONUNBUFFERED 1
    
    WORKDIR /app
    
    COPY requirements.txt /app/
    RUN pip install --no-cache-dir -r requirements.txt
    
    COPY . /app/
    
    CMD ["gunicorn", "online_store.wsgi:application", "--bind", "0.0.0.0:8000"]
    

    构建与运行容器

    docker build -t online_store .
    docker run -d -p 8000:8000 online_store
    
  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.kler.cn/a/518396.html

相关文章:

  • 【Validator】字段验证器struct与多层级验证,go案例
  • LabVIEW 保存文件 生产者/消费者设计
  • 正则表达式基础与应用
  • 罗氏线圈的学习【一】
  • 数字图像处理:实验五
  • 在 Ubuntu22.04 上安装 Splunk
  • rust如何定义全局对象变量
  • 如何成为一名LLM(大语言模型)工程师
  • 基于Flask的哔哩哔哩评论数据可视化分析系统的设计与实现
  • 亲测有效!解决PyCharm下PyEMD安装报错 ModuleNotFoundError: No module named ‘PyEMD‘
  • C++----STL(list)
  • C语言复习
  • 今何在:“思索答案就是一种对虚无的战斗”
  • 基于Springboot + vue实现的民俗网
  • 深度强化学习:PPO
  • 【统计信号处理基础——估计与检测理论】Vol1.Ch1. 引言
  • OS Copilot功能测评:智能助手的炫彩魔法
  • Arduino大师练成手册 -- 读取DHT11
  • idea对jar包内容进行反编译
  • Mellanox ConnectX 系列网卡的双驱动架构:以太网与 InfiniBand 的协同设计
  • 电商网站项目代码
  • 课题推荐——智能算法驱动的数据融合技术
  • 【Matlab高端绘图SCI绘图模板】第001期 绘制帕累托图(Pareto)
  • MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log
  • SQL UNION 和 UNION ALL 区别
  • Linux的权限和一些shell原理