tj-django / django-clone Goto Github PK
View Code? Open in Web Editor NEWControlled Django model instance replication.
Home Page: https://tj-django.github.io/django-clone
License: MIT License
Controlled Django model instance replication.
Home Page: https://tj-django.github.io/django-clone
License: MIT License
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
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?
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.
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.
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 ✌️
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
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.
No response
No response
Is your feature request related to a problem? Please describe.
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:
Expected behavior
When cloned object expected what image was create new file, not linked to source
Desktop (please complete the following information):
May be this problem with and FileField
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
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"
@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
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):
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.
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
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.
I'm searching for the possibility to modify some of the fields before on the cloned object. Two examples:
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.(Cloned)
or similar. The string field is a unique field.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.
No response
No response
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.
Is your feature request related to a problem? Please describe.
Drop python2.7 support EOL (Jan 1st 2021)
This might require a custom docker image with venv/virtualenv installed and running python2.7.
Describe the bug
A regression with the introduction of a full_clean
call when calling make_clone
To Reproduce
Steps to reproduce the behavior:
Expected behavior
It should still work. I do not think that clones of the original instance should be valid
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?
@all-contributors please add @gerbyzation for bug, tests and code
@all-contributors please add @jakebolam for infrastructure, tests and code
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.
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:
uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
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:
django-clone/model_clone/mixins/clone.py
Line 326 in 52e3570
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?
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
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.
Describe the bug
A unique TextField
is not treated the same as a unique CharField
and no new unique value is generated.
To Reproduce
TextField
marked unqiue
.IntegrityError
being thrown.Expected behavior
TextField
should be considered equivalent to CharField
in terms of unique value generation.
Desktop (please complete the following information):
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
CloneMixin
obj = Model.objects.create()
, explicitly not setting the nullable columnobj2 = obj.make_clone()
linux
The object would clone as expected.
/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
No response
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":
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
Is your feature request related to a problem? Please describe.
I'll like to import create_copy_of_instance
from the utils module instead of using a top level package import.
https://github.com/tj-django/django-clone/blob/master/model_clone/__init__.py#L9
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.
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
UniqueConstraint
for a CharField
via Model Meta
.IntegrityError
being thrown.Expected behavior
UniqueConstraint
s should be detected.
Desktop (please complete the following information):
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.
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.
This moves from the current process of releasing from my laptop.
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.
Desktop (please complete the following information):
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)
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
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, 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
ubuntu-latest or ubuntu-20.04
The test should work and there should be only 3 Sentences (2 clones and 1 original).
No response
No response
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates are currently rate-limited. Click on a checkbox below to force their creation now.
These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
.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
requirements.txt
asgiref ==3.8.1
conditional ==1.5
django ==5.0.4
six ==1.16.0
sqlparse ==0.5.0
setup.py
conditional >=1.3
pluggy >=0.7
There’s a bug with duplicating models with OneToOne fields which results in an error
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.
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.
Is your feature request related to a problem? Please describe.
I'll like fields that are declared using the class properties to be valid or raise errors if any of the fields are not declared on the model.
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.
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.
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.
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)
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)
linux
Clone all data, included the questions from a Survey
No response
django 3.1.13
django-clone 2.9.4
python 3.7
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:
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 |
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.
Describe the solution you'd like
More descriptive documentation.
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.
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
`
Add TODO's / ROADMAP.
django-clone==2.9.0 gives:
unsupported operand type(s) for +: 'NoneType' and 'str'
some_instance.make_clone(
attrs={"number": None, "title": "something", "description": "string", "order": 1}
)
linux, macos
No err.
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'
Works with django-clone 2.7.2
and 2.8.1
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.