Initializer doesn't support multiple apps
Describe the bug Initalizer only supports one app label in the arguments to passed to it. In trying to run it multiple times it fails at the first app giving an error for the other apps.
raise ConfigurationError(
tortoise.exceptions.ConfigurationError: default_connection for the model <class 'app2.models.Category'> cannot be None
This makes it difficult to test when there are multiple apps.
To Reproduce
app1/models.py:
from tortoise.models import Model
from tortoise import fields
class Users(Model):
id = fields.IntField(pk=True)
password = fields.CharField(max_length=128, blank=True, null=True)
email = fields.CharField(max_length=120, unique=True, index=True)
app2/models.py:
from tortoise.models import Model
from tortoise import fields
class Category(Model):
id = fields.IntField(pk=True)
name = fields.CharField(max_length=128)
user = fields.ForeignKeyField('app1.Users')
main.py:
import asyncio
import os
from tortoise import Tortoise
from tortoise.contrib.test import finalizer, initializer
db_url = "sqlite://:memory:?cache=shared"
def setup_db():
Tortoise.init_models(["app1.models"], "app1")
Tortoise.init_models(["app2.models"], "app2")
print('Initializing app1')
initializer(['app1.models'], db_url=db_url, app_label="app1")
print('Initializing app2')
initializer(['app2.models'], db_url=db_url, app_label="app2")
print('DB initialized')
finalizer()
print('finalized')
try:
setup_db()
print('setup_db done')
finally:
asyncio.run(Tortoise.close_connections())
At the line where app1 is run through initializer the following error is given:
raise ConfigurationError(tortoise.exceptions.ConfigurationError: default_connection for the model <class 'app2.models.Category'> cannot be None
The following also fails with the same error. Here both apps models.py are provided as modules, but there is still the restriction of only one app label.
def setup_db():
Tortoise.init_models(["app1.models"], "app1")
Tortoise.init_models(["app2.models"], "app2")
initializer(['app1.models', 'app2.models'], db_url=db_url, app_label="app1")
print('DB initialized')
finalizer()
print('finalized')
tortoise.exceptions.ConfigurationError: default_connection for the model <class 'app2.models.Category'> cannot be Non
Expected behavior There should be a a way to run multiple apps through the initializer
Additional context (Possible Fix)
Changing initalizer to accept Dict[str, Iterable[Union[str, ModuleType]]], a dictionary of app_labels and models (same as the FastAPI register_tortoise CONFIG argument or how Aerich accepts apps and models config) fixes the issue.
The way it's done here is just a demonstration and would not be backward compatibile (but it would be easy to make it so, by using a separate argument to pass through multiple apps, same as register_tortoise has both config and modules arguments).
Changes to tortoise/contrib/test/__init__.py:
from typing import Dict
def getDBConfig(app_label: str, modules: Dict[str, Iterable[Union[str, ModuleType]]]) -> dict:
"""
DB Config factory, for use in testing.
:param app_label: Label of the app (must be distinct for multiple apps).
:param modules: List of modules to look for models in.
"""
return _generate_config(
_TORTOISE_TEST_DB,
app_modules=modules,
testing=True,
connection_label=app_label,
)
def initializer(
modules: Dict[str, Iterable[Union[str, ModuleType]]],
db_url: Optional[str] = None,
app_label: str = "models",
loop: Optional[AbstractEventLoop] = None,
) -> None:
....# Rest of function
The setup_db function in main.py above would then be:
def setup_db():
Tortoise.init_models(["app1.models"], "app1")
Tortoise.init_models(["app2.models"], "app2")
initializer({'app1': ['app1.models'], 'app2': ["app2.models"]}, db_url=db_url, app_label="default")
print('DB initialized')
finalizer()
print('finalized')
I can make PR if you think I'm on the right track with this, unless I'm missing something about the current behaviour. Multiple apps are needed to be initialized in this way when there are foreign keys from one app model to another.