bugfix> python > 投稿

2つのモデルがあるとします

class Product(models.Model):
    """ A model representing a product in a website. Has new datapoints referencing this as a foreign key daily """
    name = models.CharField(null=False, max_length=1024, default="To be Scraped")
    url = models.URLField(null=False, blank=False, max_length=10000)

class DataPoint(models.Model):
    """ A model representing a datapoint in a Product's timeline. A new one is created for every product daily """
    product = models.ForeignKey(Product, null=False)
    price = models.FloatField(null=False, default=0.0)
    inventory_left = models.BigIntegerField(null=False, default=0)
    inventory_sold = models.BigIntegerField(null=False, default=0)
    date_created = models.DateField(auto_now_add=True)
    def __unicode__(self):
        return "%s - %s" % (self.product.name, self.inventory_sold)

目標は、製品に添付された最新のデータポイントのinventory_sold値に基づいて製品のQuerySetをソートすることです。ここに私が持っているものがあります:

products = Product.objects.all()
datapoints = DataPoint.objects.filter(product__in=products)
datapoints = list(datapoints.values("product__id", "inventory_sold", "date_created"))
products_d = {}
# Loop over the datapoints values array
for i in datapoints:
    # If a datapoint for the product doesn't exist in the products_d, add the datapoint
    if str(i["product__id"]) not in products_d.keys():
        products_d[str(i["product__id"])] = {"inventory_sold": i["inventory_sold"], "date_created": i["date_created"]}
    # Otherwise, if the current datapoint was created after the existing datapoint, overwrite the datapoint in products_d
    else:
        if products_d[str(i["product__id"])]["date_created"] < i["date_created"]:
            products_d[str(i["product__id"])] = {"inventory_sold": i["inventory_sold"], "date_created": i["date_created"]}
# Sort the products queryset based on the value of inventory_sold in the products_d dictionary
products = sorted(products, key=lambda x: products_d.get(str(x.id), {}).get("inventory_sold", 0), reverse=True)

これは問題なく機能しますが、製品(データポイント)の数が多い(500,000〜)ため、非常に遅くなります。これを行うためのより良い方法はありますか?

また、重要ではないが、これについては何も見つけられないため、DataPointモデルのUnicodeメソッドも不要なSQLクエリを作成しているようです。これは、Djangoモデルがテンプレートに渡されると、デフォルトの特性になりますか?

回答 1 件
  • ここでサブクエリを使用して、最新のデータポイントの値に注釈を付け、それで並べ替えることができると思います。

    これらのドキュメントの例に基づいて、次のようになります。

    from django.db.models import OuterRef, Subquery
    newest = DataPoint.objects.filter(product=OuterRef('pk')).order_by('-date_created')
    products = Product.objects.annotate(
        newest_inventory_sold=Subquery(newest.values('inventory_sold')[:1])
    ).order_by('newest_inventory_sold')
    
    

    サイドポイントでは、DataPointを出力するときに余分なクエリを避けるために、 select_related を使用する必要があります。  元のクエリで:

    datapoints = DatePoint.objects.filter(...).select_related('product')
    
    

    これによりJOINが実行されるため、製品名を取得しても新しいdbルックアップは発生しません。

あなたの答え