Reputation: 693
I created a datamodel in Django, and now I created a script to auto populate the models using web-scraped values. However when I run the script I get the following error: ValueError: variable needs to have a value for field "id" before this many to many relationship can be used
Models.py
class Books(models.Model):
title = models.CharField(max_length=100)
def __str__(self):
return self.title
class Meta:
ordering = ['-title']
class Author(models.Model):
book = models.ManyToManyField(Books)
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=200)
def __str__(self):
return "{} {}".format(self.first_name, self.last_name)
class Meta:
ordering = ['last_name','first_name']
class Book_details(models.Model):
book = models.ForeignKey(Books,
on_delete=models.CASCADE,
null=True) # models.SET_NULL weggehaald
pages = models.CharField(max_length=250)
publ_year = models.CharField(max_length=250)
edition = models.CharField(max_length=30) # paperback, hardcover, audiobook, etc
def __str__(self):
return "{} - pages: <{}>, edition: <{}>".format(self.book.title,
self.pages,
self.edition)#
class Cover(models.Model):
book = models.OneToOneField(Books,
on_delete=models.CASCADE)
path = models.CharField(max_length=500)
def __str__(self):
return "<Cover <path={}>".format(self.id, self.path)
populate_script
def add_book(title):
b = Books.objects.get_or_create(title = title)[0]
print(b)
b.save()
return b
def populate(scraped_tuple):
fake = Faker()
for _ in range(len(scraped_tuple)):
b_title = scraped_tuple[_][0][0]
new_book = add_book(b_title)
b_author_first = scraped_tuple[_][0][1].split(" ")[0]
b_author_last = scraped_tuple[_][0][1].split(" ")[1]
b_pages = scraped_tuple[_][0][2].split(" ")[0]
b_publ_year = fake.year()
b_edition = scraped_tuple[_][0][3].split(",")[0]
b_cover = scraped_tuple[_][0][4]
new_details = Book_details.objects.get_or_create(book = new_book, pages = b_pages, publ_year = b_publ_year, edition = b_edition)[0]
new_author = Author.objects.get_or_create(book = new_book, first_name = b_author_first, last_name = b_author_last)[0]
new_cover = Cover.objects.get_or_create(book = new_book, path = b_cover)[0]
The scraped_tuple is a return value from the webscraper containing the details.
(Part of) the Traceback:
Books.models.DoesNotExist: Author matching query does not exist.
File "C:\path\to\LibraryApp\Library_WebA
pp\Library\populate.py", line 45, in populate
new_author = Author.objects.get_or_create(book = new_book, first_name = b_author_first, last_nam
e = b_author_last)[0]
Followed by:
ValueError: "<Author: Mary McCarthy>" needs to have a value for field "id" before this many-to-many relationship can be used.
So, it seems that something goes awfully wrong when trying to execute the new_author statement, because of the many-to-many field "book" in the Author model. How can I resolve this. Do I need a similar function for an Author object like I have for the Book in add_book()? It seems the new_details statement executes just fine (title and book_details appear correctly in the database in the admin part of Django).
Upvotes: 0
Views: 1463
Reputation: 1152
As mentioned in the docs, user .add() to associate the records in many to many field.
def populate(scraped_tuple):
fake = Faker()
for _ in range(len(scraped_tuple)):
b_title = scraped_tuple[_][0][0]
new_book = add_book(b_title)
b_author_first = scraped_tuple[_][0][1].split(" ")[0]
b_author_last = scraped_tuple[_][0][1].split(" ")[1]
b_pages = scraped_tuple[_][0][2].split(" ")[0]
b_publ_year = fake.year()
b_edition = scraped_tuple[_][0][3].split(",")[0]
b_cover = scraped_tuple[_][0][4]
new_details = Book_details.objects.get_or_create(book = new_book, pages = b_pages, publ_year = b_publ_year, edition = b_edition)[0]
new_author = Author.objects.get_or_create(first_name = b_author_first, last_name = b_author_last)[0]
# add many to many fields this way:
new_author.book.add(new_book)
new_cover = Cover.objects.get_or_create(book = new_book, path = b_cover)[0]
Upvotes: 1