如何在 Django 中执行批量插入?

Posted

技术标签:

【中文标题】如何在 Django 中执行批量插入?【英文标题】:How do I perform a batch insert in Django? 【发布时间】:2010-04-16 19:37:08 【问题描述】:

mysql中,对于n > 0,你可以在一个表中插入多行:

INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9), ..., (n-2, n-1, n);

有没有办法使用 Django 查询集方法来实现上述目标?这是一个例子:

values = [(1, 2, 3), (4, 5, 6), ...]

for value in values:
    SomeModel.objects.create(first=value[0], second=value[1], third=value[2])

我相信上面是为 for 循环的每次迭代调用一个插入查询。我正在寻找一个查询,这在 Django 中可能吗?

【问题讨论】:

更新:django开发版将发布bulk_create方法:docs.djangoproject.com/en/dev/ref/models/querysets/#bulk-create 【参考方案1】:

这些答案已经过时。 bulk_create 已被 Django 1.4 引入:

https://docs.djangoproject.com/en/dev/ref/models/querysets/#bulk-create

【讨论】:

在 bulk_create 过程中有一些缺点,@​​987654323@ 请特别注意“如果模型的主键是 AutoField,它不会像 save() 那样检索和设置主键属性,除非数据库后端支持它(当前为 PostgreSQL)”。 【参考方案2】:

我最近自己也在寻找这样的东西(灵感来自 QuerySet.update(),我想你也是)。据我所知,当前的生产框架(截至今天的 1.1.1)中不存在批量创建。我们最终为需要批量创建的模型创建了一个自定义管理器,并在该管理器上创建了一个函数,以使用 VALUES 参数序列构建适当的 SQL 语句。

类似的东西(如果这不起作用,请道歉......希望我已经从我们的代码中对其进行了可运行的调整):

from django.db import models, connection

class MyManager(models.Manager):

    def create_in_bulk(self, values):
        base_sql = "INSERT INTO tbl_name (a,b,c) VALUES "
        values_sql = []
        values_data = []

        for value_list in values:
            placeholders = ['%s' for i in range(len(value_list))]
            values_sql.append("(%s)" % ','.join(placeholders))
            values_data.extend(value_list)

        sql = '%s%s' % (base_sql, ', '.join(values_sql))

        curs = connection.cursor()
        curs.execute(sql, values_data)

class MyObject(models.Model):
    # model definition as usual... assume:
    foo = models.CharField(max_length=128)

    # custom manager
    objects = MyManager()

MyObject.objects.create_in_bulk( [('hello',), ('bye',), ('c', )] )

这种方法确实存在非常特定于特定数据库的风险。在我们的例子中,我们希望函数返回刚刚创建的 ID,因此我们在函数中有一个特定于 postgres 的查询,以从表示对象的表的主键序列中生成必要数量的 ID。也就是说,与迭代数据并发出单独的 QuerySet.create() 语句相比,它在测试中的表现要好得多。

【讨论】:

顺便说一句。如果您有很多数据,这种方法可能会导致 mysql(可能还有其他数据库)出现“数据包太大”错误。最好将数据集拆分成更小的块。【参考方案3】:

这是执行仍然通过 Django 的 ORM 的批量插入的方法(因此保留了 ORM 提供的许多好处)。这种方法包括继承 InsertQuery 类以及创建一个自定义管理器,该管理器准备模型实例以插入数据库,其方式与 Django 的 save() 方法使用的方式非常相似。下面的 BatchInsertQuery 类的大部分代码直接来自 InsertQuery 类,只添加或修改了几行关键行。要使用 batch_insert 方法,请传入一组要插入数据库的模型实例。这种方法使视图中的代码不必担心将模型实例转换为有效的 SQL 值;管理器类与 BatchInsertQuery 类一起处理该问题。

from django.db import models, connection
from django.db.models.sql import InsertQuery

class BatchInsertQuery( InsertQuery ):

    ####################################################################

    def as_sql(self):
        """
        Constructs a SQL statement for inserting all of the model instances
        into the database.

        Differences from base class method:        

        - The VALUES clause is constructed differently to account for the
        grouping of the values (actually, placeholders) into
        parenthetically-enclosed groups. I.e., VALUES (a,b,c),(d,e,f)
        """
        qn = self.connection.ops.quote_name
        opts = self.model._meta
        result = ['INSERT INTO %s' % qn(opts.db_table)]
        result.append('(%s)' % ', '.join([qn(c) for c in self.columns]))
        result.append( 'VALUES %s' % ', '.join( '(%s)' % ', '.join( 
            values_group ) for values_group in self.values ) ) # This line is different
        params = self.params
        if self.return_id and self.connection.features.can_return_id_from_insert:
            col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column))
            r_fmt, r_params = self.connection.ops.return_insert_id()
            result.append(r_fmt % col)
            params = params + r_params
        return ' '.join(result), params

    ####################################################################

    def insert_values( self, insert_values ):
        """
        Adds the insert values to the instance. Can be called multiple times
        for multiple instances of the same model class.

        Differences from base class method:

        -Clears self.columns so that self.columns won't be duplicated for each
        set of inserted_values.        
        -appends the insert_values to self.values instead of extends so that
        the values (actually the placeholders) remain grouped separately for
        the VALUES clause of the SQL statement. I.e., VALUES (a,b,c),(d,e,f)
        -Removes inapplicable code
        """
        self.columns = [] # This line is new

        placeholders, values = [], []
        for field, val in insert_values:
            placeholders.append('%s')

            self.columns.append(field.column)
            values.append(val)

        self.params += tuple(values)
        self.values.append( placeholders ) # This line is different

########################################################################

class ManagerEx( models.Manager ):
    """
    Extended model manager class.
    """
    def batch_insert( self, *instances ):
        """
        Issues a batch INSERT using the specified model instances.
        """
        cls = instances[0].__class__
        query = BatchInsertQuery( cls, connection )
        for instance in instances:

             values = [ (f, f.get_db_prep_save( f.pre_save( instance, True ) ) ) \
                 for f in cls._meta.local_fields ]
            query.insert_values( values )

        return query.execute_sql()

########################################################################

class MyModel( models.Model ):
    myfield = models.CharField(max_length=255)
    objects = ManagerEx()

########################################################################

# USAGE:
object1 = MyModel(myfield="foo")
object2 = MyModel(myfield="bar") 
object3 = MyModel(myfield="bam")
MyModels.objects.batch_insert(object1,object2,object3)

【讨论】:

【参考方案4】:

您可以通过手动交易获得所需的性能。这将允许您做的是在一个事务中创建所有插入,然后一次提交所有事务。希望这会对您有所帮助:http://docs.djangoproject.com/en/dev/topics/db/transactions/

【讨论】:

【参考方案5】:

不,这是不可能的,因为 django 模型是对象而不是表。所以表操作不适用于 django 模型。 django 创建一个对象,然后将数据插入到表中,因此您不能一次创建多个对象。

【讨论】:

考虑到上面的答案确实有效,说这不可能似乎很疯狂。

以上是关于如何在 Django 中执行批量插入?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 r2dbc 中批量执行多个插入?

如何在 node.js 中使用 sqlite3 执行批量插入?

如何执行批量插入?

Django向数据库批量插入数据

Django 批量更新/插入性能

MyBatis如何执行批量插入数据?