如何将 CSV 文件上传到 DJANGO REST

DDD
发布: 2024-11-05 19:25:02
原创
729 人浏览过

将 CSV 文件上传到 Django REST(尤其是在原子设置中)是一项简单的任务,但让我感到困惑,直到我发现了一些我将与您分享的技巧。
在本文中,我将使用邮递员(代替前端),并将分享您需要在邮递员上设置什么才能通过图片发送请求。

我们想要的

  1. 通过 Django Rest 将 CSV 上传到数据库
  2. 使操作原子化,即 csv 中任何行中的任何错误都应导致整个操作的完全回滚,因此我们可以避免剪切 csv 文件的压力,即识别使其成功的行部分的麻烦数据库和那些由于中途错误而没有的数据库! (部分条目)。所以我们想要一个要么全有要么全无的事情!!

方法

  1. 假设您已经安装了 Django 和 Django REST,第一步是安装 pandas,一个用于数据操作的 Python 库。

pip 安装 pandas

  1. 邮递员中的下一步:在正文选项卡中,选择表单数据并添加一个键(任何任意名称)。在同一单元格中,将鼠标悬停在单元格的最右侧,然后使用下拉菜单将选项从文本更改为文件。当您执行此操作时,Postman 会自动将 headers 中的 Content-Type 设置为 multipart/form-data。

对于值单元格,单击“选择文件”按钮并上传 CSV。请看下面的截图

HOW TO UPLOAD A CSV FILE TO DJANGO REST

在 headers 下,设置 Content-Disposition 并将值设置为 form-data;名称=“文件”;文件名=“你的文件名.csv”。将 your_file_name.csv 替换为您的实际文件名。检查下面的屏幕截图。

HOW TO UPLOAD A CSV FILE TO DJANGO REST

  1. 在Django视图中,代码如下:
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.parsers import FileUploadParser
from rest_framework.response import Response
from .models import BiodataModel
from django.db import transaction
import pandas as pd

class UploadCSVFile(APIView):
    parser_classes = [FileUploadParser]

    def post(self,request): 
        csv_file = request.FILES.get('file')
        if not csv_file:
            return Response({"error": "No file provided"}, status=status.HTTP_400_BAD_REQUEST)

        # Validate file type
        if not csv_file.name.endswith('.csv'):
            return Response({"error": "File is not CSV type"}, status=status.HTTP_400_BAD_REQUEST)

        df = pd.read_csv(csv_file, delimiter=',',skiprows=3,dtype=str).iloc[:-1]
        df = df.where(pd.notnull(df), None)

        bulk_data=[]
        for index, row in df.iterrows():
            try:
              row_instance= BiodataModel(
                      name=row.get('name'),
                      age=row.get('age'),
                      address =row.get('address'))
              row_instance.full_clean()
              bulk_data.append(row_instance)
            except Exception as e:
                return Response({"error": f'Error at row {index + 2} -> {e}'}, status=status.HTTP_400_BAD_REQUEST)

        try:
            with transaction.atomic():
                BiodataModel.objects.bulk_create(bulk_data)
        except Exception as e:
            return Response({"error": f'Bulk create error--{e}'}, status=status.HTTP_400_BAD_REQUEST)
        return Response({"msg":"CSV file processed successfully"}, status=status.HTTP_201_CREATED)

登录后复制
登录后复制

解释上面的代码:
该代码首先导入必要的包,定义基于类的视图并设置解析器类(FileUploadParser)。类中 post 方法的第一部分尝试从 request.FILES 获取文件并检查其可用性。
然后进行次要验证,通过检查扩展名来检查它是否是 CSV。
下一部分将其加载到 pandas 数据框中(非常像电子表格):
df = pd.read_csv(csv_file, delimiter=',',skiprows=3,dtype=str).iloc[:-1]
我将解释一些传递给加载函数的参数:

跳船
在读取加载的 csv 文件时,应该注意的是,本例中的 csv 是通过网络传递的,因此一些元数据(例如内容)会添加到文件的开头和结尾。这些东西可能很烦人,并且不是逗号分隔值 (csv) 形式,因此实际上可能会引发解析错误。这解释了为什么我使用skiprows=3来跳过包含元数据和标题的前3行并直接落在csv的正文上。如果删除跳过行或使用较少的数字,也许您可​​能会收到如下错误:错误标记数据。 C 错误,或者您可能会注意到从标头开始的数据。

dtype=str
Pandas 喜欢通过尝试猜测某些列的数据类型来证明自己很聪明。我希望所有值都是字符串,所以我使用 dtype=str

分隔符
指定单元格的分离方式。默认值通常是逗号。

iloc[:-1]
我必须使用 iloc 对数据帧进行切片,删除 df 末尾的元数据。

然后,下一行 df = df.where(pd.notnull(df), None) 将所有 NaNvalues 转换为 None。 NaNi 是 pandas 用来表示 None 的替代值。

下一个区块有点棘手。我们循环数据帧中的每一行,使用 BiodataModel 实例化行数据,使用 full_clean() 方法执行模型级验证(不是序列化器级),因为批量创建会绕过 Django 验证,然后将我们的创建操作添加到名为的列表中批量数据。是啊,添加还没运行!请记住,我们正在尝试执行原子操作(在批处理级别),因此我们想要全部或无。单独保存行不会给我们带来全部或没有行为。

然后是最后一个重要部分。在 transaction.atomic() 块(提供所有或不提供行为)中,我们运行 BiodataModel.objects.bulk_create(bulk_data) 来一次保存所有行。

还有一件事。注意 for 循环中的索引变量和 except 块。在 except 块错误消息中,我将 2 添加到从 df.iterrows() 派生的索引变量中,因为在 Excel 文件中查看时,该值与它所在的行不完全匹配。 except 块会捕获任何错误,并在 Excel 中打开时构造一条具有确切行号的错误消息,以便上传者可以轻松找到 Excel 文件中的行!

感谢您的阅读!!!

使用的工具版本

from rest_framework import status
from rest_framework.views import APIView
from rest_framework.parsers import FileUploadParser
from rest_framework.response import Response
from .models import BiodataModel
from django.db import transaction
import pandas as pd

class UploadCSVFile(APIView):
    parser_classes = [FileUploadParser]

    def post(self,request): 
        csv_file = request.FILES.get('file')
        if not csv_file:
            return Response({"error": "No file provided"}, status=status.HTTP_400_BAD_REQUEST)

        # Validate file type
        if not csv_file.name.endswith('.csv'):
            return Response({"error": "File is not CSV type"}, status=status.HTTP_400_BAD_REQUEST)

        df = pd.read_csv(csv_file, delimiter=',',skiprows=3,dtype=str).iloc[:-1]
        df = df.where(pd.notnull(df), None)

        bulk_data=[]
        for index, row in df.iterrows():
            try:
              row_instance= BiodataModel(
                      name=row.get('name'),
                      age=row.get('age'),
                      address =row.get('address'))
              row_instance.full_clean()
              bulk_data.append(row_instance)
            except Exception as e:
                return Response({"error": f'Error at row {index + 2} -> {e}'}, status=status.HTTP_400_BAD_REQUEST)

        try:
            with transaction.atomic():
                BiodataModel.objects.bulk_create(bulk_data)
        except Exception as e:
            return Response({"error": f'Bulk create error--{e}'}, status=status.HTTP_400_BAD_REQUEST)
        return Response({"msg":"CSV file processed successfully"}, status=status.HTTP_201_CREATED)

登录后复制
登录后复制

以上是如何将 CSV 文件上传到 DJANGO REST的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!