我正在使用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"]}
答案 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方法不会,因为你实际上并没有控制创建过程。