ListAPIViewのデフォルトの動作(以下のコード)は、すべてのReportオブジェクトとネストされたLogオブジェクトをReportオブジェクトごとにシリアル化することです。レポートごとに最新のLogオブジェクトのみを表示したい場合はどうすればよいですか?どうすればそれを行うことができますか?
# models.py
class Log(models.Model):
# ...
report = models.ForeignKey(Report)
timestamp = models.DateTimeField(default=datetime.datetime.now)
class Report(models.Model):
code = models.CharField(max_length=32, unique=True)
description = models.TextField()
# serializers.py
class LogSerializer(serializers.ModelSerializer):
class Meta:
model = Log
class ReportSerializer(serializers.ModelSerializer):
log_set = LogSerializer(many=True, read_only=True)
class Meta:
model = Report
fields = ('code', 'description', 'log_set')
# views.py
class ReportListView(generics.ListAPIView):
queryset = Report.objects.all()
serializer_class = ReportSerializer
SerializerMethodFieldを使用してこれを実行できることはわかっていますが、各Reportオブジェクトに適切なLogオブジェクトを取得するための追加のSQLクエリがあるため、これは潜在的にコストのかかる操作になる可能性があります。
class ReportSerializer(serializers.ModelSerializer):
latest_log = serializers.SerializerMethodField()
class Meta:
model = Report
def get_latest_log(self, obj):
try:
latest_log = Log.objects.filter(report_id=obj.id).latest('timestamp')
except Log.DoesNotExist:
latest_log = None
return latest_log
1000個のレポートオブジェクトがある場合、それらすべてをレンダリングする場合は、1000個の追加クエリがあります。ページネーションを使用する以外に、これらの余分なクエリを回避するにはどうすればよいですか?誰かが私を正しい方向に向けることができますか?ありがとう!
編集:重複する可能性のあるタグに関して、マークによって提供されたリンクだけでは、私にとっては完全に画像がクリアされませんでした。Todorの答えはより明確でした。
あなたは何とか注釈を付ける必要latest_log
でReportQuerySet
、それは余分なクエリをすることなく、シリアライザで使用できるようにします。
これを達成するための最も簡単な方法は、prefetching
すべてのlogs
人によるものreport
です。このアプローチの欠点は、ページlogs
ごとにすべてメモリにロードすることですreport
。report
5-10-15のようなものを手に入れれば、それほど悪くはありませんlogs
。これは、50のページのreports
場合、50 * 10 = 500logs
をロードすることを意味しますが、これは大したことではありません。logs
perがもっとある場合report
(たとえば100)、の追加のフィルタリングを行う必要がありますqueryset
。
次にいくつかのサンプルコードを示します。
をプリフェッチしlogs
ます。
# views.py
class ReportListView(generics.ListAPIView):
queryset = Report.objects.all()\
.prefetch_related(Prefetch('log_set',
queryset=Log.objects.all().order_by('-timestamp'),
to_attr='latest_logs'
))
serializer_class = ReportSerializer
latest_logに簡単にアクセスするためのヘルパーメソッドを作成します
class Report(models.Model):
#...
@property
def latest_log(self):
if hasattr(self, 'latest_logs') and len(self.latest_logs) > 0:
return self.latest_logs[0]
#you can eventually implement some fallback logic here
#to get the latest log with a query if there is no cached latest_logs
return None
最後に、シリアライザーはプロパティを使用するだけです
class ReportSerializer(serializers.ModelSerializer):
latest_log = serializers.LogSerializer()
class Meta:
model = Report
のより高度なフィルタリングの例は、logs
次のようになります。
Report.objects.all().prefetch_related(Prefetch('log_set', queryset=Log.objects.all().extra(where=[
"`myapp_log`.`timestamp` = (\
SELECT max(timestamp) \
FROM `myapp_log` l2 \
WHERE l2.report == `myapp_log`.`report`\
)"]
), to_attr='latest_logs'
))
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加