Отношения "один ко многим" в Django ORM

Я создал несколько систем баз данных для управления базами данных деталей на небольших предприятиях по производству электроники. Я повторяю этот процесс, используя Django5 и MySQL. Я новичок в Django, но много раз использовал MySQL в Java и Python.

Рассмотрите таблицы


    class Bom(models.Model):
        bom = models.AutoField(primary_key=True);
        bompart = models.ForeignKey(Part, on_delete=models.CASCADE)
        revision = models.CharField(max_length=1)

    class BomLine(models.Model):
        bom = models.ForeignKey(Bom, on_delete=models.CASCADE)
        part = models.ForeignKey(Part, on_delete=models.CASCADE)
        designator = models.CharField(max_length=10, primary_key=True)
        ordinal = models.IntegerField()
        qty = models.DecimalField(max_digits=11, decimal_places=4)
    
        class Meta:
            constraints = [
                models.UniqueConstraint(fields=['bom', 'designator'], name='bomdesignator')]

    class Part(models.Model):
        groupid = models.ForeignKey(PartGroup, on_delete=models.CASCADE)
        partno = models.AutoField(primary_key=True)
        partdescription = models.CharField(max_length=100)
 

(я опустил таблицу PartGroup, так как она не имеет отношения к моему вопросу). Идея заключается в том, что в таблице Bom определяется спецификация материалов. Система автоматически присваивает ему целочисленный идентификатор, и деталь назначается "родителем" этой спецификации - т.е. спецификация представляет собой список материалов, необходимых для изготовления назначенной детали.

Каждый BOM относится к BomLines. В BOM может быть много строк для каждого компонента. Поскольку в BOM может быть много компонентов одного типа, используется "обозначение", чтобы точно указать, к какому месту в сборке относится деталь. (Поле 'ordinal' используется только для сортировки и может быть проигнорировано).

Обычно я делаю комбинированный ключ для Bom и Designator в моей таблице MySQL. Однако Django ORM возражает против этого. Чтобы попытаться обойти ограничение на использование нескольких полей в качестве ключей, в класс BomLine было добавлено ограничение UniqueConstraint. Когда я добавляю обозначение 'U1' в bom 1, а затем добавляю 'U1' в bom 2, возникает проблема, поскольку 'U1' уже используется в качестве ключа. Если я убираю параметр primary_key=True у designator, Django добавляет AutoField id в таблицу. Я подумал, что это должно сработать, за исключением того, что мне придется назначить значение по умолчанию для ID. Движок MySQL не позволяет использовать значение по умолчанию, и это тоже не сработает.

Мне кажется, что либо мое мышление неверно (у меня есть способ структурировать отношения, который в некотором роде "не нормальный") - что подразумевается тем, как Django заставляет меня делать вещи. Или же бэкэнд MySQL не подходит для такого типа ситуаций и, возможно, мне следует использовать что-то другое?

Комментарии начали отклоняться от проблемы, поэтому я взглянул еще раз. На SO есть несколько постов о проблемах использования моделей Django с более чем одним первичным ключом. Это по-прежнему вызывает у меня недоумение, поскольку это конструкция базы данных, которую я часто видел и использовал. Я не думаю, что есть какая-то теория баз данных, которая препятствует этому, и, конечно, мой опыт работы с MySQL и PostgreSQL позволяет мне создавать такие таблицы. У них есть реальное применение. Django не может их моделировать, может быть, кто-нибудь сможет объяснить, почему так происходит?

Решение, которое я использовал и буду использовать в качестве образца, заключается в создании столбца 'id':

class bomline(models.Model):
    bomline_id = models.UUIDField(primary_key=True, null=False, default=uuid.uuid1, editable=False, serialize=True)
    bom = models.ForeignKey(Bom, on_delete=models.CASCADE)
    part = models.ForeignKey(Part, on_delete=models.CASCADE)
    designator = models.CharField(max_length=10)
    ordinal = models.IntegerField()
    qty = models.DecimalField(max_digits=11, decimal_places=4)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['bom', 'designator'], name='bomdesignator')]

Использование uuid гарантирует, что для ключа используется уникальное значение, а два внешних ключа обеспечивают существование записей parts и bom. Затем ограничение UniqueConstraint предотвращает двойную запись.

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

Вернуться на верх