为了账号安全,请及时绑定邮箱和手机立即绑定

在 django 中批量创建具有相关对象的对象的有效方法是什么?

在 django 中批量创建具有相关对象的对象的有效方法是什么?

哈士奇WWW 2023-10-31 14:07:44
我有以下型号:class LocationPoint(models.Model):    latitude = models.DecimalField(max_digits=16, decimal_places=12)    longitude = models.DecimalField(max_digits=16, decimal_places=12)    class Meta:        unique_together = (            ('latitude', 'longitude',),        )class GeoLogEntry(models.Model):    device = models.ForeignKey(Device, on_delete=models.PROTECT)    location_point = models.ForeignKey(LocationPoint, on_delete=models.PROTECT)    recorded_at = models.DateTimeField(db_index=True)    created_at = models.DateTimeField(auto_now_add=True, db_index=True)我有很多传入记录要创建(可能一次有数千个)。目前我这样创建它们:# Simplified map function contents (removed mapping from dict as it's unrelated to the question topicpoints_models = map(lambda point: LocationPoint(latitude=latitude, longitude=longitude), points)LocationPoint.objects.bulk_create(     points_models,     ignore_conflicts=True)# Simplified map function contents (removed mapping from dict as it's unrelated to the question topicgeo_log_entries = map(            lambda log_entry: GeoLogEntry(device=device, location_point=LocationPoint.objects.get(latitude=latitude, longitude=longitude), recorded_at=log_entry.recorded_at),            log_entries        )GeoLogEntry.objects.bulk_create(geo_log_entries, ignore_conflicts=True)但我认为它不是很有效,因为它运行记录N SELECT查询N。有更好的方法吗?我使用 Python 3.9、Django 3.1.2 和 PostgreSQL 12.4。
查看完整描述

2 回答

?
慕后森

TA贡献1802条经验 获得超5个赞

主要问题是获取要批量链接的对象。一旦我们存储了所有这些对象,我们就可以批量获取对象:


from django.db.models import Q


points_models = [

    LocationPoint(latitude=point.latitude, longitude=point.longitude)

    for point in points

]


LocationPoint.objects.bulk_create(

     points_models,

     ignore_conflicts=True

)


qfilter = Q(

    *[

          Q(('latitude', point.latitude), ('longitude', point.longitude))

          for point in log_entries

    ],

    _connector=Q.OR

)



data = {

    (lp.longitude, lp.latitude): lp.pk

    for lp in LocationPoint.objects.filter(qfilter)

}


geo_log_entries = [

    GeoLogEntry(

        device=entry.device,

        location_point_id=data[entry.longitude, entry.latitude],

        recorded_at=entry.recorded_at

    )

    for entry in log_entries

]


GeoLogEntry.objects.bulk_create(geo_log_entries, ignore_conflicts=True)

因此,我们批量获取需要链接到的所有对象(因此使用一个查询),创建一个映射主键上的经度和纬度的字典,然后设置为该点location_point_id。


然而,重要的是使用小数,或者至少是一种匹配的类型。浮点数很棘手,因为它们很容易产生舍入误差(因此经度和纬度通常存储为“定点”数字,例如整数大 1'000 倍或大 1'000'000 倍)。否则,您应该使用将其与通过查询生成的数据相匹配的算法。


查看完整回答
反对 回复 2023-10-31
?
眼眸繁星

TA贡献1873条经验 获得超9个赞

bulk_create(...)将以列表形式返回您创建的对象。您可以在 Python 端过滤这些对象,而不是查询数据库,因为它们已经被获取。


location_points = LocationPoint.objects.bulk_create(

     points_models,

     ignore_conflicts=True

)


geo_log_entries = map(

    lambda log_entry: GeoLogEntry(

        device=device, 

        location_point=get_location_point(log_entry, location_points),      

        recorded_at=log_entry.recorded_at

    ),

    log_entries

)


GeoLogEntry.objects.bulk_create(geo_log_entries, ignore_conflicts=True)

您所需要做的就是实现get_location_point满足您的需求


查看完整回答
反对 回复 2023-10-31
  • 2 回答
  • 0 关注
  • 137 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信