diff --git a/docs/releases.md b/docs/releases.md index 0f034b5..f85243c 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -8,7 +8,8 @@ * Fix `LargeBinary` fields that can be nullable [#409](https://github.com/collerek/ormar/issues/409) * Make `ormar.Model` pickable [#413](https://github.com/collerek/ormar/issues/413) -* Make `first()` and `get()` without arguments respect ordering set by user, fallback to primary key (asc, and desc respectively) [#453](https://github.com/collerek/ormar/issues/453) +* Make `first()` and `get()` without arguments respect ordering of main model set by user, fallback to primary key (asc, and desc respectively) [#453](https://github.com/collerek/ormar/issues/453) +* Fix improper quoting of non-aliased join `on` clauses in postgress [#455](https://github.com/collerek/ormar/issues/455) # 0.10.22 diff --git a/ormar/queryset/join.py b/ormar/queryset/join.py index f40ff79..592fe4b 100644 --- a/ormar/queryset/join.py +++ b/ormar/queryset/join.py @@ -108,7 +108,14 @@ class SqlJoin: :rtype: sqlalchemy.text """ left_part = f"{self.next_alias}_{to_clause}" - right_part = f"{previous_alias + '_' if previous_alias else ''}{from_clause}" + if not previous_alias: + dialect = self.main_model.Meta.database._backend._dialect + table, column = from_clause.split(".") + quotter = dialect.identifier_preparer.quote + right_part = f"{quotter(table)}.{quotter(column)}" + else: + right_part = f"{previous_alias}'_'{from_clause}" + return text(f"{left_part}={right_part}") def build_join(self) -> Tuple[List, sqlalchemy.sql.select, List, OrderedDict]: diff --git a/ormar/queryset/queryset.py b/ormar/queryset/queryset.py index e1cbcfd..7ecb6fa 100644 --- a/ormar/queryset/queryset.py +++ b/ormar/queryset/queryset.py @@ -913,7 +913,7 @@ class QuerySet(Generic[T]): except ormar.NoMatch: return None - async def get(self, *args: Any, **kwargs: Any) -> "T": + async def get(self, *args: Any, **kwargs: Any) -> "T": # noqa: CCR001 """ Get's the first row from the db meeting the criteria set by kwargs. diff --git a/tests/test_queries/test_quoting_table_names_in_on_join_clause.py b/tests/test_queries/test_quoting_table_names_in_on_join_clause.py new file mode 100644 index 0000000..9421f01 --- /dev/null +++ b/tests/test_queries/test_quoting_table_names_in_on_join_clause.py @@ -0,0 +1,63 @@ +import datetime +import uuid +from typing import Dict, Optional, Union + +import databases +import pytest +import sqlalchemy +from sqlalchemy import create_engine + +import ormar +from tests.settings import DATABASE_URL + +database = databases.Database(DATABASE_URL) +metadata = sqlalchemy.MetaData() +engine = create_engine(DATABASE_URL) + + +class Team(ormar.Model): + class Meta: + tablename: str = "team" + database = database + metadata = metadata + + id: uuid.UUID = ormar.UUID(default=uuid.uuid4, primary_key=True, index=True) + name = ormar.Text(nullable=True) + client_id = ormar.Text(nullable=True) + client_secret = ormar.Text(nullable=True) + created_on = ormar.DateTime(timezone=True, default=datetime.datetime.utcnow()) + + +class User(ormar.Model): + class Meta: + tablename: str = "user" + database = database + metadata = metadata + + id: uuid.UUID = ormar.UUID(default=uuid.uuid4, primary_key=True, index=True) + client_user_id = ormar.Text() + token = ormar.Text(nullable=True) + team: Optional[Team] = ormar.ForeignKey(to=Team, name="team_id") + + +class Order(ormar.Model): + class Meta: + tablename: str = "order" + database = database + metadata = metadata + + id: uuid.UUID = ormar.UUID(default=uuid.uuid4, primary_key=True, index=True) + user: Optional[Union[User, Dict]] = ormar.ForeignKey(User) + + +@pytest.fixture(autouse=True, scope="module") +def create_test_database(): + metadata.create_all(engine) + yield + metadata.drop_all(engine) + + +@pytest.mark.asyncio +async def test_quoting_on_clause_without_prefix(): + async with database: + await User.objects.select_related("orders").all()