前天碰到了一個(gè)情況,思考了好久沒能想出問題字管,昨天下午在 stackoverflow 上問了一下,有一個(gè)好心人跟我溝通了一下信不,了解清楚我的問題以后嘲叔,到了晚上給出了答案,我操作了一下果然是正確的抽活,讓我感動(dòng)不已硫戈。他是一個(gè)好人,好人一生平安下硕。
問題
開始描述我的問題吧丁逝,標(biāo)題不能描述清楚我的問題汁胆,我應(yīng)該在正文詳細(xì)描述一下
有 用戶( User ) 、發(fā)布狀態(tài)( Feed ) 霜幼、評(píng)論( Comment ) 三個(gè) 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")
數(shù)據(jù)庫(kù)目前的記錄
User
| id | name |
|---|---|---|
| 1 | Jack |
| 2 | Bruce |
Feed
id | user |
---|---|
1 | 1 |
Comment
id | user | feed |
---|---|---|
1 | 2 | 1 |
2 | 1 | 1 |
我需要獲取一個(gè)用戶的所有 Feed ,每一個(gè) Feed 包含它所有的 Comment 罪既,但是每一個(gè) Comment 對(duì)應(yīng)的 user 不能是 Feed 對(duì)應(yīng)的 user 铸题。
說(shuō)白了我就是想獲取不是我自己的所有 Comment 。
解決方法
我能想到的就是使用 exclude 琢感、 Q 這些方法丢间。所以一開始我的獲取方式如下:
獲取 Jack 的所有 Feed
從上面的數(shù)據(jù)庫(kù)記錄來(lái)看, Jack 發(fā)了一條 Feed驹针, 這條 Feed 有兩條 Comment烘挫,一條是 Bruce 評(píng)論的,還有一條是 Jack 自己評(píng)論的牌捷。
Feed.objects.filter(user=1)
序列化結(jié)果如下
[
{
"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 方法過(guò)濾 Jack 自己評(píng)論的結(jié)果
Feed.objects.filter(user=1).exclude(feed_user__user=1)
這種方法獲取的序列化結(jié)果
[
{
"feed": {
"id": 1,
"user": {
"id": 1
},
"comment": []
}
}
]
此時(shí)的序列化結(jié)果竟然把整個(gè) comment 過(guò)濾掉了
通過(guò)查看 queryset 的 SQL 語(yǔ)句
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
發(fā)現(xiàn)它是通過(guò)判斷 Jack 是否出現(xiàn)在所有評(píng)論中的某一個(gè)墙牌, 因此如果出現(xiàn)了就認(rèn)為整個(gè)都不要了,這種方式不可取暗甥。
prefetch_related 返回一個(gè) QuerySet 喜滨,為每個(gè)關(guān)系單獨(dú)查找,并在 Python 中“加入”撤防。這允許它預(yù)取多對(duì)多和多對(duì)一對(duì)象虽风,除了外鍵和一對(duì)一關(guān)系。
Feed.objects.filter(user=1).prefetch_related(
Prefetch(
"feed_comment",
queryset=Comment.objects.exclude(
user=1
),
to_attr="all_comment"
)
)
此時(shí)的序列化結(jié)果如下
[
{
"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
}
}
}
]
}
}
]
結(jié)果在 Jack 所有的 Feed 基礎(chǔ)上辜膝,添加了一個(gè) all_comment 字段,而這個(gè)字段正好是過(guò)濾了 Jack 的評(píng)論漾肮,是我要的最終結(jié)果厂抖。
原文地址:獲取一些包含多對(duì)多外鍵但是不包含特定另一個(gè)外鍵的結(jié)果
我的博客:時(shí)空路由器