ForeignKey到抽象类(泛型关系)

时间:2011-10-25 03:27:43

标签: python django model-view-controller model

我正在与Django建立个人项目,以训练自己(因为我喜欢Django,但我想念技能)。我有基本要求,我知道Python,如果不是三次,我会仔细阅读Django书两次。

我的目标是创建一个简单的监控服务,使用基于Django的Web界面,允许我检查“节点”(服务器)的状态。每个节点都有多个“服务”。应用程序检查每个节点的每个服务的可用性。

我的问题是我不知道如何在我的数据库中表示不同类型的服务。我想到了两个“解决方案”:

  • 单一服务模型,带有“serviceType”字段,并且字段很乱。 (我在数据库建模方面没有很好的经验,但这看起来......对我来说很糟糕)
  • 多个服务模型。我喜欢这个解决方案,但后来我不知道如何在同一个领域中引用这些不同的服务。

这是我的 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的行是假的。我不知道该替代“*服务”。我老老实实地寻找有关此问题的解决方案,我听说过“通用关系”,三联表,但我并不真正了解这些事情。

此外,英语不是我的母语,所以进入数据库结构和语义,我对所读内容的知识和理解是有限的(但这是我的问题)

4 个答案:

答案 0 :(得分:15)

首先,使用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,这是一个非常好的一点代码。

答案 1 :(得分:6)

我认为您可能考虑的一种方法是“子类化查询集”。基本上,它允许您查询父模型,它将返回结果查询集中子模型的实例。它会让你进行如下查询:

models.service.objects.all() 

并让它返回给你的结果如下:

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

有关如何执行此操作的一些示例,请查看下面链接的博客文章中的链接。

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

但是,如果使用此方法,则不应将服务模型声明为抽象,就像在示例中一样。当然,您将引入一个额外的连接,但总的来说,我发现子类化查询集非常适合在查询集中返回一组混合对象。

无论如何,希望这有帮助, 乔

答案 2 :(得分:0)

如果您正在寻找通用的外键关系,您应该检查Django contenttypes framework(内置于Django中)。文档几乎解释了如何使用它以及如何使用泛型关系。

答案 3 :(得分:0)

实际服务只能在一个节点上,对吗?在那种情况下,没有一个字段

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

service班?