ForeignKey к абстрактному классу (родовые отношения) - PullRequest
15 голосов
/ 25 октября 2011

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

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

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

  • одиночная модель сервиса, с полем "serviceType" и большим беспорядком с полями.(У меня нет большого опыта в моделировании баз данных, но это выглядит ... "плохо" для меня)
  • несколько сервисных моделей.Мне нравится это решение, но тогда я понятия не имею, как я могу ссылаться на эти РАЗЛИЧНЫЕ сервисы в одной и той же области.

Это краткий отрывок из моих моделей.py file: (я удалил все, что не связано с этой проблемой)

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 ]

15 голосов
/ 25 октября 2011

Для начала используйте многотабличное наследование 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

Объекты '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, вероятно, самый простой в использовании.Прочитайте документацию для этого здесь , это действительно хороший маленький кусочек кода.

6 голосов
/ 25 октября 2011

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

models.service.objects.all() 

и он вернет вам результаты, подобные следующим:

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

Некоторые примеры того, как это сделать, можно найти по ссылкам в сообщении блога, указанному ниже.

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

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

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

0 голосов
/ 25 октября 2011

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

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

в классе service?

0 голосов
/ 25 октября 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...