Skip to main content

Django 学习

Django 表单

表单 html 如何获取 request 对象的值?

在 Django 的模板中,一些特定的 request 对象属性可以直接访问,这是因为 Django 通过上下文处理器将它们添加到了每个模板的上下文中。例如,你可以直接在模板中访问用户信息 (request.user) 和会话数据 (request.session)。

设置 request 上下文

默认情况下,request 对象并不包含在每个模板的上下文中。要在所有模板中访问 request,你需要确保 django.template。context_processors.request 上下文处理器在你的 settings.py 文件中的 TEMPLATES 选项里被启用

TEMPLATES = [
{
# ...
'OPTIONS': {
'context_processors': [
# ...
'django.template.context_processors.request',
# ...
],
},
},
]

一般而言,只有用户信息(request.user)和会话数据(request.session)经常在模板中使用。其他的 request 属性(比如请求方法或 GET/POST 参数)通常在视图函数中处理,而不是直接在模板中使用。

如果你的视图处理逻辑依赖于请求的其他属性,考虑在视图函数中处理这些逻辑,并将结果作为上下文传递给模板。这种做法更清晰、更易于维护。

1. 访问用户信息

{% if request.user.is_authenticated %}
<p>Hello, {{ request.user.username }}!</p>
{% else %}
<p>Hello, Guest!</p>
{% endif %}

2. 使用会话数据

<p>Your session data: {{ request.session.your_session_key }}</p>

3. 获取当前的完整路径:

<p>Current path: {{ request.get_full_path }}</p>

Django 模板

在 Django 中,模板继承是一种强大的方式来重用代码并保持模板的一致性和清晰性。下面是一个简单的例子,展示了如何使用模板继承:

基础模板 (父模板)

假设你有一个基础模板,通常称为 base.html。这个文件定义了网站的基本布局和结构。

base.html
<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}My Website{% endblock %}</title>
</head>
<body>
<header>
<h1>Welcome to My Website</h1>
</header>

{% block content %}
<!-- 默认内容 -->
{% endblock %}

<footer>
<p>Copyright &copy; 2021 My Website</p>
</footer>
</body>
</html>

在这个模板中,{% block title %}{% block content %} 是可被子模板覆盖的占位块。

子模版

子模板继承了基础模板,并填充或覆盖了其中的块。例如,你可能有一个名为 about.html 的子模板,它提供了关于页面的特定内容。

about.html
<!-- row 1 -->
{% extends "base.html" %}

<!-- row 2 -->
{% block title %}About Us - My Website{% endblock %}

<!-- row 3 -->
{% block content %}
<h2>About Us</h2>
<p>This is the about page of My Website.</p>
{% endblock %}

Django 中间件

在 Django 中,中间件是一种轻量级的、全局范围内的插件系统,用于修改 Django 的输入或输出。中间件是请求和响应处理过程的一部分,可以在视图执行之前或之后运行代码。

简单的例子

老版本

此版本提供向后兼容性,并易于迁移代码。

# middleware.py
from django.utils.deprecation import MiddlewareMixin

class SimpleMiddleware(MiddlewareMixin):
def process_request(self, request):
# 在视图函数调用之前的代码
print("在视图处理之前的逻辑")

def process_response(self, request, response):
# 在视图函数调用之后的代码
print("在视图处理之后的逻辑")
return response

新版本

def simple_middleware(get_response):
# One-time configuration and initialization.

def middleware(request):
# Code to be executed for each request before
# the view (and later middleware) are called.

response = get_response(request)

# Code to be executed for each request/response after
# the view is called.

return response

return middleware

class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.

def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.

response = self.get_response(request)

# Code to be executed for each request/response after
# the view is called.

return response

注册中间件

# settings.py

MIDDLEWARE = [
# ... 其他中间件 ...
'myapp.middleware.SimpleMiddleware',
]

Django migration

在 Django 中,迁移(migration)是一种强大的功能,用于在模型(models.py)和数据库之间同步更改。迁移使得数据库的更新变得自动化和版本化,有助于维护数据库的一致性和历史记录。迁移是 Django 模型的变更转换为数据库架构更改的方式。

基本概念

  • 迁移文件: 每当你对 Django 模型做出更改(如添加字段、更改字段类型、删除模型等)时,Django 会创建一个新的迁移文件,记录这些更改。这些文件位于每个应用的 migrations 文件夹中。
  • 应用迁移: 将迁移应用于数据库,实际执行迁移文件中的操作,如创建表、修改字段等。
  • 迁移历史: Django 跟踪哪些迁移已经应用到数据库,确保数据库结构与模型保持一致。

常用命令

  • makemigrations: 基于模型的更改创建新的迁移文件。例如,python manage.py makemigrations <app_name> 会生成必要的迁移文件。
  • migrate: 应用未执行的迁移到数据库。例如,python manage.py migrate 会应用所有挂起的迁移。如果想要回到某个特定版本,使用 python manage.py migrate <app_name> <migration_file_name> 指令。
  • sqlmigrate: python manage.py sqlmigrate <app_name> <migration_file_name> 显示迁移的 SQL 语句,有助于理解 Django 如何转换迁移文件为数据库操作。
  • showmigrations: 显示所有 migrations 信息,指令 是 python manage.py showmigrations [<app_name> <migration_file_name>]
  • squashmigrations: 迁移的数量过多,需要合并多个迁移时使用,指令是 python manage.py squashmigrations

使用流程

  • 修改模型: 在你的应用中更改 models.py 文件。
  • 创建迁移文件: 运行 python manage.py makemigrations <app_name> 生成迁移文件。
  • 检查迁移文件: 查看生成的迁移文件,确认更改。
  • 应用迁移: 运行 python manage.py migrate 应用更改到数据库。

示例

假设你有一个名为 Blog 的应用,其中有一个 Post 模型,你想给 Post 添加一个新字段 published_date

  1. 修改模型:在 blog/models.py 中添加字段
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
published_date = models.DateTimeField(null=True) # 新增字段
  1. 创建迁移文件
python manage.py makemigrations blog
  1. 检查迁移文件:在 blog/migrations/ 目录下查看生成的迁移文件。

  2. 应用迁移

python manage.py migrate

注意事项

  • 在多人协作的项目中,正确处理和合并迁移文件是很重要的。
  • 有时候,手动编辑迁移文件可能是必要的,尤其在处理复杂的数据库更改时。
  • 虽然迁移系统强大,但在生产环境中应用迁移前需要谨慎测试,以防止数据丢失或破坏。

Django 的迁移系统为数据库架构的演变提供了一种安全、可控的方式,使得数据库维护变得更加容易和可靠。

Django Test 测试

在 Django 中,编写测试涵盖了单元测试、集成测试和 API 接口测试。这些测试有助于确保你的应用在各种情况下正常工作,并且可以在代码修改后快速识别问题。下面是这些测试类型的简要概述和示例:

1. 单元测试 Unit Tests

单元测试专注于单独测试应用中的最小部分(通常是函数或方法)。在 Django 中,你可以使用 Django 的测试框架编写单元测试,该框架基于 Python 的标准 unittest 模块。

假设你有一个模型 MyModel,你想测试它的一个方法 my_method:

from django.test import TestCase
from myapp.models import MyModel

class MyModelTest(TestCase):
def test_my_method(self):
obj = MyModel()
self.assertEqual(obj.my_method(), 'expected result')

2. 集成测试 Integration Tests

集成测试检查应用中不同部分之间的交互,确保它们协同工作。例如,测试视图是否正确查询模型,并返回期望的 HTTP 响应。

假设你有一个 Django 应用,其中包含一个博客应用,你希望测试视图能否正确地显示博客文章列表。首先,我们定义了模型 Post,然后创建了一个视图 post_list 来显示文章列表,现在我们将对这个视图进行集成测试。

models.py
from django.db import models

class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
views.py
from django.shortcuts import render
from .models import Post

def post_list(request):
posts = Post.objects.all()
return render(request, 'blog/post_list.html', {'posts': posts})
urls.py
from django.urls import path
from .views import post_list

urlpatterns = [
path('posts/', post_list, name='post_list'),
]
集成测试 tests.py
from django.test import TestCase
from django.urls import reverse
from .models import Post

class PostListViewTest(TestCase):
@classmethod
def setUpTestData(cls):
# 创建一些测试文章
number_of_posts = 5
for post_num in range(number_of_posts):
Post.objects.create(title=f'Post {post_num}', content='Content of the post')

def test_view_url_exists_at_desired_location(self):
response = self.client.get('/posts/')
self.assertEqual(response.status_code, 200)

def test_view_url_accessible_by_name(self):
response = self.client.get(reverse('post_list'))
self.assertEqual(response.status_code, 200)

def test_view_uses_correct_template(self):
response = self.client.get(reverse('post_list'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'blog/post_list.html')

def test_pagination_is_five(self):
response = self.client.get(reverse('post_list'))
self.assertTrue('is_paginated' in response.context)
self.assertTrue(response.context['is_paginated'] == True)
self.assertEqual(len(response.context['posts']), 5)

def test_lists_all_posts(self):
# 获取第二页并确认仍有3个文章
response = self.client.get(reverse('post_list')+'?page=2')
self.assertEqual(response.status_code, 200)
self.assertTrue('is_paginated' in response.context)
self.assertTrue(response.context['is_paginated'] == True)
self.assertEqual(len(response.context['posts']), 3)

3. API 接口测试 API Tests

API 测试验证你的应用 API 的行为,确保它们按照预期接收请求和返回响应。

使用 Django 的 Client 类测试 API 端点:

from django.test import TestCase, Client
from myapp.models import MyModel

class MyAPITest(TestCase):
def setUp(self):
self.client = Client()
MyModel.objects.create(name='test')

def test_get_api(self):
response = self.client.get('/api/my-model/')
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(response.content, [{"name": "test"}])