前天碰到了一個情況,思考了好久沒能想出問題,昨天下午在 stackoverflow 上問了一下,有一個好心人跟我溝通了一下,了解清楚我的問題以後,到了晚上給出了答案,我操作了一下果然是正确的,讓我感動不已。他是一個好人,好人一生平安。
問題
開始描述我的問題吧,标題不能描述清楚我的問題,我應該在正文較長的描述一下
有 使用者( User ) 、釋出狀态( Feed ) 、評論( Comment ) 三個 Model,分别定義如下
class User(models.Model):
name = models.CharField(max_length=20)
class Feed(models.Model):
user = models.ForeignKey(User, related_name="feed_user")
class Comment(models.Model):
user = models.ForeignKey(User, related_name="comment_user")
feed = models.ForeignKey(Feed, related_name="comment_feed")
資料庫目前的記錄
User
| id | name |
|---|---|---|
| 1 | Jack |
| 2 | Bruce |
Feed
id | user |
---|---|
1 |
Comment
feed | ||
---|---|---|
2 | ||
我需要擷取一個使用者的所有 Feed ,每一個 Feed 包含它所有的 Comment ,但是每一個 Comment 對應的 user 不能是 Feed 對應的 user 。
說白了我就是想擷取不是我自己的所有 Comment 。
解決方法
我能想到的就是使用 exclude 、 Q 這些方法。是以一開始我的擷取方式如下:
擷取 Jack 的所有 Feed
從上面的資料庫記錄來看, Jack 發了一條 Feed, 這條 Feed 有兩條 Comment,一條是 Bruce 評論的,還有一條是 Jack 自己評論的。
Feed.objects.filter(user=1)
序列化結果如下
[
{
"feed": {
"id": 1,
"user": {
"id": 1
},
"comment": [
{
"id": 1,
"user": {
"id": 2
},
"feed": {
"id": 1,
"user": {
"id": 1
}
}
},
{
"id": 2,
"user": {
"id": 1
},
"feed": {
"id": 1,
"user": {
"id": 1
}
}
}
]
}
}
]
使用 exclude 方法過濾 Jack 自己評論的結果
Feed.objects.filter(user=1).exclude(feed_user__user=1)
這種方法擷取的序列化結果
[
{
"feed": {
"id": 1,
"user": {
"id": 1
},
"comment": []
}
}
]
此時的序列化結果竟然把整個 comment 過濾掉了
通過檢視 queryset 的 SQL 語句
SELECT `feed_feed`.`id`, `feed_feed`.`user_id` FROM `feed_feed` WHERE NOT (`feed_feed`.`id` IN (SELECT U1.`feed_id_id` AS Col1 FROM `feed_comment` U1 WHERE U1.`user_id` = 1)) ORDER BY `feed_feed`.`create_time` DESC
發現它是通過判斷 Jack 是否出現在所有評論中的某一個, 是以如果出現了就認為整個都不要了,這種方式不可取。
prefetch_related 傳回一個 QuerySet ,為每個關系單獨查找,并在 Python 中“加入”。這允許它預取多對多和多對一對象,除了外鍵和一對一關系。
Feed.objects.filter(user=1).prefetch_related(
Prefetch(
"feed_comment",
queryset=Comment.objects.exclude(
user=1
),
to_attr="all_comment"
)
)
此時的序列化結果如下
[
{
"feed": {
"id": 1,
"user": {
"id": 1
},
"comment": [
{
"id": 1,
"user": {
"id": 2
},
"feed": {
"id": 1,
"user": {
"id": 1
}
}
},
{
"id": 2,
"user": {
"id": 1
},
"feed": {
"id": 1,
"user": {
"id": 1
}
}
}
],
"all_comment": [
{
"id": 1,
"user": {
"id": 2
},
"feed": {
"id": 1,
"user": {
"id": 1
}
}
}
]
}
}
]
結果在 Jack 所有的 Feed 基礎上,添加了一個 all_comment 字段,而這個字段正好是過濾了 Jack 的評論,是我要的最終結果。
原文位址:
擷取一些包含多對多外鍵但是不包含特定另一個外鍵的結果我的部落格:
時空路由器