Python Web开发记录 Day16:Django part10 文件上传(完结篇)

名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪)
创作者:Code_流苏(****)(一个喜欢古诗词和编程的Coder????)

目录

      • 1、文件上传
      • 2、Excel上传
      • 3、Form和ModelForm回顾
      • 4、Form图片上传
      • 5、ModelForm图片上传(推荐)
      • 6、城市管理

1、文件上传

1.在urls.py中上传列表的路径upload/list/,并告诉该路径指向的视图upload_list

urls.py

import chart as chart
from django.urls import path
from api.views import depart, user, pretty, admin, account, task, order, chart

urlpatterns = [

    # 部门管理
    path("depart/list/", depart.depart_list),
    path("depart/add/", depart.depart_add),
    path("depart/delete/", depart.depart_delete),
    path("depart/<int:nid>/edit/", depart.depart_edit),

    # 用户管理
    path("user/list/", user.user_list),
    path("user/add/", user.user_add),
    path("user/model/form/add/", user.user_model_form_add),
    path('user/<int:nid>/edit/', user.user_edit),
    path("user/<int:nid>/delete/", user.user_delete),

    # 靓号管理
    path("pretty/list/", pretty.pretty_list),
    path("pretty/add/", pretty.pretty_add),
    path("pretty/<int:nid>/edit/", pretty.pretty_edit),
    path("pretty/<int:nid>/delete/", pretty.pretty_delete),

    # 管理员管理
    path('admin/list/', admin.admin_list),
    path('admin/add/', admin.admin_add),
    path('admin/<int:nid>/edit/', admin.admin_edit),
    path('admin/<int:nid>/delete/', admin.admin_delete),
    path('admin/<int:nid>/reset/', admin.admin_reset),

    # 用户登录
    path('login/', account.login),
    path('logout/', account.logout),
    path('image/code/', account.image_code),

    # 任务管理
    path('task/list/', task.task_list),
    path('task/ajax/', task.task_ajax),
    path('task/add/', task.task_add),

    # 订单管理
    path('order/list/', order.order_list),
    path('order/add/', order.order_add),
    path('order/delete/', order.order_delete),
    path('order/detail/', order.order_detail),
    path('order/edit/', order.order_edit),

    # 数据统计
    path('chart/list/', chart.chart_list),
    path('chart/bar/', chart.chart_bar),
    path('chart/pie/', chart.chart_pie),
    path('chart/line/', chart.chart_line),

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

2.在views文件夹下新建upload.py文件,以实现文件上传函数

upload.py

import re
from django.shortcuts import render, HttpResponse


def upload_list(request):
    # GET方法主要用于获取信息,而POST方法则用于提交数据
    if request.method == "GET":
        return render(request, "upload_list.html")  # render函数负责将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("上传成功")

3.修改layout.html,在templates目录下新建upload_list.html

image-20240319104710655

在layout.html中添加这一句,以便在网站里显示上传文件的列表选项:

<li><a href="/upload/list/">上传文件</a></li>

upload_list.html

{% extends 'layout.html' %}

{% block content %}

<div class="container">
    <!--method为post,说明此处表单是提交数据-->
    <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 %}

上传文件

image-20240319104412794

浏览器进行响应

image-20240319104449051

此时就能发现,文件夹下多了一张咱们上传的图片。

image-20240319104921742

基础的文件上传操作,咱们了解之后,之后来实现一下Excel表格上传。

2、Excel上传

目标:表格上传—》服务器读取保存—》数据库。

以部门管理为例,来进行操作。

1.编辑depart_list.html,增加文件上传板块。

depart_list.html

{% extends 'layout.html' %}
{% block content %}
    <div class="container">
        <div style="margin-bottom: 10px">
            <a class="btn btn-success" href="/depart/add/">
                <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
                新建部门
            </a>
        </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>
            <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>

        <div class="panel panel-default">
            <!-- Default panel contents -->
            <div class="panel-heading">
                <span class="glyphicon glyphicon-list" aria-hidden="true"></span>
                部门列表
            </div>

            <!-- Table -->
            <table class="table table-bordered">
                <thead>
                <tr>
                    <th>ID</th>
                    <th>名称</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                {% for obj in queryset %}
                    <tr>
                        <th>{{ obj.id }}</th>
                        <td>{{ obj.title }}</td>
                        <td>
                            <a class="btn btn-primary btn-xs" href="/depart/{{ obj.id }}/edit/">编辑</a>
                            <a class="btn btn-danger btn-xs" href="/depart/delete/?nid={{ obj.id }}">删除</a>
                        </td>
                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </div>
        <ul class="pagination">
            {{ page_string }}
        </ul>
    </div>
{% endblock %}

2.安装openpyxl

pip install openpyxl

image-20240319110116930

补充:openpyxl是什么?

openpyxl是一个Python库,用于读取和写入Excel 2010 xlsx/xlsm/xltx/xltm文件。这个库允许你以编程方式操作Excel文件,比如创建新的工作簿、读取已有的工作簿、添加或删除工作表、修改单元格数据以及制作图表等。由于openpyxl能够处理Excel文件,它在数据分析和自动化办公中尤其有用,为处理电子表格数据提供了很大的便利。

主要特性包括:

  • 读取和写入Excel 2010 xlsx/xlsm文件。
  • 修改工作簿和工作表,包括添加和删除工作表。
  • 读取和修改单元格内容。
  • 支持图表、图像和表格的创建和编辑。
  • 样式和格式化,包括对文本和单元格的字体、边框、颜色等属性的设置。

通过openpyxl用户可以不依赖于Microsoft Excel应用程序,就能在Python脚本或应用中自动处理Excel文件。

3.在urls.py中上传列表的路径depart/multi/,并告诉该路径指向的视图depart_multi

urls.py

import chart as chart
from django.urls import path
from api.views import depart, user, pretty, admin, account, task, order, chart, upload

urlpatterns = [

    # 部门管理
    path("depart/list/", depart.depart_list),
    path("depart/add/", depart.depart_add),
    path("depart/delete/", depart.depart_delete),
    path("depart/<int:nid>/edit/", depart.depart_edit),

    # 用户管理
    path("user/list/", user.user_list),
    path("user/add/", user.user_add),
    path("user/model/form/add/", user.user_model_form_add),
    path('user/<int:nid>/edit/', user.user_edit),
    path("user/<int:nid>/delete/", user.user_delete),

    # 靓号管理
    path("pretty/list/", pretty.pretty_list),
    path("pretty/add/", pretty.pretty_add),
    path("pretty/<int:nid>/edit/", pretty.pretty_edit),
    path("pretty/<int:nid>/delete/", pretty.pretty_delete),

    # 管理员管理
    path('admin/list/', admin.admin_list),
    path('admin/add/', admin.admin_add),
    path('admin/<int:nid>/edit/', admin.admin_edit),
    path('admin/<int:nid>/delete/', admin.admin_delete),
    path('admin/<int:nid>/reset/', admin.admin_reset),

    # 用户登录
    path('login/', account.login),
    path('logout/', account.logout),
    path('image/code/', account.image_code),

    # 任务管理
    path('task/list/', task.task_list),
    path('task/ajax/', task.task_ajax),
    path('task/add/', task.task_add),

    # 订单管理
    path('order/list/', order.order_list),
    path('order/add/', order.order_add),
    path('order/delete/', order.order_delete),
    path('order/detail/', order.order_detail),
    path('order/edit/', order.order_edit),

    # 数据统计
    path('chart/list/', chart.chart_list),
    path('chart/bar/', chart.chart_bar),
    path('chart/pie/', chart.chart_pie),
    path('chart/line/', chart.chart_line),

    # 文件上传
    path('upload/list/', upload.upload_list),
    path('depart/multi/',depart.depart_multi),
]

4.修改depart.py,增加depart_multi文件上传函数。

depart.py

from django.shortcuts import render, redirect, HttpResponse
from api import models
from api.models import Department
from api.utils.pagination import Pagination
from openpyxl import load_workbook


def depart_list(request):
    """部门列表"""
    queryset = models.Department.objects.all()

    page_object = Pagination(request, queryset, page_size=2)
    page_object.html()

    context = {
        "queryset": page_object.page_queryset,
        "page_string": page_object.page_string,
    }
    return render(request, 'depart_list.html', context)


def depart_add(request):
    """添加部门"""
    if request.method == "GET":
        return render(request, 'depart_add.html')

    # 获取用户POST提交过来的数据(title输入为空)
    title = request.POST.get("title")

    # 保存到数据库
    models.Department.objects.create(title=title)

    # 重定向回部门列表
    return redirect("/depart/list/")


def depart_delete(request):
    """删除部门"""
    # 获取ID
    # http://127.0.0.1:8000/depart/delete/?nid=1
    nid = request.GET.get('nid')

    models.Department.objects.filter(id=nid).delete()
    # 重定向回部门列表
    return redirect("/depart/list")


def depart_edit(request, nid):
    """修改部门"""
    if request.method == "GET":
        # 根据nid,获取他的数据[obj,]
        row_object = models.Department.objects.filter(id=nid).first()
        return render(request, 'depart_edit.html', {"row_object": row_object})
    # 获取用户的标题
    title = request.POST.get("title")
    # 根据ID找到数据库中的数据进行更新
    models.Department.objects.filter(id=nid).update(title=title)
    # 重定向回部门列表
    return redirect("/depart/list/")


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("上传成功")

5.手动新建一个excel表格depart.xlsx,并输入以下内容。

image-20240319110917141

上传表格,选择文件后,点击上传。

image-20240319111006891

文件上传后,显示"上传成功",则结束上传。

image-20240319111038069

可以看到部门列表处新增了几个部门信息:

image-20240319111310393

值得注意的是,如果你上传了空字段,页面会报错,但不要担心,咱们只需要针对为空的特殊情况做下if判断处理下就能解决,实现了Excel文件上传。接下来我们一起利用之前所学的Form和ModelForm的知识,实现一下图片上传,但是可能到了这里,许多朋友已经有些许生疏,那先快速回顾一下Form和ModelForm:

3、Form和ModelForm回顾

首先需要明确的是,在Django框架中,FormModelForm都是用来处理表单数据的,但它们各有特点和用途:

1.Form

  • 定义: Form是Django中用于生成和处理HTML表单的一个基础工具。它定义了表单的结构、数据类型和验证方式,但与数据库模型(models)无直接关联。
  • 用途: 主要用于那些不直接与数据库模型对应的表单。例如,一个登录表单可能只需要用户名和密码字段,而不需要创建相应的数据库模型。
  • 特点:
    • 可以手动定义表单字段。
    • 包含数据验证功能,方便验证表单输入是否符合预期。
    • 通过创建forms.Form子类来使用。

2.ModelForm