Creating copies of a model instance on the fly with explicit declaration on how the object should be cloned with support for limiting fields or related objects copied with unique field detection.
pip install django-clone
Add model_clone
to your INSTALLED_APPS
INSTALLED_APPS = [
...
'model_clone',
...
]
from django.db import models
from django.utils.translation import gettext_lazy as _
from model_clone import CloneMixin
class Tags(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return _(self.name)
class TestModel(CloneMixin, models.Model):
title = models.CharField(max_length=200)
tags = models.ManyToManyField(Tags)
_clone_many_to_many_fields = ['tags']
In [1]: test_obj = TestModel.objects.create(title='New')
In [2]: test_obj.tags.create(name='men')
In [3]: test_obj.tags.create(name='women')
In [4]: clone = test_obj.make_clone(attrs={'title': 'Updated title'})
In [5]: test_obj.pk
Out[5]: 1
In [6]: test_obj.title
Out[6]: 'New'
In [7]: test_obj.tags.all()
Out[7]: <QuerySet [<Tag: men>, <Tag: women>]>
In [8]: clone.pk
Out[8]: 2
In [9]: clone.title
Out[9]: 'Updated title'
In [10]: clone.tags.all()
Out[10]: <QuerySet [<Tag: men>, <Tag: women>]>
- Explicit field names required
_clone_model_fields: Restrict the list of fields to copy from the instance (By default: Copies all
non-unique/auto created/editable model fields).
_clone_many_to_many_fields: Restricted Many to many fields (i.e Test.tags).
_clone_many_to_one_or_one_to_many_fields: Restricted Many to One/One to Many fields.
_clone_one_to_one_fields: Restricted One to One fields.
- Implicit include all except these fields.
_clone_excluded_model_fields (list): Excluded model fields.
_clone_excluded_many_to_many_fields (list): Excluded many to many fields.
_clone_excluded_many_to_one_or_one_to_many_fields (list): Excluded Many to One/One to Many fields.
_clone_excluded_one_to_one_fields (list): Excluded one to one fields.
⚠️ NOTE: Ensure to either set_clone_excluded_*
or_clone_*
. Using both would raise errors.
In [1]: from model_clone import create_copy_of_instance
In [2]: test_obj = TestModel.objects.create(title='New')
In [3]: test_obj.tags.create(name='men')
In [4]: test_obj.tags.create(name='women')
In [5]: clone = create_copy_of_instance(test_obj, attrs={'title': 'Updated title'})
In [6]: test_obj.pk
Out[6]: 1
In [7]: test_obj.title
Out[7]: 'New'
In [8]: test_obj.tags.all()
Out[8]: <QuerySet [<Tag: men>, <Tag: women>]>
In [9]: clone.pk
Out[9]: 2
In [10]: clone.title
Out[10]: 'Updated title'
In [11]: clone.tags.all()
Out[11]: <QuerySet []>
⚠️ NOTE: This method won't copy over related objects like Many to Many/One to Many relationships.
⚠️ NOTE: Ensure that required fields skipped from being cloned are passed in using theattrs
dictionary.
Change
from django.contrib import admin
from django.contrib.admin import ModelAdmin
@admin.register(TestModel)
class ModelToCloneAdmin(ModelAdmin):
pass
to
from model_clone import CloneModelAdmin
@admin.register(TestModel)
class ModelToCloneAdmin(CloneModelAdmin):
pass
include_duplicate_action
: Enables/Disables the Duplicate action in the List view (Defaults to True)
include_duplicate_object_link
: Enables/Disables the Duplicate action in the Change view (Defaults to
True)
⚠️ NOTE: Ensure thatmodel_clone
is placed beforedjango.contrib.admin
INSTALLED_APPS = [
'model_clone',
'django.contrib.admin',
'...',
]
Thanks goes to these wonderful people:
Gerben Neven 🐛 |
Sebastian Kapunkt 💻 🐛 |
Andrés Portillo 🐛 |
This project follows the all-contributors specification. Contributions of any kind welcome!