2010-04-16 14 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 क्वेरीसमूह तरीकों के साथ ऊपर प्राप्त करने के लिए कोई तरीका है? यहाँ एक उदाहरण है:

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

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

मेरा मानना ​​है कि इसके बाद के संस्करण पाश के लिए में से प्रत्येक यात्रा के लिए एक डालने क्वेरी बुला रहा है। मैं एक प्रश्न की तलाश में हूं, क्या यह Django में संभव है?

+2

अद्यतन: https://docs.djangoproject.com/en/dev/ref/models/querysets/: Django विकास संस्करण एक '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',)]) 

यह दृष्टिकोण एक विशेष डेटाबेस के लिए बहुत विशिष्ट होने का खतरा है। हमारे मामले में, हम चाहते थे कि फ़ंक्शन अभी बनाई गई आईडी को वापस कर दे, इसलिए हमारे पास ऑब्जेक्ट का प्रतिनिधित्व करने वाली तालिका के लिए प्राथमिक कुंजी अनुक्रम से आवश्यक संख्या में आईडी उत्पन्न करने के लिए फ़ंक्शन में पोस्टग्रेस-विशिष्ट क्वेरी थी। उस ने कहा, यह आंकड़ों में पुन: प्रयास करने और अलग क्वेरीरीसेट.क्रेट() कथन जारी करने के परीक्षणों में काफी बेहतर प्रदर्शन करता है।

+2

। यदि आपके पास बहुत अधिक डेटा है तो यह दृष्टिकोण mysql (और शायद अन्य डेटाबेस) पर "पैकेट बहुत बड़ी" त्रुटि का कारण बन सकता है। अपने डेटासेट को एक छोटे भाग में विभाजित करना बेहतर है। –

-2

नहीं यह संभव नहीं है क्योंकि django मॉडल तालिका के बजाए ऑब्जेक्ट्स हैं। तो टेबल क्रियाएं django मॉडल पर लागू नहीं हैं। और django एक ऑब्जेक्ट बनाता है फिर तालिका में डेटा डालता है इसलिए आप एक समय में एकाधिक ऑब्जेक्ट नहीं बना सकते हैं।

+3

वास्तव में काम करने वाले उपरोक्त उत्तरों को ध्यान में रखते हुए, यह कहना संभव नहीं है कि यह पागल लगता है। वैसे भी – boatcoder

4

आपको मैन्युअल लेन-देन करके आपको जो प्रदर्शन चाहिए वह आपको मिल सकता है। यह आपको एक लेनदेन में सभी आवेषण बनाने के लिए क्या करने की अनुमति देगा, फिर लेनदेन को एक ही समय में प्रतिबद्ध करें। उम्मीद है कि यह आपकी मदद करेगा: http://docs.djangoproject.com/en/dev/topics/db/transactions/

9

बैच आवेषण करने का तरीका यहां है जो अभी भी Django के ORM (और इस प्रकार ओआरएम प्रदान करता है) के कई लाभों को बरकरार रखता है। इस दृष्टिकोण में InsertQuery क्लास को उप-वर्गीकरण करना और एक कस्टम मैनेजर बनाना शामिल है जो डेटाबेस में सम्मिलन के लिए मॉडल उदाहरणों को उसी तरह से तैयार करता है जैसे Django की save() विधि का उपयोग करता है। बैचइन्सर्टक्वियर क्लास के लिए अधिकांश कोड सीधे InsertQuery क्लास से है, केवल कुछ महत्वपूर्ण लाइनों को जोड़ा या संशोधित किया गया है। बैच_इन्सर्ट विधि का उपयोग करने के लिए, मॉडल उदाहरणों के एक सेट में पास करें जिन्हें आप डेटाबेस में सम्मिलित करना चाहते हैं। यह दृष्टिकोण मॉडल दृश्यों को वैध SQL मानों में अनुवाद करने के बारे में चिंता करने से आपके विचारों में कोड को मुक्त करता है; बैचइन्सर्टक्वियर क्लास के संयोजन के साथ प्रबंधक वर्ग उस पर संभालता है।

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 के दौरान कुछ कमियों के साथ, 'मॉडल की सेव() विधि को नहीं कहा जाएगा, और pre_save और post_save सिग्नल भेजे जाएंगे।' – Charlesliam

+0

विशेष रूप से ध्यान दें कि "यदि मॉडल की प्राथमिक कुंजी ऑटोफ़ील्ड है तो यह पुनर्प्राप्त नहीं होती है और प्राथमिक कुंजी विशेषता सेट करें, क्योंकि save() करता है, जब तक कि डेटाबेस बैकएंड इसका समर्थन नहीं करता (वर्तमान में PostgreSQL) "। – Ninjakannon

संबंधित मुद्दे