Giter Club home page Giter Club logo

django-clone's Introduction

4FC889E9-FF59-4E44-9EB6-2AF7DC034C74

Python Django Downloads Code Style
PyPI - Python Version PyPI - Django Version Downloads Code style: black
PyPI Test Vulnerabilities Coverage Code Quality Pre-Commit
PyPI version Test Known Vulnerabilities Codacy Badge
codecov
Codacy Badge 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

pip

pip install django-clone

poetry

poetry add django-clone

Usage

Subclassing the CloneModel

Using the CloneMixin

Using the CloneModel

Duplicating a model instance

Bulk cloning a model

Creating clones without subclassing CloneMixin.

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.

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
_clone_linked_m2m_fields Restricted Many to Many fields that should be linked to the new instance

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.

Django Admin

Duplicating Models from the Django Admin view.

List View

Screenshot

Change View

Screenshot

CloneModelAdmin class attributes

NOTE: ⚠️

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

Advanced Usage

Signals

pre_clone_save, post_clone_save

Multi-database support

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

📖
Daniel Schaffer
Daniel Schaffer

💻 ⚠️

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

django-clone's People

Contributors

akollek avatar allcontributors[bot] avatar andresp99999 avatar danielschaffer avatar dependabot-preview[bot] avatar dependabot[bot] avatar diesieben07 avatar erictheise avatar gerbyzation avatar github-actions[bot] avatar jackton1 avatar pre-commit-ci[bot] avatar ptrck avatar pyup-bot avatar renovate-bot avatar renovate[bot] avatar repo-ranger[bot] avatar sebastiankapunkt avatar yuekui avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

django-clone's Issues

[BUG] Nested and Reverse relationship don't cloned

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug?

I have the next models:

class Indication(CloneMixin, TimeStampedModel):
    ....
    _clone_m2o_or_o2m_fields = ['cases']

class Case(CloneMixin, TimeStampedModel):
    indication = models.ForeignKey(
        'presentations.Indication', on_delete=models.CASCADE, related_name='cases',
    )
    _clone_m2o_or_o2m_fields = ['slides']

class Slide(CloneMixin, TimeStampedModel):
    case = models.ForeignKey(
        'presentations.Case', on_delete=models.CASCADE, related_name='slides',
    )
    survey = models.OneToOneField(
        'surveys.Survey', on_delete=models.SET_NULL, null=True, blank=True,
    )
    
    _clone_o2o_fields = ['survey']

class Survey(CloneMixin, TimeStampedModel, LanguageModelWithDefaultMixin):
	name = models.CharField(
		_('Name'), max_length=400
	)
	description = models.TextField(
		_('Description')
	)
	type = models.CharField(
		choices=surveys_choices.SURVEY_TYPES, max_length=15, default=surveys_choices.SURVEY_GENERAL_TYPE,
	)
	is_published = models.BooleanField(
		_('Users can see it and answer it'), default=True,
	)
	show_all_questions = models.BooleanField(
		_('Show matrix questions expanded'), default=False,
	)
	chart_type = models.CharField(
		max_length=30, choices=presentations_choices.CHART_TYPES, null=True, blank=True,
		help_text=_('Only use if needed draw a specific chart type')
	)

	_clone_m2o_or_o2m_fields = ['questions', ]

	objects = SurveyManager()

	class Meta:
		verbose_name = _('survey')
		verbose_name_plural = _('surveys')

	def __str__(self):
		return self.name

class Question(CloneMixin, TimeStampedModel):
	text = models.TextField(
		_('Text')
	)
	reminder_text = models.TextField(
		null=True, blank=True,
	)
	first_number = models.PositiveSmallIntegerField(
		null=True, blank=True, help_text=_('First number for range numeric questions')
	)
	last_number = models.PositiveSmallIntegerField(
		null=True, blank=True, help_text=_('Last number for range numeric questions')
	)
	order = models.PositiveSmallIntegerField(
		_('Order'), help_text=_('Order for show questions to user')
	)
	type = models.CharField(
		_('Type'), max_length=200, choices=surveys_choices.QUESTION_TYPES, default=surveys_choices.QUESTION_SELECT
	)
	unique_group_text = models.CharField(
		max_length=500,
		help_text=_(
			'This text change in each Question that is part of a group. Used to display the proper label at the charts'
		),
		blank=True,
	)
	indication_type = models.CharField(
		max_length=30, choices=presentations_choices.INDICATION_TYPE, null=True, blank=True,
		help_text=_(
			'Questions using this field belong to demographic surveys, and only will be show when the event has '
			'indication with type equal to questions with the same type'
		)
	)
	is_with_hide_chart = models.BooleanField(
		default=False,
	)
	chart_type = models.CharField(
		max_length=30, choices=presentations_choices.CHART_TYPES, null=True, blank=True,
		help_text=_('Only use if needed draw a specific chart type')
	)
	is_with_random_choices = models.BooleanField(
		default=False, help_text=_('The choices always are random'),
	)
	# Relationships
	conditional_choices = models.ManyToManyField(
		'surveys.QuestionChoice', blank=True,
		help_text=_('If the user select the question into the QuestionChoice, then jump to current question'),
		related_name='questions',
	)
	survey = models.ForeignKey(
		'surveys.Survey', on_delete=models.CASCADE, verbose_name=_('Survey'), related_name='questions',
	)
	question_group = models.ForeignKey(
		'surveys.QuestionGroup', on_delete=models.SET_NULL, null=True, blank=True, related_name='questions',
	)

	_clone_m2m_fields = ['conditional_choices']
	_clone_m2o_or_o2m_fields = ['choices']
	# _clone_m2o_or_o2m_fields = ['choices', 'question_group']

	class Meta:
		verbose_name = _('question')
		verbose_name_plural = _('questions')
		ordering = ('survey', 'order')

	def __str__(self):
		return f'ID: {self.id} / Survey: {self.survey.name}'

class QuestionChoice(CloneMixin, TimeStampedModel):
	question = models.ForeignKey(
		'surveys.Question', on_delete=models.CASCADE, verbose_name=_('Question'), related_name='choices'
	)

I cloned almost all without problem, but, the questions from reverse relationship between Question and Survey, don't clone.

To Reproduce

  1. Configure the admin model to clone:
class CaseInline(admin.TabularInline):
	model = presentations_models.Case
	show_change_link = True
	extra = 1


class IndicationAdmin(CloneModelAdmin):
	list_display = ('id', 'title', 'language')
	inlines = [
		CaseInline,
	]

admin.site.register(presentations_models.Indication, IndicationAdmin)
  1. Clone from django admin
    Screenshot from 2021-09-16 15-02-57

Note:
Survey and Question admin are declared so:

class QuestionInline(admin.TabularInline):
	model = surveys_models.Question
	show_change_link = True
	raw_id_fields = ['conditional_choices', ]
	extra = 1

class SurveyAdmin(CloneModelAdmin):
	save_as = True
	inlines = [
		QuestionInline,
	]

admin.site.register(surveys_models.Survey, SurveyAdmin)

class QuestionChoiceInline(admin.TabularInline):
	model = surveys_models.QuestionChoice
	show_change_link = True
	extra = 1


class QuestionAdmin(admin.ModelAdmin):
	list_display = ('id', 'text', 'survey', 'type', 'question_group',)
	search_fields = ('text', 'unique_group_text',)
	list_filter = ('survey__language', 'type', )
	raw_id_fields = ['conditional_choices']
	inlines = [
		QuestionChoiceInline,
	]


admin.site.register(surveys_models.Question, QuestionAdmin)

What OS are you seeing the problem on?

linux

Expected behavior?

Clone all data, included the questions from a Survey

Relevant log output

No response

Anything else?

django 3.1.13
django-clone 2.9.4
python 3.7

Code of Conduct

  • I agree to follow this project's Code of Conduct

[BUG] Cloning fails with nullable strings on regex

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug?

A model field of models.CharField(max_length=255, null=True) with a null or None value causes the clone to fail.

TypeError: expected string or bytes-like object

To Reproduce

  1. Create a model that implements the CloneMixin
  2. Include a nullable char column
  3. obj = Model.objects.create(), explicitly not setting the nullable column
  4. obj2 = obj.make_clone()

What OS are you seeing the problem on?

linux

Expected behavior?

The object would clone as expected.

Relevant log output

/usr/local/lib/python3.9/contextlib.py:79: in inner
    return func(*args, **kwds)
.venv/lib/python3.9/site-packages/model_clone/mixins/clone.py:234: in make_clone
    duplicate = self._create_copy_of_instance(self, using=using)
.venv/lib/python3.9/site-packages/model_clone/mixins/clone.py:372: in _create_copy_of_instance
    value = clean_value(value, unique_duplicate_suffix)
.venv/lib/python3.9/site-packages/model_clone/utils.py:151: in clean_value
    return re.sub(r"([\s-]?){}[\s-][\d]$".format(suffix), "", value, flags=re.I)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

pattern = '([\\s-]?)copy[\\s-][\\d]$', repl = '', string = None, count = 0
flags = re.IGNORECASE

    def sub(pattern, repl, string, count=0, flags=0):
        """Return the string obtained by replacing the leftmost
        non-overlapping occurrences of the pattern in string by the
        replacement repl.  repl can be either a string or a callable;
        if a string, backslash escapes in it are processed.  If it is
        a callable, it's passed the Match object and must return
        a replacement string to be used."""
>       return _compile(pattern, flags).sub(repl, string, count)
E       TypeError: expected string or bytes-like object

Anything else?

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

Consolidate the sample apps

Is your feature request related to a problem? Please describe.
I'll like to reduce clutter and only add a single sample app to the root of the project.

UUIDField with unique re-uses previous value and raises an IntegrityError

Describe the bug

Per Django's documentation snippet around UUIDField https://docs.djangoproject.com/en/3.2/ref/models/fields/#uuidfield it's acceptable to use primary_key=True, default=uuid.uuid4, editable=False. In my particular case, the UUID isn't the model primary key, but it has unique=True.

When duplicating a model with such configuration, an IntegrityError is raised.

To Reproduce
Steps to reproduce the behavior:

  1. Create a model with uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
  2. Try to duplicate it using django-clone
  3. IntegrityError is raised, duplicate key value violates unique constraint...

Expected behavior
The field is unique, but has a default callable. It should use the value generated by the callable.

Additional context

This might be a more specific instance of #330 - I've created a new issue because I believe this field can be treated differently like it's done with models.DateTimeField, models.DateField here:

if isinstance(f, (models.DateTimeField, models.DateField)):

A few lines above when value = getattr(instance, f.attname, f.get_default()) is called, f.get_default() actually invokes uuid.uuid4 and a new UUID is generated, but getattr finds f.attname already there (the uuid), so it re-uses.

I propose another special case for UUIDField, just because Django's documentation explicitly contains a hint that might lead django-clone to fail. Something simple as

if isinstance(f, UUIDField):
    value = f.get_default()

Should suffice, but the conditional can be fancier.

Another approach would be to always identify if the default is callable, and call it, no matter the field type. While less specific, it probably does what the user intended more often, which is to execute the callable when saving the field for the first time.

Either way, I can create a PR if needed. Thoughts on either approach or perhaps another direction I should take?

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Error type: undefined. Note: this is a nested preset so please contact the preset author if you are unable to fix it yourself.

[BUG] 2.9.5 now clones the o2o which clones the original again, so too many clones

Is there an existing issue for this?

  • I have searched the existing issues

Does this issue exist in the latest version?

  • I'm using the latest release

Describe the bug?

5a2e65d introduces a bug where the easiest o2o configuration will create too many clones. My guess is that this PR #493 introduces one more level of recursion that is too much.

To Reproduce

To reproduce, I created this toy model setup:

class Sentence(CloneMixin, models.Model):
    _clone_o2o_fields = {'ending'}


class Ending(CloneMixin, models.Model):
    sentence = models.OneToOneField(Sentence, on_delete=models.CASCADE, related_name='ending')

with this setup I have a test that worked in 2.9.4 but is now failing:

class CloneTest(TestCase):
    def test_clone(self):
        sentence = Sentence.objects.create()
        Ending.objects.create(sentence=sentence)

        self.assertEqual(1, Sentence.objects.count())
        self.assertEqual(1, Ending.objects.count())

        clones = [sentence.make_clone() for _ in range(2)]

        self.assertEqual(2, len(clones))
        self.assertEqual(3, Sentence.objects.count())  # breaks with AssertionError: 3 != 5

What OS are you seeing the problem on?

ubuntu-latest or ubuntu-20.04

Expected behavior?

The test should work and there should be only 3 Sentences (2 clones and 1 original).

Relevant log output

No response

Anything else?

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

[Feature] o2o clone doesnt call make_clone

Is this feature missing in the latest version?

  • I'm using the latest release

Is your feature request related to a problem? Please describe.

I am trying to clone a model with a deep relationship tree. Some of the copying steps have side_effects e.g. children need to replace data based on their new clone parent. To achieve this, I thought I could overwrite the make_clone function on the models.

 def make_clone(self, attrs=None, sub_clone=False, using=None):
       # ... edit attrs here based on parent that's contained in attrs
       return super().make_clone(attrs=attrs, sub_clone=sub_clone, using=using)

This works fine for m2m and o2m/m2o but the function is called when the relationship is an o2o. I had a look at mixin.py and think the reason is that functions like __duplicate_o2m_fields actually try to call make_clone if possible while (__duplicate_o2m_fields)[https://github.com/tj-django/django-clone/blob/main/model_clone/mixin.py#L432] doesn't

Describe the solution you'd like?

I hope that adding something like this could work:

def __duplicate_o2o_fields(self, duplicate, using=None):
      for f in self._meta.related_objects:
          if f.one_to_one:
              if any(
                  [
                      f.name in self._clone_o2o_fields
                      and f not in self._meta.concrete_fields,
                      self._clone_excluded_o2o_fields
                      and f.name not in self._clone_excluded_o2o_fields
                      and f not in self._meta.concrete_fields,
                  ]
              ):
                  rel_object = getattr(self, f.name, None)
                  if rel_object:
                      if hasattr(item, "make_clone"):
                          new_rel_object = rel_object.make_clone(...)
                     else:
                          new_rel_object = CloneMixin._create_copy_of_instance(
                              rel_object,
                              force=True,
                              sub_clone=True,
                           )
                      setattr(new_rel_object, f.remote_field.name, duplicate)
                      new_rel_object.save(using=using)

      return duplicate

Not sure how exactly to propagate the attrs and whether the relationship will replaced properly?

Describe alternatives you've considered?

I think for now I will call make_clone on the object explicitly in the parent but I think above might be more consistent and maybe more the expected behaviour.

Anything else?

Do you think this is the right approach to this anyway? I thought it might be quite neat to put the logic where it belongs especially if the logic is the same no matter from where the cloning is started. The alternative would be to clone all objects and then change the things that need to be changed.

A probably much bigger ask and a long stretch

As mentioned above, we have a quite complicated dependency logic. I am going to try to simplify it here:
All models are part of an Organisation which maybe has Offices, and Employees. Within Offices there are Desks that have an o2o to Employees. Now if we set up everything correctly and we clone an Organisation it will clone all Employees and Offices and even the Desks with the correct upstream dependencies only the relationship from Desk will still point to the original Employee. It's probably beyond the scope of the library but if that would be handled that'll be amazing. I have no idea how that would be done consistently since it's tricky to do even if you know the structure!

Cheers for a great library ✌️

Code of Conduct

  • I agree to follow this project's Code of Conduct

Implicit declaration of ManyToManyField, auto_created is of type "CloneMetaClass"

Using django-clone version 0.2.0
Using django version 2.2.7

This has to be a bug since per django documentation this field is of type boolean and can only hold True or False values. But in the case where you do not specify a through model for a ManyToMany field, it somehow gets set to a value of class "CloneMetaClass".

To reproduce, do the following:

class TestA(models.Model):
    name = models.TextField(_('Name'), max_length=512, blank=True)

class TestB(CloneMixin, models.Model):
    tests_a= models.ManyToManyField('app.TestA', related_name='tests_a', blank=True)

    _clone_many_to_many_fields = [
        'tests_a'
    ]

and then use:
test_b_instance.make_clone()

When make_clone is called, the following code fails(clone.py, line 346):

# Clone many to many fields
for field in many_to_many_fields:
    if all(
        [
            field.remote_field.through,
            not field.remote_field.through._meta.auto_created,
        ]
    ):

because "not field.remote_field.through._meta.auto_created" is not a boolean but of type "CloneMetaClass".

If I specifically use through class, then it works as it should. But I do not see the logic why you would want it to fail if you don't specify the through class and instead use the implicitly generated class by django.

Screenshot from PyCharm where it is seen that auto_created is of type "CloneMetaClass":
image

If by some chance this is "correct", then please set the auto_created to True instead of assigning it a type of "CloneMetaClass".

Looking forward to your reply.

Best regards, Jan

Cloning complex model relationships

Is your feature request related to a problem? Please describe.
I have a complex database structure. Its better to be explained using an example:

class House(CloneMixin, models.Model):
   _clone_many_to_one_or_one_to_many_fields = ["rooms", "furniture"]  # either this

class Room(CloneMixin, models.Model):
   _clone_many_to_one_or_one_to_many_fields = ["furniture"]  # or this?

  house = models.ForeignKey(House, related_name="rooms")

class Furniture(CloneMixin, models.Model):
  house = models.ForeignKey(House, related_name="furniture")
  room = models.ForeignKey(Room, related_name="furniture")

Lets say I want to clone a House object which has a few Room and Furniture objects. The problem arises that, when it clones a Furniture object, it will update the foreign key to house but not room. Or when I clone Room it will update the room foreign key but not the house foreign key.

This way I can imagine to end up in a state where cloned Furniture objects exist twice with either a reference to Room or House missing.

Describe the solution you'd like
I would want to have a mechanism were I can tell the Furniture during cloning that it has to update both foreign keys accordingly.

Describe alternatives you've considered
Passing a map of old and new instances down the foreign key relationship path which lower level objects can access to update every foreign key.

[BUG] - Duplicated models are not always created in same database

Describe the bug
Duplicated models do not retain the same database as the original, if the original database is not the default.

To Reproduce
Steps to reproduce the behavior:
With settings that include a router that determines database from request (ie, models are not linked to specific database), call make_clone on an instance that is from a non-default database. The clone will be created in the default database, not the same database as the original instance.

Expected behavior
The clone should be created in the same database as the original instance.

Screenshots
2021-06-18 10_35_52-Remote Desktop Manager Free  cls-md-dbwebdv1
2021-06-18 10_38_00-Remote Desktop Manager Free  cls-md-dbwebdv1

Desktop (please complete the following information):

  • OS: Windows Server 2016
  • Version v2.5.3

Additional context
I got it working (basic testing) correctly by changing make_clone duplicate.save to:

db = self._state.db
duplicate.save(using=db)

and for create_copy_of_instance:

db = instance._state.db
new_obj = instance.__class__.objects.using(db).create(**defaults)

Improve field name validation

Is your feature request related to a problem? Please describe.
I'll like to include field names that would be valid with possible warnings when the field is renamed.

This should also include validating all fields listed below:

Explicit

Field Names 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

Field Names (include all except these fields.) 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.

Describe the solution you'd like
Add checks that includes verifying the field names using the current instance.

Update the clone link to include the model verbose name.

Is your feature request related to a problem? Please describe.
Enhancement: For the admin view rather than generic label for the button and clone action link

Switch from
"Clone select objects" ->
"Duplicate selected '%(verbose_name)'" for single item
"Duplicate selected '%(verbose_name_plural)'" for x > 1.

Describe the solution you'd like
Updating the clone action using force_text.

Question:
Should this support translation. ?

Most likely.

Doesn't duplicate with custom `id`

A model with a custom id field is not duplicated:

the Branch model has the id id = models.UUIDField(primary_key=True, default=uuid4)

>>> base_branch = Branch.objects.create(name='Main branch', parent=None, is_master=True, status=Branch.DRAFT)
>>> base_branch.pk
UUID('7a613f9d-90fe-4116-b897-69505cd4ff5c')
>>> clone = base_branch.make_clone()
>>> clone.pk
UUID('7a613f9d-90fe-4116-b897-69505cd4ff5c')

I think this is because the custom id shows up in _meta.concrete_fields the value is copied over, preventing it from generating a new PK.

>>> base_branch._meta.concrete_fields
(<django.db.models.fields.UUIDField: id>, <django.db.models.fields.CharField: name>, <django.db.models.fields.BooleanField: is_master>, <django.db.models.fields.related.ForeignKey: parent>, <django.db.models.fields.CharField: status>)

The primary key field can be found through instance._meta.pk, filtering this out of the fields to copy should resolve this problem I think.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Rate-Limited

These updates are currently rate-limited. Click on a checkbox below to force their creation now.

  • chore(deps): update codacy/codacy-analysis-cli-action action to v4.4.1
  • chore(deps): update dependency django to v5.0.6
  • chore(deps): update dependency conditional to v2
  • 🔐 Create all rate-limited PRs at once 🔐

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/auto-approve.yml
  • hmarr/auto-approve-action v4
.github/workflows/auto-merge.yml
  • dependabot/fetch-metadata v2
.github/workflows/codacy-analysis.yml
  • actions/checkout v4@1d96c772d19495a3b5c517cd2bc0cb401ea0529f
  • codacy/codacy-analysis-cli-action v4.4.0
  • github/codeql-action v3
.github/workflows/deploy.yml
  • actions/checkout v4@1d96c772d19495a3b5c517cd2bc0cb401ea0529f
  • tj-actions/semver-diff v3.0.1
  • actions/setup-python v5
  • tj-actions/github-changelog-generator v1.20
  • peter-evans/create-pull-request v6
.github/workflows/gh-pages.yml
  • actions/checkout v4.1.3@1d96c772d19495a3b5c517cd2bc0cb401ea0529f
  • peaceiris/actions-gh-pages v4.0.0
.github/workflows/greetings.yml
  • actions/first-interaction v1
.github/workflows/migration-fixer.yml
  • actions/checkout v4@1d96c772d19495a3b5c517cd2bc0cb401ea0529f
  • actions/setup-python v5
  • tj-django/django-migration-fixer v1.3.6
  • tj-actions/verify-changed-files v19
.github/workflows/test.yml
  • rokroskar/workflow-run-cleanup-action v0.3.3
  • actions/checkout v4.1.3@1d96c772d19495a3b5c517cd2bc0cb401ea0529f
  • actions/setup-python v5
  • codecov/codecov-action v4.3.0
.github/workflows/update-doc-assets.yml
  • actions/checkout v4.1.3@1d96c772d19495a3b5c517cd2bc0cb401ea0529f
  • tj-actions/remark v3
  • tj-actions/verify-changed-files v19
  • peter-evans/create-pull-request v6
  • peter-evans/create-pull-request v6.0.4
pip_requirements
requirements.txt
  • asgiref ==3.8.1
  • conditional ==1.5
  • django ==5.0.4
  • six ==1.16.0
  • sqlparse ==0.5.0
pip_setup
setup.py
  • conditional >=1.3
  • pluggy >=0.7

  • Check this box to trigger a request for Renovate to run again on this repository

Add integration with DRF

Is your feature request related to a problem? Please describe.
No this is a new feature that would enable making clones using Django Rest Framework.

This would either be a view mixin which includes an @action route/custom HTTP method/custom button for UI.

[BUG] Regression on full_clean

Describe the bug
A regression with the introduction of a full_clean call when calling make_clone

To Reproduce
Steps to reproduce the behavior:

  1. With django-clone < 2.4 cloning a model with a blank field, or otherwise invalid produces a clone.
  2. Upgrade to version > 2.4
  3. It raises a ValidationError

Expected behavior
It should still work. I do not think that clones of the original instance should be valid

[BUG] (Admin view) Cloning from "bulk" doees not work, while it works fine inside of instance editor

Describe the bug
With the same exact codebase, I am able to use the make_clone() method as well as making a duplicate of an instance from within the specific instance's "Change" view in the admin panel, but I cannot make a clone from the admin view where i select instances to duplicate, select duplicate from the dropdown, and then press "Go"

To Reproduce

  1. Define the following models:
class Tas(CloneMixin, models.Model):
    """The top level object of the TAS which only holds top-level necessary information"""
    productName = models.CharField(
        max_length=MAX_CHAR_LENGTH, verbose_name="Product Name", unique=True)
    metadata = models.OneToOneField(
        "TasMetadata", on_delete=models.DO_NOTHING, blank=True, null=True, related_name='tas')
    slug = AutoSlugField(populate_from='productName', always_update=True, unique=True, editable=False)
    _clone_m2o_or_o2m_fields = ['diagrams']
    _clone_excluded_fields = ['slug']

    def __str__(self):
        return self.productName

    class Meta:
        verbose_name_plural = "TASes"

class TasMetadata(CloneMixin, models.Model):
    """Separate class to hold information which should be easily editable and versionable about the TAS model"""
    nickname = models.CharField(
        max_length=MAX_CHAR_LENGTH, help_text="Familiar codename")

    predecessor = models.ForeignKey(
        Tas, on_delete=DO_NOTHING, blank=True, null=True, related_name='children', help_text="Optional: The \"parent\" TAS")
    npiPhase = models.ForeignKey(
        NpiPhase, blank=True, null=True, on_delete=SET_NULL, default=None, verbose_name="NPI Phase")

    _clone_m2m_fields = ['npiPhase']

    def __str__(self):
        return f"{self.tas} (a.k.a {self.nickname})"

    class Meta:
        verbose_name = 'TAS Metadata'
        verbose_name_plural = "TAS Metadata"
  1. Add them to the admin panel
@admin.register(Tas)
class TasAdmin(CloneModelAdminMixin, CompareVersionAdmin):
    formfield_overrides = {
        models.JSONField: {'widget': JSONEditorWidget},
    }
    pass

@admin.register(TasMetadata)
class TasMetadataAdmin(CloneModelAdminMixin, CompareVersionAdmin):
    formfield_overrides = {
        models.JSONField: {'widget': JSONEditorWidget},
    }

    def get_queryset(self, request):
        qs = super().get_queryset(request).prefetch_related('tas__metadata')
        return qs
    pass
  1. Go to admin view
  2. Go to the "change" view of all "TAS" objects
  3. Open one object and duplicate it just to demonstrate it works fine
  4. Go back to the change view
  5. Select one object with the checkmark and select "duplicate" in the drop-down
  6. See traceback below

Expected behavior
The duplication should work from bulk view if it works from the single instance editor

Screenshots
N/A

Desktop (please complete the following information):

  • Django version 3.1.7
  • django-clone version 2.5.3

Additional context
This is the error that I get from the bulk view. Let me know if there's anything else I can provide for this.

Environment:


Request Method: POST
Request URL: http://localhost:8000/admin/tas/tas/

Django Version: 3.1.7
Python Version: 3.9.4
Installed Applications:
['django_db_prefix',
 'django_extensions',
 'model_clone',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'tas.apps.TasConfig',
 'rest_framework',
 'debug_toolbar',
 'reversion',
 'reversion_compare',
 'bootstrap4',
 'iommi',
 'mathfilters',
 'django_bootstrap_icons',
 'markdownify.apps.MarkdownifyConfig',
 'django_fastdev',
 'django_json_widget']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'debug_toolbar.middleware.DebugToolbarMiddleware',
 'iommi.middleware']



Traceback (most recent call last):
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/contrib/admin/options.py", line 614, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/contrib/admin/sites.py", line 233, in inner
    return view(request, *args, **kwargs)
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/reversion/admin.py", line 230, in changelist_view
    return super().changelist_view(request, context)
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/utils/decorators.py", line 43, in _wrapper
    return bound_method(*args, **kwargs)
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/contrib/admin/options.py", line 1719, in changelist_view
    response = self.response_action(request, queryset=cl.get_queryset(request))
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/contrib/admin/options.py", line 1402, in response_action
    response = func(self, request, queryset)
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/model_clone/admin.py", line 53, in make_clone
    clone = obj.make_clone()
  File "/opt/homebrew/Cellar/[email protected]/3.9.4/Frameworks/Python.framework/Versions/3.9/lib/python3.9/contextlib.py", line 79, in inner
    return func(*args, **kwds)
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/model_clone/mixins/clone.py", line 209, in make_clone
    duplicate = self._create_copy_of_instance(self)
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/model_clone/mixins/clone.py", line 350, in _create_copy_of_instance
    sub_instance.save()
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/db/models/base.py", line 753, in save
    self.save_base(using=using, force_insert=force_insert,
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/db/models/base.py", line 801, in save_base
    post_save.send(
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/dispatch/dispatcher.py", line 177, in send
    return [
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/dispatch/dispatcher.py", line 178, in <listcomp>
    (receiver, receiver(signal=self, sender=sender, **named))
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/reversion/revisions.py", line 330, in _post_save_receiver
    add_to_revision(instance, model_db=using)
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/reversion/revisions.py", line 215, in add_to_revision
    _add_to_revision(obj, db, model_db, True)
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/reversion/revisions.py", line 196, in _add_to_revision
    object_repr=force_str(obj),
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/utils/encoding.py", line 64, in force_str
    s = str(s)
  File "/Users/sausage/Git/Work/webtas/tas/models.py", line 104, in __str__
    return f"{self.tas} (a.k.a {self.nickname})"
  File "/Users/sausage/Git/Work/webtas/.env/lib/python3.9/site-packages/django/db/models/fields/related_descriptors.py", line 421, in __get__
    raise self.RelatedObjectDoesNotExist(

Exception Type: RelatedObjectDoesNotExist at /admin/tas/tas/
Exception Value: TasMetadata has no tas.

Again, this works just fine when I do Tas.objects.first().make_clone() or when I run it from the instance editor.

Document model checks.

Is your feature request related to a problem? Please describe.
With the feature introduced in this branch #23.
Need to document conflicts in the model configuration.

Describe the solution you'd like
Updating the README.md

Initial Update

The bot created this issue to inform you that pyup.io has been set up on this repo.
Once you have closed it, the bot will open pull requests for updates as soon as they are available.

`UniqueConstraint` in Model Meta is not considered

Describe the bug
If a field is made unique via a UniqueConstraint it is not detected as unique and thus cloning the model will result in constraint errors instead of copy being appended to the field.

To Reproduce

  1. Create a UniqueConstraint for a CharField via Model Meta.
  2. Try to clone the model.
  3. Observe IntegrityError being thrown.

Expected behavior
UniqueConstraints should be detected.

Desktop (please complete the following information):

  • Version 2.5.3

Additional context
I don't think the fix for this is simply treating the fields as unique and appending "copy" to them. While that will probably remove the constraint error, it might not be what is desired, as a UniqueConstraint might have multiple fields (the same applies to unique_together) or even a condition.
It would be useful to provide a hook for models / fields to provide their own "get new unique value"-logic. This would additionally enable fields other than CharField to work with unique.

[BUG] unsupported operand type(s) for +: 'NoneType' and 'str'

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug?

django-clone==2.9.0 gives:

unsupported operand type(s) for +: 'NoneType' and 'str'

To Reproduce

some_instance.make_clone(
      attrs={"number": None, "title": "something", "description": "string", "order": 1}
)

What OS are you seeing the problem on?

linux, macos

Expected behavior?

No err.

Relevant log output

Environment:

Request Method: POST
Request URL: http://127.0.0.1:8000/.../clone/

Django Version: 2.2.24
Python Version: 3.8.5
Installed Applications:
[
   ...
]
Installed Middleware:
[
   ...
]

Traceback:

File "some_python_3.8.10_3.8.10/lib/python3.8/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "some_python_3.8.10_3.8.10/lib/python3.8/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "some_python_3.8.10_3.8.10/lib/python3.8/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/home/.pyenv/versions/3.8.5/lib/python3.8/contextlib.py" in inner
  75.                 return func(*args, **kwds)

File "some_python_3.8.10_3.8.10/lib/python3.8/site-packages/django/views/generic/base.py" in view
  71.             return self.dispatch(request, *args, **kwargs)

File "some_python_3.8.10_3.8.10/lib/python3.8/site-packages/django/contrib/auth/mixins.py" in dispatch
  85.         return super().dispatch(request, *args, **kwargs)

File "some_python_3.8.10_3.8.10/lib/python3.8/site-packages/django/views/generic/base.py" in dispatch
  97.         return handler(request, *args, **kwargs)

File "some_python_3.8.10_3.8.10/lib/python3.8/site-packages/fourauctions/apps/dashboard/views.py" in post
  1181.                 clone = lot.make_clone(

File "/home/.pyenv/versions/3.8.5/lib/python3.8/contextlib.py" in inner
  75.                 return func(*args, **kwds)

File "some_python_3.8.10_3.8.10/lib/python3.8/site-packages/model_clone/mixins/clone.py" in make_clone
  216.             duplicate = self._create_copy_of_instance(self, using=using)

File "some_python_3.8.10_3.8.10/lib/python3.8/site-packages/model_clone/mixins/clone.py" in _create_copy_of_instance
  350.                             value = get_unique_default(

File "some_python_3.8.10_3.8.10/lib/python3.8/site-packages/model_clone/utils.py" in get_unique_default
  324.     new = six.next(it)

File "some_python_3.8.10_3.8.10/lib/python3.8/site-packages/model_clone/utils.py" in generate_value
  199.     yield get_value(value, suffix, transform, max_length, 1)

File "some_python_3.8.10_3.8.10/lib/python3.8/site-packages/model_clone/utils.py" in get_value
  186.     total_length = len(value + duplicate_suffix)

Exception Type: TypeError at /dashboard/auctions/154/lots/3507/clone/
Exception Value: unsupported operand type(s) for +: 'NoneType' and 'str'

Anything else?

Works with django-clone 2.7.2 and 2.8.1

Code of Conduct

  • I agree to follow this project's Code of Conduct

[Feature] Handle duplicating FileField/ImageField

Is your feature request related to a problem? Please describe.

Based on https://groups.google.com/g/django-updates/c/vup1ZShMEgQ?pli=1.

I'll like to get a sense of the requirements of duplicating models with FileField's

Describe the solution you'd like?

Replicating FileFields should optionally be allowed to replicate the source object if required or set else it should be skipped if the current value would point to the same file.

Describe alternatives you've considered?

No response

Anything else?

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

Bug when installing version above 0.0.3

Hi,
have following problem:
`
command: /usr/bin/python -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-wf63qX/django-clone/setup.py'"'"'; file='"'"'/tmp/pip-install-wf63qX/django-clone/setup.py' fix "'"';f=getattr(tokenize, 'ur "'"'open'"'"', open)(file);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, file, '"'"'exec'"'"'))' egg_info --egg-base pip-egg-info
cwd: /tmp/pip-install-wf63qX/django-clone/
Complete output (3 lines):moma
Traceback (most recent call last):
File "", line 1, in
IOError: [Errno 2] No such file or directory: '/tmp/pip-install-wf63qX/django-clone/setup.py'
----------------------------------------
�[0m�[91mERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.
�[0m�[91mWARNING: You are using pip version 19.2.3, however version 19.3.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
�[0mService 'cla_test' failed to build: The command '/bin/sh -c pip install -r requirements.txt' returned a non-zero code: 1

`

Remove circle CI integration

Is your feature request related to a problem? Please describe.
PR Reviews takes longer than expected due to running both circle CI and github actions.

Describe the solution you'd like
This should be quick to remove Circle CI Integration.

Document the excluded fields.

Is your feature request related to a problem? Please describe.
Feature #23 introduced new concept of "excluding_fields".

Describe the solution you'd like
Updating the README.md

Initial Update

The bot created this issue to inform you that pyup.io has been set up on this repo.
Once you have closed it, the bot will open pull requests for updates as soon as they are available.

`TextField` is not supported with unique value generation

Describe the bug
A unique TextField is not treated the same as a unique CharField and no new unique value is generated.

To Reproduce

  1. Create a model with a TextField marked unqiue.
  2. Try to clone a model instance.
  3. Observe IntegrityError being thrown.

Expected behavior
TextField should be considered equivalent to CharField in terms of unique value generation.

Desktop (please complete the following information):

  • Version 2.5.3

Document bulk clone support.

Is your feature request related to a problem? Please describe.

  • Document support to bulk clone objects i.e clone 100/1000 objects
  • Needs to be added to the django admin UI
  • Needs documentation
  • Test time to clone objects.

Drop python2.7 support.

Is your feature request related to a problem? Please describe.
Drop python2.7 support EOL (Jan 1st 2021)

[BUG] when cloning, images are not created but are linked to the source image

Describe the bug
When cloning, images are not created but are linked to the source, if delete/change image in source - then lost image in recipient and vice versa

To Reproduce
Steps to reproduce the behavior:

  1. Create original object_1 where must be field image
  2. Clone object_1 -> created object_1-copy-1
  3. path image in object_1 and object_1-copy-1 - same
  4. Drop/change image in object_1(or object_1-copy-1) -> delete original image from disk, and image not available from another object - object_1-copy-1(or object_1)
  5. if try change where fail image object_1-copy-1(or object_1) - get error file not exists

Expected behavior
When cloned object expected what image was create new file, not linked to source

Desktop (please complete the following information):

  • OS: ubuntu 18.04
  • Django 3.0
  • django-clone 2.3.3

May be this problem with and FileField

Drop Django 1.11 support

Is your feature request related to a problem? Please describe.
Now that the support for python2.7 is dropped in other to test new features like Model.constraints dropping support for django1.11 would enable such tests.

'CloneMixin' has no attribute '_meta'

Hi,

I am using your package, very usefull! However I get this error after resetting my database and trying to make migrations. Any ideas what is causing this error?

When I already had the model in my database and then added CloneMixin to my model it was working. But with an empty database the make migrations is not working.

How to clone a question with its choices?

Hello,

I have two models:

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self):
        return f'[{self.pk}]: {self.question_text}'


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):
        return f'[{self.pk}]: {self.choice_text}'

I'd like to clone a question with its choices.

>>> from polls.models import Question
>>> question = Question.objects.get(pk=1)
>>> question
<Question: [1]: What's up?>
>>> question.choice_set.all()
<QuerySet [<Choice: [1]: choice 1>, <Choice: [2]: choice 2>]>
>>> q = question.make_clone()
>>> q
<Question: [2]: What's up?>
>>> q.choice_set.all()
<QuerySet [<Choice: [3]: choice 1>, <Choice: [4]: choice 2>]>

How to do this?

:bug: wheel does not include templates

Hey There!
The current wheel 0.1.0 on PyPi does not include the templates. You can simply change the file extension to .zip to unpack it and verify for yourself.
Best
-Joe

[Feature] Option to modify cloned object before saving

Is your feature request related to a problem? Please describe.

I'm searching for the possibility to modify some of the fields before on the cloned object. Two examples:

  • I have a field added_by which keeps track which Django User added the object. When cloning, I want this to be set to the current user and not the user of the original object.
  • I want to make clear in some string-field, that this object is a clone by adding (Cloned) or similar. The string field is a unique field.

Describe the solution you'd like?

A method in the model which gets called after the whole cloned object has been constructed but before it is saved would help here. The method could be empty per default and be overwritten in case the user needs it.

Describe alternatives you've considered?

No response

Anything else?

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.