30

I have two models as Follows:

class Book(models.Model):
    title = models.CharField(max_length=100)
    year = models.IntegerField(max_lenght=4)
    author = models.ManyToManyField(null=true, blank=true)


class Author(models.CustomUser):
    # some fields

Now, what I am looking to do is add an Author to multiple Book objects without iterating over the list of book objects.

Django's update method does not support ManyToManyField as per docs. It says:

You can only set non-relation fields and ForeignKey fields using this method. To update a non-relation field, provide the new value as a constant. To update ForeignKey fields, set the new value to be the new model instance you want to point to.

So currently I am doing the following which is highly inefficient as I'll be hitting the database for each book object.

author = Author.objects.get(pk=1)
books = Book.objects.filter(**kwargs) # say this returns 100 objects
# each loop here will hit the database making it really inefficient
# for long lists.
for book in books:
    book.authors.add(author)
    book.save()

I am pretty sure there is a way around this, but I am just not able to find it in the documentation.

ouflak
  • 2,458
  • 10
  • 44
  • 49
Amyth
  • 32,527
  • 26
  • 93
  • 135
  • 2
    https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/ check this.....Adding via the ‘other’ end of an m2m section .... – boltsfrombluesky Jun 28 '13 at 13:10
  • possible duplicate of [How to create an object for a Django model with a many to many field?](http://stackoverflow.com/questions/6996176/how-to-create-an-object-for-a-django-model-with-a-many-to-many-field) – AncientSwordRage Feb 10 '15 at 16:18

2 Answers2

21

The answer is here:
https://stackoverflow.com/a/10116452/202168

In short, (in Django >= 1.4) you can do a bulk_create on the through model, even if you haven't defined a custom through model.

Community
  • 1
  • 1
Anentropic
  • 32,188
  • 12
  • 99
  • 147
10

Django - 2.27 Python 3.8

Let filter out the set of books

books = Book.objects.filter(...)

Create a author

author = Author.objects.create(...)

Add related_name for author field in Book model as author_book.

...
author = models.ManyToManyField(null=true, blank=true, related_name="author_book")
...

now add the author into set of books using reverse relation technique.

author.author_book.add(*books)

use * to unpack the query set

Note: we do not need to explicitly call save()

Uzama Zaid
  • 374
  • 3
  • 10
  • 1
    I think its important to note that running `.add()` actually saves the addition as well, which is the entire ppint of bulk_update(). Good answer, I think it is overlooked since not many people realize that it does indeed save – Heinrich Mar 29 '21 at 18:53
  • Yeh. It does saves as well – Uzama Zaid Apr 06 '21 at 03:50
  • Where does it come from? Any link in the doc? – ThePhi Jun 09 '21 at 07:08
  • It can't work: the related name `author_book` is not an attribute of `author` but of `book`. It should be `author.book.add(*books)`. But it doesn't solve the `bulk_update` problem neither. – ThePhi Jun 09 '21 at 07:17