Multiple content_type in the same model


工作中在實作一個反正規劃的 model 時需要用到兩個 django content_type
在執行 python manage.py makemigrations之後跳出了.....

{appname.model_name.field_name1}: (fields.E304) Reverse accessor for '{model_name.field_name1}' clashes with reverse accessor for '{model_name.field_name2}'.
    HINT: Add or change a related_name argument to the definition for '{model_name.field_name1}' or '{model_name.field_name}2'.

以下是使用到的兩個 django content_type:

    content_type_1 = models.ForeignKey(
        ContentType, limit_choices_to=limit_1, on_delete=models.CASCADE)
    object_id_1 = models.IntegerField(null=True)
    object_1 = GenericForeignKey('content_type_1', 'object_id_1')

以及

    content_type_2 = models.ForeignKey(
        ContentType, limit_choices_to=limit2, on_delete=models.CASCADE)
    object_id_2 = models.IntegerField(null=True)
    object_2 = GenericForeignKey('content_type_2', 'object_id_2')

在跳 error 之前腦袋不知道出什麼差錯,以為不能再同一個 model 中同時使用兩個 content_type,看到 error 後虎軀一震,想說真的不能用嗎!?
結果仔細一看發現只是對於 django 的 content_type model 來說,在我們新增的這個 model 中這兩個 content_type 都同樣會使用同一個 model_name 進行查詢,所以如果沒有個別設定 related_name 就會 django 無法判別要查詢的是哪一個 content_type。


這其實是在使用關聯時就會產生的錯誤,不限於 models.ForeignKey 。
假設在 klass 這個 model 中有兩個 ForeignKey 分別是:

students = models.ManyToManyField(
                                    settings.AUTH_USER_MODEL, 
                                    related_name='my_admins', 
                                    blank=True,)

以及

teachers = models.ManyToManyField(
                                    settings.AUTH_USER_MODEL,
                                    related_name='my_teachers',
                                    blank=True,)

那如果今天我有個 stundet 的實體:

student = User.object.get(id=1)

我想查詢該 student 有幾個 klass:

student.klass_set.all()

這時候 django orm 會沒辦法分辨是要以 teachers 的身份查詢 klass 還是用 students 的身份查詢 klass 便會產生錯誤,不過這部分 django 已經幫忙在產生 migrations 的時候就先擋下來了。

那我們要怎樣讓 django orm 知道我們是以怎樣的身份下查詢 klass 呢?
利用 related_name 設定關聯的名稱,這樣就可以確認是以怎樣的身份在做查詢拉。
上述例子的話,便是在宣告 models.ManyToManyField 時增加一個 related_name,並個別填入適當的關聯名稱,比如related_name = Im_teacher_I_wanna_see_my_classes 以及 related_name = Im_student_I_wanna_see_my_classes ,不過要記得慎選關聯名稱,像這樣就太長拉。

#Django #content type






你可能感興趣的文章

MTR04_0627

MTR04_0627

CH2. UML

CH2. UML

瀏覽器渲染流程

瀏覽器渲染流程






留言討論