使用表单上传 csv 文件 django

She*_*pat 3 python csv django export-to-csv

我正在开发一个学校管理项目,想要上传 CSV 文件并通过覆盖当前数据或在数据库中更新它来保存它。但它没有被添加到数据库中。另外,我喜欢添加更多字段,因此如果可能的话使用动态(即通过循环),这样我以后就不必更改它。

models.py

class Student(models.Model):
  registration_number = models.CharField(max_length=200, unique=True)
  firstname = models.CharField(max_length=200)
  surname = models.CharField(max_length=200)
  date_of_birth = models.DateField(default=timezone.now)
  current_class = models.ForeignKey(StudentClass, on_delete=models.SET_NULL, blank=True, null=True)
  date_of_admission = models.DateField(default=timezone.now)
  parent_mobile_number = models.CharField(max_length=15)
  address = models.TextField()

class StudentBulkUpload(models.Model):
  date_uploaded = models.DateTimeField(auto_now=True)
  csv_file = models.FileField(upload_to='students/bulkupload/')
Run Code Online (Sandbox Code Playgroud)

forms.py

class StudentBulkUploadForm(forms.ModelForm):
  class Meta:
    model = StudentBulkUpload
    fields = ("csv_file",)

Run Code Online (Sandbox Code Playgroud)

views.py

def uploadcsv(request):
  if request.method == 'GET':
    form = StudentBulkUploadForm()
    return render(request, 'students/students_upload.html', {'form':form})

  # If not GET method then proceed
  try:
    form = StudentBulkUploadForm(data=request.POST, files=request.FILES)
    if form.is_valid():
      csv_file = form.cleaned_data['csv_file']
    if not csv_file.name.endswith('.csv'):
      messages.error(request, 'File is not CSV type')
      return redirect('students:student-upload')
    # If file is too large
    if csv_file.multiple_chunks():
      messages.error(request, 'Uploaded file is too big (%.2f MB)' %(csv_file.size(1000*1000),))
      return redirect('students:student-upload')
    
    file_data = csv_file.read().decode('utf-8')
    lines = file_data.split('\n')

    # loop over the lines and save them in db. If error, store as string and then display
    for line in lines:
      fields = line.split(',')
      data_dict = {}
      print(data_dict)
      try:
        form = StudentBulkUploadForm(data_dict)
        if form.is_valid():
          form.save()
        else:
          logging.getLogger('error_logger').error(form.errors.as_json())
      except Exception as e:
        logging.getLogger('error_logger').error(form.errors.as_json())
        pass
  except Exception as e:
    logging.getLogger('error_logger').error('Unable to upload file. ' + repr(e))
    messages.error(request, 'Unable to upload file. ' + repr(e))
  return redirect('students:student-upload')
Run Code Online (Sandbox Code Playgroud)

student_upload.html

<form method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.as_p }}

    <input type="submit" class="btn btn-success" value="Submit">
</form>
Run Code Online (Sandbox Code Playgroud)

Ach*_*ese 7

是否有必要通过表单传递数据并将其保存到数据库。否则,您可以简单地创建模型的对象并将字典传递给它并保存。

我认为你应该(简而言之):

  • 首先使用上传并保存文件StudentBulkUpload()
  • 获取文件的路径并读取内容
  • 如果模型字段和 csv 列具有相同的名称,那就更好了
  • 循环遍历每一行并创建一个字典,其中在迭代中仅包含学生详细信息
  • 创建一个实例Student()并将字典传递给它并保存
  • StudentClass()对于外键,使用get()csv 中存储的值相应地获取对象

我认为您可以通过两种方式保存学生详细信息:

  • 创建模型对象并通过以正常方式读取每一行来分配值
new_student = Student()
new_student.registration_number = fields[0]
new_student.firstname = fields[1]
# like so for other fields

new_student.save()
Run Code Online (Sandbox Code Playgroud)
  • 创建一个模型对象,创建一个键值字典,其中键对应于模型的字段名称。
# create a dictionary `new_student_details` containing values of a student

new_student = Student()
new_student.__dict__.update(new_student_details)
new_student.save()
Run Code Online (Sandbox Code Playgroud)

创建一个函数来读取 csv 文件并保存学生详细信息

import csv
def save_new_students_from_csv(file_path):
    # do try catch accordingly
    # open csv file, read lines
    with open(file_path, 'r') as fp:
        students = csv.reader(fp, delimiter=',')
        row = 0
        for student in students:
            if row==0:
                headers = student
                row = row + 1
            else:
                # create a dictionary of student details
                new_student_details = {}
                for i in range(len(headers)):
                    new_student_details[headers[i]] = student[i]

                # for the foreign key field current_class in Student you should get the object first and reassign the value to the key
                new_student_details['current_class'] = StudentClass.objects.get() # get the record according to value which is stored in db and csv file

                # create an instance of Student model
                new_student = Student()
                new_student.__dict__.update(new_student_details)
                new_student.save()
                row = row + 1
        fp.close()
Run Code Online (Sandbox Code Playgroud)

您的代码应该如下所示:

def uploadcsv(request):
    if request.method == 'GET':
        form = StudentBulkUploadForm()
        return render(request, 'students/students_upload.html', {'form':form})

    # If not GET method then proceed
    try:
        form = StudentBulkUploadForm(data=request.POST, files=request.FILES)
        if form.is_valid():
            csv_file = form.cleaned_data['csv_file']
            if not csv_file.name.endswith('.csv'):
                messages.error(request, 'File is not CSV type')
                return redirect('students:student-upload')
            # If file is too large
            if csv_file.multiple_chunks():
                messages.error(request, 'Uploaded file is too big (%.2f MB)' %(csv_file.size(1000*1000),))
                return redirect('students:student-upload')

            # save and upload file 
            form.save()

            # get the path of the file saved in the server
            file_path = os.path.join(BASE_DIR, form.csv_file.url)

            # a function to read the file contents and save the student details
            save_new_students_from_csv(file_path)
            # do try catch if necessary
                
    except Exception as e:
        logging.getLogger('error_logger').error('Unable to upload file. ' + repr(e))
        messages.error(request, 'Unable to upload file. ' + repr(e))
    return redirect('students:student-upload')
Run Code Online (Sandbox Code Playgroud)

注意: csv 文件需要遵循正确的格式。你可以Student()随心所欲地保存。无论如何,文件需要上传并读取。Student()必须逐行保存。这只是一个结构。请随意进行必要的更改,因为我已经删除了您的大部分代码