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