Django与视图
我叫补三补四,很高兴见到大家,欢迎一起学习交流和进步
今天来讲一讲视图
在web应用的MVC结构中,视图一般包含模板和表单,用来给浏览器生成响应。在实际处理过程当中,视图会根据请求的参数从数据源当中找到数据,并生成HTML文本或者XMLHttpRequest
配置URL
Django对于URL没有限制,允许开发者根据需要设计URL,要设计应用程序的URLconf模块,这个模块用于将URL路径表达式映射到Python函数
什么是URL?
在Web开发中,URL(Uniform Resource Locator,统一资源定位符)就是我们常说的网址。它是一个用来标识网络资源的字符串。
什么是URLconf?
URLconf(URL configuration)是Django用来管理URL和视图函数映射的模块。它是一个Python模块,通常是一个文件,比如 urls.py 。在这个文件中,你定义了一系列的URL模式(pattern),告诉Django如何将URL映射到视图函数。
配置顺序:
用户从Django支持的站点请求页面时,执行顺序如下:
1.(在settings.py)确定要使用的根URLCONF模块
- Django加载URLCONF模块,并在其中寻找urlpatterns变量
urlpatterns 是一个 Python 列表,通常定义在 Django 项目的 urls.py 文件中。每个元素都是一个 path() 或 re_path() 函数的调用,这些函数将 URL 模式与视图函数绑定。
- Django按照顺序遍历每一个URL模式,一但找到匹配的模式就停止遍历
- Django调用该模式映射的视图函数
- 如果没有任何匹配URL模式,或者在此过程抛出异常,Django将调用适当的错误处理视图
捕获URL
可以使用正则表达式捕获URL并将其传递给视图:
在 Django 中,URL 的正则表达式(或者更现代的路径参数语法)的主要作用是告诉服务器如何从用户请求的 URL 中提取出需要的参数,并将其传递给对应的视图函数(view)。
具体解释如下:
1. URL 分发:Django 的 URL 配置(`urls.py`)是一个映射关系,它将用户请求的 URL 路径与对应的视图函数关联起来。正则表达式(或路径参数)在这里的作用是帮助 Django 确定用户请求的 URL 是否匹配某个特定的模式。
2. 参数提取:正则表达式不仅可以匹配 URL,还可以捕获其中的动态部分(即参数)。这些参数会被提取出来,并作为参数传递给视图函数。例如:
from django.urls import path
from . import views
urlpatterns = [
path('articles/<int:year>/', views.year_archive),
]
在这个例子中,`<int:year>`是一个路径参数,它告诉 Django 从 URL 中提取一个整数类型的参数`year`,并将其传递给`year_archive`视图函数。如果用户请求的 URL 是`/articles/2023/`,那么`year`的值会被提取为`2023`,并作为参数传递给视图函数。
3. 正则表达式 vs 路径参数:在 Django 2.x 及更高版本中,推荐使用路径参数(`path`和`re_path`)而不是传统的正则表达式。路径参数更简洁易读,而正则表达式则更灵活,适合复杂的 URL 模式匹配。
• `path`:用于简单的路径参数匹配,如上面的例子。
• `re_path`:用于复杂的正则表达式匹配。例如:
from django.urls import re_path
urlpatterns = [
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
]
在这个例子中,`(?P<year>[0-9]{4})`是一个命名捕获组,它会匹配 URL 中的四位数字,并将其命名为`year`参数。
总之,Django 的 URL 配置通过正则表达式或路径参数,帮助服务器解析用户请求的 URL,提取出需要的参数,并将请求转发到对应的视图函数。
呃..这个正则表达式笔者也不是很会用啊特别是现在有AI我觉得直接在视图函数里面写校验也挺方便的(手动狗头)【一看就懂:正则表达式 - CSDN App】https://blog.csdn.net/sunnyzyq/article/details/122840555?sharetype=blog&shareId=122840555&sharerefer=APP&sharesource=2401_88885149&sharefrom=link
配置嵌套
urlpatterns可以包含其他URLconf模块,模块和模块之间构成层级关系(就是在定义了路由的基础路径以后再进一步定义路由的子路径)
from django.urls import include, path
urlpatterns = [
path('product/', include('sales.product.urls')), # 商品
path('customer/', include('sales.customer.urls')), # 顾客
]
需要在应用层创建新的url.py文件
Django 中,您可以为每个应用创建一个单独的 urls.py 文件来定义该应用的 URL 路由。这样做可以让您更好地组织和管理项目的 URL 配置。
项目结构示例如下:
myproject/
│
├── manage.py
│
└── myproject/
│
├── settings.py
├── urls.py
│
└── applications/
├── products/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations/
│ ├── models.py
│ ├── tests.py
│ ├── views.py
│ └── urls.py # 这里就是新的 urls.py 文件
│
└── customers/
├── __init__.py
├── admin.py
├── apps.py
├── migrations/
├── models.py
├── tests.py
├── views.py
└── urls.py # 另一个应用的 urls.py 文件
配置嵌套有利于URL的管理,删除冗余
反向解析URL
反向解析就是根据视图函数名或URL模式名称动态生成URL,而不是硬编码URL路径,这样能够增强代码可维护性和灵活性同时也支持了模板和视图中的动态链接
在Django当中,提供了reverse函数来实现该功能,可以是在模板文件中进行如下修改:
{% url '别名' %}
{% url '别名','参数值1’, 参数值2’ %}
eg:
{% url 'pagen' '400' %}
{% url 'person' 'age='18' name='gxn' %}
也就是通过url标签实现地址的反向解析
也可以在视图中调用reserve实现反向解析:
from django.urls import reverse
reverse('别名',args=[],_kwargs={})
ex:
print(reverse('pagen',args[300]))
print(reverse('person',kwargs={'name':'xixi','age':18}))
工作原理大概就是它读取了我们写入的别名,然后回到urls.py找对应的路径,这样我们每次修改都只需要改urls.py文件就好了
视图函数
用来处理web请求并返回相应体的python函数,按照惯例一般放在views.py文件下(可以放在任何能被python解释器读取到的地方)
如下代码:
# -*- coding: UTF-8 -*-
from django.http import HttpResponse # 引入响应类
def hello(request): # 打招呼的视图函数
html = "<html><body>高跟鞋之家欢迎您</body></html>" # 文本
return HttpResponse(html) # 返回响应
其中UTF-8是在后文中有中文的情况下,要求声明文件的编码
引入HttpResponse用来生成响应体
定义一个名为hello的视图函数,每一个视图函数都会接受一个请求体来作为第一个参数
最后会返回一个响应体
实现对Django默认错误请求的覆盖
第一步:创建错误模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Page Not Found</title>
</head>
<body>
<h1>404 - Page Not Found</h1>
<p>Sorry, the page you requested does not exist.</p>
</body>
</html>
(页面名:404.html)
第二步:添加URLconf
from django.shortcuts import render
# 自定义错误处理视图
def custom_404_view(request, exception):
return render(request, '404.html', status=404)
def custom_500_view(request):
return render(request, '500.html', status=500)
# 将自定义视图映射到错误处理变量
handler404 = custom_404_view
handler500 = custom_500_view
请求和响应对象
当一个页面被请求时,Django会创建一个包含请求元数据的HttpRequest对象,然后将这个对象作为第一个参数传给视图函数
请求对象属性有:
HttpRequest.method
HttpRequest.META
HttpRequest.COOKIES
响应对象的常见属性有:
HttpResponse.content
HttpResponse.status_code
模板响应对象
HttpResponse对象可以反应已经确定的内容,但是需要在每次返回相应之前就确定返回的内容,内容固定,适合用于简单的静态场景,但对于复杂的动态场景,我们可以使用TemplateResponse对象
from django.template.response import TemplateResponse # 引入模板响应类
from .models import Entry # 假设 Entry 是当前 app 的模型
def blog_index(request):
return TemplateResponse(
request, 'entry_list.html', {'entries': Entry.objects.all()}
) # 传入模板和上下文
TemplateResponse会保留视图模板和上下文信息,实际的渲染只有在需要时才会发生
什么是渲染?
渲染就是把模板和数据结合起来,生成最终的 HTML 页面(或其他格式的内容),然后发送给浏览器显示。
渲染的时机
在 Django 中,`TemplateResponse`对象在以下三种情况下会被渲染:
1. 调用`SimpleTemplateResponse.render()`方法时:
• 这是显式地告诉 Django 现在开始渲染模板。
2. `response.content`被显式赋值时:
• 这通常发生在你需要手动设置响应内容的情况下。
3. 模板响应中间件之后,但在响应中间件之前:
• 这是 Django 的中间件机制,可以在发送响应之前对响应进行处理。
渲染的限制
• 只能渲染一次:一个`TemplateResponse`对象只能被渲染一次。第一次调用`SimpleTemplateResponse.render()`方法后,响应的内容就被确定了,再次调用这个方法不会改变响应内容。
示例代码解释
假设你有以下代码:
from django.template.response import TemplateResponse
def my_view(request):
response = TemplateResponse(request, 'my_template.html', {'key': 'value'})
response.render() # 显式调用 render 方法进行渲染
return response
在这个例子中,`TemplateResponse`对象在`render()`方法被调用时被渲染。渲染后,响应的内容就被确定了。
如果你尝试再次调用`render()`方法,它不会改变响应内容:
response.render() # 第一次渲染
response.render() # 第二次渲染,不会改变内容
但是,如果你显式地修改`response.content`,响应的内容会发生变化:
response.render() # 第一次渲染
response.content = b'New content' # 显式修改内容
在这个例子中,`response.content`被显式地赋值为一个新的字节串,这会覆盖之前的渲染结果。
总结
• `TemplateResponse`对象在特定时机会被渲染,生成最终的响应内容。
• 一个`TemplateResponse`对象只能被渲染一次,第一次渲染后内容就确定了。
• 显式修改`response.content`可以改变响应内容,即使已经渲染过。
视图类
在Django当中,视图也可以用类来表示,这些类称为视图类,使用视图类有利于代码的重用,提高了开发效率
同时它也提供了一些自带的视图类,可以为自定义的视图类做参考
例如TemplateView,其使用方法如下:
在 Django 中使用视图类时,可以直接使用已有的模板,并在此基础上进行改进。这是 Django 模板系统的一个强大功能,它允许重用模板代码并根据需要进行定制。
基于已有模板进行改进:
1.使用已有模板
假设有一个名为`base.html`的基础模板,可以在其他模板中继承它,并添加或修改内容。
#base.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}My Website{% endblock %}</title>
</head>
<body>
{% block content %}
<!-- Default content goes here -->
{% endblock %}
</body>
</html>
2.继承并改进模板
可以创建一个新的模板,继承`base.html`并添加或修改内容。
#about.html:
{% extends 'base.html' %}
{% block title %}About Us{% endblock %}
{% block content %}
<h1>About Us</h1>
<p>This is the about page of our website.</p>
{% endblock %}
在这个示例中,`about.html`模板继承了`base.html`,并覆盖了`title`和`content`块。
3.在视图类中使用模板
在视图类中,可以指定要使用的模板名称,Django 会自动渲染该模板并返回响应。
#views.py:
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = 'about.html'
在这个示例中,`AboutView`类指定了`about.html`作为模板名称。当访问相应的 URL 时,Django 会渲染`about.html`模板并返回响应。
4.传递上下文数据
还可以在视图类中向模板传递上下文数据,以便在模板中使用。
#views.py:
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = 'about.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['extra_info'] = 'This is some extra information.'
return context
#about.html:
{% extends 'base.html' %}
{% block title %}About Us{% endblock %}
{% block content %}
<h1>About Us</h1>
<p>This is the about page of our website.</p>
<p>{{ extra_info }}</p>
{% endblock %}
在这个示例中,`AboutView`类向模板传递了一个额外的上下文变量`extra_info`,该变量可以在`about.html`模板中使用。
总结
通过继承和覆盖已有模板,可以重用模板代码并根据需要进行定制。在视图类中,可以指定要使用的模板名称,并可以向模板传递上下文数据。这使得我们能够灵活地使用和改进模板,以满足不同的业务需求。
要注意:改为视图类之后,urlconf当中应该使用的是as_view()方法
基于函数的视图:
# urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^about/$', views.about, name='about'),
]
# views.py
from django.http import HttpResponse
def about(request):
return HttpResponse("This is the about page.")
改写为基于类的视图:
# urls.py
# 注意:这里没有变化
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^about/$', views.AboutView.as_view(), name='about'),
]
# views.py
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = 'about.html'
在这个例子中,我们将 about 视图从函数改写为了类,但是我们没有更改 urlconf 。我们只是将视图函数替换为了视图类,并且在 urlconf 中使用了 as_view() 方法来将视图类转换为可以被 Django 调用的视图函数。
文件上传
当用户在一个Django网站上上传文件时,上传的数据会放在request.FILES对象当中,该对象与字典对象类似,键是上传的文件名,值是UploadedFile对象
在Django当中,表单可以支持文件上传(在应用或者项目的根目录创建一个forms.py)
步骤
1.创建表单类
在 Django 中,文件上传需要使用`forms.FileField`或`forms.ImageField`(如果上传的是图片)字段。首先,你需要定义一个表单类,例如:
from django import forms
class UploadFileForm(forms.Form):
file = forms.FileField(label="选择文件", help_text="请上传文件")
如果需要上传图片,可以使用`forms.ImageField`:
from django import forms
class UploadImageForm(forms.Form):
image = forms.ImageField(label="选择图片", help_text="请上传图片")
2.修改视图
在视图中处理文件上传时,需要确保请求方法是`POST`,并且请求的`enctype`是`multipart/form-data`。这是因为文件上传需要通过表单的`enctype`属性来支持二进制数据的传输。
示例视图代码:
from django.shortcuts import render
from django.http import HttpResponse
from .forms import UploadFileForm
def upload_file(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES) # 注意 request.FILES
if form.is_valid():
# 获取上传的文件
file = request.FILES['file']
# 处理文件,例如保存到服务器
with open(f'media/{file.name}', 'wb+') as destination:
for chunk in file.chunks():
destination.write(chunk)
return HttpResponse("文件上传成功!")
else:
form = UploadFileForm()
return render(request, 'upload.html', {'form': form})
3.修改模板
在模板中,需要确保表单的`enctype`属性设置为`multipart/form-data`,以便支持文件上传。
示例模板代码(`upload.html`):
<!DOCTYPE html>
<html>
<head>
<title>文件上传</title>
</head>
<body>
<h1>上传文件</h1>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">上传</button>
</form>
</body>
</html>
4.配置文件存储
Django 默认会将上传的文件存储在`MEDIA_ROOT`指定的目录中。你可以在项目的`settings.py`文件中配置`MEDIA_ROOT`和`MEDIA_URL`:
# settings.py
# 文件存储目录
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 文件访问 URL
MEDIA_URL = '/media/'
同时,需要在项目的`urls.py`中添加媒体文件的路由,以便可以通过 URL 访问上传的文件:
# urls.py
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
# 其他路由配置
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
5.保存文件到模型
如果你需要将文件保存到数据库模型中,可以使用`models.FileField`或`models.ImageField`字段。例如:
# models.py
from django.db import models
class FileModel(models.Model):
file = models.FileField(upload_to='files/') # 指定文件存储目录
在视图中保存文件到模型:
from .models import FileModel
def upload_file(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
file_instance = FileModel(file=request.FILES['file'])
file_instance.save()
return HttpResponse("文件上传成功!")
else:
form = UploadFileForm()
return render(request, 'upload.html', {'form': form})
总结
通过以上步骤,你可以实现 Django 表单支持文件上传的功能。关键点包括:
1. 使用`forms.FileField`或`forms.ImageField`定义表单字段。
2. 在视图中处理`request.FILES`。
3. 确保模板表单的`enctype`属性为`multipart/form-data`。
- 配置文件存储路径和 URL。
注意:为了数据库迁移时能正确迁移,不要把校验表单用的forms.py的类和models.py当中的模型类混在一起,以免影响数据库的迁移操作
{{{1.`models.py`的职责
`models.py`是用来定义 Django 模型的,这些模型是与数据库交互的核心组件。每个模型类对应数据库中的一张表,模型字段定义了表的结构(即数据库中的列)。`models.py`的主要职责包括:
• 定义数据库结构:通过模型字段(如`CharField`、`IntegerField`、`ForeignKey`等)定义表的结构。
• 数据存储和检索:模型提供了与数据库交互的接口,用于保存、查询、更新和删除数据。
• 数据关系:定义模型之间的关系(如一对多、多对多等)。
示例:
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
published_date = models.DateField()
price = models.DecimalField(max_digits=6, decimal_places=2)
def __str__(self):
return self.title
在这个例子中,`Book`模型定义了一张表,包含`title`、`author`、`published_date`和`price`字段。
---
2.`forms.py`的职责
`forms.py`是用来定义表单的,表单的主要职责是处理用户输入的数据。表单可以独立于模型存在,也可以与模型关联(通过`ModelForm`)。`forms.py`的主要职责包括:
• 定义表单字段:表单字段定义了用户可以在表单中输入哪些数据,以及这些数据的格式和验证规则。
• 数据验证:表单负责验证用户输入的数据是否符合预期格式。
• 数据清洗:将用户输入的数据转换为适合进一步处理的格式。
• 与模型交互:如果表单与模型关联(如`ModelForm`),表单可以自动将数据保存到模型。
示例:
# forms.py
from django import forms
from .models import Book
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = ['title', 'author', 'published_date', 'price']
在这个例子中,`BookForm`是一个`ModelForm`,它基于`Book`模型自动生成表单字段。
---
3.为什么不能将表单逻辑写在`models.py`中?
职责分离
• 关注点分离:`models.py`的职责是定义数据模型和数据库结构,而`forms.py`的职责是处理用户输入和表单逻辑。将它们分开可以避免代码的混乱,使每个文件只关注自己的职责。
• 可维护性:将表单逻辑写在`forms.py`中,可以让代码更加清晰和易于维护。如果需要修改表单的验证逻辑或字段,你只需要修改`forms.py`,而不需要修改`models.py`。
独立性
• 表单的独立性:表单可以独立于模型存在。例如,你可能需要一个表单用于用户注册,但注册表单并不直接对应一个模型。将表单逻辑写在`forms.py`中,可以让表单独立于模型,避免不必要的耦合。
• 灵活性:即使表单与模型关联,`forms.py`也提供了额外的灵活性。例如,你可以通过`ModelForm`自定义字段的显示方式、验证规则或排除某些字段,而不需要修改模型。
代码组织
• 清晰的项目结构:Django 的最佳实践是将代码组织到不同的文件中,每个文件负责不同的功能。`models.py`用于定义模型,`forms.py`用于定义表单,`views.py`用于定义视图,`urls.py`用于定义路由。这种结构可以让项目更加清晰,便于团队协作和代码管理。
---
4.总结
• `models.py`:定义数据库模型和结构,负责数据的存储和检索。
• `forms.py`:定义表单字段和验证逻辑,负责处理用户输入的数据。
将表单逻辑写在`forms.py`中,而不是`models.py`中,是为了保持代码的清晰性、可维护性和职责分离。这种分工是 Django 设计哲学的一部分,有助于构建更加清晰和可扩展的项目。}}}
文件存储
默认情况下,Django会在本地保存文件,文件目录通过MEDIA_ROOT和MEDIA_URL设置
在操作文件时候,Django会用到django.core.files.File对象。
在 Django 中,文件存储和数据库存储是两个不同的概念,它们的作用和机制也有所不同。文件存储主要是用来处理文件的上传、保存和访问,而数据库存储则是用来保存结构化数据(如模型实例)。
{{{文件存储≠数据库存储
1. 文件存储:
• 文件存储的目的是将文件(如图片、文档等)保存到某个存储系统中(本地文件系统、云存储等)。
• Django 默认将文件保存到本地文件系统(通过`MEDIA_ROOT`指定路径),也可以通过配置将文件保存到云存储服务中(如 Amazon S3)。
• 文件本身不会直接存储到数据库中,但文件的元数据(如文件名、路径、大小等)可以保存在数据库中,以便后续访问和管理。
2. 数据库存储:
• 数据库用于存储结构化数据,例如用户信息、文章内容等。
• Django 使用 ORM(对象关系映射)将模型(`models.Model`)映射到数据库表中。
• 数据库存储的是文本或数值类型的数据,而不是文件的二进制内容。
文件存储与数据库的结合
虽然文件本身不会存储到数据库中,但文件的元数据(如文件名、路径、大小等)通常会存储在数据库中。例如,当你使用 Django 的`FileField`或`ImageField`时:
• 文件会被保存到指定的存储系统(如本地文件系统或云存储)。
• 文件的路径(如`file.name`)会被保存到数据库中对应的字段中。
示例
假设你有一个模型`Profile`,其中包含一个`avatar`字段:
from django.db import models
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to='avatars/')
• 当你上传一个头像文件时:
• 文件本身会被保存到`MEDIA_ROOT/avatars/`目录下(或配置的存储系统中)。
• 数据库中会保存文件的路径(如`avatars/example.jpg`)。
• 通过访问数据库中的路径,你可以找到对应的文件。
为什么文件不直接存储到数据库?
1. 性能问题:将文件存储到数据库中会增加数据库的负担,尤其是对于大文件。
2. 存储效率:文件系统和云存储服务更适合存储和管理大文件。
- 可扩展性:将文件存储到云服务(如 S3)可以轻松扩展,而数据库存储文件则难以扩展。}}}
简单来说,文件存储的数据会被保存到服务器本身的本地文件系统
这是默认的,也可以选择云系统服务等等方式进行存储,具体使用方法:
在 Django 中引入文件存储系统(例如本地文件存储、云存储等)是一个常见的需求,用于存储用户上传的文件(如图片、文档等)。以下是实现这一功能的全过程:
1\.定义需求
首先明确你的需求:
• 是否需要支持本地存储?
• 是否需要支持云存储(如 AWS S3、阿里云 OSS、腾讯云 COS 等)?
• 文件存储的路径如何组织?
• 是否需要对文件进行访问控制?
2\.安装必要的依赖
根据你的存储需求,安装相关的库。例如:
• 本地存储:Django 默认支持本地文件存储,无需额外安装。
• 云存储:
• 如果使用AWS S3,需要安装`django-storages`和`boto3`:
pip install django-storages[boto3]
• 如果使用阿里云 OSS,需要安装`django-storages`和`oss2`:
pip install django-storages oss2
• 如果使用腾讯云 COS,需要安装`django-storages`和`cos-python-sdk-v5`:
pip install django-storages cos-python-sdk-v5
3\.配置存储后端
在项目的`settings.py`文件中,配置文件存储的后端。
3.1 配置本地存储
默认情况下,Django 使用本地文件系统存储文件。你只需要设置`MEDIA_ROOT`和`MEDIA_URL`:
# settings.py
# 文件存储的本地路径
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 文件访问的 URL 前缀
MEDIA_URL = '/media/'
3.2 配置云存储
如果使用云存储,需要安装并配置`django-storages`:
1. AWS S3:
# settings.py
# 安装 django-storages
INSTALLED_APPS = [
...
'storages',
...
]
# 配置文件存储后端为 S3
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
# S3 相关配置
AWS_ACCESS_KEY_ID = 'your-access-key-id'
AWS_SECRET_ACCESS_KEY = 'your-secret-access-key'
AWS_STORAGE_BUCKET_NAME = 'your-bucket-name'
AWS_S3_REGION_NAME = 'your-region'
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'
AWS_DEFAULT_ACL = 'public-read' # 根据需求设置文件的访问权限
MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/'
2. 阿里云 OSS:
# settings.py
INSTALLED_APPS = [
...
'storages',
...
]
DEFAULT_FILE_STORAGE = 'storages.backends.oss.OSSStorage'
# 阿里云 OSS 配置
OSS_ACCESS_KEY_ID = 'your-access-key-id'
OSS_ACCESS_KEY_SECRET = 'your-access-key-secret'
OSS_ENDPOINT = 'oss-cn-hangzhou.aliyuncs.com' # 替换为你的区域
OSS_BUCKET_NAME = 'your-bucket-name'
MEDIA_URL = f'https://{OSS_BUCKET_NAME}.{OSS_ENDPOINT}/'
3. 腾讯云 COS:
# settings.py
INSTALLED_APPS = [
...
'storages',
...
]
DEFAULT_FILE_STORAGE = 'storages.backends.cos.COSStorage'
# 腾讯云 COS 配置
COS_SECRET_ID = 'your-secret-id'
COS_SECRET_KEY = 'your-secret-key'
COS_REGION = 'ap-guangzhou' # 替换为你的区域
COS_BUCKET_NAME = 'your-bucket-name'
MEDIA_URL = f'https://{COS_BUCKET_NAME}.cos.{COS_REGION}.myqcloud.com/'
4\.在模型中使用文件字段
在你的 Django 模型中,使用`FileField`或`ImageField`来定义文件字段。
# models.py
from django.db import models
class MyModel(models.Model):
title = models.CharField(max_length=255)
file = models.FileField(upload_to='uploads/') # 文件存储路径
image = models.ImageField(upload_to='images/') # 图片存储路径
def __str__(self):
return self.title
• `upload_to`参数用于指定文件存储的子目录。例如,`uploads/`表示文件存储在`MEDIA_ROOT/uploads/`或云存储的对应路径下。
5\.处理文件上传
在视图中处理文件上传逻辑,通常使用表单来接收用户上传的文件。
5.1 创建表单
# forms.py
from django import forms
from .models import MyModel
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['title', 'file', 'image']
5.2 视图处理文件上传
# views.py
from django.shortcuts import render, redirect
from .forms import MyModelForm
def upload_file(request):
if request.method == 'POST':
form = MyModelForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('success_view') # 跳转到成功页面
else:
form = MyModelForm()
return render(request, 'upload.html', {'form': form})
5.3 模板文件
创建一个简单的模板文件来上传文件:
<!-- templates/upload.html -->
<!DOCTYPE html>
<html>
<head>
<title>Upload File</title>
</head>
<body>
<h1>Upload File</h1>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload</button>
</form>
</body>
</html>
6\.配置 URL
在项目的`urls.py`文件中,添加上传文件的 URL 路由:
# urls.py
from django.urls import path
from .views import upload_file
urlpatterns = [
path('upload/', upload_file, name='upload_file'),
# 其他路由...
]
7\.配置静态文件和媒体文件的访问
在开发环境中,Django 可以通过`django.views.static.serve`来提供媒体文件的访问。在生产环境中,通常需要配置 Web 服务器(如 Nginx)来提供静态文件和媒体文件的服务。
7.1 开发环境
在`urls.py`中添加以下配置:
# urls.py
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('upload/', upload_file, name='upload_file'),
# 其他路由...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
7.2 生产环境
在生产环境中,使用 Web 服务器(如 Nginx)来提供媒体文件的访问。例如,Nginx 配置如下:
server {
location /media/ {
alias /path/to/your/media/;
}
}
8\.测试
启动 Django 开发服务器,访问上传页面,上传文件并验证文件是否正确存储到指定路径(本地或云存储)
生成文件
生成csv文件:
在 Django 中,可以通过 Python 的内置`csv`模块来生成 CSV 文件内容。以下是一个简单的示例,展示如何在 Django 视图中生成 CSV 数据并将其作为响应返回给用户。
示例代码
import csv
from django.http import HttpResponse
def export_csv(request):
# 创建 HTTP 响应对象,指定内容类型为 CSV 文件
response = HttpResponse(content_type='text/csv')
# 设置文件名(用户下载时的文件名)
response['Content-Disposition'] = 'attachment; filename="exported_data.csv"'
# 创建 CSV 写入器
writer = csv.writer(response)
# 写入 CSV 文件的表头
writer.writerow(['ID', 'Name', 'Email', 'Age'])
# 假设这里是从数据库中获取数据
data = [
[1, 'Alice', 'alice@example.com', 25],
[2, 'Bob', 'bob@example.com', 30],
[3, 'Charlie', 'charlie@example.com', 35],
]
# 写入数据行
for row in data:
writer.writerow(row)
# 返回响应
return response
代码说明
1. `HttpResponse`对象:
• 创建一个`HttpResponse`对象,并设置`content_type`为`text/csv`,告诉浏览器这是一个 CSV 文件。
• 使用`Content-Disposition`设置文件名,用户下载时会看到这个文件名。
2. `csv.writer`:
• 使用 Python 的`csv.writer`来写入 CSV 数据。
• `writer.writerow()`方法用于写入单行数据。
3. 数据来源:
• 在实际应用中,数据通常是从数据库中查询得到的。例如,你可以使用 Django 的 ORM 查询数据并传递给`writer.writerow()`。
4. 返回响应:
• 将生成的 CSV 数据作为 HTTP 响应返回给用户,用户会看到一个下载提示。
示例扩展:从数据库中获取数据
如果你需要从数据库中动态获取数据,可以结合 Django 的 ORM。例如,假设你有一个`User`模型:
from django.http import HttpResponse
import csv
from .models import User
def export_users_csv(request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="users.csv"'
writer = csv.writer(response)
writer.writerow(['ID', 'Name', 'Email', 'Age'])
# 查询数据库中的用户数据
users = User.objects.all().values_list('id', 'name', 'email', 'age')
# 写入数据
for user in users:
writer.writerow(user)
return response
注意事项
• 如果数据包含非 ASCII 字符(如中文),可能需要指定编码。例如:
response = HttpResponse(content_type='text/csv; charset=utf-8')
• 如果数据量很大,可以考虑使用流式响应(`StreamingHttpResponse`)来避免内存占用过高。
生成pdf文件:
ReportLab是生成 PDF 文件的一种非常灵活且强大的方式。ReportLab 是一个纯 Python 的库,可以直接操作 PDF 文件,适合生成简单的报表、文档等。以下是一个详细的步骤指南,帮助你在 Django 中使用 ReportLab 生成 PDF 文件
---
以下是一个示例代码,展示如何在 Django 中使用 ReportLab 生成一个简单的 PDF 文件,并将其作为 HTTP 响应返回给用户。
示例代码
import io
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch
from django.http import FileResponse
def generate_pdf(request):
# 创建一个字节流缓冲区,用于存储生成的 PDF 文件
buffer = io.BytesIO()
# 创建一个 PDF 对象,指定页面大小(例如 letter)
pdf = canvas.Canvas(buffer, pagesize=letter)
# 获取页面宽度和高度
width, height = letter
# 在 PDF 上绘制内容
pdf.setFont("Helvetica", 12) # 设置字体和字号
pdf.drawString(1 * inch, height - 1 * inch, "Hello, this is a PDF generated by ReportLab!") # 在指定位置绘制文本
# 添加更多内容
pdf.drawString(1 * inch, height - 2 * inch, "This is a simple example.")
# 添加表格(示例)
data = [
["Name", "Age", "City"],
["Alice", "25", "New York"],
["Bob", "30", "Los Angeles"],
["Charlie", "35", "Chicago"]
]
table_x = 1 * inch
table_y = height - 3 * inch
table_width = 400
table_height = 200
from reportlab.platypus import Table, TableStyle
from reportlab.lib import colors
table = Table(data)
table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
('BACKGROUND', (0, 1), (-1, -1), colors.beige),
('GRID', (0, 0), (-1, -1), 1, colors.black)
]))
table.wrapOn(pdf, table_width, table_height)
table.drawOn(pdf, table_x, table_y)
# 保存 PDF 文件
pdf.save()
# 将缓冲区内容返回为 HTTP 响应
buffer.seek(0)
return FileResponse(buffer, as_attachment=True, filename="example.pdf")
3.代码说明
1. 字节流缓冲区:
• 使用`io.BytesIO()`创建一个字节流缓冲区,用于存储生成的 PDF 文件。这样可以避免将文件保存到磁盘,直接通过 HTTP 响应返回给用户。
2. PDF 对象:
• 使用`canvas.Canvas()`创建一个 PDF 对象,并指定页面大小(例如`letter`,即 8.5x11 英寸)。
3. 绘制内容:
• 使用`pdf.drawString()`方法在 PDF 上绘制文本。
• 使用`reportlab.platypus.Table`创建表格,并通过`TableStyle`设置表格样式。
4. 保存和返回:
• 使用`pdf.save()`保存 PDF 文件到缓冲区。
• 使用`FileResponse`将缓冲区内容作为 HTTP 响应返回给用户,并设置文件名为`example.pdf`。
4.添加到 Django URL
将上述视图函数添加到你的 Django 项目的`urls.py`文件中,以便可以通过 URL 访问它。
示例
# urls.py
from django.urls import path
from .views import generate_pdf
urlpatterns = [
path('generate-pdf/', generate_pdf, name='generate_pdf'),
]
5.扩展功能
ReportLab 提供了丰富的功能,可以生成复杂的 PDF 文件,包括:
• 图形和图表
• 多页文档
• 自定义字体和样式
• 图像插入
你可以根据需要进一步扩展上述代码,以满足复杂的需求。
生成excel文件:
使用 xlsxwriter 生成 Excel 文件 xlsxwriter 是一个非常流行的库,专门用于生成 Excel 文件,支持丰富的功能,如格式化、图表等。
以下是一个简单的示例,展示如何使用 xlsxwriter 在 Django 中生成一个 Excel 文件并返回给用户:
python import io
import xlsxwriter
from django.http import FileResponse
def generate_excel(request):
# 创建一个字节流缓冲区
buffer = io.BytesIO()
# 创建一个 Excel 工作簿
workbook = xlsxwriter.Workbook(buffer)
worksheet = workbook.add_worksheet("Sheet1") # 添加一个工作表
# 添加标题
worksheet.write("A1", "Name")
worksheet.write("B1", "Age")
worksheet.write("C1", "City")
# 添加数据
data = [
["Alice", 25, "New York"],
["Bob", 30, "Los Angeles"],
["Charlie", 35, "Chicago"]
]
for row_num, row_data in enumerate(data, start=1):
for col_num, value in enumerate(row_data):
worksheet.write(row_num, col_num, value)
# 关闭工作簿
workbook.close()
# 将缓冲区内容返回为 HTTP 响应
buffer.seek(0)
return FileResponse(buffer, as_attachment=True, filename="example.xlsx")
代码说明
1. 字节流缓冲区:使用 io.BytesIO() 创建一个字节流缓冲区,用于存储生成的 Excel 文件。
2. 创建工作簿和工作表:
• 使用 xlsxwriter.Workbook(buffer) 创建一个工作簿。
• 使用 workbook.add_worksheet() 添加一个工作表。
3. 写入数据:
• 使用 worksheet.write() 方法写入单元格数据。
• row_num 和 col_num 分别表示行号和列号(从 0 开始)。
4. 关闭工作簿:调用 workbook.close() 保存文件。
5. 返回文件:使用 FileResponse 将缓冲区内容作为 HTTP 响应返回给用户。
中间件
相当于一个插件,可以实现全局的输入输出和通信,使得开发人员可以更专注于实现业务逻辑——在调用阶段,调用视图之前Django会按照MIDDLEWARE_CLASSES定义的自上而下顺序使用中间件
中间件和钩子函数
1.中间件(Middleware)
中间件是一种在请求处理过程中插入的代码,用于在请求到达目标处理函数(如视图函数)之前或之后执行一些通用逻辑。它通常用于处理跨多个请求的公共任务,如身份验证、日志记录、请求修改等。
2.钩子函数(Hook Function)
钩子函数是一种在特定事件或操作发生前后插入自定义代码的技术。它允许开发者在不修改现有代码的情况下扩展功能。在Web开发中,钩子函数常用于框架中,例如Django和Flask的中间件机制。
3.中间件中的钩子函数
在Web框架中,中间件通常会定义多个钩子函数,用于在请求处理的不同阶段执行逻辑。例如:
• Django中间件的钩子函数:
• `process_request(request)`:在视图函数之前被调用。
• `process_view(request, view_func, view_args, view_kwargs)`:在调用视图函数之前被调用。
• `process_response(request, response)`:在响应返回浏览器之前被调用。
• `process_exception(request, exception)`:当视图函数抛出异常时被调用。
• `process_template_response(request, response)`:在视图函数返回`TemplateResponse`对象后被调用。
• Flask中间件的钩子函数:
• `before_request()`:在每次请求之前执行。
• `after_request(response)`:在每次请求之后执行。
• `teardown_appcontext()`:在应用上下文被移除时执行。
4.中间件和钩子函数的应用
中间件和钩子函数在实际开发中被广泛应用,例如:
• 身份验证和权限校验:通过中间件在请求到达视图函数之前验证用户身份。
• 日志记录:记录请求的详细信息,如请求路径、时间戳等。
• 性能监控:统计请求的处理时间。
• 数据预处理和后处理:在请求进入视图函数之前或响应返回之前修改数据。
通过中间件和钩子函数,开发者可以在不修改核心代码的情况下,灵活地扩展功能,增强代码的可维护性和重用性。
每一个中间件都是一种python类,定义了以下一种或者多种方法
常见中间件
process_request(request)
`process_request(request)`是 Django 中间件中的一个方法,主要用于在请求到达视图函数之前对请求进行预处理。它的具体用途包括:
1. 请求预处理:在请求到达视图函数之前,对请求进行一些全局性的处理。例如,可以检查请求的来源、解析请求参数、设置请求的上下文等。
2. 身份验证和权限检查:在请求到达视图之前,验证用户是否已经登录或是否具有访问权限。如果用户未通过验证,可以直接返回一个`HttpResponse`对象,阻止请求继续执行。
3. 日志记录:记录请求的相关信息,如请求的 URL、请求方法、请求时间等,方便后续的调试和分析。
4. 修改请求对象:可以对`HttpRequest`对象进行修改,例如添加或修改请求头、设置请求的属性等。
5. 全局限制功能:可以实现一些全局的限制功能,例如限制某些 IP 地址的访问、设置请求频率限制等。
6. 返回响应:如果`process_request`方法返回一个`HttpResponse`对象,则后续的中间件和视图函数将不再执行,直接将该响应返回给客户端。
`process_request(request)`的执行顺序是按照`MIDDLEWARE`配置中中间件的顺序从上到下依次执行的。
process_view(request, view_func, view_args, view_kwargs):
`process_view(request, view_func, view_args, view_kwargs)`是 Django 中间件中的另一个方法,它在请求到达视图函数之前被调用,但与`process_request`不同的是,它提供了更多关于视图的信息。它的主要用途和功能如下:
1.用途
`process_view`是在请求被路由到具体的视图函数之前被调用的。它允许中间件在视图函数执行之前对请求进行进一步的处理,或者根据视图函数的特性执行一些特定的操作。
2.参数说明
`process_view`方法接收以下参数:
• `request`:当前的`HttpRequest`对象,包含了请求的所有信息。
• `view_func`:即将被调用的视图函数(或类视图)。
• `view_args`:即将传递给视图函数的位置参数(`args`)。
• `view_kwargs`:即将传递给视图函数的关键字参数(`kwargs`)。
这些参数让中间件能够根据视图函数的特性(如视图名称、参数等)做出更灵活的处理。
3.主要功能
以下是`process_view`的一些常见用途:
(1)视图级别的权限控制
在视图函数执行之前,根据视图函数的名称或路径,检查用户是否有权限访问该视图。如果用户没有权限,可以直接返回一个`HttpResponse`对象(例如 403 Forbidden 或 401 Unauthorized),阻止请求继续执行。
(2)视图级别的日志记录
记录即将被调用的视图函数的名称、参数等信息,方便调试和监控。
(3)修改视图函数的参数
在视图函数执行之前,可以修改`view_args`或`view_kwargs`,为视图函数添加额外的参数或修改现有的参数。
(4)视图预处理
根据视图函数的特性,执行一些预处理操作。例如,某些视图可能需要特定的上下文或配置,可以在`process_view`中提前设置。
(5)跳过某些视图的处理
如果某些视图不需要执行中间件的某些逻辑,可以在`process_view`中根据视图函数的名称或路径跳过处理。
4.返回值
• 如果`process_view`返回`None`,则请求会继续传递给下一个中间件的`process_view`方法,或者最终到达视图函数。
• 如果`process_view`返回一个`HttpResponse`对象,则后续的中间件和视图函数将不再执行,直接将该响应返回给客户端。
5.执行顺序
`process_view`的执行顺序与`process_request`类似,也是按照`MIDDLEWARE`配置中中间件的顺序从上到下依次执行。但如果某个中间件的`process_view`返回了响应,则后续中间件的`process_view`方法将不再执行。
6.示例
以下是一个简单的`process_view`示例,用于检查用户是否登录,并根据视图函数的名称决定是否允许访问:
from django.http import HttpResponseForbidden
class LoginRequiredMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_view(self, request, view_func, view_args, view_kwargs):
# 检查用户是否登录
if not request.user.is_authenticated:
# 如果视图函数名称包含 "admin",禁止访问
if "admin" in view_func.__name__:
return HttpResponseForbidden("You do not have permission to access this page.")
return None
在这个例子中,如果用户未登录且试图访问包含"admin"的视图函数,中间件会直接返回一个 403 Forbidden 响应,阻止请求继续执行。
总结
`process_view`是一个强大的中间件方法,它允许中间件在视图函数执行之前进行更细粒度的控制和处理,是实现视图级别逻辑的理想选择。
process_template_response(request, response):
`process_template_response(request, response)`是 Django 中间件中的一个可选方法,它在视图函数返回一个`TemplateResponse`对象时被调用。这个方法的主要作用是在模板渲染之前对响应进行处理,例如修改模板上下文或调整渲染流程。
特点
1. 触发条件:只有当视图函数返回的对象具有`render`方法时(通常是`TemplateResponse`),`process_template_response`才会被调用。
2. 参数:
• `request`:当前请求对象。
• `response`:视图函数返回的响应对象。
3. 返回值:必须返回一个`HttpResponse`对象。
4. 执行顺序:按照中间件注册顺序的倒序执行。
使用场景
• 动态修改模板上下文:可以在模板渲染之前向上下文中添加或修改变量。例如,可以全局添加用户通知信息或广告内容。
• 统一处理模板逻辑:避免在每个视图中重复添加相同的上下文数据。
示例
以下是一个简单的中间件示例,展示了如何使用`process_template_response`方法向模板上下文中添加额外数据:
from django.utils.deprecation import MiddlewareMixin
class TemplateContextMiddleware(MiddlewareMixin):
def process_template_response(self, request, response):
if isinstance(response, TemplateResponse):
response.context_data['extra_data'] = 'Some extra data added by middleware'
return response
在模板中可以直接访问`extra_data`,而无需在每个视图中手动传递。
注意事项
• `process_template_response`方法的调用依赖于视图返回的响应对象是否具有`render`方法。
• 如果返回值不是`HttpResponse`对象,会引发错误。
通过合理使用`process_template_response`,可以实现模板渲染的灵活扩展和全局统一管理。
process_response:
`process_response`是中间件中的一个方法,用于在接收到响应后对其进行处理。它在 Scrapy 和 Django 等框架中都有应用,具体用途和使用方法如下:
用途
1. 修改响应内容:可以对响应进行修改,例如更改 HTML 或 JSON 数据。
2. 错误处理:根据响应状态码或内容执行相应的错误处理逻辑。
3. 数据清洗:在将响应数据传递给后续处理流程之前,进行预处理,如删除不需要的标签或字段。
4. 记录日志:记录响应的相关信息,如响应时间、状态码等。
使用方法
以 Scrapy 框架为例:
1. 定义中间件类:在`middlewares.py`文件中定义一个中间件类,并实现`process_response`方法。
class MyMiddleware:
def process_response(self, request, response, spider):
# 在这里编写处理响应的逻辑
# 例如修改响应内容
if 'example' in response.text:
modified_text = response.text.replace('example', 'modified_example')
response = response.replace(body=modified_text)
return response
2. 启用中间件:在`settings.py`文件中,将自定义中间件添加到`DOWNLOADER_MIDDLEWARES`或`MIDDLEWARE`配置中。
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.MyMiddleware': 543,
}
在 Django 框架中,`process_response`方法的使用方式类似,但主要用于处理视图函数返回的响应。
process_exception:
`process_exception`是中间件中的一个方法,用于处理在请求处理过程中发生的异常。以下是它在不同框架中的使用场景和方法:
Django 中的`process_exception`
在 Django 中,`process_exception`是中间件的一个可选方法,当视图函数抛出异常时会被调用。它的作用包括:
1. 记录异常信息:可以将异常记录到日志中,方便后续排查问题。
2. 返回自定义响应:可以返回一个自定义的`HttpResponse`对象,例如显示一个友好的错误页面。
3. 隐藏错误细节:在生产环境中,可以隐藏具体的错误信息,避免暴露系统内部的实现细节。
示例代码
import logging
from django.http import HttpResponse
class ExceptionHandlingMiddleware:
def process_exception(self, request, exception):
logging.error(f"Exception occurred: {exception}, Path: {request.path}")
return HttpResponse("An error occurred. Please try again later.", status=500)
在`settings.py`中启用该中间件:
MIDDLEWARE = [
# 其他中间件...
'path.to.ExceptionHandlingMiddleware',
]
Scrapy 中的`process_exception`
在 Scrapy 中,`process_exception`是下载中间件的一部分,用于处理在下载请求期间发生的异常。它的主要功能包括:
1. 异常处理:捕获下载过程中发生的异常,例如网络错误。
2. 重试请求:根据条件决定是否重试请求,并可以修改请求参数(如代理、头信息)。
3. 记录日志:记录异常信息,方便调试和监控。
示例代码
import logging
from scrapy.exceptions import IgnoreRequest
class RetryExceptionMiddleware:
def process_exception(self, request, exception, spider):
logging.warning(f"Exception {exception} occurred while processing {request.url}")
max_retries = 3
retries = request.meta.get('retry_times', 0) + 1
if retries <= max_retries:
logging.info(f"Retrying {request.url} (retry {retries}/{max_retries})")
request.meta['retry_times'] = retries
return request
else:
logging.error(f"Failed to retrieve {request.url} after {max_retries} retries")
raise IgnoreRequest(f"Failed to retrieve {request.url} after {max_retries} retries")
在`settings.py`中启用该中间件:
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.RetryExceptionMiddleware': 543,
}
总结
`process_exception`是中间件中用于处理异常的强大工具。在 Django 中,它主要用于处理视图函数中的异常;在 Scrapy 中,它用于处理下载请求中的异常。通过合理使用该方法,可以实现异常记录、重试机制、自定义错误响应等功能。
中间件通常用来处理和所有业务都有关的信息
中间件的使用不需要修改models.py和view.py文件,而是专门设立一个middleware.py文档来定义中间件