【问题标题】:ForeignKey to abstract class (generic relations)抽象类的外键(通用关系)
【发布时间】:2011-12-14 14:57:19
【问题描述】:

我正在使用 Django 构建一个个人项目,以训练自己(因为我喜欢 Django,但我缺少技能)。我有基本要求,我会 Python,我仔细阅读了 Django 的书,如果不是三次。

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

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

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

这是我的 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 这条线是假的。我不知道用什么代替“*Service”。老实说,我为此寻找解决方案,我听说过“泛型关系”,三联表,但我并没有真正理解这些东西。

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

【问题讨论】:

    标签: python django model-view-controller model


    【解决方案1】:

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

    【讨论】:

    • 感谢详细,完整的代码,回答。使用@Joe J 中的一个,我很确定它会在整个应用程序建模过程中帮助我。这个网站很棒,它的用户也很棒:)
    • 好的,这是你在这里提供的一个很好的解决方案,尤其是 InheritanceManager 技巧,以及整个 django-model-utils 包。再次感谢
    【解决方案2】:

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

    models.service.objects.all() 
    

    并让它返回给您如下结果:

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

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

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

    但是,如果您使用这种方法,则不应像示例中那样将您的服务模型声明为抽象的。当然,您将引入一个额外的连接,但总的来说,我发现子类化查询集可以很好地返回查询集中的混合对象集。

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

    【讨论】:

    • 非常感谢您的回答,@Voightkampff 的回答帮助我理解了模型继承,并为我提供了一种思考模型数据结构的新方法。另一方面,这是我在 stackoverflow.com 上的第一个问题,我对网站、界面、用户和答案非常满意,现在我也很乐意分享我的知识。 :)
    【解决方案3】:

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

    【讨论】:

    • 谢谢,但正如我所说,我已经检查过了,但我不是很明白,也无法将互联网上的示例映射到我的用例。
    【解决方案4】:

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

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

    service 类中?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-11
      • 2015-10-20
      • 2015-03-19
      • 2010-12-15
      • 2011-01-30
      • 2013-08-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多