獲取一些包含多對多外鍵可是不包含特定另外一個外鍵的結果

前天碰到了一個狀況,思考了很久沒能想出問題,昨天下午在 stackoverflow 上問了一下,有一個好心人跟我溝通了一下,瞭解清楚個人問題之後,到了晚上給出了答案,我操做了一下果真是正確的,讓我感動不已。他是一個好人,好人一輩子平安。數據庫

問題

開始描述個人問題吧,標題不能描述清楚個人問題,我應該在正文詳細描述一下fetch

用戶( User )發佈狀態( Feed )評論( Comment ) 三個 Model,分別定義以下code

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 。ci

說白了我就是想獲取不是我本身的全部 Comment 。路由

解決方法

我能想到的就是使用 exclude 、 Q 這些方法。因此一開始個人獲取方式以下:get

獲取 Jack 的全部 Feed

從上面的數據庫記錄來看, Jack 發了一條 Feed, 這條 Feed 有兩條 Comment,一條是 Bruce 評論的,還有一條是 Jack 本身評論的。博客

Feed.objects.filter(user=1)

序列化結果以下it

[
    {
        "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)

這種方法獲取的序列化結果table

[
    {
        "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 的評論,是我要的最終結果。

原文地址:獲取一些包含多對多外鍵可是不包含特定另外一個外鍵的結果
個人博客:時空路由器

相關文章
相關標籤/搜索