Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion docs/models.rst
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,15 @@ The ``Meta`` class
.. attribute:: table
:annotation: = ""

Set this to configure a manual table name, instead of a generated one
Set this to configure a manual table name, instead of a generated one.
``db_table`` is also supported as an alias. If both ``table`` and
``db_table`` are set, they must have the same value.

.. attribute:: db_table
:annotation: = ""

Alias for ``table``. This is useful for users coming from Django, where
``db_table`` is the standard option name.

.. attribute:: table_description
:annotation: = ""
Expand Down
43 changes: 41 additions & 2 deletions tests/test_table_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from tortoise import fields
from tortoise.context import TortoiseContext
from tortoise.exceptions import ConfigurationError
from tortoise.models import Model


Expand All @@ -11,19 +12,36 @@ def table_name_generator(model_cls: type[Model]):


class Tournament(Model):
id = fields.IntField(pk=True)
id = fields.IntField(primary_key=True)
name = fields.TextField()
created_at = fields.DatetimeField(auto_now_add=True)


class CustomTable(Model):
id = fields.IntField(pk=True)
id = fields.IntField(primary_key=True)
name = fields.TextField()

class Meta:
table = "my_custom_table"


class CustomDBTable(Model):
id = fields.IntField(primary_key=True)
name = fields.TextField()

class Meta:
db_table = "my_custom_db_table"


class CustomTableAndDBTable(Model):
id = fields.IntField(primary_key=True)
name = fields.TextField()

class Meta:
table = "my_matching_table"
db_table = "my_matching_table"


@pytest_asyncio.fixture
async def table_name_db():
"""Fixture for table name generator tests with in-memory SQLite."""
Expand All @@ -46,3 +64,24 @@ async def test_glabal_name_generator(table_name_db):
@pytest.mark.asyncio
async def test_custom_table_name_precedence(table_name_db):
assert CustomTable._meta.db_table == "my_custom_table"


@pytest.mark.asyncio
async def test_custom_db_table_name_precedence(table_name_db):
assert CustomDBTable._meta.db_table == "my_custom_db_table"


@pytest.mark.asyncio
async def test_custom_table_and_db_table_name_match(table_name_db):
assert CustomTableAndDBTable._meta.db_table == "my_matching_table"


def test_conflicting_table_and_db_table_names():
with pytest.raises(ConfigurationError, match="Meta.table and Meta.db_table"):

class ConflictingTableName(Model):
id = fields.IntField(primary_key=True)

class Meta:
table = "first_table"
db_table = "second_table"
14 changes: 13 additions & 1 deletion tortoise/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ def prepare_default_ordering(meta: Model.Meta) -> tuple[tuple[str, Order], ...]:
return parsed_ordering


def resolve_db_table(meta: Model.Meta) -> str:
table = getattr(meta, "table", "")
db_table = getattr(meta, "db_table", "")
if not db_table:
return table
if table and table != db_table:
raise ConfigurationError(
"Meta.table and Meta.db_table must have the same value, or set only one."
)
return db_table


class FkSetterKwargs(TypedDict):
_key: str
relation_field: str
Expand Down Expand Up @@ -222,7 +234,7 @@ class MetaInfo:
def __init__(self, meta: Model.Meta) -> None:
self.abstract: bool = getattr(meta, "abstract", False)
self.manager: Manager = getattr(meta, "manager", Manager())
self.db_table: str = getattr(meta, "table", "")
self.db_table: str = resolve_db_table(meta)
self.schema: str | None = getattr(meta, "schema", None)
self.app: str | None = getattr(meta, "app", None)
self.unique_together: tuple[tuple[str, ...], ...] = get_together(meta, "unique_together")
Expand Down
Loading