简体   繁体   English

使用表格上传 csv 文件 django

[英]uploading csv file django using a form

I am working on a school management project and want to upload a CSV file and save it either by overriding the current data or updating it in the database.我正在做一个学校管理项目,想上传一个 CSV 文件,并通过覆盖当前数据或在数据库中更新它来保存它。 but it's not being added in the database.但它没有被添加到数据库中。 Also, I like to add more fields so if possible using dynamic(ie by loops) so I don't have to change it later.另外,我喜欢添加更多字段,因此如果可能的话,使用动态(即通过循环),这样我以后就不必更改它了。

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/')

forms.py

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

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')

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>

is it necessary that you should pass the data through the form and save it to db.是否有必要通过表单传递数据并将其保存到数据库。 Otherwise you could simply create an object of the model and pass the dictionary to it and save.否则,您可以简单地创建 model 的 object 并将字典传递给它并保存。

I think that you should (in short):我认为你应该(简而言之):

  • Upload and save the file first using StudentBulkUpload()首先使用StudentBulkUpload()上传并保存文件
  • Get the path for the file and read the contents获取文件路径并读取内容
  • It would be better if you have the same names for model fields and csv columns如果 model 字段和 csv 列具有相同的名称会更好
  • Loop through each line and create a dictionary which contains only a student details at an iteration遍历每一行并创建一个字典,其中仅包含迭代中的学生详细信息
  • Make an instance of Student() and pass the dictionary to it and save创建一个Student()实例并将字典传递给它并保存
  • For the foreign key, get the object from StudentClass() using get() accordingly with the value that is stored in csv对于外键,使用get()StudentClass()获取 object,相应地使用存储在 csv 中的值

You could save the student details in two ways, I think:我认为,您可以通过两种方式保存学生的详细信息:

  • Create a model object and assign values by reading each line the normal way创建一个 model object 并通过以正常方式读取每一行来分配值
new_student = Student()
new_student.registration_number = fields[0]
new_student.firstname = fields[1]
# like so for other fields

new_student.save()
  • Create a model object, create a dictionary of key values where keys corresponds to the field names of the model.创建一个 model object,创建一个键值字典,其中键对应于 model 的字段名称。
# create a dictionary `new_student_details` containing values of a student

new_student = Student()
new_student.__dict__.update(new_student_details)
new_student.save()

Create a function to read the csv file and save student details创建 function 以读取 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()

Your code should look something like this after:您的代码应如下所示:

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')

NOTE: The csv file needs to follow proper formatting.注意: csv 文件需要遵循正确的格式。 You could save Student() anyway you like.您可以随意保存Student() Anyhow, the file needs to be uploaded and read.无论如何,文件需要上传和阅读。 Line by line Student() has to be saved.必须逐行保存Student() This is just a structure.这只是一个结构。 Feel free to make necessary changes since I've removed most of your code由于我已经删除了您的大部分代码,请随意进行必要的更改

In your code, you're doing all the parsing and decoding of the CSV file instead of using already written code.在您的代码中,您正在执行 CSV 文件的所有解析和解码,而不是使用已经编写的代码。 I'd suggest to approach this using the CSV import module for Django.我建议使用 Django 的CSV 导入模块来解决这个问题。 That way you can create a model that automatically takes in CSV data and nicely converts that to a model:这样您就可以创建一个 model 自动接收 CSV 数据并将其转换为 model:

from model import CsvModel

class MyCSvModel(CsvModel):
    student_name = CharField()
    foo = IntegerField()

Then, you'd create a django form as usual that can point to this model and that way you can handle user uploads.然后,您将像往常一样创建一个 django 表单,该表单可以指向此 model,这样您就可以处理用户上传。

However, if this isn't an option because you already have a certain model that you want to leave untouched (or whatever reason), check this StackOverflow question out that explains how to use the module Pandas to read the csv and transform it into a dataframe.但是,如果这不是一个选项,因为您已经有某个 model 想要保持不变(或任何原因),请检查这个StackOverflow 问题,该问题解释了如何使用模块Pandas读取 Z628CB5675FF524F3E7197B8 dataframe。

Hope this helped!希望这有帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM