5

Using Django 1.9 and Python 3.4 I want to copy an existing model instance and all its related data. Below is an example of how I have currently achieved this. My question is, is there a better way?

I have read over posts e.g. Duplicating model instances and their related objects in Django / Algorithm for recusrively duplicating an object this one but, they are over 8 years old and no longer work with Django 1.9+.

Below is how I try to achieve this already, ok or a better way in Django 1.9?

Models

class Book(models.Model):
     name = models.CharField(max_length=80)

class Contributor(models.Model):
     name = models.CharField(max_length=80)
     books = models.ForeignKey("Book", related_name="contributors")

The copy function. I have to recreate contributors after saving the new Book instance otherwise, it will assign existing contributors from the instance I'm copying from.

def copy_book(self, id):
    view = self.context['view']
    book_id = id
    book = Book.objects.get(pk=book_id)
    copy_book_contributors = book.contributors.all()

    book.id = None
    # make a copy of the contributors items.
    book.save()
    for item in copy_book_contributors:
        # We need to copy/save the item as it will reassign the existing one.
        item.id = None
        item.save()
        book.contributors.add(item)
Community
  • 1
  • 1
MarkK
  • 968
  • 2
  • 14
  • 30

1 Answers1

6

For this particular case, you can bulk_create the contributors:

contributor_names = list(book.contributors.values_list('name', flat=True))

book.id = None
book.save()

# create the contributor object with the name and new book id.
contributors = [Contributor(name=name, book_id=book.id) for name in contributor_names]
Contributor.objects.bulk_create(contributors)
AKS
  • 18,983
  • 3
  • 43
  • 54
  • Pants, I forgot about the 'bulk_create' function! The use of a list comprehensions makes it so tidy. Thanks good answer! – MarkK Jan 13 '17 at 14:55
  • I will accept the answer, thanks. The only potential problem I can see is if later I add FK's to contributors model, I'm not sure flat=True would work in this case, maybe I could use values instead. But, this answers my initial question. Thanks. – MarkK Jan 13 '17 at 15:06
  • That's why I mentioned `for this particular case`. If you add additional pks then you can use `values` and the convert that to dict and pass it into Contributor constructor. That will work as well.. – AKS Jan 13 '17 at 15:34