Как связать внешний ключ с областью «многие ко многим»? - PullRequest
1 голос
/ 30 января 2020

У меня есть следующие модели:

class Work_Music(MPTTModel, Work):
    name = models.CharField(max_length=10, null=True, blank=True)

class Opera(models.Model):
    work = models.OneToOneField(Work_Music, verbose_name=_('work_music'), related_name='opera', on_delete=models.PROTECT)
    cast = models.ManyToManyField('Cast', through='WorkCast')
    source_writer = models.ForeignKey(Person, verbose_name=_('author'), null=True, blank=True, on_delete=models.PROTECT)
    numbering = models.CharField(max_length=8000, null=True, blank=True)
    work_type = models.CharField(max_length=8000, null=True, blank=True)

class Cast(models.Model):
    name = models.CharField(max_length=100, null=True, blank=True)

    def __str__(self):
        return self.name


class WorkCast(models.Model):
    work = models.ForeignKey(Work_Music, verbose_name=_('work'), related_name='workcast', null=True, blank=True, on_delete=models.PROTECT)
    cast = models.ManyToManyField(Cast, verbose_name=_('cast'), related_name='workcast', blank=True)

    def __str__(self):
        return "%s" % (
            ", ".join(character.name for character in self.cast.all())
        )

В настоящее время структура данных:

WorkCast
   |-----> Opera
   |-----> Opera cast member #1
           Opera cast member #2
           Opera cast member #3

Теперь я хотел бы прикрепить диапазон пения к каждому члену актерского состава для этой конкретной пьесы. работы. Что-то вроде:

WorkCast
   |-----> (ForeignKey)   Opera
   |-----> (Many-to-many) Opera cast member #1, lowest note: c, highest note: A#
                          Opera cast member #2, lowest note: b, highest note: D
                          Opera cast member #3, lowest note: a, highest note: E

Как я могу прикрепить:

lowest_note = models.CharField(max_length=10, null=True, blank=True)
highest_note = models.CharField(max_length=10, null=True, blank=True)

для каждого члена актерского состава для этой конкретной детали?

Ответы [ 3 ]

0 голосов
/ 30 января 2020

Моделирование немного " нечетное ". Обычно можно ожидать, что вы определяете ManyToManyField для модели WorkMusic и используете вашу WorkCast в качестве сквозной модели между ними. Это можно указать с помощью параметра through=… [Django -doc] . Например:

class WorkMusic(MPTTModel, Work):
    name = models.CharField(max_length=10, null=True, blank=True)
    <b>cast</b> = models.ManyToManyField(<b>'Cast', through='WorkCast'</b>)


class Cast(models.Model):
    name = models.CharField(max_length=100, null=True, blank=True)

    def __str__(self):
        return self.name


class WorkCast(models.Model):
    work = models.ForeignKey(
        WorkMusic,
        verbose_name=_('work'),
        related_name='workcast',
        null=True,
        blank=True,
        on_delete=models.PROTECT
    )
    cast = models.ForeignKey(
        Cast,
        verbose_name=_('cast'),
        related_name='workcast',
        blank=True,
        on_delete=models.PROTECT
    )
    <b>lowest_note</b> = models.CharField(max_length=10, null=True, blank=True)
    <b>highest_note</b> = models.CharField(max_length=10, null=True, blank=True)

Здесь мы, таким образом, будем кодировать для каждого WorkCast объекта, который связан с одним WorkMusic объектом и одним Cast возражать дополнительные поля. Однако объект WorkMusic может иметь ноль, один или несколько связанных WorkCast объектов и, следовательно, связан с нолем, одним или несколькими Cast объектами.

Примечание : обычно Django модели, как и всем классам в Python, присваивают имена в PerlCase , а не snake_case , поэтому должно быть: WorkMusic вместо Work_Music.

0 голосов
/ 30 января 2020

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

Документы: https://docs.djangoproject.com/en/3.0/topics/db/models/#extra -поля-на-многих-ко-многим-отношениям

0 голосов
/ 30 января 2020

Я думаю, что вы ищете through table. Подробнее об этом здесь

Ваш код будет выглядеть примерно так:

class WorkCast(models.Model):
    work = models.ForeignKey(Work_Music, verbose_name=_('work'), related_name='workcast', null=True, blank=True, on_delete=models.PROTECT)
    cast = models.ManyToManyField(Cast, verbose_name=_('cast'), related_name='workcast', blank=True, through='SingRange')

    def __str__(self):
        return "%s" % (
            ", ".join(character.name for character in self.cast.all())
        )

class SingRange(models.Model):
    lowest_note = models.CharField(max_length=10, null=True, blank=True)
    highest_note = models.CharField(max_length=10, null=True, blank=True)
...