简体   繁体   中英

How to import django-taggit tag in django-import-export

I can't import Django-taggit tags using Django-import-export.

This error is when the value is entered.

Line number: 1 - invalid literal for int() with base 10: 'def'

Also, this error is when the value is blank.

Line number: 2 - Cannot add <QuerySet []> (<class 'django.db.models.query.QuerySet'>). Expected <class 'django.db.models.base.ModelBase'> or str.

I also posted question in This issue .

xlsx table there is an id column too.

在此处输入图片说明

models.py

from django.db import models
from django.urls import reverse
from taggit.managers import TaggableManager

class KnowHow(models.Model):    

    author = models.ForeignKey('auth.User',on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    text = models.TextField(blank=True)
    file = models.FileField(blank=True,upload_to='explicit_knowhows')
    free_tags = TaggableManager(blank=True)

    def __str__(self):
        return self.title

admin.py

from django.contrib import admin
from import_export import resources
from import_export import fields
from import_export.admin import ImportExportModelAdmin

from .models import KnowHow
# Register your models here.

class KnowHowResource(resources.ModelResource):

    class Meta:
        model = KnowHow
        import_id_fields = ['id']

@admin.register(KnowHow)
class knowHowAdmin(ImportExportModelAdmin):
    resource_class = KnowHowResource

My solution:

Custom widget:

from import_export import fields
from import_export import widgets
from taggit.forms import TagField
from taggit.models import Tag

class TagWidget(widgets.ManyToManyWidget):
    def render(self, value, obj=None):
        return self.separator.join(
            [obj.name for obj in value.all()]
        )

    def clean(self, value, row=None, *args, **kwargs):
        values = TagField().clean(value)
        return [
            Tag.objects.get_or_create(name=tag)[0]
            for tag in values
        ]

Then we have to override field as well:

class TagFieldImport(fields.Field):

    def save(self, obj, data, is_m2m=False):
        # This method is overridden because originally code
        # getattr(obj, attrs[-1]).set(cleaned, clean=True) doesn't unpack cleaned value
        if not self.readonly:
            attrs = self.attribute.split('__')
            for attr in attrs[:-1]:
                obj = getattr(obj, attr, None)
            cleaned = self.clean(data)
            if cleaned is not None or self.saves_null_values:
                if not is_m2m:
                    setattr(obj, attrs[-1], cleaned)
                else:
                    # Change only here
                    getattr(obj, attrs[-1]).set(*cleaned, clean=True)

And then to use in the resource like that:

    tags = cure_widgets.TagFieldImport(
        attribute="tags",
        column_name="tags",
        widget=cure_widgets.TagWidget(Tag, separator=", ")
    )

Another possible solution / workaround I use.

Define a helper model text-field to store the comma-separated tag list. Let's call it "tags_char". Then override the save method of the model to convert the comma-separated list to Tag objects.

# models.py
from django.db import models
from taggit.managers import TaggableManager

class Book(models.Model):
    
    tags_char = models.TextField(blank=True)
    tags = TaggableManager(blank=True)
    
    def save(self, *args, **kwargs):
        if ',' in self.tags_char and self.pk:
            for tag in self.tags_char.split(','):
                self.tags.add(tag)
        super(Book, self).save(*args, **kwargs)

The final step is to adjust the ModelResource. We exclude tags from import and instead import to tags_char from the file. Knowing that the conversion will happen afterwards once the objects are saved.

#admin.py
class BookResource(resources.ModelResource):
    class Meta:
        model = Book
        exclude = ('id', 'tags') 

Unfortunately there's one problem with this workaround. Beacuse of the way django import-export works the save() method is called prior to objects being assigned an id (pk). And a tag can not be added if an id does not eixst. This is the reason for checking if self.pk exists in the save method. if ',' in self.tags_char and self.pk

There are three alternatives to get around the issue.

  1. If you're going to complement the data or review it before publishing them to the website. You will need to re-save them manually => Problems solved (this is the case for me)
  2. Just import the file two times. The second time around the id's will exists.
  3. Provide an id in the file to import.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM