miller
miller

Reputation: 1728

Import into tables from Django import_export

I am struggling to populate models in Django by using ForeignKey. Let's say we have as in import_export documentation the following example:

class Author(models.Model):
    id = models.BigAutoField(primary_key=True)
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class Category(models.Model):
    id = models.BigAutoField(primary_key=True)
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class Book(models.Model):
    name = models.CharField('Book name', max_length=100)
    author = models.ForeignKey(Author, blank=True, null=True, )
    ...
    price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    categories = models.ManyToManyField(Category, blank=True)

    def __str__(self):
        return self.name

How can I implement import_export module that can check if there is an existing author by name (not by id), that is not case sensitive, and that can generate a new author if it does not exist?

As an example, let's say the CSV file looks like:

name,author,...,price,categories
J.R.R. Tolkien,Lord of the Rings,...,40,["cat1","cat2"]

Also, if there is a DateTime field, how to generate that in ForeignKey table?

NOTE: I know about use of natural key:

from import_export.fields import Field
from import_export.widgets import ForeignKeyWidget

class AuthorManager(models.Manager):

    def get_by_natural_key(self, name):
        return self.get(name=name)

class Author(models.Model):

    objects = AuthorManager()

    name = models.CharField(max_length=100)
    birthday = models.DateTimeField(auto_now_add=True)

    def natural_key(self):
        return (self.name,)

# Only the author field uses natural foreign keys.
class BookResource(resources.ModelResource):

    author = Field(
        column_name = "author",
        attribute = "author",
        widget = ForeignKeyWidget(Author, use_natural_foreign_keys=True)
    )

    class Meta:
        model = Book

But I am not sure how to check for UPPER or lower case in the CSV. And how to generate a new Author if it does not exist.

Upvotes: 0

Views: 321

Answers (1)

Matthew Hegarty
Matthew Hegarty

Reputation: 4231

There are a couple of ways of creating an FK relation during import if it does not already exist.

Option 1 - override the before_import_row() method

class BookResource(resources.ModelResource):

    # note use of 'iexact' for case-insensitive lookup
    def before_import_row(self, row, **kwargs):
        author_name = row["author"]
        Author.objects.get_or_create(name__iexact=author_name, 
            defaults={"name": author_name})

    # other code omitted

Option 2 - subclass ForeignKeyWidget

Simply subclass ForeignKeyWidget and implement the check in clean():

class AuthorForeignKeyWidget(widgets.ForeignKeyWidget):

    def clean(self, value, row=None, **kwargs):
        author, created = Author.objects.get_or_create(name__iexact=value,
            defaults={"name": value})
        return author


class BookResource(resources.ModelResource):
    author = fields.Field(
        column_name='author',
        attribute='author',
        widget=AuthorForeignKeyWidget(Author))

    # other code omitted

Either way will work fine. I would personally use option 2.

Also, if there is a DateTime field, how to generate that in ForeignKey table?

Since you are calling Author.objects.get_or_create() you can add a date if you wish, for example:

author, created = Author.objects.get_or_create(name__iexact=value, 
    defaults={"name": value, "created": timezone.now()})

If using natural keys you can adjust the code as desired.

Related answer about creating in bulk mode

Upvotes: 1

Related Questions