2010-04-16 3 views
30

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); 

장고의 검색어 방법과 위를 달성하는 방법이 있나요? 다음은 그 예입니다 :

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

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

위의 내용은 for 루프의 각 반복마다 삽입 쿼리를 호출한다고 생각합니다. 단일 쿼리를 찾고 있는데 장고에서 가능합니까?

+2

업데이트 : https://docs.djangoproject.com/en/dev/ref/models/querysets/ : 장고 개발 버전은'bulk_create' 방법을 발표 할 예정이다 # bulk-create –

답변

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를 반환하기를 원했기 때문에 객체를 나타내는 테이블의 기본 키 시퀀스에서 필요한 수의 ID를 생성하기 위해 함수에 postgres 관련 쿼리가있었습니다. 즉, 테스트에서 데이터를 반복 실행하고 별도의 QuerySet.create() 문을 실행하는 것보다 훨씬 뛰어난 성능을 보입니다.

+2

그건 그렇고. 이 접근법은 매우 많은 데이터를 가지고 있다면 mysql (그리고 아마도 다른 데이터베이스)에서 "Packet too large"오류를 일으킬 수 있습니다. 데이터 세트를 작은 덩어리로 분할하는 것이 좋습니다. –

-2

django 모델이 테이블이 아닌 객체이기 때문에 불가능하지 않습니다. 테이블 동작은 장고 모델에는 적용되지 않습니다. django는 객체를 생성 한 다음 테이블에 데이터를 삽입하므로 한 번에 여러 객체를 작성할 수 없습니다.

+3

위의 답변을 고려하면 실제로 작동한다는 것은 가능하지 않다고 말하는 것은 어렵습니다. – boatcoder

4

수동 트랜잭션을 수행하여 필요한 성능을 얻을 수 있습니다. 이렇게하면 한 트랜잭션에 모든 삽입을 작성한 다음 한 번에 트랜잭션을 완결 할 수 있습니다. 다행히도 이것이 당신을 도울 것입니다 : http://docs.djangoproject.com/en/dev/topics/db/transactions/

9

Django의 ORM을 계속 수행하는 배치 삽입을 수행하는 방법입니다 (따라서 ORM이 제공하는 많은 이점이 유지됩니다). 이 접근법은 InsertQuery 클래스를 서브 클래 싱하는 것과 Django의 save() 메소드가 사용하는 것과 거의 같은 방식으로 모델 인스턴스를 데이터베이스에 삽입하기 위해 준비하는 커스텀 매니저를 생성하는 것을 포함한다. 아래의 BatchInsertQuery 클래스에 대한 대부분의 코드는 InsertQuery 클래스에서 직접 작성되었으며 몇 줄의 키 라인이 추가되거나 수정되었습니다. batch_insert 메소드를 사용하려면 데이터베이스에 삽입 할 모델 인스턴스 세트를 전달하십시오. 이 접근법은 뷰의 코드를 모델 인스턴스를 유효한 SQL 값으로 변환하는 것에 대해 걱정할 필요가 없도록 해줍니다. BatchInsertQuery 클래스와 함께 manager 클래스가이를 처리합니다.

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은 장고 1.4에서 가져온되었습니다

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

+4

bulk_create 동안의 단점을 가지고'모델의 save() 메소드는 호출되지 않고 pre_save와 post_save 신호는 전송되지 않을 것입니다. ' – Charlesliam

+0

특히 모델의 기본 키가 AutoField 인 경우 데이터베이스 백엔드가 지원하지 않는 한 (현재 PostgreSQL) "save()는 기본 키 속성을 설정합니다." – Ninjakannon

관련 문제