I've models for
Pages. They are all written by a
from django.db import models class Book(models.Model) author = models.ForeignKey('auth.User') class Chapter(models.Model) author = models.ForeignKey('auth.User') book = models.ForeignKey(Book) class Page(models.Model) author = models.ForeignKey('auth.User') book = models.ForeignKey(Book) chapter = models.ForeignKey(Chapter)
What I'd like to do is duplicate an existing
Book and update it's
User to someone else. The wrinkle is I would also like to duplicate all related model instances to the
Book - all it's
Pages as well!
Things get really tricky when look at a
Page - not only will the new
Pages need to have their
author field updated but they will also need to point to the new
Does Django support an out of the box way of doing this? What would a generic algorithm for duplicating a model look like?
The classes given above are just an example to illustrate the problem I'm having!
This no longer works in Django 1.3 as CollectedObjects was removed. See changeset 14507
from django.db.models.query import CollectedObjects from django.db.models.fields.related import ForeignKey def duplicate(obj, value, field): """ Duplicate all related objects of `obj` setting `field` to `value`. If one of the duplicate objects has an FK to another duplicate object update that as well. Return the duplicate copy of `obj`. """ collected_objs = CollectedObjects() obj._collect_sub_objects(collected_objs) related_models = collected_objs.keys() root_obj = None # Traverse the related models in reverse deletion order. for model in reversed(related_models): # Find all FKs on `model` that point to a `related_model`. fks =  for f in model._meta.fields: if isinstance(f, ForeignKey) and f.rel.to in related_models: fks.append(f) # Replace each `sub_obj` with a duplicate. sub_obj = collected_objs[model] for pk_val, obj in sub_obj.iteritems(): for fk in fks: fk_value = getattr(obj, "%s_id" % fk.name) # If this FK has been duplicated then point to the duplicate. if fk_value in collected_objs[fk.rel.to]: dupe_obj = collected_objs[fk.rel.to][fk_value] setattr(obj, fk.name, dupe_obj) # Duplicate the object and save it. obj.id = None setattr(obj, field, value) obj.save() if root_obj is None: root_obj = obj return root_obj
Here's an easy way to copy your object.
(1) set the id of your original object to None:
book_to_copy.id = None
(2) change the 'author' attribute and save the ojbect:
book_to_copy.author = new_author
(3) INSERT performed instead of UPDATE
(It doesn't address changing the author in the Page--I agree with the comments regarding re-structuring the models)