Django学生成绩系统前端CSV导入失效排查请求
前端CSV导入失效但Django Admin导入正常的排查问题
我开发了一款学生评估用的Django应用,app_nilai模块包含absensi、tugas、uts、uas等成绩字段,通过外键关联app_matkul模块的mahasiswa、kelas、matakuliah对象。当前端页面上传CSV文件时导入功能完全失效,但Django Admin后台的导入功能可正常运行,且前端操作后无任何错误提示。以下是相关代码,请求排查问题所在。
Forms.py (app_nilai)
class NilaiForm(ModelForm): def __init__(self, *args, **kwargs): super(NilaiForm, self).__init__(*args, **kwargs) self.fields['mahasiswa'].widget.attrs['readonly'] = True self.fields['mahasiswa'].widget.attrs['class'] = 'form-control-plaintext' class Meta: model = Nilai fields = ('mahasiswa', 'absensi', 'tugas', 'uts', 'uas') widgets = { 'mahasiswa': forms.TextInput(attrs={'readonly': 'readonly', 'class': 'form-control-plaintext'}), 'absensi': forms.TextInput(attrs={'class': 'form-control'}), 'tugas': forms.TextInput(attrs={'class': 'form-control'}), 'uts': forms.TextInput(attrs={'class': 'form-control'}), 'uas': forms.TextInput(attrs={'class': 'form-control'}) } class UploadCSVForm(forms.Form): csv_file = forms.FileField()
views.py (app_nilai)
def import_csv(request, kelas_id): kelas = get_object_or_404(Kelas, id=kelas_id) if request.method == 'POST': form = UploadCSVForm(request.POST, request.FILES) if form.is_valid(): csv_file = request.FILES['csv_file'] dataset = Dataset() imported_data = dataset.load(csv_file.read().decode('utf-8'), format='csv') nilai_resource = NilaiResource() result = nilai_resource.import_data(imported_data, dry_run=True, use_transactions=True, raise_errors=True, kelas_id=kelas_id) if not result.has_errors(): import_result = nilai_resource.import_data(imported_data, dry_run=False, use_transactions=True, raise_errors=True, kelas_id=kelas_id, update=True) messages.success(request, 'Data berhasil diimpor.') else: messages.error(request, 'Terjadi kesalahan saat mengimpor data.') else: messages.error(request, 'Form tidak valid.') return redirect('nilai_kelas', kelas_id=kelas_id) form = UploadCSVForm() return render(request, 'import_csv.html', {'form': form, 'kelas': kelas})
resource.py (app_nilai)
class NilaiResource(resources.ModelResource): data_id = fields.Field( column_name='id', attribute='pk' ) mahasiswa = fields.Field( column_name='mahasiswa', attribute='mahasiswa', widget=ForeignKeyWidget(Mahasiswa, 'id') ) matakuliah = fields.Field( column_name='matakuliah', # Ganti nama kolom ini untuk menghindari konflik attribute='mata_kuliah', widget=ForeignKeyWidget(MataKuliah, 'id') ) kelas = fields.Field( column_name='kelas', attribute='kelas', widget=ForeignKeyWidget(Kelas, 'id') ) class Meta: model = Nilai fields = ('data_id', 'mahasiswa', 'matakuliah', 'kelas', 'absensi', 'tugas', 'uts', 'uas') import_id_fields = ('data_id',) def before_import_row(self, row, **kwargs): kelas_id = kwargs.get('kelas_id') row['kelas'] = kelas_id
模型代码 (app_nilai)
from tkinter import CASCADE from django.core.validators import MaxValueValidator from django.db import models from django.contrib.auth.models import User from app_matkul.models import Kelas, Mahasiswa, MataKuliah class Nilai(models.Model): mahasiswa = models.ForeignKey(Mahasiswa, on_delete=models.SET_NULL, blank=True, null=True) mata_kuliah = models.ForeignKey(MataKuliah, on_delete=models.SET_NULL,blank=True,null=True) kelas = models.ForeignKey(Kelas, on_delete=models.SET_NULL,blank=True,null=True) absensi = models.IntegerField(default=0, validators=[MaxValueValidator(100)]) tugas = models.IntegerField(default=0, validators=[MaxValueValidator(100)]) uts = models.IntegerField(default=0, validators=[MaxValueValidator(100)]) uas = models.IntegerField(default=0, validators=[MaxValueValidator(100)]) updated_at = models.DateTimeField(auto_now=True) @property def perhitungan_nilai(self): nilai_absen = self.absensi * (15/100) nilai_tugas = self.tugas * (35/100) nilai_uts = self.uts * (20/100) nilai_uas = self.uas * (30/100) olah = nilai_absen + nilai_tugas + nilai_uts + nilai_uas if olah >=60 and olah <=70 : return 70 else : return int (olah) def logika_nilai(self): if self.perhitungan_nilai >=80: return 'A' elif self.perhitungan_nilai >=75: return 'AB' elif self.perhitungan_nilai >=70: return 'B' elif self.perhitungan_nilai >=50: return 'D' else : return 'E' @classmethod def rata_rata_nilai(cls, kelas_id): nilai_mahasiswa = cls.objects.filter(kelas__id=kelas_id) total_nilai = sum(a.perhitungan_nilai for a in nilai_mahasiswa) count = nilai_mahasiswa.count() return total_nilai / count if count > 0 else 0 def __str__(self): return str(self.mahasiswa.nama) class Meta : ordering = ['mahasiswa'] verbose_name_plural = '1. Nilai Akhir'
前端HTML代码
<form method="POST" enctype="multipart/form-data" action="{% url 'import_csv' kelas.id %}"> {% csrf_token %} <div class="mb-3"> <label for="csv_file" class="form-label">Import File CSV</label> <input type="file" class="form-control" id="csv_file" name="csv_file" accept=".csv" required> </div> <button type="submit" class="btn btn-sm btn-outline-primary">Import CSV</button> <a href="{% url 'export_csv' kelas.id %}" class="btn btn-sm btn-outline-info">Unduh Template CSV</a> </form>
排查方向
- CSV字段匹配问题:检查前端上传的CSV是否包含
data_id字段,因为*import_id_fields = ('data_id',)*指定该字段为导入匹配依据。如果CSV缺少该字段,会导致无法匹配或创建记录,而Admin可能自动处理了主键生成逻辑。同时确认matakuliah、mahasiswa列是否对应关联模型的id值,避免非数值内容。 - 参数传递有效性:在
before_import_row中添加日志打印(如print(kelas_id)),确认kelas_id是否正确传递。若传递失败,row['kelas']会被设为None,导致导入记录未关联指定班级,看起来像是导入失效。 - dry_run与实际导入的差异:暂时注释
dry_run=True的校验步骤,直接执行实际导入,排查是否dry_run未检测到但实际导入存在的错误。同时确认update=True参数的逻辑:若CSV中data_id不存在,是否会自动创建新记录? - 消息提示渲染问题:检查前端页面是否正确渲染了Django的messages框架。如果成功/错误提示未显示,会误以为导入失效,但实际操作已执行。
- CSV编码问题:若上传的CSV不是UTF-8编码,
csv_file.read().decode('utf-8')会出现解码错误但未抛出异常,导致导入中断。可以添加try-except捕获UnicodeDecodeError并给出提示。
内容的提问来源于stack exchange,提问作者Muhammad Saputra
相关产品推荐
相关产品推荐

