Автономные приложения Django, в которых одно приложение зависит от другого - django.db.migrations.exceptions.NodeNotFoundError
Я использую Django 4.0
Я написал два автономных приложения. Foo
и FooBar
.
Приложение Foo
использует пакет django_nyt. Приложение FooBar
использует функциональность из приложения Foo
.
Я могу makemigrations
и migrate
модели в приложении Foo
, однако, при работе с приложением FooBar
, когда я пытаюсь makemigrations
, я получаю следующий след ошибки:
Traceback (most recent call last):
File "manage.py", line 14, in <module>
execute_from_command_line(sys.argv)
File "/path/to/project/foobar/env/lib/python3.8/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
utility.execute()
File "/path/to/project/foobar/env/lib/python3.8/site-packages/django/core/management/__init__.py", line 440, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/path/to/project/foobar/env/lib/python3.8/site-packages/django/core/management/base.py", line 414, in run_from_argv
self.execute(*args, **cmd_options)
File "/path/to/project/foobar/env/lib/python3.8/site-packages/django/core/management/base.py", line 460, in execute
output = self.handle(*args, **options)
File "/path/to/project/foobar/env/lib/python3.8/site-packages/django/core/management/base.py", line 98, in wrapped
res = handle_func(*args, **kwargs)
File "/path/to/project/foobar/env/lib/python3.8/site-packages/django/core/management/commands/makemigrations.py", line 100, in handle
loader = MigrationLoader(None, ignore_no_migrations=True)
File "/path/to/project/foobar/env/lib/python3.8/site-packages/django/db/migrations/loader.py", line 58, in __init__
self.build_graph()
File "/path/to/project/foobar/env/lib/python3.8/site-packages/django/db/migrations/loader.py", line 276, in build_graph
self.graph.validate_consistency()
File "/path/to/project/foobar/env/lib/python3.8/site-packages/django/db/migrations/graph.py", line 198, in validate_consistency
[n.raise_error() for n in self.node_map.values() if isinstance(n, DummyNode)]
File "/path/to/project/foobar/env/lib/python3.8/site-packages/django/db/migrations/graph.py", line 198, in <listcomp>
[n.raise_error() for n in self.node_map.values() if isinstance(n, DummyNode)]
File "/path/to/project/foobar/env/lib/python3.8/site-packages/django/db/migrations/graph.py", line 60, in raise_error
raise NodeNotFoundError(self.error_message, self.key, origin=self.origin)
django.db.migrations.exceptions.NodeNotFoundError: Migration foo.0001_initial dependencies reference nonexistent parent node ('django_nyt', '0010_auto_20220802_2211')
содержимое /path/to/proj/foo/env/lib/python3.8/site-packages/django_nyt/migrations/0010_auto_20220802_2211
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_nyt', '0009_alter_notification_subscription_and_more'),
]
operations = [
migrations.AlterField(
model_name='notification',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='settings',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='subscription',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]
Поскольку это автономные приложения, я использую шаблон проектирования, предложенный в Django Standalone Reusable Libraries.
common.py (приложение foo)
from pathlib import Path
from django.conf import settings
SAMPLE_PROJECT_DIR_NAME='sample_project'
BASE_DIR = f"{Path(__file__).parent.resolve()}/{SAMPLE_PROJECT_DIR_NAME}"
STANDALONE_APP_NAME='foo'
STATIC_URL = '/static/'
INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.admin',
'django.contrib.messages',
'django.contrib.humanize',
'django.contrib.sites',
'django_nyt.apps.DjangoNytConfig',
STANDALONE_APP_NAME,
'myapp',
]
SECRET_KEY="lskfjklsdfjalkfslfjslfksdjfslkfslkfj"
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
DEBUG=True
# AUTH_USER_MODEL='testdata.CustomUser',
DATABASES={
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': f"{BASE_DIR}/db.sqlite3",
},
}
SITE_ID=1
ROOT_URLCONF=f"{SAMPLE_PROJECT_DIR_NAME}.urls"
TEMPLATES=[
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.messages.context_processors.messages',
]
},
},
]
USE_TZ=True
MIDDLEWARE=[
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
]
settings.configure(
SECRET_KEY=SECRET_KEY,
DEFAULT_AUTO_FIELD = DEFAULT_AUTO_FIELD,
DEBUG=DEBUG,
# AUTH_USER_MODEL=AUTH_USER_MODEL,
DATABASES=DATABASES,
SITE_ID=SITE_ID,
ROOT_URLCONF=ROOT_URLCONF,
INSTALLED_APPS=INSTALLED_APPS,
STATIC_URL=STATIC_URL,
TEMPLATES=TEMPLATES,
USE_TZ=USE_TZ,
MIDDLEWARE=MIDDLEWARE,
)
common.py (foobar)
import sys
from pathlib import Path
from django.conf import settings
SAMPLE_PROJECT_DIR_NAME='sample_project'
PARENT_DIR = f"{Path(__file__).parent.parent.resolve()}"
BASE_DIR = f"{Path(__file__).parent.resolve()}/{SAMPLE_PROJECT_DIR_NAME}"
STANDALONE_APP_NAME='foobar'
STATIC_URL = '/static/'
sys.path.insert(0, f"{PARENT_DIR}/django-foo")
INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.admin',
'django.contrib.messages',
'django.contrib.humanize',
'django.contrib.sites',
'django_nyt.apps.DjangoNytConfig',
'notifications',
STANDALONE_APP_NAME,
'myapp',
]
SECRET_KEY="lskfjklsdfjalkfslfjslfksdjfslkfslkfj"
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
DEBUG=True
# AUTH_USER_MODEL='testdata.CustomUser',
DATABASES={
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': f"{BASE_DIR}/db.sqlite3",
},
}
SITE_ID=1
ROOT_URLCONF=f"{SAMPLE_PROJECT_DIR_NAME}.urls"
TEMPLATES=[
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.messages.context_processors.messages',
]
},
},
]
USE_TZ=True
MIDDLEWARE=[
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
]
FOOBAR_DATA={}
settings.configure(
SECRET_KEY=SECRET_KEY,
DEFAULT_AUTO_FIELD = DEFAULT_AUTO_FIELD,
DEBUG=DEBUG,
# AUTH_USER_MODEL=AUTH_USER_MODEL,
DATABASES=DATABASES,
SITE_ID=SITE_ID,
ROOT_URLCONF=ROOT_URLCONF,
INSTALLED_APPS=INSTALLED_APPS,
STATIC_URL=STATIC_URL,
TEMPLATES=TEMPLATES,
USE_TZ=USE_TZ,
MIDDLEWARE=MIDDLEWARE,
FOOBAR_DATA = FOOBAR_DATA,
)
manage.py (одинаково для автономных приложений foo и foobar)
import os
import sys
import django
from common import *
sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), SAMPLE_PROJECT_DIR_NAME))
django.setup()
if __name__ == '__main__':
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
Я просмотрел папки миграций для обоих приложений и вот что я нашел:
- foo app имеет миграцию
0010_auto_20220802_2211
, созданную и примененную к django_nyt, чтобы изменить поля PK auto на использование BigInteger.
У приложения - foobar нет файла миграции
0010_auto_20220802_2211
, созданного для django_nyt (отсюда ошибка миграции). Я не могу понять, почему foobar не использует миграции, сгенерированные foo, поскольку у него есть зависимость от foo. .
Беспроигрышным вариантом было бы просто скопировать недостающий файл миграции, но это слишком хрупко, а я хочу понять, что происходит, и почему foobar не использует/применяет миграции, созданные зависимостью
Вот что я пробовал до сих пор (не решило проблему):
- Я проверил файлы миграции для
foo
. Первоначально я хранил их вне репозитория .
- Создал фиктивный класс модели в foobar, который явно имел зависимость от модели в dyango_nyt - рассуждая так, если бы модель django_nyt существовала в foobar, я бы заставил миграции создаваться для django_nyt.
Вот как выглядел мой фиктивный класс в foobar/models.py
:
from django_nyt.models import Notification
class FooBarMigrationHack(Notification):
name = models.CharField(max_length=10)
Ничто из вышеперечисленного не сработало, и я все еще получаю ту же самую трассировку стека ошибок, как показано выше, в моем вопросе.
Как мне решить эту проблему без необходимости вручную копировать "недостающий" файл миграции из foo/migrations/
в foobar/migrations/
?