I'm trying to upgrade Django from 3.1 to 3.2, and one of the error that I encountered was endpoints that I have that duplicate resources and their related models.
In this example:
@action(
detail=True,
methods=['POST'],
name='Duplicate one resource',
url_name='duplicate',
url_path='duplicate',
)
@transaction.atomic
def duplicate(self, request, pk=None):
foo = self.get_object() # gets foo
foo_bars = foo.foo_bars.all() # gets a queryset of FooBar
new_name = self.append_copy_suffix(foo.name)
new_foo = foo.copy_instance(name=new_name) # code below
print(foo, new_foo)
FooBar.objects.bulk_create(
[FooBar(foo=new_foo, bar=foo_bar.bar, stuff=foo_bar.stuff) for foo_bar in foo_bars]
)
return Response(self.get_serializer(new_foo).data, status=status.HTTP_201_CREATED)
def copy_instance(self, **kwargs):
"""
Method to copy a model instance
More info here: https://docs.djangoproject.com/en/3.1/topics/db/queries/#copying-model-instances
:param kwargs: additional parameters to update
:return: the duplicated resource
"""
new_resource = self
new_resource.pk = None
for k, v in kwargs.items():
setattr(new_resource, k, v)
new_resource._state.adding = True
new_resource.save()
return new_resource
This works pretty well in Django 3.1, but in Django 3.2, the related resources are not created for some reason.
Things that I noticed in debug and printing the values:
foo
and new_foo
, they are the same one (new_foo
).foo_bars
is empty. Maybe because the foo
and new_foo
are the same one before the bulk_create
and the foo_bars
is just a queryset that was yet to be executed.What do I need to fix in order for this to work?
EDIT:
Tried the Serhii Fomenko approach, and it worked. Also I've implemented another way that it works (might be the same).
def copy_instance(self, **kwargs):
new_resource = copy.deepcopy(self)
new_resource.pk = None
for k, v in kwargs.items():
setattr(new_resource, k, v)
new_resource._state.adding = True
new_resource.save()
return new_resource
def copy_instance(self, **kwargs):
new_resource = self.__class__()
for attr, value in self.__dict__.items():
setattr(new_resource, attr, value)
new_resource.pk = None
new_resource.id = None
for k, v in kwargs.items():
setattr(new_resource, k, v)
new_resource._state.adding = True
new_resource.save()
return new_resource
Regarding this problem, I've tried the Serhii Fomenko approach and it worked. Thanks for the help :)
def copy_instance(self, **kwargs):
new_resource = copy.deepcopy(self)
new_resource.pk = None
for k, v in kwargs.items():
setattr(new_resource, k, v)
new_resource._state.adding = True
new_resource.save()
return new_resource