django - 选择 ForeignKey 的最新条目并使用 Django ORM 作为嵌套对象返回
问题描述
我正在尝试通过导致此对象的 Django ORM 返回一个查询:
[
...,
{
"id": 1,
"product_id": 1,
"created_at": "2020-03-03T03:34:59.275941Z",
"updated_at": "2020-03-03T03:34:59.291653Z",
"latest_comment": {
"comment": "Leaving a comment",
"author": {
"id": 1,
"name": "John Lennon",
"profile": {
"avatar": null
}
},
"updated_at": "2020-03-03T03:34:59.329229Z"
}
}
]
我有以下型号:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to="avatar", blank=True)
created_at = models.DateTimeField(default=timezone.now)
updated_at = AutoDateTimeField(default=timezone.now)
class Product(models.Model):
name = models.CharField(max_length=100)
created_at = models.DateTimeField(default=timezone.now)
updated_at = AutoDateTimeField(default=timezone.now)
class Note(models.Model):
product = models.ForeignKey(Product, related_name="notes", on_delete=models.CASCADE)
created_at = models.DateTimeField(default=timezone.now)
updated_at = AutoDateTimeField(default=timezone.now)
class Comment(models.Model):
note = models.ForeignKey(Note, related_name="comments", on_delete=models.CASCADE)
comment = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(default=timezone.now)
updated_at = AutoDateTimeField(default=timezone.now)
class Meta:
get_latest_by = "updated_at"
我已经能够使用以下代码实现上述结构,但它引入了 N + 1 问题。
product = Product.objects.get(id=1)
notes = []
for note in product.notes.all():
try:
note.latest_comment = note.comments.latest()
except ObjectDoesNotExist:
note.latest_comment = None
notes.append(note)
我一直在玩弄prefetch_related
该功能以及对该Max
功能进行注释,但没有取得多大成功。我还能够编写一个原始查询,可以使用一些 Postgres 和该json_build_object
函数来重现它。我试图让 ORM 工作的原因是我想使用CursorPagination
和使用.raw()
orRawSql()
似乎不兼容CursorPagination
.
解决方案
好吧,您不能完全添加嵌套对象,而是Comment
使用Subquery
with queryset 从对象中添加一些值,如下所示:
from django.db.models import OuterRef, Subquery
newest = Comment.objects.filter(note=OuterRef('pk')).order_by('-created_at')
notes = product.notes.annotate(
comment_author=Subquery(
newest.values('author_id')[:1]
)
).annotate(
comment_comment=Subquery(
newest.values('comment')[:1]
)
)
# and so on
或者,用于prefetch_related
减少数据库命中,如下所示:
for note in product.notes.prefetch_related('comments').all():
note.latest_comment = note.comments.order_by('-created_at').last()
notes.append(note)
推荐阅读
- flutter - 泛型类型不能很好地与 StatefulWidget & Function 配合使用
- node.js - 为什么使用管理员时firebase返回null
- django - 电子邮件确认课程视图
- excel - 为什么 Laravel Excel 找不到单元格中的数据?
- python - 使用 Scrapy 遍历 footballdb 上的 Boxscore 链接
- python - 获取 TypeError: slice() 缺少 2 个必需的位置参数:“result1”和“result2”
- vb.net - 如何从公共模块访问控制值,例如 textbox.text?
- tinymce - 设置为内联时 TinyMCE 剥离样式标签
- javascript - javascript - 构造函数的问题
- reactjs - 如何从谷歌地方 api 获取图像?