Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

create multiple objects without multiple hits to the db in Django 1.8

With Django 1.8 how do I create multiple objects and write to db in a single db transaction?

It seems previous versions had @commit_manually. But when I try to modify this code: https://stackoverflow.com/a/29834940 I get an exception

django.db.transaction.TransactionManagementError: The outermost 'atomic' block cannot use savepoint = False when autocommit is off.

At lot of posts I’ve seen say to wrap in “with transaction.atomic()” block but this hits the db in every loop.

Here is a simplified version of my code. I haven’t ran it but it should show what I’m trying to do.

class Foo(models.Model):
    Name = models.CharField( max_length=200 )

class Bar(models.Model):
    Foos = models.ManyToManyField( foo )

class Processor(object):
    def run(self,):
        myBar = Bar.object.create()

        myList = ['a', 'b', 'c', 'd']

        if True:
            set_autocommit( False )

            for char in myList:
                myFoo = Foo.objects.create(Name=char)
                myBar.Foos.add( myFoo )

            commit()
            set_autocommit( True )

I'm trying to make all these changes and only hit the db once. I know there is model.objects.bulk_create but I couldn't find a what to handle the m2m relationships. Also bulk_create doesn't return the the db instances so I have to pull them out of the db for the m2m relationships.

like image 774
brian Avatar asked Aug 04 '15 21:08

brian


People also ask

How do I make multiple objects in Django?

To create multiple records based on a Django model you can use the built-in bulk_create() method.

How do you create multiple objects in Python?

We can create list of object in Python by appending class instances to list. By this, every index in the list can point to instance attributes and methods of the class and can access them. If you observe it closely, a list of objects behaves like an array of structures in C.

What does objects all () do in Django?

all() Returns a copy of the current QuerySet (or QuerySet subclass). This can be useful in situations where you might want to pass in either a model manager or a QuerySet and do further filtering on the result. After calling all() on either object, you'll definitely have a QuerySet to work with.

What is QuerySet in Django?

A QuerySet is a collection of data from a database. A QuerySet is built up as a list of objects. QuerySets makes it easier to get the data you actually need, by allowing you to filter and order the data.


1 Answers

You can't do all of them in one query. However, you are on the right track. You should be using .bulk_create() for batch insertion and do one more lookup for getting them back as objects in order to add into foos.

If you are too concerned that the latter query will perform slowly, you can set unique=True or, db_index=True for improving performance.

You also want to maintain the atomicity of operations using transactions, so that any INSERT fails, all of them should be rolled back:

from django.db import transaction

class Foo(models.Model):
    name = models.CharField(max_length=200, unique=True)

class Bar(models.Model):
    foos = models.ManyToManyField(Foo)

class Processor(object):
    def run(self):
        transaction.set_autocommit(False)
        try:
            myList = ['a', 'b', 'c', 'd']
            Foo.objects.bulk_create([Foo(n) for n in myList])

            myBar = Bar.object.create()
            myBar.foos.add(Foo.objects.filter(name__in=myList))
        except:
            transaction.rollback()
            raise
        else:
            transaction.commit()
        finally:
            transaction.set_autocommit(True)

Please see Django documentation for more information about autocommit behavior.

like image 112
Ozgur Vatansever Avatar answered Sep 23 '22 21:09

Ozgur Vatansever