Wan*_*der 4 django django-rest-framework
ListAPIView(下面的代码)的默认行为是序列化所有Report对象和每个Report对象的嵌套Log对象.如果我只希望每个报告显示最新的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
Run Code Online (Sandbox Code Playgroud)
我知道我可以通过使用SerializerMethodField来实现这一点,但这可能是一个可能很昂贵的操作,因为会有一个额外的SQL查询来检索每个Report对象的相应Log对象.
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
Run Code Online (Sandbox Code Playgroud)
如果我有1000个报表对象,如果我想要全部渲染它们,将会有1000个额外的查询.除了使用分页之外,我如何避免这些额外的查询?有人能指出我正确的方向吗?谢谢!
编辑:关于可能的重复标签,马克提供的链接并没有完全清除我的图片.Todor的回答更清楚.
你需要以某种方式注释latest_log的ReportQuerySet,因此它可以通过串行未做任何额外的查询中使用.
实现这一目标的最简单方法是prefetching所有的logs每report.这种方法的缺点是,你在内存中加载所有logs每report每页.如果有人report得到5-10-15之类的东西,那就不那么糟糕了logs.这意味着对于一个50的页面reports,你将加载50*10 = 500 logs,这不是什么大问题.如果有更多logsper 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
Run Code Online (Sandbox Code Playgroud)创建一个帮助方法,以便轻松访问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
Run Code Online (Sandbox Code Playgroud)最后,序列化程序只使用该属性
class ReportSerializer(serializers.ModelSerializer):
latest_log = serializers.LogSerializer()
class Meta:
model = Report
Run Code Online (Sandbox Code Playgroud)更高级过滤的示例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'
))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1160 次 |
| 最近记录: |