Лучший способ обработки одного поля ForeignKey, которое может быть получено из нескольких таблиц базы данных - PullRequest
1 голос
/ 30 октября 2019

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

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

Идея первая:

Несколько полей ForeignKey. У меня будут преимущества каскадных опций, прямого доступа к экземпляру модели внешнего ключа из MyModel (хотя это легко добавить свойство) и связанных поисков. Подводные камни включают необходимость проверки произвольного количества полей в модели для FK. Другая логика заключается в том, чтобы убедиться, что только одно поле FK имеет значение в определенный момент времени (легко проверить перед сохранением), хотя .update создает проблему. Затем в базу данных было добавлено место из всех столбцов, хотя это не так.

class MyModel(models.Model):

    source_one = models.ForeignKey(
        SourceOne,
        null=True, 
        blank=True, 
        on_delete=models.SET_NULL,
        db_index=True
    )

    source_two = models.ForeignKey(
        SourceTwo,
        null=True, 
        blank=True, 
        on_delete=models.SET_NULL,
        db_index=True
    )

    source_three = models.ForeignKey(
        SourceThree,
        null=True, 
        blank=True, 
        on_delete=models.SET_NULL,
        db_index=True
    )

Идея вторая:

Сохранение source_id и source в модели. Наибольшее беспокойство у меня вызывает необходимость сохранения логики для установки этих полей в нуль, если источник удален. В противном случае это кажется более чистым решением, но не уверен, что затраты на обеспечение точности данных того стоят. Я, вероятно, могу написать некоторую логику в хуке удаления на моделях fk, чтобы очистить MyModel при необходимости.

class MyModel(models.Model):

    ONE = 1
    TWO = 2
    THREE = 3

    SOURCES = (
        (ONE, "SourceOne"),
        (TWO, "SourceTwo"),
        (THREE, "SourceThree")
    )

    source_id = models.PositiveIntegerField(null=True, blank=True)
    source = models.PositiveIntegerField(null=True, blank=True, choices=SOURCES)

Мне бы очень хотелось мнение сообщества.

Ответы [ 2 ]

0 голосов
/ 30 октября 2019

Как уже упоминалось в комментариях, вы ищете универсальное отношение :

из django.contrib.contenttypes.fields import GenericForeignKey из django.contrib.contenttypes.models import ContentType

class SourceA(models.Model):
    name = models.CharField(max_length=45)

class SourceB(models.Model):
    name = models.CharField(max_length=45)

class MyModel(models.Model):
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    source = GenericForeignKey('content_type', 'object_id')

Настройка общего отношения состоит из трех частей:

  • Дайте вашей модели ForeignKey для ContentType. Обычно это поле называется «content_type».
  • Дайте вашей модели поле, в котором можно хранить значения первичного ключа из моделей, к которым вы будете относиться. Для большинства моделей это означает PositiveIntegerField. Обычно это поле называется «object_id».
  • Дайте вашей модели GenericForeignKey и передайте ей имена двух полей, описанных выше. Если эти поля названы «content_type» и «object_id», вы можете опустить это - это имена полей по умолчанию, которые будет искать GenericForeignKey.

Теперь вы можете передать любой Source экземплярв поле source, равное MyModel, независимо от того, к какой модели оно принадлежит:

source_a = SourceA.objects.first()
source_b = SourceB.objects.first()

MyModel.objects.create(source=source_a)
MyModel.objects.create(source=source_b)
0 голосов
/ 30 октября 2019

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

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

Для примера скажем, что ваш проект - это фотографиягалерея и что ваша модель представляет собой тег. Теги могут быть связаны с фотографиями, фотоальбомами или пользователями (например, тегами, за которыми они хотят следовать).

Подход будет следующим:

class Tag(models.Model):
    pass

class Photo(models.Model):
    tags = models.ManyToManyField(Tag)


class Album(models.Model):
    tags = models.ManyToManyField(Tag)


class User(AbstractUser):
    followed_tags = models.ManyToManyField(Tag)

Вы можете даже учитывать факторв этих отношениях в абстрактной модели, как указано ниже:

class Tag(models.Model):
    pass

class TaggedModel(models.Model):
    tags = models.ManyToManyField(Tag)

    class Meta:
        abstract = True


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