2011-10-25 68 views
15

我正在用Django構建一個個人項目,以訓練自己(因爲我喜歡Django,但我錯過了技能)。我有基本要求,我知道Python,如果不是三次,我會仔細閱讀Django書籍兩次。ForeignKey到抽象類(通用關係)

我的目標是創建一個簡單的監控服務,使用基於Django的Web界面來檢查我的「節點」(服務器)的狀態。每個節點都有多個「服務」。應用程序檢查每個節點的每個服務的可用性。

我的問題是,我不知道如何在我的數據庫中表示不同類型的服務。我想到了兩個「解決方案」:

  • 服務模式,用「的serviceType」領域,並與領域的大混亂。 (我沒有很好的數據庫建模經驗,但是這看起來......對我來說「不好」)
  • multiple服務模型。我喜歡這個解決方案,但後來我不知道如何在同一領域中引用這些不同服務。

這是從我的models.py文件很短的摘錄:(我刪除未與此問題相關的一切)

from django.db import models 

# Create your models here.                               
class service(models.Model): 
    port = models.PositiveIntegerField() 
    class Meta: 
     abstract = True 

class sshService(service): 
    username = models.CharField(max_length=64) 
    pkey = models.TextField() 

class telnetService(service): 
    username = models.CharField(max_length=64) 
    password = models.CharField(max_length=64) 

class genericTcpService(service): 
    pass 

class genericUdpService(service): 
    pass 

class node(models.Model): 
    name = models.CharField(max_length=64) 
    # various fields                                 
    services = models.ManyToManyField(service) 

當然,與ManyToManyField線是僞造的。我不知道應該用什麼來代替「*服務」。我誠實地尋找解決方案,我聽說過「通用關係」,三連接表,但我沒有真正理解這些東西。

此外,英語不是我的母語,所以來的數據庫結構和語義,我什麼我讀的認識和理解是有限的(但是這是我的問題)

回答

13

首先,使用Django的multi-table inheritance,而不是目前的抽象模型。

那麼您的代碼將變爲:

from django.db import models 

class Service(models.Model): 
    port = models.PositiveIntegerField() 

class SSHService(Service): 
    username = models.CharField(max_length=64) 
    pkey = models.TextField() 

class TelnetService(Service): 
    username = models.CharField(max_length=64) 
    password = models.CharField(max_length=64) 

class GenericTcpService(Service): 
    pass 

class GenericUDPService(Service): 
    pass 

class Node(models.Model): 
    name = models.CharField(max_length=64) 
    # various fields                                 
    services = models.ManyToManyField(Service) 

在數據庫級別上,這將創建一個「服務」表,行其中將通過爲每個子業務獨立的表一對一的關係鏈接。

這種方法唯一的困難是,當你這樣做的以下內容:

node = Node.objects.get(pk=node_id) 

for service in node.services.all(): 
    # Do something with the service 

的「服務」對象,您在循環訪問將是父類的。 如果你知道孩子鍵入這些都會事先有,你可以訪問子類以下列方式:

from django.core.exceptions import ObjectDoesNotExist 

try: 
    telnet_service = service.telnetservice 
except (AttributeError, ObjectDoesNotExist): 
    # You chose the wrong child type! 
    telnet_service = None 

如果你不知道孩子事先類型,它變得有點棘手。有一些hacky/messy解決方案,其中包括父模型中的'serviceType'字段,但是Joe J提到的更好的方法是使用'子類化查詢集'。 django-model-utils的InheritanceManager類可能是最容易使用的。閱讀它的文檔here,這是一個非常好的一點代碼。

+0

感謝您的詳細,完整的代碼,答案。有了@Joe J之一,我很確定它會在整個應用程序的建模過程中幫助我。這個網站是偉大的,其用戶也:) – pistache

+0

好的,這是一個很好的解決方案,特別是繼承管理器技巧,以及整個django-model-utils軟件包。再次感謝 – pistache

6

我覺得你可以考慮一種方法是一個「子類化查詢集」。基本上,它允許你查詢父模型,它將返回結果查詢集中子模型的實例。這將讓你查詢:

models.service.objects.all() 

,並使其返回到你的結果如下所示:

[ <sshServiceInstance>, <telnetServiceInstance>, <telnetServiceInstance>, ...] 

有關如何做到這一點的一些示例,請在博客文章的鏈接下面鏈接。

http://jazstudios.blogspot.com/2009/10/django-model-inheritance-with.html

但是,如果使用這種方法,你不應該因爲你在做例子聲明你的服務模式爲抽象的。當然,你會引入一個額外的連接,但總的來說,我發現子類化查詢集對於在查詢集中返回一組混合對象很有效。

不管怎麼說,希望這有助於 喬

+1

非常感謝你的回答,與@Voightkampff的幫助我瞭解了模型繼承,並給了我一種思考我的模型數據結構的新方法。另一方面,這是我在stackoverflow.com的第一個問題,我對網站,界面,用戶,答案感到非常滿意,現在我很樂意分享我的知識。 :) – pistache

0

如果你正在尋找通用的外鍵關係,你應該檢查Django contenttypes framework(內置於Django的)。該文檔非常詳細地解釋瞭如何使用它以及如何使用泛型關係。

+1

謝謝,但正如我所說,我已經檢查過了,但我沒有真正理解,也不能將互聯網上的示例映射到我的使用案例。 – pistache

0

一個實際的服務只能在一個節點上,對嗎?在那種情況下,當沒有一個字段

node = models.ForeignKey('node', related_name='services') 

service類?