Почему Django не создает ограничение внешнего ключа в MySQL?

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

Действительно ли Django не создает ограничения ForeignKey в базе данных для проверки существования ID? Это просто логическая вещь питона, которая работает только при запущенном сервере? Или это проблема, которая возникает на MySQL?

Чтобы было понятно, о чем я говорю, первое, что я заметил, поскольку, будучи разработчиком Laravel на стороне PHP, я привык всегда проверять диаграмму базы данных, которую генерирует PhpStorm/PyCharm, подключаясь к базе данных, и на перенесенных таблицах Laravel мы можем видеть стрелки, указывающие на соответствующие связи таблиц с внешними ключами, но на созданной базе данных Django нет ни одной стрелки на диаграмме базы данных, сгенерированной JetBrains IDE. Поэтому я занялся тестированием.

Например, у меня есть следующие модели:

    class Series(models.Model):

        class Meta:
            app_label = 'core'
            db_table = 'km_series'
            verbose_name_plural = 'series'  # added this to avoid plural being "seriess"

        name = models.CharField(max_length=200)
        description = models.TextField(default=None, blank=True, null=True)
        cover_img = models.CharField(max_length=100, default=None, blank=True, null=True)
        on_going = models.BooleanField(default=False)
        date_added = models.DateTimeField(auto_now_add=True)
        date_updated = models.DateTimeField(auto_now=True)

        def __str__(self):
            return "{} - ID #{}".format(self.name, self.id)


    class Chapter(models.Model):
    

        class Meta:
            app_label = 'core'
            db_table = 'km_chapter'

        series = models.ForeignKey(Series, to_field='id', on_delete=models.CASCADE)
        number = models.IntegerField(validators=[MinValueValidator(0)])
        name = models.CharField(max_length=150, default=None, blank=True, null=True)
        date_added = models.DateTimeField(auto_now_add=True)
        date_updated = models.DateTimeField(auto_now=True)

        def __str__(self):
            return "#{} - {}".format(self.number, self.name)

У меня уже создано более 15 моделей с использованием models.ForeignKey наряду с другими полями. Я только что попробовал создать новую строку в MySQL, используя python manage.py shell.

$ python manage.py shell
>>> from core.models import *
>>> Series
<class 'core.models.base_models.Series'>
>>> one = Series.objects.create(name='Test')
>>> one
<Series: Test - ID #1>
>>> one.id
1
>>> chapter = Chapter.objects.create(number=1)
MySQLdb.OperationalError: (1048, "Column 'series_id' cannot be null")
>>> chapter = Chapter.objects.create(number=1, series_id=2)
>>> chapter
<Chapter: #1 - None>
>>> chapter_1 = Chapter.objects.create(number=1, series=one)
>>> chapter_1
<Chapter: #1 - None>
>>> chapter = Chapter.objects.create(number=1, series_id=25)

В базе данных есть только один идентификатор, где идентификатор равен "1" km_series table

Итак, как я могу добавить любой ID при ручном присвоении, а не передавать весь инстанцированный объект в качестве значения foreign_key? km_chapter table

Почему Django позволяет мне устанавливать ID на несуществующие ID в базе данных? Разве это не должно приводить к ошибке? Я что-то упускаю в своих моделях? Почему нет ограничений и валидаций для таких вещей?

После того, как я немного покопался в том, почему MySQL может иметь проблемы с FK-контрактами, и после использования команды python manage.py dbshell для проверки создания таблицы, как мне рекомендовали сделать в комментарии, используя SHOW CREATE TABLE core_chapter; , я обнаружил проблему.

По какой-то причине, которую я не знаю, моя база данных была создана с использованием MyISAM, который является движком хранения MySQL, не поддерживающим FK ограничения. Мне пришлось изменить значение по умолчанию на InnoDB, так как он работает с этим типом валидации для отношений между таблицами.

В моем файле my.cnf, который содержит конфиги для MySQL, я установил InnoDB по умолчанию, добавив свойство default-storage-engine

[mysqld]
default-storage-engine = InnoDB

В настройках Django я также добавил DATABASE "OPTIONS", как указано в Django documentation for databases, поэтому мое значение DATABASES изменилось на:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'database_name',
        'USER': 'database_user',
        'PASSWORD': '****',
        'HOST': 'localhost',
        'PORT': '3306',
        'OPTIONS': {'init_command': 'SET default_storage_engine=INNODB'},
    }
}

После изменения всего этого, сброса всех таблиц базы данных и повторного вызова команды python manage.py migrate ограничения были созданы, как и ожидалось, и теперь моя диаграмма базы данных, сгенерированная PyCharm, безупречно отображает все стрелки, показывающие взаимосвязи.

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