简体   繁体   English

ForeignKey到抽象类(泛型关系)

[英]ForeignKey to abstract class (generic relations)

I'm building a personal project with Django, to train myself (because I love Django, but I miss skills). 我正在与Django建立一个私人项目来训练自己(因为我喜欢Django,但我很想念技能)。 I have the basic requirements, I know Python, I carefully read the Django book twice if not thrice. 我有基本要求,我知道Python,如果不是三次,我会仔细阅读两次Django书。

My goal is to create a simple monitoring service, with a Django-based web interface allowing me to check status of my "nodes" (servers). 我的目标是创建一个简单的监控服务,使用基于Django的Web界面,允许我检查我的“节点”(服务器)的状态。 Each node has multiple "services". 每个节点都有多个“服务”。 The application checks the availability of each service for each node. 应用程序检查每个节点的每个服务的可用性。

My problem is that I have no idea how to represent different types of services in my database. 我的问题是我不知道如何在我的数据库中表示不同类型的服务。 I thought of two "solutions" : 我想到了两个“解决方案”:

  • single service model, with a "serviceType" field, and a big mess with the fields. 单一服务模型,带有“serviceType”字段,并且字段很乱。 (I have no great experience in database modeling, but this looks... "bad" to me) (我在数据库建模方面没有很好的经验,但这看起来......对我来说“糟糕”)
  • multiple service models. 多种服务模式。 i like this solution, but then I have no idea how I can reference these DIFFERENT services in the same field. 我喜欢这个解决方案,但后来我不知道如何在同一领域引用这些不同的服务。

This is a short excerpt from my models.py file : (I removed everything that is not related to this problem) 这是我的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)

Of course, the line with the ManyToManyField is bogus. 当然,与ManyToManyField的界限是虚假的。 I have no idea what to put in place of "*Service". 我不知道该替代“*服务”。 I honestly searched for solutions about this, I heard of "generic relations", triple-join tables, but I did'nt really understand these things. 我老老实实地寻找有关此问题的解决方案,我听说过“通用关系”,三联表,但我并不真正了解这些事情。

Moreover, English is not my native language, so coming to database structure and semantics, my knowledge and understanding of what I read is limited (but that's my problem) 而且,英语不是我的母语,所以进入数据库结构和语义,我对所读内容的知识和理解是有限的(但那是我的问题)

For a start, use Django's multi-table inheritance , rather than the abstract model you have currently. 首先,使用Django的多表继承 ,而不是当前的抽象模型。

Your code would then become: 您的代码将变为:

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)

On the database level, this will create a 'service' table, the rows of which will be linked via one to one relationships with separate tables for each child service. 在数据库级别,这将创建一个“服务”表,其中的行将通过一对一的关系与每个子服务的单独表链接。

The only difficulty with this approach is that when you do something like the following: 这种方法的唯一困难是,当您执行以下操作时:

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

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

The 'service' objects you access in the loop will be of the parent type. 您在循环中访问的“服务”对象将是父类型。 If you know what child type these will have beforehand, you can just access the child class in the following way: 如果您事先知道这些将具有哪种子类型,则可以通过以下方式访问子类:

from django.core.exceptions import ObjectDoesNotExist

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

If you don't know the child type beforehand, it gets a bit trickier. 如果你事先不知道孩子的类型,那就有点棘手了。 There are a few hacky/messy solutions, including a 'serviceType' field on the parent model, but a better way, as Joe J mentioned, is to use a 'subclassing queryset'. 有一些hacky / messy解决方案,包括父模型上的'serviceType'字段,但正如Joe J所提到的,更好的方法是使用'子类化查询集'。 The InheritanceManager class from django-model-utils is probably the easiest to use. 来自django-model-utils的InheritanceManager类可能是最容易使用的。 Read the documentation for it here , it's a really nice little bit of code. 这里阅读它的文档,这是一个非常好的一点代码。

I think one approach that you might consider is a "subclassing queryset". 我认为您可能考虑的一种方法是“子类化查询集”。 Basically, it allows you to query the parent model and it will return instances of the child models in the result queryset. 基本上,它允许您查询父模型,它将返回结果查询集中子模型的实例。 It would let you do queries like: 它可以让你做如下查询:

models.service.objects.all() 

and have it return to you results like the following: 并让它返回给你如下结果:

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

For some examples on how to do this, check out the links on the blog post linked below. 有关如何执行此操作的一些示例,请查看下面链接的博客文章中的链接。

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

However, if you use this approach, you shouldn't declare your service model as abstract as you do in the example. 但是,如果使用此方法,则不应将服务模型声明为抽象,如示例中所示。 Granted, you will be introducing an extra join, but overall I've found the subclassing queryset to work pretty well for returning a mixed set of objects in a queryset. 当然,您将引入一个额外的连接,但总的来说,我发现子类化查询集可以很好地返回查询集中的一组混合对象。

Anyway, hope this helps, Joe 无论如何,希望这会有所帮助,乔

If you are looking for generic foreign key relations you should check the Django contenttypes framework (built into Django). 如果您正在寻找通用外键关系,您应该检查Django contenttypes框架 (内置于Django)。 The docs pretty much explain how to use it and how to work with generic relations. 文档几乎解释了如何使用它以及如何使用泛型关系。

An actual service can only be on one node, right? 实际服务只能在一个节点上,对吗? In that case when not have a field 在那种情况下,没有一个字段

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

in the service class? service类?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM