Reputation: 1728
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
Reputation: 4231
There are a couple of ways of creating an FK relation during import if it does not already exist.
before_import_row()
methodclass 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
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