web开发 django的表单处理详解

这一段时间想要利用django来搭建web应用的后台逻辑,完成面向工作部门的web应用,该应用中需要使用文件上传功能,通过对官方文档的学习,我将如何完成文件上传的功能学习成果分享给需要的伙伴们。

首先要理解django的表单提交过程

django对于表单提交的模块是django.form,而form有两种,一种是form.Form,一种是form.ModelForm,它们的区别是

是否需要和模型连接。如下图所示,我们在应用中添加一个forms.py文件并分别利用这两个类来实现同样的表单提交内容

from django import forms #导入forms模块
from .models import ToolsDocument

class UploadFileForm(forms.Form):
    document_name = forms.CharField(max_length=50)#定义表单input的内容
    description = forms.CharField(max_length=100) #对应 <input type='text' name=description id=id_description>
    submiter = forms.CharField(max_length=30)
    upload_file = forms.FileField()#文件上传input 对应 <input type="file" name=upload_file id=id_upload_file>

class UploadFileForm(forms.ModelForm):
    class Meta:
        model =ToolsDocument
        #fields = ['document_name','description','submiter','upload_file'] #fields为对应的模型包含的字段
        exclude = ['release_time']#exclude为去除某个模型包含的字段

我们可以查看一下model的样子

from django.db import models

# Create your models here.

class ToolsDocument(models.Model):
    document_name = models.CharField(max_length=50)
    description = models.CharField(max_length=100)
    submiter = models.CharField(max_length=30)
    release_time = models.DateField(auto_now=True)
    upload_file = models.FileField(upload_to='tools_document/%Y-%m-%d')

    def __str__(self):
        return self.document_name

可以看到,我忽略的是release_time字段,这个字段设置了auto_now=True,在每次存到数据库的时候,会自动填入时间,因此不需要输入。这里的关键内容是文件上传的字段upload_file。该字段的upload_to参数将指定上传到的位置,默认是在你设置了media的路径下自动创建指定的文件,例如这里将存在media/tools_document/xxxx-xx-x的目录下。

下面来看看如何使用上述的任意一种form模型来实现该功能:

第一步设置好应用目录下的urls.py文件,这里我设置了两个URL,分别用于表单提交和返回内容

from django.urls import path,re_path
from auth_system import views

urlpatterns = [
    path('file_upload/',views.file_upload,name='file_upload'),
    path('thanks/',views.thanks,name ='thanks'),
]

第二步是写views.py文件

import os

from django.shortcuts import render
from django.http import HttpResponseRedirect,HttpResponse
from .forms import UploadFileForm

# Create your views here.
def upload_file_handler(f,filename):
    base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    ofile = os.path.join(base_dir,'media',filename)
    with open(ofile,'wb+') as odata:
        for chunk in f.chunks():
            odata.write(chunk)

def file_upload(request):
    '''
    save the file to media
    '''
    if request.method == 'POST':
        upload_form = UploadFileForm(request.POST,request.FILES)#注意这里文件存放在request请求中的FILES属性中
        if upload_form.is_valid():#根据forms.py中设定的表单字段要求来判断内容是否有效
            filename = upload_form.cleaned_data['document_name']#这里的cleaned_data为有效的表单字段
            upload_file_handler(request.FILES['upload_file'],filename)#这里我们自己写了一个存放文件的函数,用于存放文件
            # upload_form.save() #在form连接model的时候使用
            return HttpResponseRedirect('/test/thanks/')
        #else:
        #    pass #这一部分可以用于返回错误信息
    else:
        upload_form = UploadFileForm()
    
    return render(request,'file_upload.html',context={'upload_form':upload_form})



def thanks(request):
    '''
    thanks request
    '''
    return HttpResponse('<p>Thanks for submiting your file</p>')

这里的核心函数就是file_upload(request)函数,这个函数的工作原理是这样的:

第一次当我们请求该上传文件的页面的时候,是一个get请求,因此会执行else中的部分,也就是返回一个空的表单让我们来添加

第二次我们提交了表单内容以后,我们的请求方法设置为POST,然后会直接传给之前写好的form模型,用来判断模型是否有效,如果有效就可以使用这些数据了,这里因为我们没有连接模型,就需要自己写一个存放文件的函数,我这里写了一个upload_file_handler()的函数来处理,存放到media目录下。如果我们的使用forms.ModelForm连接了某个模型,那么我们就不需要这个函数了,直接使用upload_form.save()即可存放所有的内容到数据库中,同时将文件存放到模型中的FileField()的参数upload_to指定的地方。当执行完毕无误之后可以使用HTTPResponseRedirect()来重定向到指定的模板URL。

第三步就是编写模板了

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
    <title>File Upload</title>
</head>
<body>
    <form action="{% url 'file_upload' %}" method="POST" enctype="multipart/form-data">
        {% csrf_token %}
        {{ upload_form.as_p }}
        
        <!-- <input type="text" name="document_name">
        <input type="text" name="description">
        <input type="text" name="submiter">
        <input type="file" name = "upload_file" > -->
        <input type="submit">  
    </form>
</body>
</html>

我们需要自己写一个form标签,指定url来接收文件,同时指定为post方法提交数据,然后添加一个属性为submit的input标签即可。

form的内部需要添加{% csrf_token %},是用于防御跨域伪造请求的(Cross Site Request Forgeries),同时添加一个我们通过render()提交的字典中的form内容即可,这里为每一个内容都包围一个p标签。细心的朋友已经发现我下面注释掉的内容,事实上,我们如果把上面的{{ upload_form.as_p }}注释掉,然后使用下面的内容也一样可以实现该功能,这样做的好处是我们可以自己添加一些属性,来通过js或者bootstrap来美化或者操作表单。{{ upload_form.as_p }}将我们的模型中的字段document_name,descirption,submiter和upload_file分别变成四个input标签,同时添加name标签和id标签,name标签就是这些字段的名字本身,而id标签则是id_document_name,id_description...这样的内容。因此,事实上我们只需要自己造几个input标签,将name属性修改为字段的名称就可以了!

接下来让我们测试一下我们的劳动成功:

web开发 django的表单处理详解

然后提交即可跳转

web开发 django的表单处理详解

最后我们验证一下我们的文件是否存放到了指定地点即可。

如果你连接了模型,那么可以在shell中进行查看验证,输入python manage.py shell

web开发 django的表单处理详解

然后导入模型进行查看即可。