ForeignKey для абстрактного класса (общие отношения)

Я создаю персональный проект с Django, чтобы тренироваться (потому что я люблю Django, но я пропускаю навыки). У меня есть основные требования, я знаю Python, я внимательно читал книгу Django дважды, если не трижды.

Моя цель - создать простую службу мониторинга с помощью веб-интерфейса на основе Django, позволяющего проверить статус моих "узлов" (серверов). Каждый node имеет несколько "сервисов". Приложение проверяет доступность каждой службы для каждого node.

Моя проблема в том, что я понятия не имею, как представлять различные типы сервисов в моей базе данных. Я подумал о двух "решениях":

  • единственная модель сервиса с полем "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 является фиктивной. Я понятия не имею, что поставить вместо "Сервис". Я честно искал решения об этом, я слышал о "родовых отношениях", таблицах с тройным соединением, но я действительно этого не понимал.

Кроме того, английский не является моим родным языком, поэтому, приходя в структуру и семантику базы данных, мои знания и понимание того, что я читаю, ограничены (но это моя проблема)

Ответы

Ответ 1

Для начала используйте Django наследование нескольких таблиц, а не абстрактную модель, которую вы сейчас используете.

Тогда ваш код будет выглядеть следующим образом:

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

Если вы не знаете тип ребенка заранее, это становится немного сложнее. Существует несколько хакерских/беспорядочных решений, в том числе поле "serviceType" в родительской модели, но лучший способ, как сказал Джо Дж, - использовать "набор запросов подкласса". Класс InheritanceManager из django-model-utils, вероятно, самый простой в использовании. Прочитайте документацию для него здесь, это действительно хороший маленький код.

Ответ 2

Я думаю, что один из подходов, который вы можете рассмотреть, - это "выборка для подкласса". В принципе, он позволяет запрашивать родительскую модель и возвращает экземпляры дочерних моделей в наборе запросов результатов. Это позволит вам делать такие запросы, как:

models.service.objects.all() 

и верните ему результаты, как показано ниже:

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

В некоторых примерах о том, как это сделать, посмотрите ссылки в блоге, приведенные ниже.

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

Однако, если вы используете этот подход, вы не должны объявлять свою модель обслуживания абстрактной, как в примере. Конечно, вы добавите дополнительное соединение, но в целом я обнаружил, что запрос на выполнение подкласса работает очень хорошо для возврата смешанного набора объектов в набор запросов.

В любом случае, надеюсь, что это поможет, Джо

Ответ 3

Если вы ищете общие отношения с внешним ключом, вы должны проверить структуру содержимого Django (встроенный в Django). Документы в значительной степени объясняют, как использовать его и как работать с родовыми отношениями.

Ответ 4

Фактическое обслуживание может быть только на одном node, правильно? В этом случае, когда не имеет поля

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

в классе service?