Django web 开发(四) - Django项目实战(十一) - 文件上传

文件上传

实现图片的上传并保存到服务器中

编辑myproject/employee_management/templates/layout.html

<li><a href="/upload/list/">上传图片</a></li>

编辑myproject/myproject/urls.py

# 文件上传
path('upload/list/', upload.upload_list),

新建myproject/employee_management/views/upload.py

import re
from django.shortcuts import render,HttpResponse


def upload_list(request):

    if request.method == "GET":
        return render(request, "upload_list.html")

    # 声明图片的对象
    file_object = request.FILES.get("avatar")
    
    # 分块进行存储
    # file_object.name 表示图片上传时图片本身是什么名字,保存图片时就用什么名字
    f = open(file_object.name, mode='wb')
    for chunk in file_object.chunks():
        f.write(chunk)
    f.close()

    return HttpResponse("上传成功")

新建myproject/employee_management/templates/upload_list.html

注意:
下面代码中的form标签中必须加上enctype="multipart/form-data"才能实现图片的上传

{% extends 'layout.html' %}

{% block content %}

<div class="container">

    <form method="post" enctype="multipart/form-data">
        {% csrf_token %}
        <input type="text" name="name">
        <input type="file" name="avatar">
        <input type="submit" value="提交">
    </form>

</div>

{% endblock %}

浏览器测试
在这里插入图片描述
浏览器返回成功的提示
在这里插入图片描述
去服务器下查看上传的文件
在这里插入图片描述

Excel表格上传

实现:表格上传至服务器并将数据读取保存至数据库中

我们在部门管理代码的基础上进行操作
编辑myproject/employee_management/templates/depart_list.html

<div class="panel panel-default">
    <!-- Default panel contents -->
    <div class="panel-heading">
        <span class="glyphicon glyphicon-th-list" aria-hidden="true" style="margin-right: 5px;"></span>
        <span>文件上传</span>
    </div>
    <div class="panel-body">
        <form method="post" enctype="multipart/form-data" action="/depart/multi/">
            {% csrf_token %}
            <div class="form-group">
                <input type="file" name="exc">
            </div>
            <input type="submit" class="btn btn-sm btn-info" value="上传">
        </form>
    </div>
</div>

在这里插入图片描述
安装openpyxl

pip3 install openpyxl

编辑myproject/employee_management/views/depart.py

from django.shortcuts import render, redirect, HttpResponse
from openpyxl import load_workbook


def depart_multi(request):
    """ 文件上传(Excel) """

    # 获取上传的 Excel 文件对象
    file_object = request.FILES.get('exc')
    print(file_object)

    # 打开 Excel 文件读取内容
    wb = load_workbook(file_object)
    sheet = wb.worksheets[0]

    # 循环获取每一行数据,并更新至数据库
    for row in sheet.iter_rows(min_row=2):
        exc_title = row[0].value
        # 如果表格中的数据在数据库中不存在,则进行创建
        if not Department.objects.filter(title=exc_title).exists():
            Department.objects.create(title=exc_title)

    return HttpResponse("上传成功")

编辑myproject/myproject/urls.py

path('depart/multi/', depart.depart_multi),

浏览器访问测试

手动创建一个表格,填入如下内容

在这里插入图片描述
将表格进行上传
在这里插入图片描述
上传成功
在这里插入图片描述
返回部门列表并刷新页面
在这里插入图片描述

这里会存在一个问题,如果上传的字段就为空,页面就会报错
处理起来也简单,针对为空的情况做一下简单的判断即可

Form图片上传

编辑myproject/employee_management/views/upload.py

from django.shortcuts import render,HttpResponse
from employee_management.utils.modelform import BootStrapForm
from django import forms

class UpForm(BootStrapForm):
    name = forms.CharField(label="姓名")
    age = forms.IntegerField(label="年龄")
    img = forms.FileField(label="头像")

def upload_form(request):
    """ 上传图片 """
    
    title = "Form上传"
    form = UpForm()

    return render(request, 'upload_form.html', {
    
    "form": form, "title": title})

编辑myproject/myproject/urls.py

path('upload/form/', upload.upload_form),

新建myproject/employee_management/templates/upload_form.html

注意: 需要在 <form> 标签中添加 enctype="multipart/form-data" 属性

{% extends 'layout.html' %}

{% block content %}

<div class="container">
    <div class="panel panel-default">
        <div class="panel-heading">
            <h3 class="panel-title">{
   
   { title }}</h3>
        </div>
        <div class="panel-body">
            <form method="post" enctype="multipart/form-data" novalidate>
                {% csrf_token %}
                
                {% for field in form %}
                    <div class="form-group">
                        <label>{
   
   { field.label }}: </label>
                        {
   
   { field }}
                        <!-- 数据校验,显示错误信息 -->
                        <span style="color: red;">{
   
   { field.errors.0 }}</span>
                    </div>
                {% endfor %}

                <button type="submit" class="btn btn-primary">保存</button>
            </form>
        </div>
    </div>
</div>

{% endblock %}
</body>
</html>

浏览器访问,会发现有一个问题,详情如下:
在这里插入图片描述
解决方式如下:
编辑myproject/employee_management/utils/modelform.py

class BootStrapForm(forms.Form):

    bootstrap_exclude_fields = []

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 循环ModelForm中的所有字段,给每个字段的插件设置
        for name, field in self.fields.items():
            if name in self.bootstrap_exclude_fields:
                continue
            # 字段中有属性,保留原来的属性,没有属性,才增加
            if field.widget.attrs:
                field.widget.attrs["class"] = "form-control"
            else:
                field.widget.attrs = {
    
    
                    "class": "form-control",
                }

在这里插入图片描述
编辑myproject/employee_management/views/upload.py

bootstrap_exclude_fields = ['img']

在这里插入图片描述
浏览器刷新
在这里插入图片描述

在 Django 的开发中有两个特殊的文件夹:

  • static: 存放静态文件
  • media:存放用户上传的数据,但是使用 media 需要做一些配置

media的配置:
编辑myproject/myproject/urls.py

from django.urls import path, re_path
from django.conf import settings
from django.views.static import serve

urlpatterns = [
	re_path(r'^media/(?P<path>.*)$', serve, {
    
    'document_root': settings.MEDIA_ROOT}, name='media'),
]

编辑myproject/myproject/settings.py

MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = '/media/'

新建目录myproject/media

接下来实现图片的上传(保存至本地与数据库)

创建一个新的数据库
编辑myproject/employee_management/models.py

class Boss(models.Model):
    """ 老板 """
    name = models.CharField(verbose_name="姓名", max_length=32)
    age = models.IntegerField(verbose_name="年龄")
    img = models.CharField(verbose_name="头像", max_length=128)

在服务器中项目根目录下执行命令

python3 manage.py makemigrations
python3 manage.py migrate

编辑myproject/employee_management/views/upload.py

from django.conf import settings


def upload_form(request):
    """ 上传图片 """
    
    title = "Form上传"
    if request.method == "GET":
        form = UpForm()
        return render(request, 'upload_form.html', {
    
    "form": form, "title": title})

    form = UpForm(data=request.POST, files=request.FILES)
    if form.is_valid():
        # {'name': '张三', 'age': 21, 'img': <InMemoryUploadedFile: code.png (image/png)>}
        # 1.读取图片内容,写入到文件夹中并获取文件的路径
        image_object = form.cleaned_data.get("img")
        # media_path = os.path.join(settings.MEDIA_ROOT, image_object.name)
        media_path = os.path.join('media', image_object.name)

        # django 默认从 app 下开始找文件,也就是目前的 employee_management 下,所以存入数据库时不必加上 employee_management
        # 例如 http://127.0.0.1/static/img/image.png
        # file_path_db = os.path.join("static", "img", image_object.name)
        # file_path = os.path.join("employee_management", file_path_db)
        f = open(media_path, mode="wb")
        for chunk in image_object.chunks():
            f.write(chunk)
        f.close()

        # 2.将图片文件路径写入数据库
        Boss.objects.create(
            name = form.cleaned_data['name'],
            age = form.cleaned_data['age'],
            img = media_path,
        )

        return HttpResponse('ok')
    return render(request, 'upload_form.html', {
    
    "form": form, "title": title})

浏览器测试
在这里插入图片描述
在这里插入图片描述
查看本地media目录
在这里插入图片描述
查看数据库
在这里插入图片描述

ModelForm图片上传(推荐)

使用 ModelForm 方式进行图片的存储将更加便捷,看代码即可有体会

编辑myproject/employee_management/models.py创建一个新的数据库

class City(models.Model):
    """ 城市 """
    name = models.CharField(verbose_name="城市", max_length=32)
    count = models.IntegerField(verbose_name="人口")
    img = models.FileField(verbose_name="Logo", max_length=128, upload_to='city/')

在服务器中项目根目录下执行命令

python3 manage.py makemigrations
python3 manage.py migrate

然后优化一下BootStrap工具类(之前忘了弄了,这里补充一下)

编辑myproject/employee_management/utils/modelform.py

from django import forms


class BootStrap:
    
    bootstrap_exclude_fields = []

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 循环ModelForm中的所有字段,给每个字段的插件设置
        for name, field in self.fields.items():
            if name in self.bootstrap_exclude_fields:
                continue
            # 字段中有属性,保留原来的属性,没有属性,才增加
            if field.widget.attrs:
                field.widget.attrs["class"] = "form-control"
            else:
                field.widget.attrs = {
    
    
                    "class": "form-control",
                }
class BootStrapModelForm(BootStrap, forms.ModelForm):
    pass

class BootStrapForm(BootStrap, forms.Form):
    pass

编辑myproject/myproject/urls.py

path('upload/model/form/', upload.upload_model_form),

编辑myproject/employee_management/views/upload.py

class UpModelForm(BootStrapModelForm):

    bootstrap_exclude_fields = ['img']

    class Meta:
        model = City
        fields = "__all__"

def upload_model_form(request):
    """ 上传文件和数据 """
    title = "ModelForm上传文件"
    if request.method == "GET":
        form = UpModelForm()
        return render(request, 'upload_form.html', {
    
    "form": form, "title": title})

    form = UpModelForm(data=request.POST, files=request.FILES)
    if form.is_valid():
        # 对于文件会自动保存
        # 目录信息会自动保存至数据库中
        form.save()

        return HttpResponse("success!")
    return render(request, 'upload_form.html', {
    
    "form": form, "title": title})

编辑myproject/employee_management/templates/layout.html

<ul class="nav navbar-nav">
    <li class="dropdown">
        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
            aria-expanded="false">上传<span class="caret"></span></a>
        <ul class="dropdown-menu">
            <li><a href="/upload/list/">上传图片</a></li>
            <li><a href="/upload/form/">Form上传图片</a></li>
            <li><a href="/upload/model/form/">ModelForm上传图片</a></li>
        </ul>
    </li>
</ul>

在这里插入图片描述
浏览器测试
在这里插入图片描述

返回则上传成功
在这里插入图片描述
查看数据库中是否保存
在这里插入图片描述
查看本地文件是否保存
在这里插入图片描述

城市列表

使用一个例子简单应用一下上面的内容

编辑myproject/myproject/urls.py

# 城市管理
path('city/list/', city.city_list),
path('city/add/', city.city_add),

新建myproject/employee_management/views/city.py

from employee_management.utils.modelform import BootStrapModelForm
from django.shortcuts import render, redirect
from employee_management.models import City
from employee_management.utils.pagination import Pagination


def city_list(request):
    """ 城市列表 """
    data_dict = {
    
    }
    # 如果是空字典,表示获取所有
    # 不加后面的 "", 首次访问浏览器,搜索框中不会显示前端页面中的 placeholder="Search for..." 属性
    search_data = request.GET.get('query', "")
    if search_data:
        data_dict["name__contains"] = search_data 
    
    queryset = City.objects.filter(**data_dict).order_by("-count")

    ### 引入封装的 Pagination 类并初始化
    # 初始化
    page_object = Pagination(request, queryset, page_size=10, page_param="page")
    page_queryset = page_object.page_queryset

    # 调用对象的html方法,生成页码
    page_object.html()

    page_string = page_object.page_string

    context = {
    
    
        "page_queryset": page_queryset,   # 分页的数据
        "search_data": search_data,     # 搜索的内容
        "page_string": page_string,     # 页码
    }

    return render(request, 'city_list.html', context)


class UpModelForm(BootStrapModelForm):

    bootstrap_exclude_fields = ['img']

    class Meta:
        model = City
        fields = "__all__"

def city_add(request):
    """ 新建城市 """
    if request.method == "GET":
        form = UpModelForm()
        return render(request, 'upload_form.html', {
    
    "form": form})

    form = UpModelForm(data=request.POST, files=request.FILES)
    if form.is_valid():
        # 对于文件会自动保存
        # 目录信息会自动保存至数据库中
        form.save()

        return redirect('/city/list/')
    return render(request, 'upload_form.html', {
    
    "form": form})

新建myproject/employee_management/templates/city_list.html

{% extends 'layout.html' %}

{% block content %}
<div class="container">
    <div style="margin-bottom: 10px">
        <a class="btn btn-primary" href="/city/add/" target="_blank">新建城市</a>

        <div style="float: right; width: 300px;">
            <form method="get">
                <div class="input-group">
                    <input type="text" name="query" class="form-control" placeholder="Search for..."
                        value="{
     
     { search_data }}">
                    <span class="input-group-btn">
                        <button class="btn btn-default" type="submit">
                            <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
                        </button>
                    </span>
                </div>
            </form>
        </div>
    </div>


    <div>
        <div class="panel panel-default">
            <!-- Default panel contents -->
            <div class="panel-heading">
                <span class="glyphicon glyphicon-th-list" aria-hidden="true" style="margin-right: 5px;"></span>
                <span>部门列表</span>
            </div>

            <!-- Table -->
            <table class="table table-bordered">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>Logo</th>
                        <th>名称</th>
                        <th>人口</th>
                    </tr>
                </thead>
                <tbody>
                    {% for obj in page_queryset %}
                    <tr>
                        <th>{
   
   { obj.id }}</th>
                        <td>
                            <img src="/media/{
     
     { obj.img }}" style="height: 60px;">
                        </td>
                        <td>{
   
   { obj.name }}</td>
                        <td>{
   
   { obj.count }}</td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
    <ul class="pagination">
        {
   
   { page_string }}
    </ul>
</div>

{% endblock %}

编辑myproject/employee_management/templates/layout.html

<li><a href="/city/list/">城市管理</a></li>

浏览器访问测试
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

至此,Django的学习之旅到此结束,
但是对于我们热爱学习的童鞋来说,这只是旅途的开始,前方还有未知的道路等待我们去探索,
学无止境,用于求知,付诸行动,让我们迈向更好的明天,加油吧,少年们!

猜你喜欢

转载自blog.csdn.net/qq_43139145/article/details/128736292