2010-04-16 75 views
30

在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中可能嗎?

+2

更新:Django開發版本將發佈'bulk_create'方法:https://docs.djangoproject.com/en/dev/ref/models/querysets/ #bulk-create – 2012-02-26 06:28:26

回答

12

我最近自己找了這樣的東西(受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()語句。

+2

。如果您擁有非常多的數據,這種方法可能會導致mysql(以及其他數據庫)出現「Packet too large」錯誤。最好將數據集分成更小的塊。 – 2010-11-15 14:09:41

-2

不,這是不可能的,因爲django模型是對象而不是表格。所以table操作不適用於django模型。和Django創建一個對象,然後將數據插入到表中,因此您無法一次創建多個對象。

+3

考慮到上面的實際工作的答案,說這是不可能的。順便說一句, – boatcoder 2012-05-26 05:37:48

9

這裏是做批量插入的方法,它仍然通過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) 
61

這些答案已過時。 bulk_create已提請在Django 1.4:

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

+4

在bulk_create過程中有一些缺點:'不會調用模型的save()方法,並且不會發送pre_save和post_save信號。「 – Charlesliam 2014-09-10 00:30:31

+0

特別要注意的是」如果模型的主鍵是自動調整,像save()那樣設置主鍵屬性,除非數據庫後端支持它(當前PostgreSQL)「。 – Ninjakannon 2017-07-31 21:44:54