[Django]-Initializing objects belonging to ManyToManyField from data in-memory

2👍

The only solution I can think off is queuing the operations and perform them when you call save().

class PostponedOpMixin(models.Model):
    def __init__(self, *args, **kwargs):
        self._postponed_ops = []
        super(PostponedOpMixin, self).__init__(*args, **kwargs)

    def _getattr(self, attr):
        result = self
        for part in attr.split('.'):
            result = getattr(result, part)
        return result

    def postpone(self, op, *args, **kwargs):
        if self.pk:  # execute now if self already has a pk
            return self._getattr(op)(*args, **kwargs)             
        self._postponed_ops.append((op, *args, **kwargs))

    def save(self, *args, *kwargs):
        super(PostponedOpMixin, self).save(*args, **kwargs)
        while self._postponed_ops:
            op, args, kwargs = self._postponed_ops.pop(0):
                self._getattr(op)(*args, **kwargs)

    def Meta:
        abstract = True

This way you can do:

class Book(PostponedOpMixin):
    ...
    authors = models.ManyToManyField(Author)
    ...

instance = Book()
instance.title = "Romeo and Juliet"
instance.postpone('authors.add', shakespeare)
...

# and some time later:
instance.save()

This code is untested and intended as a start point. Any bug is left as an exercise for the reader.

2👍

Maybe you could postpone the actual book’s authors adding to the moment you are sure you want to save that book into the database.
In the meantime, you could store a list of processed (but not yet saved) author objects for each book object.

class Book(models.Model):
    title = models.CharField(max_length=128)
    pages = models.PositiveIntegerField()
    authors = models.ManyToManyField(Author)

    @classmethod
    @transaction.atomic
    def create(cls, data):
        try:
            b = cls(title=data["title"],
                    pages=data["pages"])
            b.author_list = list()
            for a in data["authors"]:
                b.authors_list.append(Author.create(a))
            return b
        except Exception:
            # ...
            raise

Then, when you are sure you want to save the object, you have to save all the authors in that list, and then add them to the ‘authors’ field in the corresponding book object.

Leave a comment