如何将 Pandas DataFrame 写入 Django 模型

31
我一直在使用 Python 中的 Pandas,通常将 DataFrame 写入到我的数据库表中,如下所示。现在我正在迁移到 Django,如何通过名为 MyModel 的模型将相同的 DataFrame 写入到表中?真的需要帮助。
# Original pandas code
    engine = create_engine('postgresql://myuser:mypassword@localhost:5432/mydb', echo=False)
    mydataframe.to_sql('mytable', engine, if_exists='append', index=True)
3个回答

47

目前我也在进行同样的练习。我采用的方法是从DataFrame中创建一个新对象列表,然后使用bulk_create批量创建它们:

bulk_create(objs, batch_size=None)

此方法以高效的方式将提供的对象列表插入到数据库中(通常只有1个查询,无论有多少对象)

一个示例可能如下所示:

# Not able to iterate directly over the DataFrame
df_records = df.to_dict('records')

model_instances = [MyModel(
    field_1=record['field_1'],
    field_2=record['field_2'],
) for record in df_records]

MyModel.objects.bulk_create(model_instances)

请注意限制,例如:“如果模型的主键是AutoField,则它不会检索和设置主键属性,除非数据库后端支持它(目前仅适用于PostgreSQL),就像save()函数一样。” - lmblanes
2
但是你又在循环中了:for record in df_records - Nitin Raturi
2
我认为这个问题的整个重点是避免使用for循环和调用MyModel构造函数。 - Courvoisier
1
值得注意的是,在我的特定情况下,使用 Pandas 1.5.2 中的 df.to_dict(orient="records") 方法并迭代其输出结果比调用 df.iterrows()(慢约 x1.9)或 df.itertuples(index=False)(慢约 x1.1)更快。我使用了 %%timeit 进行测试。当然,这可能取决于 DataFrame 的形状和内容,但这可以给你一个大致的顺序。 - scūriolus

34

将您自己的pandas代码与映射到相同SQL表的Django模型一起使用

我不知道是否有明确支持将pandas数据框写入Django模型的方法。但是,在Django应用程序中,您仍然可以使用自己的代码读取或写入数据库,除了使用ORM(例如通过Django模型)

考虑到您很可能之前已经使用pandas的to_sql将数据写入数据库,因此您可以继续使用相同的数据库和相同的pandas代码,并简单地创建一个Django模型来访问该表

例如,如果您的pandas代码正在写入SQL表mytable,只需创建如下模型:

class MyModel(Model):
    class Meta:
        db_table = 'mytable' # This tells Django where the SQL table is
        managed = False # Use this if table already exists
                        # and doesn't need to be managed by Django

    field_1 = ...
    field_2 = ...

现在,您可以将Django中的此模型与现有的pandas代码(可能在单个Django应用程序中)同时使用。

Django数据库设置

为了使pandas SQL函数读取相同的DB凭据,只需从Django设置中读取字段,例如:

from django.conf import settings

user = settings.DATABASES['default']['USER']
password = settings.DATABASES['default']['PASSWORD']
database_name = settings.DATABASES['default']['NAME']
# host = settings.DATABASES['default']['HOST']
# port = settings.DATABASES['default']['PORT']

database_url = 'postgresql://{user}:{password}@localhost:5432/{database_name}'.format(
    user=user,
    password=password,
    database_name=database_name,
)

engine = create_engine(database_url, echo=False)

这种替代方法不被推荐,因为它效率低下

我并没有看到除了逐行读取数据框然后创建模型实例并保存之外的其他方法,而这个方法非常慢。你可能可以通过一些批量插入操作来解决,但是为什么要费事呢?因为 pandas 的 to_sql 已经可以为我们完成这个任务了。当 pandas 可以更快地将 Django 查询集读入数据框时,直接读取 Django 查询集就变得效率低下。

# Doing it like this is slow
for index, row in df.iterrows():
     model = MyModel()
     model.field_1 = row['field_1']
     model.save()

谢谢Bakkal,是的,我一直在直接使用Python中的pandas和通过Qt。我卡在了设置create_engine()值上。我将尝试将值设置为从设置文件中选择的值。 - Avagut
太棒了!正是我所需要的!非常感谢你! - Avagut
我认为有一个打字错误,应该是database_name而不是database+name。 - Shh

0

我的解决方案使用pickle,可选使用zlib进行压缩

import pickle
#optional
#import zlib

class SuperModel(models.Model):
    DFToStore = models.BinaryField(default=None, null=True, blank=True)

    def save(self, *args, **kwargs):
        if not isinstance(self.DFToStore, (bytes)):
            self.DFToStore = pickle.dumps(self.DFToStore)
            #optional with compression
            #self.DFToStore = zlib.compress(pickle.dumps(self.DFToStore))
        super(SuperModel, self).save(*args, **kwargs)

    def get_DFToStore(self):
        if isinstance(self.DFToStore, (bytes)):
            return pickle.loads(self.DFToStore)
            #optional with compression
            #return pickle.loads(zlib.decompress(self.DFToStore))
        if not isinstance(self.DFToStore, (bytes)):
            return self.DFToStore


 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接