Django适配器CSV需要几个小时才能导入

时间:2013-04-14 21:56:47

标签: python django

我正在使用Django adaptors上传简单的CSV。当我导入100或200个联系人时,它似乎工作得很好。但是当我尝试上传一个包含5000个联系人的165kb文件时,它永远不会完成。我让它继续尝试,当我在1小时后回来时它还在尝试。

这有什么问题?使用Django adaptors导入165kb文件不可能需要一个多小时才能导入。 代码有问题吗?

 def process(self):
        self.date_start_processing = timezone.now()
        try:


            # Try and import CSV
            ContactCSVModel.import_data(data=self.filepath, extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])

            self._mark_processed(self.num_records)
        except Exception as e:
            self._mark_failed(unicode(e))

CsvModel

class ContactCSVModel(CsvModel):

    first_name = CharField()
    last_name = CharField()
    company = CharField()
    mobile = CharField()
    group = DjangoModelField(Group)
    contact_owner = DjangoModelField(User)


    class Meta:
        delimiter = "^"
        dbModel = Contact
        update = {'keys': ["mobile", "group"]}

4 个答案:

答案 0 :(得分:7)

将较大的任务拆分为较小的部分。

第1步 - 只需阅读CSV文件

ContactCSVModel.import_from_filename()和ContactCSVModel.import_from_file()都返回csv行。禁用与django模型的交互以跳过与数据库的交互。这应该会大大加快任务并打印导入的数据。这绝对有用!

CSVModel

class ContactCSVModel(CsvModel):

    first_name = CharField()
    last_name = CharField()
    company = CharField()
    mobile = CharField()
    group = DjangoModelField(Group)
    contact_owner = DjangoModelField(User)


    class Meta:
        delimiter = "^"

您的代码

 def process(self):
        self.date_start_processing = timezone.now()
        try:


            # Try and import CSV
            lines = ContactCSVModel.import_data(data=self.filepath, extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])
            print lines # or use logging

            self._mark_processed(self.num_records)
        except Exception as e:
            self._mark_failed(unicode(e))

第2步 - 启用django模型交互但禁用以检查数据库中的现有项目。

禁用它,因为启用此功能会在数据库中查询CSV中的每一行,以根据您的自然密钥规范检查现有项目(我已阅读源代码)。您可能知道CSV中的所有行都是唯一的联系人。

如果您的问题在整个导入期间是缓慢的数据库查询,这将有所帮助,但如果导入消耗太多内存,则无法提供帮助。

class ContactCSVModel(CsvModel):

    first_name = CharField()
    last_name = CharField()
    company = CharField()
    mobile = CharField()
    group = DjangoModelField(Group)
    contact_owner = DjangoModelField(User)


    class Meta:
        delimiter = "^"
        dbModel = Contact

第3步 - 导入同等大小的CSV

使用CSVModel并启用与Contact模型的交互,但为ContactCSVModel.import_data()提供较小的可迭代次数。我将其设置为500.根据您的需要进行更改。下面的代码示例(link)是为了让您了解。您需要稍微更改它以将其放入现有代码中。如果内存消耗是问题,这将有所帮助。

import csv
reader = csv.reader(open(self.filepath, 'rb'))

def gen_chunks(reader, chunksize=100):
    """ 
    Chunk generator. Take a CSV `reader` and yield
    `chunksize` sized slices. 
    """
    chunk = []
    for i, line in enumerate(reader):
        if (i % chunksize == 0 and i > 0):
            yield chunk
            del chunk[:]
        chunk.append(line)
    yield chunk

for chunk in gen_chunks(reader, chunksize=500):
    ContactCSVModel.import_data(data=chunk, extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])

第4步 - 瞄准大量内存消耗和慢速操作

因为django-adapters在导入和缓慢操作期间将所有Contact模型实例保存在内存中,因为多次单次提交而不是批量插入操作 - 它不适合较大的文件。

你与django-adapters有点联系。如果您依赖此django包,则无法切换到批量插入。使用任务管理器在Windows上用top或htop检查linux下的内存消耗。如果进程耗尽并且操作系统开始交换,请切换到另一个具有更高效内存消耗的django附加组件和批量插入作为选项 - 有很多用于csv导入。

另一个提示是使用csv模块进行读取,并使用django模型知识与数据库进行交互。这对你来说并不是一个真正的挑战 - 只要用你的大图片中的孤立任务来尝试它,如果它们正在工作就把它们放在一起 - 祝你好运。

答案 1 :(得分:2)

首先要尝试将iterable传递给import_data函数:

ContactCSVModel.import_data(open(self.filepath), extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])

要尝试的第二件事是使用import_from_filename

ContactCSVModel.import_from_filename(self.filepath, extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])

如果这没有帮助,请尝试找出悬挂的位置。你可以通过减小csv文件的大小来手动完成,或者你可以在csv.reader上模拟,或者你可以模拟CsvImporter.process_line而不是处理行,打印出它们以查看它在哪里停。如果您需要嘲笑帮助,请告诉我。

此外,此issue可以相关。

答案 2 :(得分:2)

我首先会检查csv中是否存在数据错误。例如,如果列具有错误的转义字符或不正确的数据类型 - 可能DB在某些列上不能接受空值。

虽然它挂起了,你可以手动检查数据库是否正在填充吗?通过命令行MySQL提示或工作台?如果是,则打开自动提交,您应该能够看到它挂在哪一行 - 然后在CSV中检查该记录。

但是,如果关闭自动提交(我不知道Django默认做了什么,或者你的数据库是如何配置的)那么你可能会溢出事务缓冲区。应该有一种方法可以分阶段手动刷新/提交事务来解决这个问题。

答案 3 :(得分:2)

我对django-adapters知之甚少,但是在数据库导入速度慢的时候帮助我的一些事情是在方法上使用@transaction.commit_manually()装饰器,或者使用Model.objects.bulk_create()方法。对你来说,看起来commit_manually方法可能会有所帮助,但是bulk_create方法不会,因为你实际上并没有控制创建过程。