'django-import-export, importing ManyToMany from XLSX

For my current project I need to import data from excelsheets format .xlsx to the django-admin. I'm struggling with the many2many relations that I have in my models. The importing feature just doesn't recognize the many2many fields and imports only the remaining fields (normal and foreignkey). Same goes for exporting, all fields except the many2many get exported into a excelsheet, although I dont need this feature, just tested it.

For better understanding the code snippets from my models.py, admin.py, resources.py and widgets.py.

I tried the default many2many widget -> in the TeacherResource in resources.py and I also tired overriding the default m2m widget -> StudyfieldWidget in widgets.py

models.py

class Translation(models.Model):
    de = models.TextField(unique=True)
    en = models.TextField(unique=True)

    def __str__(self):
        return self.de 

class Studyfield(models.Model):
    degree = models.CharField(max_length=50)
    title = models.ForeignKey("Translation", on_delete=models.CASCADE, related_name="+")

    def __str__(self):
        return self.title


class Course(models.Model):
    number = models.CharField(max_length=50)
    title = models.ForeignKey("Translation", on_delete=models.CASCADE, related_name="+")

    studyfields = models.ManyToManyField("Studyfield", related_name="course", blank=True)

    def __str__(self):
        return self.title
        

class Teacher(models.Model):
    name = models.CharField(max_length=50, blank=True)

    studyfields = models.ManyToManyField("Studyfield", related_name="teacher", blank=True)
    courses = models.ManyToManyField("Course", related_name="teacher", blank=True)

    def __str__(self):
        return self.name



admin.py

@admin.register(Translation)
class TranslationAdmin(ImportExportModelAdmin):
    resource_class = TranslationResource
    ordering = ('id',)
    formats = [base_formats.XLSX]

@admin.register(Studyfield)
class StudyfieldAdmin(ImportExportModelAdmin):
    resource_class = StudyfieldResource
    ordering = ('id',)
    formats = [base_formats.XLSX]

@admin.register(Course)
class CourseAdmin(ImportExportModelAdmin):
    resource_class = CourseResource
    ordering = ('id',)
    formats = [base_formats.XLSX]
    
@admin.register(Teacher)
class TeacherAdmin(ImportExportModelAdmin):
    resource_class = TeacherResource
    ordering = ('id',)
    formats = [base_formats.XLSX]


resources.py

class TranslationResource(resources.ModelResource):
    class Meta:
        model = Translation
        fields = ('id', 'de', 'en')


class StudyfieldResource(resources.ModelResource):
    title = fields.Field(
        column_name = 'title',
        attribute = 'title',
        widget = TranslationWidget(Translation, 'de'),
    )

    class Meta:
        model = Studyfield
        fields = ('id', 'degree', 'title')


class CourseResource(resources.ModelResource):
    title = fields.Field(
        column_name = 'title',
        attribute = 'title',
        widget = TranslationWidget(Translation, 'de')
    )

    studyfields = fields.Field(
        widget = StudyfieldWidget(Studyfield)
    )

    class Meta:
        model = Course
        fields = ('id', 'number', 'title', 'studyfields')


class TeacherResource(resources.ModelResource):
    studyfields = fields.Field(
        widget = ManyToManyWidget(Studyfield)
    )

    courses = fields.Field(
        widget = ManyToManyWidget(Course)
    )

    class Meta:
        model = Teacher
        fields = ('id', 'name', 'studyfields', 'courses')
    

    
widgets.py

class TranslationWidget(widgets.ForeignKeyWidget):
    def clean(self, value, row=None, *args, **kwargs):
        return self.model.objects.get_or_create(de=value)[0] if value else None

       

class StudyfieldWidget(widgets.ManyToManyWidget):
    def clean(self, value, row=None, *args, **kwargs):
        if not value:
            return self.model.objects.none()
        if isinstance(value, (float, int)):
            ids = [int(value)]
        else:
            ids = value.split(self.separator)
            ids = filter(None, [i.strip() for i in ids])
        return self.model.objects.filter(**{
            '%s__in' % self.field: ids
        })


Solution 1:[1]

So i finally got around to fix this problem and the solution was actually pretty simple:

studyfields = fields.Field(
    column_name = 'provider',
    attribute='provider',
    widget=widgets.ManyToManyWidget(Studyfield, field='title', separator=',')
)

As soon as I added the column_name it worked.

Solution 2:[2]

It seems you didn't set the separator?

This is how I make it work.

studyfields = fields.Field(
    attribute='provider',
    widget=widgets.ManyToManyWidget(Studyfield, field='title', separator=',')
)

Attribute = the name of the column as in your model. field = the name of the related name that you are going to send, in your case, title or degree.

separator = how its going to be separated in the excel (boo,baa,bii)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Lena M
Solution 2 Dharman