前天碰到了一個情況,思考了好久沒能想出問題,昨天下午在 stackoverflow 上問了一下,有一個好心人跟我溝通了一下,了解清楚我的問題以后,到了晚上給出了答案,我操作了一下果然是正確的,讓我感動不已。他是一個好人,好人一生平安。
問題
開始描述我的問題吧,標題不能描述清楚我的問題,我應該在正文詳細描述一下
有 用戶( User ) 、發(fā)布狀態(tài)( 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 | 1 |
Comment
| id | user | feed |
|---|---|---|
| 1 | 2 | 1 |
| 2 | 1 | 1 |
我需要獲取一個用戶的所有 Feed ,每一個 Feed 包含它所有的 Comment ,但是每一個 Comment 對應的 user 不能是 Feed 對應的 user 。
說白了我就是想獲取不是我自己的所有 Comment 。
解決方法
我能想到的就是使用 exclude 、 Q 這些方法。所以一開始我的獲取方式如下:
獲取 Jack 的所有 Feed
從上面的數據庫記錄來看, Jack 發(fā)了一條 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
發(fā)現它是通過判斷 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 的評論,是我要的最終結果。
原文地址:獲取一些包含多對多外鍵但是不包含特定另一個外鍵的結果
我的博客:時空路由器