2012-10-16 272 views
2

我有問題搞清楚MySQL插入...在與django 1.4的重複密鑰更新。MySQL的插入...在django 1.4的批量插入的重複密鑰更新

我試圖插入記錄的表有2列(複合)唯一鍵。我收到的記錄來自第三方來源,除了那些設置唯一密鑰的字段以外,值會隨時間變化。我一次只能收到1〜5k個記錄,需要

目前我正在使用Model.objects.bulk_create進行批量插入,因爲它通常發出一個查詢,無論記錄集多大。但是,由於我的記錄可能隨着時間的推移而在第三方結束時發生變化,因此我需要在記錄集上執行MySQL INSERT ... ON DUPLICATE KEY UPDATE查詢。

我打算寫原始SQL語句,並使用類似本地執行:

sql = "MySQL INSERT ... ON DUPLICATE KEY UPDATE" 

raw_insert(sql) 

def raw_insert(sql): 
    from django.db import connection, transaction 
    cursor = connection.cursor() 

    # Data modifying operation - commit required 
    cursor.execute(sql) 
    transaction.commit_unless_managed() 

    return 1 

不知道是否有更好的解決我的問題。另外我將如何清理原始插入的字段值?

+0

你爲什麼要做原始光標的東西,而不是使用djangos內置的ORM與MySQL? –

+1

問題是批量插入。當我收到表中已經存在的5k條記錄時,創建5k個ORM對象效率不高。但是,使用objects.bulk_create插入新記錄非常高效。我將得到重複輸入錯誤(IntegrityError)。 – mmohiudd

回答

8

所以我創建了一個自定義管理器。這裏是經理:

class BulkInsertManager(models.Manager): 
    def _bulk_insert_or_update(self, create_fields, update_fields, values): 

     from django.db import connection, transaction 
     cursor = connection.cursor() 

     db_table = self.model._meta.db_table 

     values_sql = [] 
     values_data =[] 

     for value_lists in values: 
      values_sql.append("(%s)" % (','.join([ "%s" for i in range(len(value_lists))]),)) 
      values_data.extend(value_lists) 

     base_sql = "INSERT INTO %s (%s) VALUES " % (db_table, ",".join(create_fields)) 

     on_duplicates = [] 

     for field in update_fields: 
      on_duplicates.append(field + "=VALUES(" + field +")") 

     sql = "%s %s ON DUPLICATE KEY UPDATE %s" % (base_sql, ", ".join(values_sql), ",".join(on_duplicates)) 

     cursor.executemany(sql, [values_data]) 
     transaction.commit_unless_managed() 

和樣品模型:

class User_Friend(models.Model): 
    objects = BulkInsertManager() # assign a custom manager to handle bulk insert 

    id = models.CharField(max_length=255) 
    user = models.ForeignKey(User, null=False, blank=False) 
    first_name = models.CharField(max_length=30) 
    last_name = models.CharField(max_length=30) 
    city = models.CharField(max_length=50, null=True, blank=True) 
    province = models.CharField(max_length=50, null=True, blank=True) 
    country = models.CharField(max_length=30, null=True, blank=True) 

而且樣本實施:

def save_user_friends(user, friends): 
    user_friends = [] 
    for friend in friends: 

     create_fields = ['id', 'user_id', 'first_name', 'last_name', 'city', 'province', 'country'] 
     update_fields = ['first_name', 'last_name', 'city', 'province', 'country'] 

     user_friends.append(
      [ 
       str(user.id), 
       str(friend['id']), 
       friend['first_name'], 
       friend['last_name'], 
       friend['city'], 
       friend['province'], 
       friend['country'], 
      ] 
     ) 

    User_Friend.objects._bulk_insert_or_update(create_fields, update_fields, user_friends) 

這裏是gist

1

你可以用消毒的ModelForm:

from django.forms.models import modelform_factory 
form_class = modelform_factory(MyModel) 

for obj in my_data: 
    form = form_class(obj) 
    if not form.is_valid(): 
     raise Hell() 

至於原始SQL,我說去了。看起來Django的ORM不支持ON DUPLICATE KEY UPDATE,所以不要讓它阻礙你。 The Django docs talk about doing it without any reservation

雖然這可能值得使用Manager.raw