Import fails with KeyError: 'id'

1.6k views Asked by At

I am trying to import an excel file with some data to a model with foreignKey but I get KeyError: 'id'

I have exported the data and imported same exported file with success, when I add new data to same file the problem shows up.

Import:

This importer will import the following fields: name, Brand__BrandName, gender, stockPrice

File to import: Format: xlsx Errors Line number: 1 - 'id' p3, B1, unisex, 99999

Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/import_export/resources.py", line 639, in import_row
instance, new = self.get_or_init_instance(instance_loader, row)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/import_export/resources.py", line 334, in get_or_init_instance
instance = self.get_instance(instance_loader, row)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/import_export/resources.py", line 321, in get_instance
import_id_fields = [
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/import_export/resources.py", line 322, in <listcomp>
self.fields[f] for f in self.get_import_id_fields()
KeyError: 'id'

Line number: 2 - 'id' p2, b2, unisex, 99999

Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/import_export/resources.py", line 639, in import_row
instance, new = self.get_or_init_instance(instance_loader, row)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/import_export/resources.py", line 334, in get_or_init_instance
instance = self.get_instance(instance_loader, row)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/import_export/resources.py", line 321, in get_instance
import_id_fields = [
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/import_export/resources.py", line 322, in <listcomp>
self.fields[f] for f in self.get_import_id_fields()
KeyError: 'id'

my model:

    from django.db import models
    from django.utils import timezone
    from django.conf import settings
    
 
    class Brand(models.Model):
       
        BrandName= models.CharField(max_length=40)
         
      
        def __str__(self):
             return self.BrandName
    
    
    
    
    class Perfume(models.Model):
        
        genderChoice = (
            ('unisex','unisex'), ('male', 'male'), ('female', 'female'))
             
        name = models.CharField(max_length=50)
        Brand= models.ForeignKey(to=Brand,on_delete=models.CASCADE, blank=False)
    
        gender = models.CharField(max_length=7, choices=genderChoice, default='unisex')
        
        description = models.TextField()
        stockPrice = models.IntegerField(default=99999)
    
        active = models.BooleanField(default=False)
        show_for_consumer = models.BooleanField(default=False) 
        created = models.DateField()
        author =models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
      
       
    
        def __str__(self):
            return self.name        
    
    
    class Pricing(models.Model):
        product =models.ForeignKey(Perfume, on_delete=models.CASCADE,related_name='prices')
        
        
         
        price= models.IntegerField()
        volume= models.IntegerField()
        def __str__(self):
             return 'Perfume {} - Price{} - Volume {}'.format(self.product.name,self.price, self.volume)

my admin.py

    from django.contrib import admin
    from .models import Perfume, Brand, Pricing
    from import_export.admin import ImportExportModelAdmin
    from import_export import resources, fields
    from import_export.widgets import ForeignKeyWidget
    
    # Register your models here.
    
    class PriceInline(admin.StackedInline):
        model = Pricing
    
    
        
    class ModelInline(admin.StackedInline):
        model = Perfume
    class BrandAdmin(admin.ModelAdmin):
        model = Brand
        inlines = [ModelInline,]
    
    #for import settings
    class ProductResorce(resources.ModelResource):
    
                          
     
        class Meta:
           
            model = Perfume
            exclude = ('active', 'show_for_consumer', 'created', 'author')
            fields=('name', 'Brand__BrandName', 'gender', 'stockPrice')
            export_order = ('name', 'Brand__BrandName', 'gender', 'stockPrice')
    #showing in product add

    class ProductImportAdmin(ImportExportModelAdmin):
    
        resource_class = ProductResorce
        list_display = ['name', 'gender', 'Brand']
        fields = ['name', 'gender', 'Brand', 'author', 'created', 'stockPrice', 'description']
        
        inlines = [PriceInline]
        
    
    
    admin.site.register(Brand, BrandAdmin)
    admin.site.register(Perfume, ProductImportAdmin)

exported file

1

There are 1 answers

3
Matthew Hegarty On

The problem is that when you import the file for the second time, the import process will try to match each row against existing records in the database, and try to update the stored data.

It will use a field called 'id' by default to do this (docs).

However, you have no field called 'id' in your dataset or on your resource (ProductResorce), hence it throws the error you are seeing.

To fix it, you should add a field which uniquely identifies each entry, and declare it on the resource. For example if you use a field called 'sku', you can declare it on your resource:

class ProductResource(resources.ModelResource):
  class Meta:         
    model = Perfume
    import_id_fields = ('sku',)

You can use more than one field as the unique identifier.

Having a unique identifier is really useful because it means you can safely re-run jobs without the risk of failures or creating duplicates.