1

This question is about RelatedManager.add() which is used to add model object instances to a many-to-one or many-to-many relation. To illustrate my question, let's say I have the following two models:

class Book(models.Model):
    title = models.CharField()

class Author(models.Model):
    name = models.CharField()
    books = models.ManyToManyField(Book)

Now, for each Author I have a list of Book objects that I want to add to the relation. At the moment, I'm doing it like this:

book_objects = [
    <Book: Harry Potter and the Philosopher's Stone>, 
    <Book: Harry Potter and the Chamber of Secrets>,
    <Book: Harry Potter and the Prisoner of Azkaban>
]

jk_rowling = Author.objects.get(name='J. K. Rowling')

for book in book_objects:
    jk_rowling.books.add(book)
jk_rowling.save()

However, this is not very efficient as I have to deal with thousands of objects to be added to a relation of this kind and it takes ages. Rather, the Django documentation recommends to call add() with more than one model object as an argument. That is, I would have to do it like the following:

jk_rowling.books.add(book1, book2, book3)

But I cannot do this because I don't know the number of objects added to the relation beforehand. I don't understand why I can't do the following:

jk_rowling.books.add(book_objects)

Why doesn't add() accept an iterable as an argument? And how can I add a large amount of objects to a relation like this more efficiently than calling add() for each object separately? Thanks a lot!

pemistahl
  • 9,304
  • 8
  • 45
  • 75
  • 1
    This will help: http://stackoverflow.com/questions/6996176/how-to-create-an-object-for-a-django-model-with-a-many-to-many-field – Thomas Schwärzl Oct 16 '12 at 21:05
  • @init3 Ah, this is interesting. I wasn't aware of the fact that you can actually pass a list of primary keys to the relation like this: `jk_rowling.books = [1, 2, 3]`. This is not stated in Django's documentation. Great, this should save me some database queries. Thanks! – pemistahl Oct 16 '12 at 21:22

1 Answers1

4

You can just expand your array as varargs:

book_objects = [
    <Book: Harry Potter and the Philosopher's Stone>, 
    <Book: Harry Potter and the Chamber of Secrets>,
    <Book: Harry Potter and the Prisoner of Azkaban>
]

jk_rowling = Author.objects.get(name='J. K. Rowling')

jk_rowling.books.add(*book_objects)   # <---------------

jk_rowling.save()
dokkaebi
  • 9,004
  • 3
  • 42
  • 60
  • Oh my! Why haven't I thought about that? It totally makes sense. Thank you very much! You saved me a lot of trouble. :) – pemistahl Oct 16 '12 at 21:35