Импорт CSV-файла из фронт-энда React в бэкэнд Django/Python оказывается ненадежным

Я пытаюсь получить данные из csv-файла, загруженного через фронт-енд ReactJs, а затем импортировать их в базу данных sqlite в бэкенде Django/Python.

Я пытаюсь использовать один и тот же код для нескольких разных импортов, и у меня есть по крайней мере два случая, которые работают, но когда я пытаюсь повторить код, он не работает.

Кажется, что csv, созданный в Excel или другой системе, работает, но у меня mac, и если я пытаюсь отредактировать csv либо с помощью редактора VS Code, либо с помощью Numbers (но экспортируя в csv), файл не работает. Однако если я просматриваю файл в VS Code, то не вижу никакой разницы между теми, которые работают, и теми, которые не работают. Все они находятся в формате utf.

Является ли это проблемой с csv? И есть ли код, который я могу добавить, чтобы диагностировать это... и, возможно, исправить?

FRONTEND:

import axios from 'axios'
import { formatTimeDate } from '../../lib/helpers'

class CSVDataLoader extends Component {


  constructor() {
    super()

    this.state = {
      loading: false,
      updated: ''
    }

    this.handleSubmitData = this.handleSubmitData.bind(this)
    this.handleFile = this.handleFile.bind(this)
  }

  handleFile (e) {
    e.preventDefault()

    const fileToUpload = e.target.files[0]
    this.setState({
      fileToUpload: fileToUpload
    })
  }

  async handleSubmitData(e) {
    e.preventDefault()
    this.cancelTokenSource = axios.CancelToken.source()
    this.setState({ loading: true })

    const formData = new FormData()
    formData.append('file', this.state.fileToUpload)

    try {

      const retrievedData = await axios.post(this.props.url, formData, {
        headers: {
          'Content-Type': 'multipart/form-data'
        },
        cancelToken: this.cancelTokenSource.token
      })
      console.log(retrievedData.data)

      this.setState({ updated: Date.now(), loading: false })

    } catch (err) {
      if (axios.isCancel(err)) {
        // ignore
      } else {
        // propegate
        throw err
      }
    } finally {
      this.cancelTokenSource = null
    }
  }
  componentWillUnmount() {
    this.cancelTokenSource && this.cancelTokenSource.cancel()
  }

  render() {
    const { loading } = this.state

    return (
      <form onSubmit={this.handleSubmitData}>
        <div className="file has-name is-right">
          <label className="file-label">
            <input
              className="file-input"
              type="file"
              name="file"
              multiple={false}
              accept=".xls,.xlsx,.csv,.txt"
              onChange={this.handleFile}
            />
            <span className="file-cta">
              <span className="file-icon">
                <i className="fas fa-upload"></i>
              </span>
              <span className="file-label">
                Choose a file…
              </span>
            </span>
            <span className="file-name">
            Select the csv file to import ...
            </span>
          </label>
        </div>

        <button className={`${this.props.class} button is-primary`}>
          {loading && <span className="spinner"><i
            className="fas fa-spinner fa-spin"
          />Loading ...</span>
          }
          {!loading && <span>{this.props.buttonText}</span>}
        </button>
        <p><small>{!this.state.updated ? '' : `Updated: ${formatTimeDate(this.state.updated)}`}</small></p>
      </form>

    )
  }
}

export default CSVDataLoader

BACKEND

МОДЕЛЬ

from django.db import models

class EventOrder(models.Model):
    event=models.CharField(max_length=40)
    event_order=models.IntegerField()

VIEW

import os
import csv, sys
import tempfile
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser, FormParser, ParseError
from ..serializers import WriteEventOrderSerializer
from ..models import EventOrder, Crew

class EventOrderImport(APIView):
    # This function ATTEMPTS to import the csv from frontend
    # Start by deleting all existing event cats

    parser_classes = (FormParser, MultiPartParser)

    def post(self, request):
        EventOrder.objects.all().delete()
        # Convert the InMemoryUploadedFile to a NamedTemporaryFile
        for csv_upload in request.FILES.values():
            file_temp = tempfile.NamedTemporaryFile()
            file_temp.write(csv_upload.read())
            print(file_temp.name) # This is the path.

            with open(file_temp.name, newline='') as f:
                reader = csv.reader(f)
                next(reader) # skips the first row

                for row in reader:

                    if row:
                        data = {
                            'event': row[0],
                            'event_order': row[1]
                        }
                        serializer = WriteEventOrderSerializer(data=data)
                        if serializer.is_valid():
                            serializer.save()

                event_orders = EventOrder.objects.all()

                serializer = WriteEventOrderSerializer(event_orders, many=True)

                file_temp.close()

                return Response(serializer.data)

        return Response({"Success!": "CSV imported OK"})

SERIALIZER

class WriteEventOrderSerializer(serializers.ModelSerializer):

    class Meta:
        model = EventOrder
        fields = ('event', 'event_order',)

ПРИМЕР CSV, КОТОРЫЙ НЕ РАБОТАЕТ

Event,Event_Order
W 2x Championship,1
W 2x Senior,2
W Lwt 2x ,3
W J18 2x ,4
W 2x Intermediate,5
W 2x Club,6
W J16 2x ,7
W MasB 2x ,8
W MasC 2x ,9
W MasD 2x Championship,10
W MasD 2x Club,11
W MasE 2x ,12
W MasE 2x Championship,13
W MasE 2x Club,14
W MasF 2x ,15
W MasG 2x ,16
W 2- Championship,17
W 2- Senior,18
W J18 2- ,19
W 2- Club,20
W MasB 2- ,21
W MasC/D 2- ,22
W MasD 2- ,23
W MasE/F 2- ,24
Mx 2x ,25
Mx MasB/C 2x ,26
Mx MasD 2x ,27
Mx MasE 2x ,28
Mx MasF/G/H 2x ,29
Op 2x Championship,30
Op 2x Senior,31
Op Lwt 2x ,32
Op J18 2x ,33
Op 2x Club,34
Op J16 2x ,35
Op MasB 2x ,36
Op MasC 2x Championship,37
Op MasC 2x Club,38
Op MasD 2x ,39
Op MasE 2x Championship,40
Op MasE 2x Club,41
Op MasF 2x ,42
Op MasG 2x ,43
Op MasH/I 2x ,44
Op 2- Championship,45
Op 2- Senior,46
Op J18 2- ,47
Op 2- Club,48
Op MasB/C 2- ,49
Op MasD 2- ,50
Op MasE 2- ,51
Op MasF 2- ,52
Op MasG/H 2- ,53

ERROR TRACEBACK

/var/folders/3l/z2qry1p532n1kjhtwb5g6_fm0000gn/T/tmpo72y4kfn
Internal Server Error: /api/event-order-import/
Traceback (most recent call last):
  File "/Users/sianalcock/.local/share/virtualenvs/pairsheadoftheriver-vxdBwZ1a/lib/python3.10/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/Users/sianalcock/.local/share/virtualenvs/pairsheadoftheriver-vxdBwZ1a/lib/python3.10/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/sianalcock/.local/share/virtualenvs/pairsheadoftheriver-vxdBwZ1a/lib/python3.10/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/sianalcock/.local/share/virtualenvs/pairsheadoftheriver-vxdBwZ1a/lib/python3.10/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/sianalcock/.local/share/virtualenvs/pairsheadoftheriver-vxdBwZ1a/lib/python3.10/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "/Users/sianalcock/.local/share/virtualenvs/pairsheadoftheriver-vxdBwZ1a/lib/python3.10/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/Users/sianalcock/.local/share/virtualenvs/pairsheadoftheriver-vxdBwZ1a/lib/python3.10/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "/Users/sianalcock/.local/share/virtualenvs/pairsheadoftheriver-vxdBwZ1a/lib/python3.10/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "/Users/sianalcock/Development/pairsheadoftheriver/results/views/event_order.py", line 70, in post
    next(reader) # skips the first row
StopIteration
[29/Mar/2024 15:39:12] "POST /api/event-order-import/ HTTP/1.1" 500 103983

> ИСПРАВЛЕНО ПУТЕМ ДЕКОДИРОВАНИЯ В UTF-8 ОБНОВЛЕННЫЙ ВИД

class EventOrderImport(APIView):
    # This function ATTEMPTS to import the csv from frontend
    # Start by deleting all existing event cats

    parser_classes = (FormParser, MultiPartParser)

    def post(self, request):
        EventOrder.objects.all().delete()

        def decode_utf8(input_iterator):
            for l in input_iterator:
                yield l.decode('utf-8')

        reader = csv.reader(decode_utf8(request.FILES['file']))
        next(reader) # skips the first row
        for row in reader:
            print(row)

            if row:
                data = {
                    'event': row[0],
                    'event_order': row[1]
                }
                serializer = WriteEventOrderSerializer(data=data)
                if serializer.is_valid():
                    serializer.save()

        event_orders = EventOrder.objects.all()

        serializer = WriteEventOrderSerializer(event_orders, many=True)

        return Response(serializer.data)
Вернуться на верх