bugfix> python > 投稿

クエリセット内のオブジェクトにデータを追加する必要がある状況があり、モデルクラスにカスタムメソッドを実装するだけでなく、注釈付きのクエリを実行することが本当に有益な場合を理解するのに苦労しています。

一般的な例として:

この:

MyModel.objects
        .filter(
            some_field=some_value
        )
        .annotate(
            has_something=Exists(
                Something.objects.filter(
                    whatever=OuterRef('pk'),
                    start_date__gte=SIX_MONTHS_AGO
                )
            ),
            something_else= ... # Some complex query
        )

これに対して:

class MyModel(models.Model):
    some_field = CharField()
    def has_something(self):
        return (self.something_set
                    .filter(start_date__gte=SIX_MONTHS_AGO)
                    .count() > 0)
    def something_else(self):
        ... # Make some other queries
class Something(models.Model):
    whatever = models.ForeignKey(MyModel)
    start_date = models.DateField()

したがって、どちらの場合も、必要な場所(テンプレートなど)で次のようなことができます。

{% for obj in my_model_queryset %}
    {{ obj.has_something }}
{% endfor %}

私の知る限り、注釈を付ける場合、すべての作業はデータベースエンジンによって行われ、すべてを1回のデータベースヒットで行うことができます(私は思いますか?)。一方、モデルクラスにメソッドを実装する場合、逆引き参照や外部モデルからのデータを取得する必要がある場合は、追加のクエリが実行される可能性があります。

質問したいのですが、あるアプローチを別のアプローチよりも使用する場合について、経験則やガイダンスはありますか?特に注釈が非常に複雑になる場合(たとえば、Qオブジェクト、サブクエリ、GroupConcatなどを使用する場合)

ありがとう。

回答 1 件
  • ここに例があります:

    model_datas = Model.objects.filter(
        field_date__date__range=[date_from, date_to]
    ).order_by(
        'field_date'
    )
    
    # each line will perform a query here :(
    for model in model_datas:
        writer.writerow([
            model.user.id,
            model.user.email,
            model.business_provider.encode('utf-8'),
            model.field_date.date()
        ])
    
    

    これにより、すべてに対して1つのクエリが実行されます Model +1つのクエリと結合 User それぞれについて model ループ中

    ただし、1つのクエリでのみ実行できます。

    csv_datas = Model.objects.filter(
        field_date__date__range=[date_from, date_to]
    ).order_by(
        'field_date'
    ).annotate(
        user_id=F('model__user_id'),
        user_email=F('model__user__email'),
        business_provider=F('model__provider_name'),
        field_date=F('model__field_date'),
    ).values(
        'user_id',
        'user_email',
        'business_provider',
        'field_date'
    )
    for data in csv_datas:
        writer.writerow([
            data["user_id"],
            data["user_email"],
            data["business_provider"].encode('utf-8'),
            data["field_date"].date()
        ])
    
    

    ここでは、1つのクエリのみが実行されます。それが実行することです。

    Djangoでは、サブクエリを実行してループを実行することはありません。パフォーマンスに非常に悪影響を及ぼします。

    F そして Q あなたはかなり複雑なことをすることができます、それが本当に複雑なときに私がしたいことは、複数のクエリに分割して使用することです:

    query_1_ids = Model.objects.filter(XXXX).values_list('pk', flat=True)
    query2 = Model2.objects.filter(model__pk__in=query_1_ids)
    
    

    それは私の経験則ですが、これが適合しない場合は、Pythonプロセスを実行して set または dict 私が必要なもの(しかし、ここにいることは本当にまれです)

あなたの答え