Django import export get() returned more than one

Question:

I have a small Django application with cities_light, smart_selects, and import_export. I am able to properly export from the admin site into csv files using resources and ForeignKeyWidget from import_export. However, there is something wrong with my import and I’m not sure how to fix it. When trying to import through the admin site from the same exported csv file, I get jobs.models.City.MultipleObjectsReturned: get() returned more than one City -- it returned 2!.

This question is similar to this one but I still do not understand what is wrong. Would greatly appreciate if the responses contain links to the documentation that explain what I am doing wrong, since I’ve gone through ReadTheDocs but have been unsuccessful at finding my mistake.

Thanks.

models.py

class Country(AbstractCountry):
    pass
connect_default_signals(Country)

class Region(AbstractRegion):
    def __str__(self):
        return self.name

    country = models.ForeignKey(Country, on_delete=models.PROTECT)
connect_default_signals(Region)

class SubRegion(AbstractSubRegion):
    pass
connect_default_signals(SubRegion)


class City(AbstractCity):
    def __str__(self):
        return self.name

    region = models.ForeignKey(Region, on_delete=models.PROTECT)
connect_default_signals(City)


class JobPost(models.Model):
    title = models.CharField(max_length=100)
    company = models.CharField(max_length=100)
    urlCompany = models.URLField(blank=True)
    urlApplication = models.URLField(blank=True)
    contactEmail = models.EmailField(max_length=100, blank=True)
    jobType = models.CharField(max_length=100, blank=True)
    country = models.ForeignKey(Country, on_delete=models.PROTECT, blank=True)
    region = ChainedForeignKey(
        Region,
        chained_field="country",
        chained_model_field="country",
        show_all=False,
        auto_choose=True,
        sort=True,
        blank=True
    )
    city = ChainedForeignKey(
        City,
        chained_field="region",
        chained_model_field="region",
        show_all=False,
        auto_choose=True,
        sort=True,
        blank=True
    )
    createdAt = models.DateTimeField(auto_now_add=True)
    remainingDays = models.IntegerField(default=90, blank=False)
    valid = models.BooleanField(default=True)
    visible = models.BooleanField(default=True)
    description = models.TextField()

admin.py

from .models import JobPost, Country, Region, City

class JobPostResource(resources.ModelResource):
    country = fields.Field(
        column_name='country',
        attribute='country',
        widget=ForeignKeyWidget(Country, 'name'))

    region = fields.Field(
        column_name='region',
        attribute='region',
        widget=ForeignKeyWidget(Region, 'name'))

    city = fields.Field(
        column_name='city',
        attribute='city',
        widget=ForeignKeyWidget(City, 'name'))


    class Meta:
        model = JobPost
        export_order = ('id', 'title', 'company', 'urlCompany', 'urlApplication', 'contactEmail',
                        'jobType', 'country', 'region', 'city', 'createdAt', 'remainingDays',
                        'valid', 'visible', 'description')
        fields = ('id', 'title', 'company', 'urlCompany', 'urlApplication', 'contactEmail',
                        'jobType', 'country', 'region', 'city', 'createdAt', 'remainingDays',
                        'valid', 'visible', 'description')

class JobPostAdmin(SummernoteModelAdmin, ImportExportModelAdmin):
    summernote_fields = ('description', )
    resource_class = JobPostResource
admin.site.register(JobPost, JobPostAdmin)

csv

id,title,company,urlCompany,urlApplication,contactEmail,jobType,country,region,city,createdAt,remainingDays,valid,visible,description
888,xxx,asfasdf,,,,,United States,Texas,Austin,2022-06-10 19:27:36,90,1,1,<p>asdfasdf</p>
Asked By: strontivm

||

Answers:

Came back some months later to answer my own question…

In widgets.py, in the source code of import_export, when I searched for get_queryset to try to understand a bit better what was going on I found this comment:

        Overwrite this method if you want to limit the pool of objects from
        which the related object is retrieved.

        :param value: The field's value in the datasource.
        :param row: The datasource's current row.

        As an example; if you'd like to have ForeignKeyWidget look up a Person
        by their pre- **and** lastname column, you could subclass the widget
        like so::

            class FullNameForeignKeyWidget(ForeignKeyWidget):
                def get_queryset(self, value, row, *args, **kwargs):
                    return self.model.objects.filter(
                        first_name__iexact=row["first_name"],
                        last_name__iexact=row["last_name"]
                    )

This is exactly what I did, I overwrote the method. For example I now have:

class RegionCountryForeignKeyWidget(ForeignKeyWidget):
    def get_queryset(self, value, row, *args, **kwargs):
        return self.model.objects.filter(
            name__iexact=row["region"],
            country__name__iexact=row["country"]
        )

and it works.

Answered By: strontivm