14

In Django, is there a way to create a object, create its related objects, then save them all at once?

For example, in the code below:

from django.db import models


class Post(models.Model):
    title = models.CharField(max_length=255)
    body = models.CharField(max_length=255)


class Tag(models.Model):
    post = models.ForeignKey(Post)
    title = models.CharField(max_length=255)

post = Post(title='My Title', body='My Body')
post.tag_set = [Tag(post=post, title='test tag'), Tag(post=post, title='second test tag')]
post.save()

I create a Post object. I then also want to create and associate my Tag objects. I want to avoid saving the Post then saving the Tags because if a post.save() succeeds, then a tag.save() fails, I'm left with a Post with no Tags.

Is there a way in Django to save these all at once or at least enforce better data integrity?

AdamY
  • 851
  • 2
  • 9
  • 20

2 Answers2

12

transactions to the rescue !

from django.db import transaction

with transaction.atomic():
   post = Post.objects.create('My Title', 'My Body')
   post.tag_set = [Tag(post, 'test tag'), Tag(post, 'second test tag')]

As a side note: I think you really want a many to many relationship between Post and Tag...

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
3

override save...

class Post(models.Model):

    ...

    def save(self, *args, **kwargs):
        super(Post, self).save(*args, **kwargs)
        for tag in self.tag_set:
            tag.save()

This way you don't have to write the transaction thing over and over again. If you want to use transactions, just implement it in the save method instead of doing the loop.

Ian Kirkpatrick
  • 1,861
  • 14
  • 33
  • Nice thought but not correctly implemented. You need to call super twice. [Here](https://stackoverflow.com/a/41781704/2996101) and [here](https://stackoverflow.com/a/46962714/2996101) are two examples. – raratiru Oct 28 '17 at 19:12
  • 2
    I would disagree. They're doing super multiple times cause that's how their logic is set up. It doesn't matter how many times you call super. Technically you don't even have to call the super method at all. My logic here goes: do super post stuff, then do your stuff on top of super post stuff, then return the result. You're simply adding on to what happened in the super post. The examples you gave are actually doing something completely different than what is wanted in this question. It just happens to use a method that it is overriding. It's very much unrelated. – Ian Kirkpatrick Oct 28 '17 at 19:52
  • The issue is that when I tried to implement this solution, `post` was a `NoneType` Object. This is probably because the `save()` [method](https://github.com/django/django/blob/master/django/db/models/base.py#L645) does not return something. I just found a [good answer here](https://stackoverflow.com/a/10936874/2996101). – raratiru Oct 28 '17 at 20:09
  • You're right about the save not returning anything. I revised my answer. You still only need to call it once. It's just that self IS the Post object. – Ian Kirkpatrick Oct 28 '17 at 21:53