353
Foo.objects.get(pk="foo")
<Foo: test>

In the database, I want to add another object which is a copy of the object above.

Suppose my table has one row. I want to insert the first row object into another row with a different primary key. How can I do that?

Artem Likhvar
  • 103
  • 2
  • 7
user426795
  • 11,073
  • 11
  • 35
  • 35

15 Answers15

627

Just change the primary key of your object and run save().

obj = Foo.objects.get(pk=<some_existing_pk>)
obj.pk = None
obj.save()

If you want auto-generated key, set the new key to None.

More on UPDATE/INSERT here.

Official docs on copying model instances: https://docs.djangoproject.com/en/2.2/topics/db/queries/#copying-model-instances

WBC
  • 1,854
  • 4
  • 21
  • 34
miah
  • 8,190
  • 2
  • 24
  • 23
  • 2
    Worth noting that this quotes Django 1.2, we're now up to Django 1.4. Haven't tested whether or not this works, but don't use this answer without being sure that it works for you. – Joe Oct 04 '12 at 11:37
  • 8
    Works fine in 1.4.1 This is probably one of those things that will continue to work for a long while. – frnhr Oct 28 '12 at 13:13
  • this is not a general solution for when you have a database with automatic primary keys... guessing at an unused primary key seems like a bad idea. – FizxMike Jan 09 '14 at 21:38
  • 12
    I had to set both `obj.pk` and `obj.id` to make this work in Django 1.4 – Petr Peller Jan 10 '14 at 18:00
  • 5
    @PetrPeller - the [docs](https://docs.djangoproject.com/en/1.6/topics/db/queries/#copying-model-instances) suggest that's because you're using model inheritance . – Dominic Rodger Jan 15 '14 at 14:10
  • I prefer the solution from @S. Kirby - you should never start setting pks manually – Ron Apr 23 '14 at 08:22
  • 20
    Note: things may be a bit more complex if there are foreign keys, one2one's and m2m's involved (i.e., there may be more complex "deep copy" scenarios) – B Robster Sep 30 '14 at 21:47
  • 3
    if there is a `datetime` field it will change – suhailvs Apr 30 '15 at 04:17
  • This method only works if you don't intend to use the original object after making the copy. – kloddant Jan 28 '19 at 17:27
  • this doesn't copy over the relationships of models towrads the instance aka related fields – Julio Marins Aug 15 '19 at 15:03
  • What if model have other unique fields than id? How would you copy in that case without setting them to None in hard coded way? – Elgin Cahangirov Jan 10 '20 at 07:44
  • @miah, how do you get the newly created object after that? – Krasimir Atanasov Jun 02 '20 at 09:10
  • What if I want to copy say 500 instances (an entire queryset)? Looping over it is slow so wondering if I can somehow use bulk_create? – Kritz Jun 28 '20 at 16:57
  • This is expensive in that in incurs in a database call. It also doesn't clone the object, but the database entry itself (the in-memory one is the same as the original, and they'll mutate together). – WhyNotHugo Sep 16 '20 at 20:05
  • I haven't seen that mentioned, but you might (or might not) want to add obj._state.adding = True before saving, depending on if you want it to act like a brand new object or an already existing one. It might have (or might not) impact on your save() calls and so on. – Ochmar Feb 24 '21 at 11:42
  • This creates an error for me, presumably because both objects are pointing to the same momory location?? – PowerAktar Oct 08 '21 at 20:46
160

The Django documentation for database queries includes a section on copying model instances. Assuming your primary keys are autogenerated, you get the object you want to copy, set the primary key to None, and save the object again:

blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1

blog.pk = None
blog.save() # blog.pk == 2

In this snippet, the first save() creates the original object, and the second save() creates the copy.

If you keep reading the documentation, there are also examples on how to handle two more complex cases: (1) copying an object which is an instance of a model subclass, and (2) also copying related objects, including objects in many-to-many relations.


Note on miah's answer: Setting the pk to None is mentioned in miah's answer, although it's not presented front and center. So my answer mainly serves to emphasize that method as the Django-recommended way to do it.

Historical note: This wasn't explained in the Django docs until version 1.4. It has been possible since before 1.4, though.

Possible future functionality: The aforementioned docs change was made in this ticket. On the ticket's comment thread, there was also some discussion on adding a built-in copy function for model classes, but as far as I know they decided not to tackle that problem yet. So this "manual" way of copying will probably have to do for now.

Flimm
  • 136,138
  • 45
  • 251
  • 267
S. Kirby
  • 7,105
  • 3
  • 25
  • 22
61

Be careful here. This can be extremely expensive if you're in a loop of some kind and you're retrieving objects one by one. If you don't want the call to the database, just do:

from copy import deepcopy

new_instance = deepcopy(object_you_want_copied)
new_instance.id = None
new_instance.save()

It does the same thing as some of these other answers, but it doesn't make the database call to retrieve an object. This is also useful if you want to make a copy of an object that doesn't exist yet in the database.

the_drow
  • 18,571
  • 25
  • 126
  • 193
Troy Grosfield
  • 2,133
  • 20
  • 19
  • 1
    This works great if you have an object, you can deep copy the original object before making changes make changes to the new object and save it. Then you can do some condition checking and depending on if they pass, i.e. the object is in another table that you are checking, you can set the new_instance.id = original_instance.id and save :) Thanks! – radtek Sep 25 '14 at 21:43
  • 3
    This does not work if the model has multiple inheritance levels. – chubao Nov 19 '15 at 02:21
  • 1
    on my case I wanted to create a clone method for the model, which would use the "self" variable and I cannot simply just set the self.pk None, so this solution worked like a charm. I thought about the model_to_dict solution below, but it requires an extra step and it would have the same issue with the through relations, which I have to deal manually anyway so it has no major impact for me. – Anderson Santos Aug 18 '16 at 14:18
53

Use the below code :

from django.forms import model_to_dict

instance = Some.objects.get(slug='something')

kwargs = model_to_dict(instance, exclude=['id'])
new_instance = Some.objects.create(**kwargs)
t_io
  • 1,952
  • 1
  • 19
  • 27
23

There's a clone snippet here, which you can add to your model which does this:

def clone(self):
  new_kwargs = dict([(fld.name, getattr(old, fld.name)) for fld in old._meta.fields if fld.name != old._meta.pk]);
  return self.__class__.objects.create(**new_kwargs)
Dominic Rodger
  • 97,747
  • 36
  • 197
  • 212
  • @user426975 - ah, oh well (I've removed it from my answer). – Dominic Rodger Jan 19 '11 at 12:27
  • Not sure if this is a Django version thing, but the `if` now needs to be `if fld.name != old._meta.pk.name`, ie, the `name` property of the `_meta.pk` instance. – Chris Jun 03 '19 at 15:46
22

How to do this was added to the official Django docs in Django1.4

https://docs.djangoproject.com/en/1.10/topics/db/queries/#copying-model-instances

The official answer is similar to miah's answer, but the docs point out some difficulties with inheritance and related objects, so you should probably make sure you read the docs.

Michael Bylstra
  • 5,042
  • 4
  • 28
  • 24
  • when you open the link it says page not found – Amrit Jan 24 '17 at 07:52
  • The docs no longer exist for Django 1.4. I'll update the answer to point to the latest docs. – Michael Bylstra Jan 25 '17 at 05:24
  • 1
    @MichaelBylstra A good way to have evergreen links is to use `stable` instead of the version number in the URL, like this: https://docs.djangoproject.com/en/stable/topics/db/queries/#copying-model-instances – Flimm Apr 26 '17 at 18:43
12

I've run into a couple gotchas with the accepted answer. Here is my solution.

import copy

def clone(instance):
    cloned = copy.copy(instance) # don't alter original instance
    cloned.pk = None
    try:
        delattr(cloned, '_prefetched_objects_cache')
    except AttributeError:
        pass
    return cloned

Note: this uses solutions that aren't officially sanctioned in the Django docs, and they may cease to work in future versions. I tested this in 1.9.13.

The first improvement is that it allows you to continue using the original instance, by using copy.copy. Even if you don't intend to reuse the instance, it can be safer to do this step if the instance you're cloning was passed as an argument to a function. If not, the caller will unexpectedly have a different instance when the function returns.

copy.copy seems to produce a shallow copy of a Django model instance in the desired way. This is one of the things I did not find documented, but it works by pickling and unpickling, so it's probably well-supported.

Secondly, the approved answer will leave any prefetched results attached to the new instance. Those results shouldn't be associated with the new instance, unless you explicitly copy the to-many relationships. If you traverse the the prefetched relationships, you will get results that don't match the database. Breaking working code when you add a prefetch can be a nasty surprise.

Deleting _prefetched_objects_cache is a quick-and-dirty way to strip away all prefetches. Subsequent to-many accesses work as if there never was a prefetch. Using an undocumented property that begins with an underscore is probably asking for compatibility trouble, but it works for now.

morningstar
  • 8,952
  • 6
  • 31
  • 42
  • I was able to get this to work, but it looks like it may have already changed in 1.11, as I had a property called `_[model_name]_cache`, which, once deleted, i was able to assign a new ID for that related model, then call `save()`. There still could be side effects I haven't determined yet. – trpt4him Aug 02 '19 at 19:34
  • This is extremely important info if you are doing the cloning in a function on the class/mixin, as it will otherwise mess up 'self' and you'll get all confused. – Andreas Bergström Sep 19 '19 at 08:09
6

This is yet another way of cloning the model instance:

d = Foo.objects.filter(pk=1).values().first()   
d.update({'id': None})
duplicate = Foo.objects.create(**d)
Ahtisham
  • 9,170
  • 4
  • 43
  • 57
5

setting pk to None is better, sinse Django can correctly create a pk for you

object_copy = MyObject.objects.get(pk=...)
object_copy.pk = None
object_copy.save()
Ardine
  • 339
  • 4
  • 3
3

This does an in-memory copy that you can mutate independently.

original = CheckoutItem(title="test", ...)
copy = CheckoutItem()

for f in CheckoutItem._meta.fields:
   setattr(copy, f.attname, getattr(original, f.attname))

Or, as a method:


    def clone(self):
        """Returns a clone of this instance."""

        clone = self.__class__()
        for f in self.__class__._meta.fields:
            setattr(clone, f.attname, getattr(self, f.attname))

        return clone
WhyNotHugo
  • 9,423
  • 6
  • 62
  • 70
  • This does not work as intended since it copies the `pk` and `id` as well, saving the clone will effectively update the cloned object. – The-null-Pointer- Dec 02 '21 at 12:22
  • Indeed, the clone will be identical. If you want to save it as a _new_ instance, then just set `clone.pk = None`. (I recommend using `pk` instead of `id` in case the primary key is some other field, e.g.: `uuid`). – WhyNotHugo Dec 03 '21 at 13:04
  • does it work for Foreign Key and ManyToMany field ? – Sandeep Balagopal Feb 20 '23 at 10:31
  • @SandeepBalagopal I'm not entirely sure (I encourage you to try and confirm this), but I think it that the clone would simply have `ForeignKey`s pointing to the same instances as the original. – WhyNotHugo Mar 14 '23 at 15:18
0

To clone a model with multiple inheritance levels, i.e. >= 2, or ModelC below

class ModelA(models.Model):
    info1 = models.CharField(max_length=64)

class ModelB(ModelA):
    info2 = models.CharField(max_length=64)

class ModelC(ModelB):
    info3 = models.CharField(max_length=64)

Please refer the question here.

Community
  • 1
  • 1
chubao
  • 5,871
  • 6
  • 39
  • 64
0

Try this

original_object = Foo.objects.get(pk="foo")
v = vars(original_object)
v.pop("pk")
new_object = Foo(**v)
new_object.save()
Pulkit Pahwa
  • 1,412
  • 11
  • 8
0

There is a package that can do this which creates a UI within the django admin site: https://github.com/RealGeeks/django-modelclone

pip install django-modelclone

Add "modelclone" to INSTALLED_APPS and import it within admin.py.

Then, whenever you want to make a model clonable, you just replace "admin.ModelAdmin" in the given admin model class "modelclone.ClonableModelAdmin". This results in a "Duplicate" button appearing within the instance details page for that given model.

Scott
  • 526
  • 6
  • 10
0

If you have a OneToOneField then you should do it this way:

    tmp = Foo.objects.get(pk=1)
    tmp.pk = None
    tmp.id = None
    instance = tmp
VivienG
  • 2,143
  • 3
  • 24
  • 43
0

This simple process works fine for me:

foo_obj = Foo.objects.get(pk="foo")
foo_values = foo_obj.__dict__
foo_values.pop('_state')
foo_values.pop('id')
foo_new_obj = Foo(**foo_values)
foo_new_obj.save()
Mayur Gupta
  • 303
  • 2
  • 14