【问题标题】:Django Integrity Error in Bulk Import via CSV in Admin在管理员中通过 CSV 批量导入时出现 Django 完整性错误
【发布时间】:2016-10-10 05:14:10
【问题描述】:

我正在尝试在 Django Admin 中实现 CSV 导入并保存与 CSV 文件行相对应的批量数据。我有一个模型 Employee 和一个 OneToOneField 到 Django 的 Auth 模型。我编写了一个接受 csv 文件的自定义表单。但是,当我调用 super().save() 方法时,我得到了一个完整性错误。

我的模型类是:

class Employee(models.Model):
    user = models.OneToOneField(User, primary_key=True)
    company = models.ForeignKey(Companies)
    department = models.ForeignKey(Departments)
    mobile = models.CharField(max_length=16, default="0", blank=True)
    gender = models.CharField(max_length=1, default="m", choices=GENDERS)
    image = models.ImageField(upload_to=getImageUploadPath, null=True, blank=True)
    designation = models.CharField(max_length=64)
    is_hod = models.BooleanField(default=False)
    is_director = models.BooleanField(default=False)

这是我的管理类:

class EmployeeAdmin(admin.ModelAdmin):
    list_display = ('user', 'company', 'department', 'designation', 'is_hod', 'is_director')
    search_fields = ['user__email', 'user__first_name', 'user__last_name']
    form = EmployeeForm

这是我的表单类:

class EmployeeForm(forms.ModelForm):
    company = forms.ModelChoiceField(queryset=Companies.objects.all())
    file_to_import = forms.FileField()

    class Meta:
        model = Employee
        fields = ("company", "file_to_import")

    def save(self, commit=True, *args, **kwargs):
        try:
            company = self.cleaned_data['company']
            records = csv.reader(self.cleaned_data['file_to_import'])
            for line in records:
                # Get CSV Data.

                # Create new employee.
                employee = CreateEmployee(email, firstName, lastName, gender, '', company.id, dept[0].id, designation, 
                           isSuperuser, isHod, isDirector)
            super(EmployeeForm, self).save(*args, **kwargs)

        except Exception as e:
            traceback.print_exc()
            raise forms.ValidationError('Something went wrong.')

CreateEmployee 方法定义为:

@transaction.atomic
def CreateEmployee(email='', firstName='', lastName='', gender='', mobile='',
    companyId='', departmentId='', designation='', isSuperuser=False, isHod=False, isDirector=False):
    try:
        user = User(
            username=email,
            email=email,
            first_name=firstName,
            last_name=lastName,
            is_superuser=isSuperuser,
            is_active=True)
        password = getPassword(firstName, lastName)
        user.set_password(password)
        user.save()

        company = Companies(id=companyId)
        dept = Departments(id=departmentId)

        employee = Employee(
            user=user,
            mobile=mobile,
            gender=gender,
            designation=designation,
            company=company,
            department=dept,
            is_hod=isHod,
            is_director=isDirector)
        employee.save()
        return employee
    except DatabaseError as e:
        raise e
    return None

我在表单的 except 块中遇到异常,错误为:IntegrityError: (1048, "Column 'user_id' cannot be null")

在回溯中,我可以看到super(EmployeeForm, self).save(*args, **kwargs) 行中引发了异常。我假设超级方法正在尝试保存一个实例。

完整的回溯是:

Traceback (most recent call last):
  File "/home/rachit/Projects/project/users/forms.py", line 81, in save
    super(EmployeeForm, self).save(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/forms/models.py", line 455, in save
    construct=False)
  -- lot of text --
IntegrityError: (1048, "Column 'user_id' cannot be null")

我猜 ModelForm 的保存方法正在尝试保存一个实例,但我不希望这种情况发生,因为我创建了多个用户。

我在这里做错了什么?有没有其他方法可以实现我想要的?

TIA。

【问题讨论】:

    标签: python django csv


    【解决方案1】:

    由于您自己进行保存,因此您无需在 Super 表单上调用 save。通常,当您有需要像这样填写的外键字段时,您可以使用 commit=False 来获取未保存模型的实例。但您可以执行以下任一操作:

    def save(self, commit=True, *args, **kwargs):
        try:
            company = self.cleaned_data['company']
            records = csv.reader(self.cleaned_data['file_to_import'])
            for line in records:
                # Get CSV Data.
    
                # Create new employee.
                employee = CreateEmployee(email, firstName, lastName, gender, '', company.id, dept[0].id, designation, 
                           isSuperuser, isHod, isDirector)
            # super(EmployeeForm, self).save(*args, **kwargs)
            # - or -
            super(EmployeeForm, self).save(commit=False)
            # updated based on additional comment
            return employee
    
        except Exception as e:
            traceback.print_exc()
            raise forms.ValidationError('Something went wrong.')
    

    【讨论】:

    • 我评论了super(EmployeeForm, self).save(*args, **kwargs) 行,但它抛出了错误'NoneType' object has no attribute 'save'。当我将其更改为 super(ElmoyeeForm, self).save(commit=False) 时抛出相同的错误:/
    • 你能发布堆栈跟踪吗?
    • 嗯,看起来调用函数可能希望 save 返回一个值,我不确定返回什么值有意义,所以我返回了这个示例中的最后一个员工。 . .
    • 哇,成功了!只有一个小问题仍然存在,在该过程完成后,如预期的那样,管理员显示The Employee "<email>" was added successfully.,其中电子邮件作为最后一条记录的电子邮件。有什么简单的方法可以覆盖此消息吗?
    • @R4chi7: 遗憾的是,没有简单的方法,您基本上必须编写自己的ModelAdmin 风格并将其添加到Employee 模型中,然后覆盖response_add 方法。这可能比它的价值要多得多。
    猜你喜欢
    • 2013-09-06
    • 1970-01-01
    • 2012-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多