Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parametrize related field queries #1797

Merged
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Fixed
Changed
^^^^^^^
- Parametrizes UPDATE, DELETE, bulk update and create operations (#1785)
- Parametrizes related field queries (#1797)

0.22.1
------
Expand Down
8 changes: 4 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ classifiers = [

[tool.poetry.dependencies]
python = "^3.8"
pypika-tortoise = "^0.3.1"
pypika-tortoise = "^0.3.2"
iso8601 = "^2.1.0"
aiosqlite = ">=0.16.0, <0.21.0"
pytz = "*"
Expand Down
7 changes: 1 addition & 6 deletions tortoise/backends/base/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

from pypika import JoinType, Parameter, Table
from pypika.queries import QueryBuilder
from pypika.terms import Parameterizer

from tortoise.exceptions import OperationalError
from tortoise.expressions import Expression, ResolveContext
Expand Down Expand Up @@ -192,10 +191,6 @@ async def _process_insert_result(self, instance: "Model", results: Any) -> None:
def parameter(self, pos: int) -> Parameter:
return Parameter(idx=pos + 1)

@classmethod
def parameterizer(cls) -> Parameterizer:
return Parameterizer()

async def execute_insert(self, instance: "Model") -> None:
if not instance._custom_generated_pk:
values = [
Expand Down Expand Up @@ -455,7 +450,7 @@ async def _prefetch_m2m_relation(
if modifier.having_criterion:
query = query.having(modifier.having_criterion)

_, raw_results = await self.db.execute_query(query.get_sql())
_, raw_results = await self.db.execute_query(*query.get_parameterized_sql())
relations: List[Tuple[Any, Any]] = []
related_object_list: List["Model"] = []
model_pk, related_pk = self.model._meta.pk, field_object.related_model._meta.pk
Expand Down
21 changes: 21 additions & 0 deletions tortoise/backends/psycopg/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import psycopg.pq
import psycopg.rows
import psycopg_pool
from pypika.dialects.postgresql import PostgreSQLQuery, PostgreSQLQueryBuilder
from pypika.terms import Parameterizer

import tortoise.backends.base.client as base_client
import tortoise.backends.base_postgres.client as postgres_client
Expand All @@ -28,7 +30,26 @@ async def release(self, connection: psycopg.AsyncConnection):
await self.putconn(connection)


class PsycopgSQLQuery(PostgreSQLQuery):
@classmethod
def _builder(cls, **kwargs) -> "PostgreSQLQueryBuilder":
return PsycopgSQLQueryBuilder(**kwargs)


class PsycopgSQLQueryBuilder(PostgreSQLQueryBuilder):
"""
Psycopg opted to use a custom parameter placeholder, so we need to override the default
"""

def get_parameterized_sql(self, **kwargs) -> typing.Tuple[str, list]:
parameterizer = kwargs.pop(
"parameterizer", Parameterizer(placeholder_factory=lambda _: "%s")
)
return super().get_parameterized_sql(parameterizer=parameterizer, **kwargs)


class PsycopgClient(postgres_client.BasePostgresClient):
query_class: typing.Type[PsycopgSQLQuery] = PsycopgSQLQuery
executor_class: typing.Type[executor.PsycopgExecutor] = executor.PsycopgExecutor
schema_generator: typing.Type[PsycopgSchemaGenerator] = PsycopgSchemaGenerator
_pool: typing.Optional[AsyncConnectionPool] = None
Expand Down
6 changes: 1 addition & 5 deletions tortoise/backends/psycopg/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from typing import Optional

from pypika import Parameter, Parameterizer
from pypika import Parameter

from tortoise import Model
from tortoise.backends.base_postgres.executor import BasePostgresExecutor
Expand All @@ -26,7 +26,3 @@ async def _process_insert_result(

def parameter(self, pos: int) -> Parameter:
return Parameter("%s")

@classmethod
def parameterizer(cls) -> Parameterizer:
return Parameterizer(placeholder_factory=lambda _: "%s")
2 changes: 1 addition & 1 deletion tortoise/backends/sqlite/executor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import datetime
from decimal import Decimal
import sqlite3
from decimal import Decimal
from typing import Optional, Type, Union

import pytz
Expand Down
3 changes: 2 additions & 1 deletion tortoise/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ def __init__(self, query: "AwaitableQuery") -> None:

def get_sql(self, **kwargs: Any) -> str:
self.query._choose_db_if_not_chosen()
return self.query._make_query(**kwargs)[0]
self.query._make_query()
return self.query.query.get_parameterized_sql(**kwargs)[0]

def as_(self, alias: str) -> "Selectable": # type: ignore
self.query._choose_db_if_not_chosen()
Expand Down
8 changes: 5 additions & 3 deletions tortoise/fields/relational.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,9 @@ async def add(self, *instances: MODEL, using_db: "Optional[BaseDBAsyncClient]" =
criterion = forward_field == pks_f[0] if len(pks_f) == 1 else forward_field.isin(pks_f)
select_query = select_query.where(criterion)

_, already_existing_relations_raw = await db.execute_query(str(select_query))
_, already_existing_relations_raw = await db.execute_query(
*select_query.get_parameterized_sql()
)
already_existing_forward_pks = {
related_pk_formatting_func(r[forward_key], self.instance)
for r in already_existing_relations_raw
Expand All @@ -194,7 +196,7 @@ async def add(self, *instances: MODEL, using_db: "Optional[BaseDBAsyncClient]" =
query = db.query_class.into(through_table).columns(forward_field, backward_field)
for pk_f in pks_f_to_insert:
query = query.insert(pk_f, pk_b)
await db.execute_query(str(query))
await db.execute_query(*query.get_parameterized_sql())

async def clear(self, using_db: "Optional[BaseDBAsyncClient]" = None) -> None:
"""
Expand Down Expand Up @@ -237,7 +239,7 @@ async def _remove_or_clear(
[related_pk_formatting_func(i.pk, i) for i in instances]
)
query = db.query_class.from_(through_table).where(condition).delete()
await db.execute_query(str(query))
await db.execute_query(*query.get_parameterized_sql())


class RelationalField(Field[MODEL]):
Expand Down
Loading