Pertanyaan Bagaimana cara melakukan insert batch dalam Django?


Di mysql, Anda dapat memasukkan beberapa baris ke tabel dalam satu query untuk n> 0:

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

Apakah ada cara untuk mencapai hal di atas dengan metode query Django? Inilah contohnya:

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

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

Saya percaya hal di atas memanggil query insert untuk setiap iterasi dari for loop. Saya mencari satu pertanyaan, apakah itu mungkin di Django?


33
2018-04-16 19:37


asal


Jawaban:


Saya baru-baru ini mencari hal semacam itu sendiri (terinspirasi oleh QuerySet.update (), seperti yang saya bayangkan Anda juga). Sepengetahuan saya, tidak ada pembuatan massal dalam kerangka produksi saat ini (1.1.1 hingga hari ini). Kami akhirnya membuat manajer khusus untuk model yang perlu dibuat massal, dan membuat fungsi pada manajer tersebut untuk membuat pernyataan SQL yang sesuai dengan urutan parameter VALUES.

Sesuatu seperti (meminta maaf jika ini tidak berhasil ... semoga saya telah mengadaptasi ini secara ketat dari kode kami):

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

Pendekatan ini menjalankan risiko menjadi sangat spesifik ke database tertentu. Dalam kasus kami, kami ingin fungsi untuk mengembalikan ID yang baru dibuat, jadi kami memiliki permintaan spesifik postgres dalam fungsi untuk menghasilkan jumlah ID yang diperlukan dari urutan kunci utama untuk tabel yang mewakili objek. Yang mengatakan, itu melakukan secara signifikan lebih baik dalam tes versus iterasi atas data dan mengeluarkan laporan QuerySet.create () terpisah.


12
2018-04-16 23:50



Jawaban-jawaban ini sudah ketinggalan zaman. bulk_create telah dibawa dalam Django 1.4:

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


64
2018-02-02 20:22



Berikut ini adalah cara melakukan insert batch yang masih berjalan melalui ORM Django (dan dengan demikian mempertahankan banyak manfaat yang disediakan ORM). Pendekatan ini melibatkan subclassing kelas InsertQuery serta membuat manajer kustom yang menyiapkan contoh model untuk penyisipan ke dalam basis data dengan cara yang hampir sama seperti metode simpan Django (). Sebagian besar kode untuk kelas BatchInsertQuery di bawah ini langsung dari kelas InsertQuery, dengan hanya beberapa baris kunci ditambahkan atau dimodifikasi. Untuk menggunakan metode batch_insert, berikan serangkaian contoh model yang ingin Anda masukkan ke dalam database. Pendekatan ini membebaskan kode dalam pandangan Anda dari harus khawatir tentang menerjemahkan contoh model menjadi nilai SQL yang valid; kelas manajer dalam hubungannya dengan kelas BatchInsertQuery yang menangani itu.

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)

9
2017-08-11 15:17



Anda mungkin mendapatkan kinerja yang Anda butuhkan dengan melakukan transaksi manual. Apa yang akan Anda lakukan adalah membuat semua sisipan dalam satu transaksi, lalu melakukan transaksi sekaligus. Semoga ini akan membantu Anda: http://docs.djangoproject.com/en/dev/topics/db/transactions/


4
2018-04-16 23:19



Tidak, itu tidak mungkin karena model Django adalah objek daripada meja. jadi tindakan tabel tidak berlaku untuk model Django. dan Django membuat objek lalu memasukkan data ke dalam tabel sehingga Anda tidak dapat membuat beberapa objek dalam satu waktu.


-3
2018-04-16 22:59