如何在Django中执行批量插入?
在MySQL中,你可以在对n> 0一个查询中插入多行到表:如何在Django中执行批量插入?
INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9), ..., (n-2, n-1, n);
有没有办法实现上述Django的queryset的方法呢?这里有一个例子:
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中可能吗?
我最近自己找了这样的东西(受QuerySet.update()的启发,我想你也是)。据我所知,目前的生产框架中没有批量生产(截至今日1.1.1)。我们最终为需要批量创建的模型创建了一个自定义管理器,并在该管理器上创建了一个函数,以便使用VALUES参数序列构建适当的SQL语句。
喜欢的东西(道歉,如果这也不行......希望我已经从我们的代码适应了这个runnably):
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(以及其他数据库)出现“Packet too large”错误。最好将数据集分成更小的块。 – 2010-11-15 14:09:41
不,这是不可能的,因为django模型是对象而不是表格。所以table操作不适用于django模型。和Django创建一个对象,然后将数据插入到表中,因此您无法一次创建多个对象。
考虑到上面的实际工作的答案,说这是不可能的。顺便说一句, – boatcoder 2012-05-26 05:37:48
您可能通过执行手动事务来获得所需的性能。这将允许您执行的操作是在一个事务中创建所有插入,然后一次完成所有事务。希望这会帮助你:http://docs.djangoproject.com/en/dev/topics/db/transactions/
这里是做批量插入的方法,它仍然通过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)
这些答案已过时。 bulk_create
已提请在Django 1.4:
https://docs.djangoproject.com/en/dev/ref/models/querysets/#bulk-create
在bulk_create过程中有一些缺点:'不会调用模型的save()方法,并且不会发送pre_save和post_save信号。“ – Charlesliam 2014-09-10 00:30:31
特别要注意的是”如果模型的主键是自动调整,像save()那样设置主键属性,除非数据库后端支持它(当前PostgreSQL)“。 – Ninjakannon 2017-07-31 21:44:54
更新:Django开发版本将发布'bulk_create'方法:https://docs.djangoproject.com/en/dev/ref/models/querysets/ #bulk-create – 2012-02-26 06:28:26