Skip to content

Commit

Permalink
Merge pull request #190 from yuekui/master
Browse files Browse the repository at this point in the history
  • Loading branch information
jackton1 committed Nov 5, 2020
2 parents 8b912a0 + 1cce9f0 commit 5b3db57
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 12 deletions.
48 changes: 36 additions & 12 deletions model_clone/mixins/clone.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,25 @@ def make_clone(self, attrs=None, sub_clone=False):
):
many_to_one_or_one_to_many_fields.append(f)

elif all(
[
f.many_to_many,
f.name in self._clone_many_to_many_fields,
]
):
many_to_many_fields.append(f)

elif all(
[
f.many_to_many,
not self._clone_many_to_many_fields,
self._clone_excluded_many_to_many_fields,
f not in many_to_many_fields,
f.name not in self._clone_excluded_many_to_many_fields,
]
):
many_to_many_fields.append(f)

for f in self._meta.many_to_many:
if not sub_clone:
if f.name in self._clone_many_to_many_fields:
Expand Down Expand Up @@ -352,27 +371,32 @@ def make_clone(self, attrs=None, sub_clone=False):

# Clone many to many fields
for field in many_to_many_fields:
if hasattr(field, "field"):
# ManyToManyRel
field_name = field.field.m2m_reverse_field_name()
through = field.through
source = getattr(self, field.related_name)
destination = getattr(duplicate, field.related_name)
else:
through = field.remote_field.through
field_name = field.m2m_field_name()
source = getattr(self, field.attname)
destination = getattr(duplicate, field.attname)
if all(
[
field.remote_field.through,
not field.remote_field.through._meta.auto_created,
through,
not through._meta.auto_created,
]
):
objs = field.remote_field.through.objects.filter(
**{field.m2m_field_name(): self.pk}
)
objs = through.objects.filter(**{field_name: self.pk})
for item in objs:
if hasattr(field.remote_field.through, "make_clone"):
item.make_clone(
attrs={field.m2m_field_name(): duplicate}, sub_clone=True
)
if hasattr(through, "make_clone"):
item.make_clone(attrs={field_name: duplicate}, sub_clone=True)
else:
item.pk = None
setattr(item, field.m2m_field_name(), duplicate)
setattr(item, field_name, duplicate)
item.save()
else:
source = getattr(self, field.attname)
destination = getattr(duplicate, field.attname)
destination.set(source.all())
return duplicate

Expand Down
22 changes: 22 additions & 0 deletions model_clone/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,28 @@ def test_cloning_with_explicit__clone_many_to_many_fields(
list(book.authors.values_list("first_name", "last_name")),
list(book_clone.authors.values_list("first_name", "last_name")),
)

@patch("sample.models.Author._clone_many_to_many_fields", new_callable=PropertyMock)
def test_cloning_with_explicit_related__clone_many_to_many_fields(
self,
_clone_many_to_many_fields_mock,
):
author = Author.objects.create(
first_name="Opubo", last_name="Jack", age=24, sex="F", created_by=self.user
)

_clone_many_to_many_fields_mock.return_value = ["books"]

book_1 = Book.objects.create(name="New Book 1", created_by=self.user)
book_2 = Book.objects.create(name="New Book 2", created_by=self.user)
author.books.set([book_1, book_2])

author_clone = author.make_clone()

self.assertEqual(
list(author.books.values_list("name")),
list(author_clone.books.values_list("name")),
)
_clone_many_to_many_fields_mock.assert_called_once()

def test_cloning_unique_fields_is_valid(self):
Expand Down

0 comments on commit 5b3db57

Please sign in to comment.