pythondjangodjango-import-export

ForeignKeyWidget in django-import-export Showing ID Instead of Custom Column Value


I am using the django-import-export library to handle data import and export in my Django application. I have several models with ForeignKey relationships, and I want to display and use the custom column values instead of the IDs in my import/export files.

# models.py

class YearCategoryMalnutrition(models.Model):
    id = models.AutoField(primary_key=True)
    year_recorded = models.CharField(max_length=4, default="")
    last_update = models.DateTimeField(auto_now=True, null=True)

    def __str__(self):
        return self.year_recorded


class AgeCategory(models.Model):
    AGE_CATEGORIES = (
        ('0-5 years old (0-60 months)', '0-5 years old (0-60 months)'),
        ('5.08-10.0 years old (61-120 months)', '5.08-10.0 years old (61-120 months)'),
        ('10.08-19.0 years old (121-228 months)', '10.08-19.0 years old (121-228 months)'),
        ('Adults 20 years old above', 'Adults 20 years old above'),
    )
    age_category_name = models.CharField(
        max_length=50,
        choices=AGE_CATEGORIES,
        default='0-5 years old (0-60 months)',
    )
    last_update = models.DateTimeField(auto_now=True, null=True)

    class Meta:
        verbose_name_plural = "Age Categories"

    def __str__(self):
        return self.age_category_name


class LevelCategory(models.Model):
    LEVEL_CATEGORIES = (
        ('National Level', 'National Level'),
        ('Regional Level', 'Regional Level'),
        ('Province Level', 'Province Level'),
        ('Municipality Level', 'Municipality Level'),
        ('Barangay Level', 'Barangay Level'),
    )
    level_category_name = models.CharField(
        max_length=50,
        choices=LEVEL_CATEGORIES,
        default='National Level',
    )
    last_update = models.DateTimeField(auto_now=True, null=True)

    class Meta:
        verbose_name_plural = "Level Categories"

    def __str__(self):
        return self.level_category_name


class NationalLevel(models.Model):
    id = models.AutoField(primary_key=True)
    country_name = models.CharField(max_length=255, default="Philippines")
    overweight = models.FloatField(default=0.0)
    underweight = models.FloatField(default=0.0)
    stunting = models.FloatField(default=0.0)
    wasting = models.FloatField(default=0.0)
    overweight_error = models.FloatField(default=0.0)
    underweight_error = models.FloatField(default=0.0)
    stunting_error = models.FloatField(default=0.0)
    wasting_error = models.FloatField(default=0.0)
    year_record = models.ForeignKey(YearCategoryMalnutrition, on_delete=models.PROTECT)
    age_category = models.ForeignKey(AgeCategory, on_delete=models.PROTECT, null=True)
    level_category = models.ForeignKey(LevelCategory, on_delete=models.PROTECT, null=True)
    last_update = models.DateTimeField(auto_now=True)
# resources.py

from import_export import resources, fields
from import_export.widgets import ForeignKeyWidget
from .models import NationalLevel, YearCategoryMalnutrition, AgeCategory, LevelCategory

class NationalLevelResource(resources.ModelResource):
    
    year_record = fields.Field(
        column_name='year_record',
        attribute='year_record',
        widget=ForeignKeyWidget(YearCategoryMalnutrition, 'year_recorded')
    )
    
    age_category = fields.Field(
        column_name='age_category',
        attribute='age_category',
        widget=ForeignKeyWidget(AgeCategory, 'age_category_name')
    )
    
    level_category = fields.Field(
        column_name='level_category',
        attribute='level_category',
        widget=ForeignKeyWidget(LevelCategory, 'level_category_name')
    )

    class Meta:
        model = NationalLevel
        fields = ('country_name', 'overweight', 'underweight', 'stunting',
                  'wasting', 'overweight_error', 'underweight_error', 'stunting_error',
                  'wasting_error', 'year_record__year_recorded', 'age_category', 'level_category', 'last_update')
        export_order = ('country_name', 'overweight', 'underweight', 'stunting',
                        'wasting', 'overweight_error', 'underweight_error', 'stunting_error',
                        'wasting_error', 'year_record', 'age_category', 'level_category', 'last_update')
# admin.py
from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
from .models import NationalLevel
from .resources import NationalLevelResource

class NationalLevelAdmin(ImportExportModelAdmin):
    list_display = (
        'id', 
        'country_name',
        'overweight',
        'underweight',
        'stunting',
        'wasting',
        'overweight_error',
        'underweight_error',
        'stunting_error',
        'wasting_error',
        'year_record',
        'age_category',
        'level_category',
        'last_update',
    )
    ordering = ('year_record',)
    list_filter = ('year_record', 'age_category', 'level_category')
    search_fields = ('country_name',)
    list_per_page = 25
    resource_class = NationalLevelResource

When I export data, I want to show the value instead of the ID. Additionally, when I import data, I want to accept the value instead of the ID for my year_record.


Solution

  • This section looks to me like it is declared correctly:

    class NationalLevelResource(resources.ModelResource):
        
        year_record = fields.Field(
            column_name='year_record',
            attribute='year_record',
            widget=ForeignKeyWidget(YearCategoryMalnutrition, 'year_recorded')
        )
    

    You are correctly defining the FK relationship and using 'year_recorded' as the lookup value. It is assumed that year_recorded can uniquely identify the FK record (Docs).

    I think the issue is that you have declared year_record__year_recorded in your fields list. Try changing this to year_record, and see if that helps.