2010-04-16 37 views
30

mysql, sen n> 0 için bir sorguda bir tabloya birden çok satır ekleyebilirsiniz:Django'da toplu iş parçacığını nasıl uygularım?

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

Django QuerySet yöntemleri ile yukarıda ulaşmak için bir yol var mı? İşte bir örnek:

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

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

Yukarıda döngü her okunması için bir ekleme sorgusu çağırıyor inanıyoruz. Tek bir sorgu arıyorum, bu Django'da mümkün mü?

+2

Güncelleme: https://docs.djangoproject.com/en/dev/ref/models/querysets/: Django geliştirme sürümü 'bulk_create' yöntemi yayınlayacak # bulk-create –

cevap

12

Son zamanlarda kendimi böyle bir şey için aradım (ben de hayal ettiğim gibi, QuerySet.update()'dan esinlenerek). Bilgime göre, mevcut üretim çerçevesinde (bugün itibariyle 1.1.1) yığınsal bir yapı bulunmamaktadır. Toplu oluşturma gerektiren model için özel bir yönetici oluşturduk ve VALUES parametrelerinin sırasına uygun bir SQL ifadesi oluşturmak için bu yönetici üzerinde bir işlev oluşturduk. (Bu işe yaramazsa ... umarım ben bizim kodundan runnably bu adapte ettik özür) gibi

şey:

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',)]) 

Bu yaklaşım, belirli bir veritabanına çok özel olma riskiyle gelmez. Bizim durumumuzda, yeni oluşturulan kimlikleri döndürme işlevini istedik, bu nedenle, nesneyi temsil eden tablonun birincil anahtar sırasından gerekli sayıda kimlik üretmek için işleve bir postgrese özgü sorguyu aldık. Bu, testler üzerinde, verilerin üzerinde yineleme ve ayrı QuerySet.create() ifadeleri yayınlayana kıyasla daha iyi performans gösteriyor.

+2

Bu arada. Çok fazla veriye sahipseniz, bu yaklaşım mysql (ve muhtemelen diğer veritabanları) üzerinde "Paket çok büyük" hatasına yol açabilir. Veri kümenizi daha küçük parçalara bölmek daha iyidir. –

-2

Hayır, bu mümkün değildir, çünkü django modelleri bir tablodan ziyade nesnelerdir. Tablo eylemleri django modellerine uygulanamaz. ve django bir nesne oluşturur ve ardından verileri tabloya ekler, böylece tek seferde birden fazla nesne oluşturamazsınız.

+3

Gerçekten işe yarayan cevaplar göz önüne alındığında, mümkün olmadığını söyleyerek fındık gibi görünüyor. – boatcoder

4

İhtiyaç duyduğunuz performansı manuel işlemler yaparak alabilirsiniz. Bunun yapabilmeniz için yapmanız gereken tek bir işlemdeki tüm eklemeleri oluşturmak, daha sonra işlemi bir kerede taahhüt etmektir. Umarım bu size yardımcı olur: http://docs.djangoproject.com/en/dev/topics/db/transactions/

9

Django'nun ORM'sini (ve böylece ORM'nin sağladığı birçok avantajı koruyan) toplu eklemeleri gerçekleştirmenin bir yolu. Bu yaklaşım, Django'nun save() yönteminin kullandığı yöntemle aynı şekilde veritabanına yerleştirmek için model örneklerini hazırlayan bir özel yönetici oluşturmanın yanı sıra, InsertQuery sınıfının alt sınıflarını da içerir. Aşağıdaki BatchInsertQuery sınıfı için kodun çoğu, eklenen veya değiştirilen birkaç anahtar satırı ile doğrudan InsertQuery sınıfından alınmıştır. Batch_insert yöntemini kullanmak için, veritabanına eklemek istediğiniz bir dizi model örneğini iletin. Bu yaklaşım, model örneklerini geçerli SQL değerlerine çevirmekten endişelenmenize gerek kalmadan görünümünüzdeki kodu serbest bırakır; BatchInsertQuery sınıfı ile birlikte yönetici sınıfı bunu ele alır.

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

Bu yanıtlar güncel değil. bulk_create Django 1.4 getirilen edilmiştir:

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

+4

bulk_create sırasında bazı dezavantajlarla, 'modelin save() yöntemi çağrılmayacak ve pre_save ve post_save sinyalleri gönderilmeyecektir. ' – Charlesliam

+0

Özellikle" modelin birincil anahtarı bir AutoField ise " veritabanı arka ucu desteklemediği sürece (şu anda PostgreSQL) "," birincil anahtar özniteliğini kaydedin(). – Ninjakannon

İlgili konular