工作中在實作一個反正規劃的 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
,不過要記得慎選關聯名稱,像這樣就太長拉。