Проблема с внешним ключом Django с библиотекой django-import-export (ошибка IntegrityError при ограничении /import/ FOREIGN KEY)

avatar
user15950957
1 июля 2021 в 16:34
476
1
0

Я относительно новичок в Django и не являюсь продвинутым программистом, поэтому прошу прощения за мое невежество.

Что работает: У меня есть приложение Django, которое использует одну основную модель, которая подключается к двум вторичным моделям с внешними ключами. Приложение может правильно создавать компании из шаблона и из администратора, может правильно отображать раскрывающееся поле «ниша» с использованием внешнего ключа модели «Категория» и может правильно отображать изображения с использованием внешнего ключа из модели CompanyImage.

Что не работает: Библиотека django-import-export может правильно импортировать документ XLS из внешнего интерфейса и из администратора, но ТОЛЬКО в том случае, если я отключу модели Category и CompanyImage, которые полагаются на внешние ключи. Библиотека корректно импортирует с user=models.ForeignKey(User) по умолчанию в моей основной модели компании, но внешние ключи, которые подключаются к вторичным моделям, вызывают ошибку внешнего ключа: IntegrityError at /import/ограничение FOREIGN KEY не удалось.

>

Что мне нужно Импортируемый лист XLS не импортирует поля, использующие внешний ключ, поэтому я хотел бы отключить эти поля, чтобы избежать ошибки внешнего ключа. Было бы неплохо импортировать поле ниши/категории, но я могу обойтись без него.

Что я пробовал Я потратил два дня, пытаясь решить эту проблему. Я пробовал читать документацию по django-import-export. Я попытался добавить list_filter и exclude в класс Meta для модели ресурсов. Я прочитал Импорт иностранных ключей в django-import-export. Я прочитал внешний ключ в django-import-export.

Я был бы очень признателен, если бы кто-нибудь помог мне направить меня в правильном направлении. Спасибо.

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')

Источник

Ответы (1)

avatar
Matthew Hegarty
2 июля 2021 в 15:21
0
  1. У вас есть CompanyResource, определенный в двух местах, так что это может быть источником вашей проблемы. Удалите объявление из admin.py и посмотрите, поможет ли это.

  2. Как вы сказали, fields и exclude используются для определения импортируемых полей модели. fields — это белый список, а exclude — это черный список, поэтому вам не нужны оба списка.

  3. Настройте отладчик (если вы еще этого не сделали) и пройдитесь, чтобы выяснить, что происходит (это может сэкономить дни усилий).

  4. Если он по-прежнему не работает, обновите свой ответ и постарайтесь конкретизировать характер проблемы (см., как задать вопрос).