Sql 如何在Django中执行批插入?
在mysql中,您可以在一个查询中将多行插入到一个表中,以获得n>0:Sql 如何在Django中执行批插入?,sql,django,Sql,Django,在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 queryset方法实现上述功能?下面是一个例子: values = [(1, 2, 3), (4, 5, 6), ...] for value in values: SomeModel.objects.create(first=
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循环的每个迭代调用一个insert查询。我在寻找一个查询,这在Django中可能吗?不可能,因为Django模型是对象而不是表。因此,表操作不适用于django模型。django创建一个对象,然后将数据插入到表中,因此不能一次创建多个对象。您可以通过手动事务获得所需的性能。这将允许您在一个事务中创建所有插入,然后一次提交所有事务。希望这能对你有所帮助:我最近自己也在寻找这样一件事(灵感来自,我想你也是)。据我所知,当前的生产框架(截至今天的1.1.1)中不存在批量创建。我们最终为需要批量创建的模型创建了一个自定义管理器,并在该管理器上创建了一个函数,以使用值序列参数构建适当的SQL语句 类似(如果这不起作用,请道歉…希望我已经从我们的代码中很好地修改了这个):
这种方法确实存在非常特定于特定数据库的风险。在我们的例子中,我们希望函数返回刚刚创建的ID,因此我们在函数中有一个特定于postgres的查询,以从表示对象的表的主键序列生成所需数量的ID。也就是说,与迭代数据并发出单独的QuerySet.create()语句相比,它在测试中的性能要好得多。这里有一种方法可以执行仍然通过Django的ORM的批插入(从而保留ORM提供的许多好处)。这种方法包括对InsertQuery类进行子类化,以及创建一个定制管理器,该管理器以Django的save()方法使用的方式准备模型实例插入数据库。下面BatchInsertQuery类的大部分代码直接来自InsertQuery类,只添加或修改了几行关键代码。要使用batch_insert方法,请传入一组要插入到数据库中的模型实例。这种方法使视图中的代码不必担心将模型实例转换为有效的SQL值;manager类与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)
这些答案已经过时了
顺便说一下。如果您有很多数据,这种方法可能会在mysql(可能还有其他数据库)上导致“数据包太大”错误。最好将数据集分割成更小的块。更新:django开发版本将发布一个
bulk\u create
方法:考虑到上面的答案,认为这是不可能的,这似乎是胡说八道。由于bulk\u create过程中存在一些缺点,模型的save()方法将不会被调用,并且不会发送预保存和后保存信号。
请特别注意,“如果模型的主键是自动字段,则不会像save()那样检索和设置主键属性,除非数据库后端支持它(当前为PostgreSQL)”。
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)