2012-10-16 128 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