首先先进入我们的testsite项目下,打开members/models.py文件,先添加我们保存文件的数据模型:
class Document(models.Model):
name=models.CharField(max_length=255)
file=models.FileField(upload_to='uploads/') # 'uploads/' 是文件上传的相对路径
uploaded_at=models.DateTimeField(auto_now_add=True)
这里的文件存储目录可以自定义我们需要存储文件的目录,也可以在我们的 env文件 做配置,这里直接读取相应的配置 env字段 就可以了。
进入testsite根目录执行更新迁移文件命令,这一步前面有说到,是为了更新迁移文件,
py manage.py makemigrations
可以看到我们的migrations目录增加了新的迁移文件
执行迁移
py manage.py migrate
我们新增加的文件模型表就在数据库生成好了。
进入testsite/members目录,新增form.py表单文件,这里会定义文件上传的字段。
from django import forms
from .models import Document
class UploadFileForm(forms.ModelForm):
class Meta:
model=Document
fields=['name', 'file'] # 包含你想要用户填写的字段
接下来将上传的文件绑定到表单中,打开testsite/members/views.py视图文件:
from .forms import UploadFileForm #需要我们引入上一步建立好的表单文件
def upload_file(request):
if request.method=="POST":
form=UploadFileForm(request.POST, request.FILES)
if form.is_valid():
form.save() # 保存表单数据到数据库
return HttpResponse("上传成功")
else:
form=UploadFileForm()
return render(request, "polls/upload.html", {"form": form})
我们传递request.FILES 到表单的构造函数;这就是文件数据绑定到表单的方式。
请注意,request.FILES仅当请求方法为POST,实际发送了至少一个文件字段且<form>表单有发送请求的并且具有enctype="multipart/form-data"属性时。否则request.FILES将为空。
最后一步,添加上传页面,进入members/templates/members目录,新增upload.html文件,注意跟我们的视图里面指向的文件名保持一致。
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">提交</button>
</form>
打开地址http://127.0.0.1:8000/polls/upload/进入文件上传页面
点写完表单点击提交
这个时候我们的文件上传就完成了,可以打开我们的testsite目录,进入我们设置的uploads目录,就看到我们上传好的文件:
在这里插入图片描述
然后我们再打开数据库,进入模型对应的members_document表,也可以看到文件上传生成的数据。
在这里插入图片描述
为了使用一个表单字段上传多个文件,创建该字段的子类并将其allow_multiple_selected类属性设置为True。
进入表单文件forms.py:
class MultipleFileInput(forms.ClearableFileInput):
allow_multiple_selected=True
class MultipleFileField(forms.FileField):
def __init__(self, *args, **kwargs):
kwargs.setdefault("widget", MultipleFileInput())
super().__init__(*args, **kwargs)
def clean(self, data, initial=None):
single_file_clean=super().clean
if isinstance(data, (list, tuple)):
result=[single_file_clean(d, initial) for d in data]
else:
result=[single_file_clean(data, initial)]
return result
class FileFieldForm(forms.Form):
file_field=MultipleFileField()
然后进入view.py视图文件重写子类form_valid()的方法 FormView来处理多个文件上传:
from .forms import FileFieldForm # 确保导入正确的表单定义
from .models import Document # 引入Document模型
def batch_upload_view(request):
if request.method=='POST':
form=FileFieldForm(request.POST, request.FILES)
if form.is_valid():
# 获取表单中title的值
title=form.cleaned_data['title']
# 处理文件上传
for file in request.FILES.getlist('file_field'):
document=Document(file=file, name=title) # 假设title作为文件的名称或描述
document.save()
return HttpResponse("上传成功")
else:
# 请求方法为GET时,初始化表单
form=FileFieldForm()
return render(request, "polls/uploads.html", {"form": form})
假如你不想使用模型关联上传,则使用如下方式:
from django.core.files.storage import default_storage
#使用默认存储引擎保存文件
file_name=default_storage.save(file.name, file)
然后重新渲染我们的批量上传页面,跟我们的forms django表单关联
<form method="post" enctype="multipart/form-data"> <!-- enctype 必须为 multipart/form-data 以支持文件上传 -->
{% csrf_token %} <!-- Django表单必须的安全令牌 -->
{{ form.title }}
{{ form.file_field }} <!-- 渲染文件上传字段,MultipleFileField将会生成相应的input元素 -->
<button type="submit">上传文件</button>
</form>
上面的 csrf_token django表单必须的安全令牌,form.file_field 也是我们form.py定义的批量上传字段。
打开members/urls.py,添加我们的路由
path('uploads/', views.batch_upload_view, name='upload'),
我们的批量上传功能就已经写好了,我们打开http://127.0.0.1:8000/polls/uploads/页面
填好表单,批量选择文件,点击上传,这里我只是演示,没有做页面渲染样式与字段验证,感兴趣的小伙伴可以加上自己喜欢的样式。
然后进入我们的数据库可以看到我们与上传模型的记录被添加
我们打开uploads目录,也可以看到我们上传好的文件。
需要注意的是在保存上传的文件之前,数据需要存储在某个地方。
默认情况下,如果上传的文件小于 2.5 MB,django 会将上传的全部内容保存在内存中。
这意味着保存文件仅涉及从内存读取和写入磁盘,因此速度非常快。
但是,如果上传的文件太大,django 会将上传的文件写入存储在系统临时目录中的临时文件。
例如/tmp/tmpzfp6I6.upload。如果上传的文件足够大,您可以看到该文件的大小随着 django 将数据传输到磁盘而增长
文件上传算是一种很常见的需求,几乎构建很多项目系统,以及插件都需要用到。
通过上面例子可以看到django通过forms表单的形式灵活定义文件上传的页面,字段,以及数据库存储。
在实际项目中我们还要考虑到安全性,包括检查文件大小(可通过FileField的max_length属性设置)、防止路径遍历攻击。
还需注意服务器端的验证,比如检查文件类型、内容安全等。
例子里面有提到,django的FileSystemStorage和DefaultStorage类提供了基本的文件存储管理。
自定义存储后端可以提供更多灵活性,如自定义文件命名规则、存储路径等,以避免文件名冲突和优化组织结构。
大量文件上传或下载也会影响应用性能。最好采用云存储服务、内容分发网络(CDN)、以及对静态和媒体文件的有效缓存策略,可以显著提升用户体验。
对于我们本地的存储策略,需要考虑上传文件的生命周期管理,包括旧文件的定期清理策略,以避免无限制增长占用存储空间。
– 欢迎点赞、关注、转发、收藏【我码玄黄】,gonghao同名
道Django某一个功能的用途,比单纯地知道怎么用更有用。
今天我们要学习的内容是Django的页面跳转,重点讲的是使用url里的name参数
根据前面做好的web项目,登录页面的url为: http://127.0.0.1:8000/login/;如果我们想把路径名login换成signin,该怎么做呢? 这时候我们就想到了在urls的urlpatterns里直接修改path路径名,
如果views里对应的方法有重定向跳转就麻烦了,因为重定向跳转的redirect方法里已经写了我们改名之前的路径名。如果我们在url里将login改成signin,做了一次改名操作了,那么在views里我们还要再改一次。如果有很多个页面都做了重定向到login页面,我们就需要对每一个函数进行修改操作,太麻烦了!有没有什么办法能让我们做路径修改的时候,只需要改一处就能搞定的呢?
实际上Django给我们提供了URL的name属性用来标记url,我们可以在path中给指定路径设置一个name属性,
urlpatterns = [
path('signin/', app01_views.login, name="login"),
]
在views中做重定向时不直接写死路径名,而是通过reverse('属性名')反向查出前面的url,这样修改路径名就很方便了,直接修改path就可以了。
return redirect(reverse('login'))
通常情况下,我们是根据path里的路径名,找到views里的方法,然后渲染页面。而这里的重定向反转操作是根据路径名的name属性,反向找到路径的名称。 这样我们需要修改路径名的时候,views里都不要动的,反正它找的是指定的名称。name属性规定好了之后,url路径名想怎么改就怎么改。
注意:使用反转路径名的方法,需要导入包from django.shortcuts import reverse。
当前阶段我们主要用在两处: 在View中使用redirect(reverse("login"));
def index(request):
username = request.GET.get('username')
if username:
return render(request,'index.html')
else:
return redirect(reverse('login'))
在html中使用模板语言{% url 'login' %};
<a href="{% url 'login'%}">登录</a>
我们先复习一下url传值的两种常见方法:
(1)直接使用url传值
以传递电影编号movie_id为例,如果想通过url传值。在定义path的时候,使用path("movile_detail/<movie_id>",...),然后在views里定义方法来接收movie_id这个值,就定义成def movie_id(request,movie_id),浏览器中访问的时候可以通过127.0.0.1:8000/movie_detail/8001就能把movie_id作为参数通过url传过去。
(2)使用查询字符串传值
我们还是以传递电影编号为例,如果想通过url传值。在定义path时,使用path("movie_detail/",...),在views里定义方法来接收就写成
def movie_id(request):
movie_id = request.GET.get(movie_id)
在浏览器中访问是,url需要输入127.0.0.1:8000/?movie_id=8001。这样也能完成url的传值。
(3)传递参数
在views中,我们使用redirect重定向到一个新的url如果要传递参数该怎么办呢? 有两种方式:
url设置如下:
urlpatterns = [
# 登录url
path('login/<username>/<password>',app01_views.login, name='login'),
]
在views里定义login方法准备接收两个参数:
def login(request, username, password):
return HttpResponse("用户名:%s \t 密码:%s" % (username, password))
传递参数时:
def index(request):
username = request.GET.get('username')
if username:
return HttpResponse('=== 这是首页 ===')
else:
return redirect(reverse('login', kwargs={'username': 'rico', 'password': '123'}))
注意:是在reverse方法里添加参数,传的如果是元组则注意前后顺序,如果是字典就不用考虑前后顺序。
如果要在templates中通过模板语言调用url的名称, 模板语言里使用url传递参数可以写成这样:
{% url 'login' username='xiaoyu' password='123' %}
在多app项目中,如果各自的name属性相同,在重定向访问的时候会出现无法访问指定app下的url的问题。这时候我们就在各自app的urls中添加命名空间
app_name = 'app01'
然后在重定向访问指定url名称的时候,添加命名空间属性即可。
def index(request):
username = request.GET.get('username')
if username:
return render(request, 'app01/index.html')
else:
return redirect(reverse('app01:login'))
注意:项目总体url配置使用include关键字,将多个app配置到总路由中。
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include('app01.urls')),
path('app02/',include('app02.urls')),
]
这一节,我们主要介绍了Django中url路由跳转的进阶知识,下一节,我们将介绍Django下html页面中的模板语言DTL,感谢大家的阅读~
*请认真填写需求信息,我们会在24小时内与您取得联系。