7

I have the following code which takes an existing instance and copies, or 'archives' it, in another model and then deletes it replacing it with the draft copy.

Current Code

def archive_calc(self, rev_num, primary_field):
    model_a = Calc.objects.get(tag_number__tag_number = primary_field, revision_number = rev_num) #Current Revision instance
    model_b = CalcArchive() #Draft instance

    #Copies data to archive model
    for field in model_a._meta.fields:
        setattr(model_b, field.name, getattr(model_a, field.name))

    model_b.pk = None
    model_b.current_revision = False
    model_b.save()

    model_a.delete()

This works fine however i need to change the system to allow for certain models with foreign keys as when an instance is archived/deleted the related records are deleted along with it. So my idea to fix this is to have the changes from the draft record copied to the previous record and then have the draft deleted thus maintaining the foreign key related records.

Solution idea

def archive_calc(self, rev_num, primary_field):
    model_a = Calc.objects.get(tag_number__tag_number = primary_field, revision_number = rev_num) #Current Revision instance
    model_b = CalcArchive() #Archive Instance
    model_c = Calc.objects.get(pk = self.object.pk) #Draft instance

    #Copies data to archive model
    for field in model_a._meta.fields:
        setattr(model_b, field.name, getattr(model_a, field.name))

    model_b.pk = None
    model_b.current_revision = False
    model_b.save()

    #Copies data from draft instance to current revision instance
    for field in model_c._meta.fields:
        setattr(model_a, field.name, getattr(model_c, field.name))

    model_c.delete()

Unfortunately the above solution doesn't work, it just seems to ignore the copy and continues to work as per 'Current Code'. If I add model_a.save() after for field in model_c._meta.fi... the system gets stuck in a loop and eventually throws maximum recursion depth exceeded in cmp.

Any help would be much appreciate as usual and if im barking up the wrong tree please let me know.

Karl
  • 733
  • 1
  • 14
  • 28
  • probably duplicated: https://stackoverflow.com/questions/437166/duplicating-model-instances-and-their-related-objects-in-django-algorithm-for/57515362#57515362 – Julio Marins Aug 15 '19 at 20:49
  • Possible duplicate of [Duplicating model instances and their related objects in Django / Algorithm for recusrively duplicating an object](https://stackoverflow.com/questions/437166/duplicating-model-instances-and-their-related-objects-in-django-algorithm-for) – Julio Marins Aug 15 '19 at 20:49

3 Answers3

3

After alot of poking around and reading the Django docs I have come up with what seems to be a pretty nice, simple solution.

def archive_calc(self, rev_num, primary_field):
    model_a = Calc.objects.get(calc_details__calc_serial_number = primary_field, revision_number = rev_num)
    model_b = CalcArchive()

    object_list_annual = model_a.calcreview_set.filter(calc__calc_details = primary_field)
    object_list_ageing = model_a.calcitem_set.filter(calc__calc_details = primary_field)

    for obj in object_list_annual:
        obj.calc_id = self.object.id
        obj.save()
    for obj in object_list_ageing:
        obj.calc_id = self.object.id
        obj.save()

    for field in model_a._meta.fields:
        setattr(model_b, field.name, getattr(model_a, field.name))
    model_b.pk = None
    model_b.current_revision = False
    model_b.save()

    model_a.delete()

This 'moves' the related objects by setting the _id fields to the same as self.object.id.

Ive ran several tests and this seems to achieve exactly what I was looking for with minimal code and no extra installs.

Hope this helps someone and please feel free to point out any potential pitfalls in my answer.

Karl
  • 733
  • 1
  • 14
  • 28
2
obj = Model.objects.get(pk=1)
obj.pk = get_unused_pk()
obj.save()

You just need to change primary key (I don't know how you should evaluate it in your database schema) and save model instance.

Eugene Soldatov
  • 9,755
  • 2
  • 35
  • 43
  • Thanks for the comment. Which instance do I need to change? I have tried to set the 'pk' of the 'draft' instance as the 'pk' from the 'current revision' but the related objects/records where still deleted. – Karl Nov 17 '14 at 19:31
  • This doesn't copy over the relationships on different models aka related fields – Julio Marins Aug 15 '19 at 15:01
0

It sounds like you want to 'deep copy' a model instance.

Django-forkit should do the trick.

It looks like it copies ForeignKeys but not ManyToManyFields, hopefully that will be okay for you:

For deep resets, direct foreign keys will be traversed and reset. Many-to-many and reverse foreign keys are not attempted to be reset because the comparison between the related objects for reference and the related objects for instance becomes ambiguous.

seddonym
  • 16,304
  • 6
  • 66
  • 71
  • Thanks, I was looking at deep copy but couldnt see how it would work. Ive just downloaded Forkit so ill let you know how i get on however the documentation seems a little thin! :) – Karl Nov 19 '14 at 13:34
  • If it doesn't give you what you're looking for, try this thread: http://stackoverflow.com/questions/437166/duplicating-model-instances-and-their-related-objects-in-django-algorithm-for – seddonym Nov 19 '14 at 14:15