user15950957
user15950957

Reputation: 41

Django foreign key issue with django-import-export library (IntegrityError at /import/ FOREIGN KEY constraint failed)

What is working: I have a Django application that uses one main model which connects to two secondary models with foreign keys. The application can correctly create companies from template and from admin, and can correctly display the "niche" drop-down field using a foreign key to the Category model and can correctly display the images using a foreign key from the CompanyImage model.

What is not working: The django-import-export library can correctly import an XLS document from front end and from admin, but ONLY if I disable the Category and CompanyImage model that are relying on foreign keys. The library does import correctly with the default user=models.ForeignKey(User) in my main Company model, but the foreign keys that connect to the secondary models are causing a foreign key error: IntegrityError at /import/ FOREIGN KEY constraint failed.

What I need The XLS sheet I am importing does not import the fields that use a foreign key, so I would like to disable those fields to avoid the foreign key error. It would be nice to import a niche/category field, but I can do without.

What I've tried I've spent two days trying to fix this problem. I've tried reading the django-import-export documentation. I've tried adding list_filter and exclude in class Meta for the Resource model. I've read through Dealing with import of foreignKeys in django-import-export. I've read through foreign key in django-import-export.

I would be very grateful someone can help steer me in the right direction. Thank you.

Models.py

from django.db import models
from django.contrib.auth.models import User

from phonenumber_field.modelfields import PhoneNumberField
#had to use pip install django-phone-verify==0.1.1
from django.utils import timezone

import uuid
from django.template.defaultfilters import slugify

class Category(models.Model):
    kind = models.CharField(verbose_name='Business Type',max_length=100,blank=True,)

    class Meta:
        verbose_name_plural = "Categories"

    def __str__(self):
        return self.kind    

class Company(models.Model):
    #BASIC
    title = models.CharField(verbose_name='company name',max_length=100,blank=True)
    contact = models.CharField(verbose_name='director',max_length=100,blank=True)
    phone_number = PhoneNumberField(blank=True) 
    email = models.EmailField(max_length=200,blank=True)
    email_host = models.CharField(max_length=200,blank=True)
    website = models.URLField(max_length=200,blank=True)
    facebook = models.URLField(max_length=200,blank=True)
    memo = models.TextField(blank=True)
    niche = models.ForeignKey(Category, default=0000,on_delete=models.SET_DEFAULT)

    #UPLOADS
    profile_picture = models.ImageField(upload_to='prospects/images/', blank=True)
    image = models.ImageField(upload_to='prospects/images/', blank=True)
    file = models.FileField(upload_to='prospects/uploads', blank=True)

    #TIME
    date = models.DateField(default=timezone.now)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    datecompleted = models.DateTimeField(null=True, blank=True) #null for datetime object 

    #BOOLIANS
    important = models.BooleanField(default=False)
    cold = models.BooleanField(default=False, verbose_name='these are cold leads')
    warm = models.BooleanField(default=False, verbose_name='these are warm leads')
    hot = models.BooleanField(default=False, verbose_name='these are hot leads')
    
    #USER
    user = models.ForeignKey(User, on_delete=models.CASCADE,null=True,blank=True)

    #TEST MODEL
    decimal = models.DecimalField(max_digits=5, decimal_places=2, blank=True, default=00.00)
    integer = models.IntegerField(blank=True, default=0000)
    positive_int = models.PositiveIntegerField(null=True, blank=True, default=0000)
    positive_small_int = models.PositiveSmallIntegerField(null=True, blank=True, default=0000)
  

  
    #ADMIN CONSOLE
    class Meta:
        verbose_name_plural = "Companies"
        

    def __str__(self):
        if self.title == "":
            print('empty string')
            return "No Name"
        elif type(self.title) == str:
            return self.title
        else:
            return "No Name" 
    # this makes the title appear in admin console instead of object number


class CompanyImage(models.Model):
    company = models.ForeignKey(Company, default=None, on_delete=models.CASCADE)
    image = models.FileField(upload_to = 'prospects/images/',blank=True)

    def __str__(self):
        return self.company.title

resource.py

from import_export import resources
# from import_export import fields
from import_export.fields import Field
from import_export.fields import widgets
from .models import Company

from django.utils.encoding import force_str, smart_str


# The following widget is to fix an issue with import-export module where if i import any number from an xls file, it imports as a float with a trailing ,0
#could keep it a number and use trunc function to take away decimal but will make string
class DecimalWidget(widgets.NumberWidget):
    def clean(self, value, row=None, *args, **kwargs):
        print()
        print(f"type of value is {type(value)}")
        print()
        if self.is_empty(value):
            return ""
        elif type(value) == float:
            new_string = force_str(value)
            seperator = '.'
            new_string_witout_0 = new_string.split(seperator, 1)[0]
            print()
            print(f"the new type of value is {type(value)}")
            print(f"the new value is {value}")
            print()
            return new_string_witout_0
        else:
            print("Aborting! it's not a float or empty string. will just return it as it is.")
            return value
            print()
            print(f"type of value is {type(value)}")
            print(f" the value returned is {value}")
            print()



class CompanyResource(resources.ModelResource):
    title = Field(attribute='title', column_name='name',widget=DecimalWidget())
    contact = Field(attribute='contact', column_name='contact',widget=DecimalWidget())
    phone_number = Field(attribute='phone_number', column_name='phone',widget=DecimalWidget())
    # niche = Field(attribute='niche', column_name='niche',widget=DecimalWidget())
    class Meta:
        model = Company
        exclude = ('niche')
        fields = ('id','title','contact','phone_number', 'email','email_host','website','facebook')
        export_order = ['id','title','contact','phone_number', 'email','email_host','website','facebook']
        # fields = ( 'id', 'weight' )

admin.py

from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
from import_export.fields import Field
from import_export import resources


# from import_export import resources
from .models import Company,Category, CompanyImage
from.resources import CompanyResource

class CompanyResource(resources.ModelResource):

    class Meta:
        model = Company
       
class CompanyImageAdmin(admin.StackedInline):
    model = CompanyImage

class CompanyAdmin(ImportExportModelAdmin):
    resource_class = CompanyResource
    inlines = [CompanyImageAdmin]


# Register your models here.
admin.site.register(Category)
admin.site.register(Company,CompanyAdmin)

@admin.register(CompanyImage)
class CompanyImageAdmin(admin.ModelAdmin):
    pass

views.py

def importcompanies(request):
    if request.method == 'GET':
        return render(request, 'prospects/import.html')
    else:
        file_format = request.POST['file-format']
        company_resource = CompanyResource()
        dataset = Dataset()
        new_companies = request.FILES['myfile']

        if file_format == 'CSV':
            imported_data = dataset.load(new_companies.read().decode('utf-8'),format='csv')
            result = company_resource.import_data(dataset, dry_run=True, raise_errors=True)
        elif file_format == 'XLSX':
            imported_data = dataset.load(new_companies.read(),format='xlsx')
            result = company_resource.import_data(dataset, dry_run=True, raise_errors=True)

        elif file_format == 'XLS':
            imported_data = dataset.load(new_companies.read(),format='xls')
            result = company_resource.import_data(dataset, dry_run=True, raise_errors=True)

        if result.has_errors():
            messages.error(request, 'Uh oh! Something went wrong...')

        else:
            # Import now
            company_resource.import_data(dataset, dry_run=False)
            messages.success(request, 'Your words were successfully imported')

    return render(request, 'prospects/import.html')

Upvotes: 0

Views: 932

Answers (1)

Matthew Hegarty
Matthew Hegarty

Reputation: 4231

  1. You have CompanyResource defined in two places, so this could be the source of your problem. Remove the declaration from admin.py and see if that helps.

  2. As you say, fields and exclude are used to define which model fields to import. fields is a whitelist, whilst exclude is a blacklist, so you shouldn't need both.

  3. Set up a debugger (if you haven't already) and step through to find out what is going on (this can save days of effort).

Upvotes: 0

Related Questions