9
votes

When I clone a django model instance I used to clean the 'pk' field. This seems not to work with an inherited model :

Take this :

class ModelA(models.Model):
    info1 = models.CharField(max_length=64)

class ModelB(ModelA):
    info2 = models.CharField(max_length=64)

class ModelC(ModelB):
    info3 = models.CharField(max_length=64)

Now let's create an instance and clone it by the 'usual' way ( I am using a django shell ):

In [1]: c=ModelC(info1="aaa",info2="bbb",info3="ccc")

In [2]: c.save()

In [3]: c.pk
Out[3]: 1L

In [4]: c.pk=None  <------ to clone

In [5]: c.save()   <------ should generate a new instance with a new index key

In [6]: c.pk       
Out[6]: 1L         <------ but don't

In [7]: ModelC.objects.all()
Out[7]: [<ModelC: ModelC object>]   (only one instance !)

The only way I found was to do :

In [16]: c.pk =None

In [17]: c.id=None

In [21]: c.modela_ptr_id=None

In [22]: c.modelb_ptr_id=None

In [23]: c.save()

In [24]: c.pk
Out[24]: 2L    <---- successful clone containing info1,info2,info3 from original instance

In [25]: ModelC.objects.all()
Out[25]: [<ModelC: ModelC object>, <ModelC: ModelC object>]

I find that very ugly, is there a more nice way to clone an instance from an inherited model ?

1
Will you be using the base models by themselves at all or will you only be using the model that inherits from them? If you don't need to use the base models by themselves, then it should work to use abstract base classes.Andrew Clark
Actually inheritance in Django models can be considered a bad practice, you can use abstract base classes, or Foreign Keys.PepperoniPizza
Can you please paste you save() method, how are you setting the pk on the model ?PepperoniPizza
Actually, in my program, I have several inheritence levels, the 2 first levels are abstract, then I have 2 other levels bringing their own parameters. I planned to have one more, that's why I made this example. When I see tables generated, for that, I may go back to a more 'usual' model pattern : only one non-abstract model level.Eric
I am using polymorphic models, setting basemodel_ptr_id to None doesnt create new records for me, except it works if I set id to None as well like what you showed.James Lin

1 Answers

0
votes
c=ModelC(info1="aaa",info2="bbb",info3="ccc")
# creates an instance

c.save()
# writes instance to db

c.pk=None
# I doubt u can nullify the auto-generated pk of an existing object, because a pk is not nullable
c.save()
# if I'm right nothing will happen here.

So c will always be the same object. If you want to clone it you need to generate a new object. Either with a constructor within ModelC:

def __init__(another_modelC_obj=null, self):
   if another_modelC_obj:
      # for every field in another_modelC_obj: do self.field = another_modelC_obj.field
   super().__init__()

so you can go

c2=ModelC(c)

Or call it directly with:

c2=ModelC(c.info1, c.info2, c.info3)

Then c2 and c will be identical despite of their pk