All Projects → tj-django → django-clone

tj-django / django-clone

Licence: MIT license
Controlled Django model instance replication.

Programming Languages

python
139335 projects - #7 most used programming language
Makefile
30231 projects
HTML
75241 projects

Projects that are alternatives of or similar to django-clone

apfs-clone-checker
An utility to check if two files are clones in macOs APFS.
Stars: ✭ 30 (-66.29%)
Mutual labels:  clone, cloning
Netflix
Netflix in React
Stars: ✭ 16 (-82.02%)
Mutual labels:  clone, cloning
itc.lua
A Lua implementation of Interval Tree Clocks
Stars: ✭ 21 (-76.4%)
Mutual labels:  replication
kein-instagram
Instagram UI with React Native using Native Base UI kit
Stars: ✭ 14 (-84.27%)
Mutual labels:  clone
vrrm
rough code for running consensus
Stars: ✭ 18 (-79.78%)
Mutual labels:  replication
go-clone
Clone any Go data structure deeply and thoroughly.
Stars: ✭ 182 (+104.49%)
Mutual labels:  clone
radiusd
Distributed Radius-server to do authentication+accounting.
Stars: ✭ 50 (-43.82%)
Mutual labels:  replication
ldb
Replication of CRDTs
Stars: ✭ 37 (-58.43%)
Mutual labels:  replication
backbeat
Zenko Backbeat is the core engine for asynchronous replication, optimized for queuing metadata updates and dispatching work to long-running tasks in the background.
Stars: ✭ 51 (-42.7%)
Mutual labels:  replication
django-admin-search
Modal filter for django admin
Stars: ✭ 60 (-32.58%)
Mutual labels:  django-admin
OpenGnsys
OpenGnsys (pronounced Open Genesis) is a free and open source project that provides tools for managing and deploying different operating systems.
Stars: ✭ 15 (-83.15%)
Mutual labels:  cloning
BruteSniffing Fisher
hacking tool
Stars: ✭ 24 (-73.03%)
Mutual labels:  cloning
Gmail-Clone
Gmail Clone using flutter
Stars: ✭ 82 (-7.87%)
Mutual labels:  clone
wgd
Python package and CLI for whole-genome duplication related analyses
Stars: ✭ 68 (-23.6%)
Mutual labels:  duplication
cloner
A deep copy algorithm for haxe
Stars: ✭ 24 (-73.03%)
Mutual labels:  clone
uniquify
Uniquify is a Telegram bot interface used to remove duplicate media files from a chat
Stars: ✭ 45 (-49.44%)
Mutual labels:  duplicate
django-admin-extra-urls
Single mixin class to easily add buttons on any Django ModelAdmin related page
Stars: ✭ 26 (-70.79%)
Mutual labels:  django-admin
android-gradle-plugin-transform-patch
android gradle plugin transform patch
Stars: ✭ 28 (-68.54%)
Mutual labels:  duplicate
djadmin
Djadmin is a django admin theme
Stars: ✭ 42 (-52.81%)
Mutual labels:  django-admin
TorrentLeechBot
A powerful telegram mirror bot..
Stars: ✭ 94 (+5.62%)
Mutual labels:  clone
Python Django Downloads Code Style
PyPI - Python Version PyPI - Django Version Downloads Code style: black
PyPI Test Vulnerabilities Coverage Code Quality Dependencies Pre-Commit
PyPI version Test Known Vulnerabilities Codacy Badge
codecov
Codacy Badge Updates pre-commit.ci status

django-clone

Create copies of a model instance with explicit control on how the instance should be duplicated (limiting fields or related objects copied) with unique field detection.

This solves the problem introduced by using instance.pk = None and instance.save() which results in copying more object state than required.

Features

  • 100% test coverage.
  • More control over how a model instance should be duplicated
  • Multi Database support i.e Create duplicates on one or more databases.
  • Restrict fields used for creating a duplicate instance.
  • Detects unique fields and naively adds a suffix copy {count} to each duplicate instance (for supported fields only).
  • Optionally differentiate between a duplicate instance and the original by appending a copy suffix to non unique fields (for supported fields only).

Table of Contents

Installation

Run

pip install django-clone

Usage

Subclassing the CloneModel

CHANGE

from django.db import models

class MyModel(models.Model):
    title = models.CharField(max_length=200)

TO

from django.db import models
from model_clone.models import CloneModel

class MyModel(CloneModel):
    title = models.CharField(max_length=200)

Example

from django.db import models
from django.utils.translation import gettext_lazy as _
from model_clone.models import CloneModel

class TestModel(CloneModel):
    title = models.CharField(max_length=200)
    tags =  models.ManyToManyField('Tags')

    _clone_m2m_fields = ['tags']


class Tags(models.Model):  #  To enable cloning tags directly use `CloneModel` as shown above.
    name = models.CharField(max_length=255)

    def __str__(self):
        return _(self.name)

Using the CloneMixin

from django.db import models
from django.utils.translation import gettext_lazy as _
from model_clone import CloneMixin

class TestModel(CloneMixin, models.Model):
    title = models.CharField(max_length=200)
    tags =  models.ManyToManyField('Tags')

    _clone_m2m_fields = ['tags']


class Tags(models.Model):  #  To enable cloning tags directly use `CloneMixin` as shown above.
    name = models.CharField(max_length=255)

    def __str__(self):
        return _(self.name)

Duplicating a model instance

In [1]: test_obj = TestModel.objects.create(title='New')

In [2]: test_obj.pk
Out[2]: 1

In [3]: test_obj.title
Out[3]: 'New'

In [4]: test_obj.tags.create(name='men')

In [4]: test_obj.tags.create(name='women')

In [5]: test_obj.tags.all()
Out[5]: <QuerySet [<Tag: men>, <Tag: women>]>

In [6]: test_obj_clone = test_obj.make_clone()

In [7]: test_obj_clone.pk
Out[7]: 2

In [8]: test_obj_clone.title
Out[8]: 'New copy 1'

In [9]: test_obj_clone.tags.all()
Out[9]: <QuerySet [<Tag: men>, <Tag: women>]>

In [6]: test_obj_clone = test_obj.make_clone(attrs={'title': 'Updated title'})

In [7]: test_obj_clone.pk
Out[7]: 3

In [8]: test_obj_clone.title
Out[8]: 'Updated title'

In [9]: test_obj_clone.tags.all()
Out[9]: <QuerySet [<Tag: men>, <Tag: women>]>

Bulk cloning a model

In [1]: test_obj = TestModel.objects.create(title='New')

In [2]: test_obj.pk
Out[2]: 1

In [3]: test_obj.title
Out[3]: 'New'

In [4]: test_obj.tags.create(name='men')

In [4]: test_obj.tags.create(name='women')

In [5]: test_obj.tags.all()
Out[5]: <QuerySet [<Tag: men>, <Tag: women>]>

In [6]: test_obj_clones = test_obj.bulk_clone(1000)

In [7]: len(test_obj_clones)
Out[7]: 1000

In [8]: test_obj_clone = test_obj_clones[0]

In [9]: test_obj_clone.pk
Out[9]: 2

In [10]: test_obj_clone.title
Out[10]: 'New copy 1'

In [11]: test_obj_clone.tags.all()
Out[11]: <QuerySet [<Tag: men>, <Tag: women>]>

Multi database support

In [6]: test_obj_clone = test_obj.make_clone(using='replica')  # Replicate test object to a different database.

In [7]: test_obj_clone.pk
Out[7]: 1

In [8]: test_obj_clone.title
Out[8]: 'New'

In [9]: test_obj_clone.tags.all()
Out[9]: <QuerySet [<Tag: men>, <Tag: women>]>

In [10]: test_obj_clone._state.db
Out[10]: 'replica'

CloneMixin attributes

Attribute Description
DUPLICATE_SUFFIX Suffix to append to duplicates
(NOTE: This requires USE_DUPLICATE_SUFFIX_FOR_NON_UNIQUE_FIELDS
to be enabled and supports string fields).
USE_DUPLICATE_SUFFIX_FOR_NON_UNIQUE_FIELDS Enable appending the DUPLICATE_SUFFIX to new cloned instances.
UNIQUE_DUPLICATE_SUFFIX Suffix to append to unique fields
USE_UNIQUE_DUPLICATE_SUFFIX Enable appending the UNIQUE_DUPLICATE_SUFFIX to new cloned instances.
MAX_UNIQUE_DUPLICATE_QUERY_ATTEMPTS The max query attempt while generating unique values for a case of unique conflicts.

Explicit (include only these fields)

Attribute Description
_clone_fields Restrict the list of fields to copy from the instance (By default: Copies all fields excluding auto-created/non editable model fields)
_clone_m2m_fields Restricted Many to many fields (i.e Test.tags)
_clone_m2o_or_o2m_fields Restricted Many to One/One to Many fields
_clone_o2o_fields Restricted One to One fields

Implicit (include all except these fields)

Attribute Description
_clone_excluded_fields Excluded model fields.
_clone_excluded_m2m_fields Excluded many to many fields.
_clone_excluded_m2o_or_o2m_fields Excluded Many to One/One to Many fields.
_clone_excluded_o2o_fields 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.utils import create_copy_of_instance

In [2]: test_obj = TestModel.objects.create(title='New')

In [3]: test_obj.pk
Out[3]: 1

In [4]: test_obj.title
Out[4]: 'New'

In [5]: test_obj.tags.create(name='men')

In [6]: test_obj.tags.create(name='women')

In [7]: test_obj.tags.all()
Out[7]: <QuerySet [<Tag: men>, <Tag: women>]>

In [8]: test_obj_clone = create_copy_of_instance(test_obj, attrs={'title': 'Updated title'})

In [9]: test_obj_clone.pk
Out[9]: 2

In [10]: test_obj_clone.title
Out[10]: 'Updated title'

In [11]: test_obj_clone.tags.all()
Out[11]: <QuerySet []>

NOTE: ⚠️

  • This method won't copy over related objects like Many to Many/One to Many relationships.
  • Ensure that required fields skipped from being cloned are passed in using the attrs kwargs.

Django Admin

Duplicating Models from the Django Admin view.

Change

from django.contrib import admin
from django.contrib.admin import ModelAdmin

@admin.register(TestModel)
class TestModelAdmin(ModelAdmin):
    pass

to

from django.contrib import admin
from model_clone import CloneModelAdmin

@admin.register(TestModel)
class TestModelAdmin(CloneModelAdmin):
    pass
List View

Screenshot

Change View

Screenshot

CloneModelAdmin class attributes

from model_clone import CloneModelAdmin

@admin.register(TestModel)
class TestModelAdmin(CloneModelAdmin):
    # Enables/Disables the Duplicate action in the List view (Defaults to True)
    include_duplicate_action = True
    # Enables/Disables the Duplicate action in the Change view (Defaults to True)
    include_duplicate_object_link = True

NOTE: ⚠️

  • Ensure that model_clone is placed before django.contrib.admin
INSTALLED_APPS = [
    'model_clone',
    'django.contrib.admin',
    '...',
]

Signals

pre_clone_save, post_clone_save

from django.dispatch import receiver
from django.utils import timezone

from model_clone.signals import post_clone_save, pre_clone_save

from sample.models import Edition


@receiver(pre_clone_save, sender=Edition)
def increase_seq(sender, instance, **kwargs):
    instance.seq += 1


@receiver(post_clone_save, sender=Edition)
def update_book_published_at(sender, instance, **kwargs):
    if instance.book:
        instance.book.published_at = timezone.now()
        instance.book.save(update_fields=["published_at"])

Compatibility

Python Supported version
Python2.x <=2.5.3
Python3.5 <=2.9.6
Python3.6+ All versions
Django Supported version
1.11 <=2.7.2
2.x All versions
3.x All versions

Running locally

$ git clone [email protected]:tj-django/django-clone.git
$ make default-user
$ make run

Spins up a django server running the demo app.

Visit http://127.0.0.1:8000

Found a Bug?

To file a bug or submit a patch, please head over to django-clone on github.

If you feel generous and want to show some extra appreciation:

Support me with a

Buy me a coffee

Contributors

Thanks goes to these wonderful people:

Gerben Neven
Gerben Neven

🐛 ⚠️ 💻
Sebastian Kapunkt
Sebastian Kapunkt

💻 🐛 ⚠️
Andrés Portillo
Andrés Portillo

🐛
WhiteSource Renovate
WhiteSource Renovate

🚧
Yuekui
Yuekui

💻 🐛 ⚠️ 📖 🚧
Take Weiland
Take Weiland

⚠️ 🐛 💻
Patrick
Patrick

🐛 💻
Amiel Kollek
Amiel Kollek

💻 🐛 ⚠️
Eric Theise
Eric Theise

📖

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

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].