django-import-export

Django-Import-Export: delete field causes import to stop working


I have a Django model that I want to import from the admin panel using django-import-export.

Everything works fine without the delete field. Here is my resource code:

class DeviceResource(resources.ModelResource):
    def before_import(self, dataset, **kwargs):
        # Some logic to raise errors

    class Meta:
        model = Device
        name = _("Create devices by importing a CSV")
        exclude = (
            "id",
            "created",
            "modified",
            "created_icinga",
        )
        import_id_fields = ("hostname",)

In the DeviceAdmin class, I specify the resource:

@admin.register(Device)
class DeviceAdmin(ImportMixin, admin.ModelAdmin):
    # Some other fields
    resource_classes = [DeviceResource]

    def get_readonly_fields(self, request, obj=None):
        return ["hostname"] if obj else []

    class Meta:
        model = Device

Up to this point, everything works as expected. The problem comes when I add the delete field. Following the documentation, I add this lines to the DeviceResource:

    delete = fields.Field(widget=widgets.BooleanWidget())

    def for_delete(self, row, instance):
        return self.fields["delete"].clean(row)

And I include the delete header in the CSV. This setup allows me to delete devices, but issues occur when creating new devices. If the delete field is set to 0 in the CSV (or 1, which doesn't solve the problem), I get this error for each new device:

- Line number: N - Device object can't be deleted because its id attribute is set to None.

Obviously, if I'm creating the device it's id attribute is None, moreover, I'm not trying to delete the device, the delete field in the CSV is set to 0, so I shouldn't get this error in any case.

If I set skip_diff = True in the DeviceResource Meta class the error goes away and everything works again, but I want to see the differences so I do not want to set skip_diff to True.

I think I can understand why the problem is happening, and actually the documentation states something about this.

However, I do not think this applies to my case because hostname uniquely identifies a Device instance.

Do I have to decide between having the delete option or using skip_diff? It makes no sense to me.

I don't have any idea of how to solve this problem, so any help is appreciated.


Solution

  • One of the main contributors of the library answered me in Github, so I'll basically explain the solution he provided me.

    If you have my case, where you want to have a column in the import CSV called "delete" which if set to '1' deletes the the row, whether it is in the database or not, you have to delete the delete field and declare the for_delete function like so:

    def for_delete(self, row, instance):
        return row.get("delete") == "1"
    

    This solves the problem and works like a charm.