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

我有以下型号:


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 topic

points_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 topic

geo_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。


哈士奇WWW
浏览 138回答 2
2回答

慕后森

主要问题是获取要批量链接的对象。一旦我们存储了所有这些对象,我们就可以批量获取对象:from django.db.models import Qpoints_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 倍)。否则,您应该使用将其与通过查询生成的数据相匹配的算法。

眼眸繁星

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满足您的需求
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python