Create a clone of a django model instance.


Keywords
django, django-clone, django clone, django object clone, clone-django, model cloning, django instance duplication, django duplication
Licenses
MIT/Apache-2.0
Install
pip install django-clone==0.1.2

Documentation

PyPI version CircleCI All Contributors PyPI - Python Version PyPI - License PyPI - Django Version Codacy Badge Codacy Badge Known Vulnerabilities

django-clone

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.

Table of contents

Installation

pip install django-clone

Add model_clone to your INSTALLED_APPS

INSTALLED_APPS = [
    ...
    'model_clone',
    ...
]

Usage

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']

Duplicating a model instance

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>]>

CloneMixin attributes

  • 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.

Creating clones without subclassing CloneMixin.

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 the attrs dictionary.

Duplicating Models from Django Admin view.

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

List View

Screenshot

Change View

Screenshot

SETTINGS

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 that model_clone is placed before django.contrib.admin

INSTALLED_APPS = [
    'model_clone',
    'django.contrib.admin',
    '...',
]

Contributors

Thanks goes to these wonderful people:

Gerben Neven
Gerben Neven

🐛 ⚠️ 💻
Sebastian Kapunkt
Sebastian Kapunkt

💻 🐛
Andrés Portillo
Andrés Portillo

🐛

This project follows the all-contributors specification. Contributions of any kind welcome!