1

What is the best way to go about creating multiple identical objects as a result of a user completing a single form in Django?

If I call form.save() in a loop (based on a set number, either set by the form or something else), it only creates one instance with the same ID several times.

What I'm actually trying to do is to create a load of identical instances, every day/week/whatever based on a range of user-specified dates (using django-recurrence at the moment).

Any ideas?

bodger
  • 1,112
  • 6
  • 24

2 Answers2

0

You're looking for Django's atomic transactions. Then copy the model instance using pk=None to create the copy:

from django.db import IntegrityError, transaction

def save(self, commit=False):
    model1 = super(YourForm, self).save(commit=False)    
    try:
        with transaction.atomic():
            model1.save()
            model1.pk = None
            model1.save() # Saves the second instance, you can loop this.
    except IntegrityError:
        handle_exception()

    return model1

Edit - if speed is your issue, you can use bulk_create like so:

def save(self, commit=False):
    number_of_objects = range(10) # How many objects you want to save
    n = 0
    model1 = super(YourForm, self).save(commit=False)
    objs = [
        YourModel(
            attribute1=model1.attribute1, 
            attribute2=model1.attribute2
        )
        for obj in number_of_objects
    ]
    YourModel.objects.bulk_create(objs)
Community
  • 1
  • 1
YPCrumble
  • 26,610
  • 23
  • 107
  • 172
  • Interesting - what's the idea behind doing the transactions atomically? Just to ensure none are saved if there's an error? – bodger Jun 27 '16 at 12:33
  • Also, if I do it this way, all of the new objects share the same foreign keys, so if I change the foreign key for one of them, it will be reflected in all of them. I want to generate totally new objects for each one but using the same criteria, from the form. – bodger Jun 27 '16 at 13:04
  • @bodger `transaction.atomic` is so that you make one call to the DB rather than many. It's optional. If you change one foreignkey it will _not_ change all of them. You'd have to use [`update`](https://docs.djangoproject.com/en/1.9/topics/db/queries/#updating-multiple-objects-at-once) on a queryset of the appropriate model instances for that. – YPCrumble Jun 27 '16 at 13:24
  • @bodger if the issue is the speed of saving these objects (i.e., you're saving thousands of objects) you can use `bulk_create`, see my update. – YPCrumble Jun 27 '16 at 13:26
  • Sorry, with the foreign key thing, what I meant was "if I change something in the object that the foreign key points to, the change will be reflected in all of the created objects". For example, if the foreign key is the Venue of the created events, then if I change the postcode of the Venue object, that postcode will be changed in all of the events. Maybe a good thing but possibly not always. – bodger Jun 28 '16 at 11:47
  • @bodger I'd have to know more about the specific use case, but creating many objects to be updated in parallel is error-prone. You're looking for one object with multiple dates associated with it. One option might be to create just one object, and multiple `ObjectDate` objects that contain a `DateField` and a `ForeignKey` related to the main object. That way you can add and remove dates as you want, and making one change to the main `Object` will affect every relation with the associated `ObjectDate` object. – YPCrumble Jun 28 '16 at 12:10
  • Ah, sorry - I actually want to AVOID changes to the foreign keys changing the main objects, not update them all in parallel. ie. I want the created events to be totally separate entities once they're created. – bodger Jun 28 '16 at 12:41
0

I needed to post reminders for multiple clients depending whether the checkbox was checked when creating the reminder. Deepcopy makes seperate objects = separate rows in de database.

from copy import deepcopy

... in a function based view....
if request.method == 'POST':
    form = NoticeForm(request.POST)
    if form.is_valid():
        # municipalities is a checkbox list on the screen
        municipalities_id_list = request.POST.getlist('municipalities')
        municipalities = Group.objects.filter(id__in=municipalities_id_list)

        for gm in municipalities:
            notic = deepcopy(form.save(commit=False))
            notic.id = None    # let Django assign a new id when doing the save
            notic.client = gm
            notic.last_modified_user = user
            notic.save()
MZA
  • 999
  • 11
  • 16