2018年9月28日 星期五

[Django] 如何使外來鍵指向多個模型


[本文翻譯自Bhrigu Srivastava-Django: How to add ForeignKey to multiple models]
https://medium.com/@bhrigu/django-how-to-add-foreignkey-to-multiple-models-394596f06e84

如何用一個外來鍵(Foreign Key)和1種以上的模型(Model)進行關聯。

先假設你有兩個模型,Article和Post
class Article(models.Model):
    content = models.CharField(max_length=100)
class Post(models.Model):
    content = models.CharField(max_length=100)

現在,我們新增一個Comment的模型,且Article和Post皆與此關聯。因此,我們要如何在Comment中新增一個FK,並指向上述模型中其一。

能達到這功能的概念即為通用關係(Generic Relation),Django包含一種contenttypes的應用,你的模型和content type模型在應用中的關係,能使一個模型物件與任何你建立的模型物件之間啟用通用關係,

Comment模型的格式如下:
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

class Comment(models.Model):
    comm = models.CharField(max_length=50)
    content_type =   models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object=GenericForeignKey('content_type', 'object_id')
(建立Generic Relations時,固定加上最後三行)

ContentType是一種模型,一個ContentType的物件儲存了關於專案中的模型資訊,提供方法來回傳他們代表的模型或進行搜尋。

因此,我們能將Comment與任何種類的模型建立關聯,儲存在content_type。(content_type回傳模型種類,object_id回傳此模型物件的id)

接下來,我們新增一個Comment的物件(instance),並將此指向一個Articel物件或Post物件。

將Comment物件指向Articel物件:
>>> art = Article.objects.get(id=1)
>>> c = Comment(content_object=art, comm='asdf')
>>> c.save()
>>> c.content_object
<Post: post1>
將Comment物件指向Post物件:
>>> pos= Post.objects.get(id=1)
>>> c= Comment(content_object=pos, comm='new comment')
>>> c.save()
>>> c.content_object
<Post: post1>

逆向Generic Relations
再來,為了取得所有與Article和Post關聯的Comment物件,我們能利用GenericRelations來達成。在Article和Post中定義一個新的欄位,在你的模型中新增一個欄位來逆向搜尋。
from django.contrib.contenttypes.fields import GenericRelation

class Article(models.Model):
    content = models.CharField(max_length=100)
    comments = GenericRelation(Comment)
class Post(models.Model):
    content = models.CharField(max_length=100)
    comments = GenericRelation(Comment)

接下來,指令model_object.comments.all()會取得所有指向這個Comment物件,舉例:art是一個Article物件;post是一個Post物件。
>>> art.comments.all()
<QuerySet [<Comment: asdf>, <Comment: test>]>
>>> pos.comments.all()
<QuerySet [<Comment: new_comment>, <Comment: test2>]>
以上就是「物件指向多個模型」你所有需要知道的基本使用方式。

請參考The contenttypes framework,能知道更多關於contenttypes的應用。

沒有留言:

張貼留言