check limit as subquery
This commit is contained in:
@ -20,7 +20,7 @@ class OrderAction(QueryAction):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, order_str: str, model_cls: Type["Model"], alias: str = None
|
self, order_str: str, model_cls: Type["Model"], alias: str = None
|
||||||
) -> None:
|
) -> None:
|
||||||
self.direction: str = ""
|
self.direction: str = ""
|
||||||
super().__init__(query_str=order_str, model_cls=model_cls)
|
super().__init__(query_str=order_str, model_cls=model_cls)
|
||||||
@ -34,6 +34,18 @@ class OrderAction(QueryAction):
|
|||||||
def field_alias(self) -> str:
|
def field_alias(self) -> str:
|
||||||
return self.target_model.get_column_alias(self.field_name)
|
return self.target_model.get_column_alias(self.field_name)
|
||||||
|
|
||||||
|
def get_field_name_text(self) -> str:
|
||||||
|
"""
|
||||||
|
Escapes characters if it's required.
|
||||||
|
Substitutes values of the models if value is a ormar Model with its pk value.
|
||||||
|
Compiles the clause.
|
||||||
|
|
||||||
|
:return: complied and escaped clause
|
||||||
|
:rtype: sqlalchemy.sql.elements.TextClause
|
||||||
|
"""
|
||||||
|
prefix = f"{self.table_prefix}_" if self.table_prefix else ""
|
||||||
|
return f"{prefix}{self.table}" f".{self.field_alias}"
|
||||||
|
|
||||||
def get_text_clause(self) -> sqlalchemy.sql.expression.TextClause:
|
def get_text_clause(self) -> sqlalchemy.sql.expression.TextClause:
|
||||||
"""
|
"""
|
||||||
Escapes characters if it's required.
|
Escapes characters if it's required.
|
||||||
|
|||||||
@ -18,16 +18,16 @@ if TYPE_CHECKING: # pragma no cover
|
|||||||
|
|
||||||
class Query:
|
class Query:
|
||||||
def __init__( # noqa CFQ002
|
def __init__( # noqa CFQ002
|
||||||
self,
|
self,
|
||||||
model_cls: Type["Model"],
|
model_cls: Type["Model"],
|
||||||
filter_clauses: List[FilterAction],
|
filter_clauses: List[FilterAction],
|
||||||
exclude_clauses: List[FilterAction],
|
exclude_clauses: List[FilterAction],
|
||||||
select_related: List,
|
select_related: List,
|
||||||
limit_count: Optional[int],
|
limit_count: Optional[int],
|
||||||
offset: Optional[int],
|
offset: Optional[int],
|
||||||
excludable: "ExcludableItems",
|
excludable: "ExcludableItems",
|
||||||
order_bys: Optional[List["OrderAction"]],
|
order_bys: Optional[List["OrderAction"]],
|
||||||
limit_raw_sql: bool,
|
limit_raw_sql: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.query_offset = offset
|
self.query_offset = offset
|
||||||
self.limit_count = limit_count
|
self.limit_count = limit_count
|
||||||
@ -137,7 +137,10 @@ class Query:
|
|||||||
) = sql_join.build_join()
|
) = sql_join.build_join()
|
||||||
|
|
||||||
if self._pagination_query_required():
|
if self._pagination_query_required():
|
||||||
self._build_pagination_condition()
|
limit_qry, on_clause = self._build_pagination_condition()
|
||||||
|
self.select_from = sqlalchemy.sql.join(
|
||||||
|
self.select_from, limit_qry, on_clause
|
||||||
|
)
|
||||||
|
|
||||||
expr = sqlalchemy.sql.select(self.columns)
|
expr = sqlalchemy.sql.select(self.columns)
|
||||||
expr = expr.select_from(self.select_from)
|
expr = expr.select_from(self.select_from)
|
||||||
@ -149,7 +152,10 @@ class Query:
|
|||||||
|
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
def _build_pagination_condition(self) -> None:
|
def _build_pagination_condition(
|
||||||
|
self
|
||||||
|
) -> Tuple[
|
||||||
|
sqlalchemy.sql.expression.TextClause, sqlalchemy.sql.expression.TextClause]:
|
||||||
"""
|
"""
|
||||||
In order to apply limit and offset on main table in join only
|
In order to apply limit and offset on main table in join only
|
||||||
(otherwise you can get only partially constructed main model
|
(otherwise you can get only partially constructed main model
|
||||||
@ -164,25 +170,33 @@ class Query:
|
|||||||
primary key values. Whole query is used to determine the values.
|
primary key values. Whole query is used to determine the values.
|
||||||
"""
|
"""
|
||||||
pk_alias = self.model_cls.get_column_alias(self.model_cls.Meta.pkname)
|
pk_alias = self.model_cls.get_column_alias(self.model_cls.Meta.pkname)
|
||||||
qry_text = sqlalchemy.text(f"{self.table.name}.{pk_alias}")
|
pk_aliased_name = f"{self.table.name}.{pk_alias}"
|
||||||
limit_qry = sqlalchemy.sql.select([qry_text])
|
qry_text = sqlalchemy.text(pk_aliased_name)
|
||||||
|
maxes = dict()
|
||||||
|
for order in list(self.sorted_orders.keys()):
|
||||||
|
if order is not None and order.get_field_name_text() != pk_aliased_name:
|
||||||
|
maxes[order.get_field_name_text()] = sqlalchemy.text(
|
||||||
|
f"max({order.get_field_name_text()})")
|
||||||
|
|
||||||
|
limit_qry = sqlalchemy.sql.select([qry_text]+list(maxes.values()))
|
||||||
limit_qry = limit_qry.select_from(self.select_from)
|
limit_qry = limit_qry.select_from(self.select_from)
|
||||||
limit_qry = self._apply_expression_modifiers(limit_qry)
|
limit_qry = self._apply_expression_modifiers(limit_qry)
|
||||||
limit_qry = FilterQuery(filter_clauses=self.filter_clauses).apply(limit_qry)
|
limit_qry = FilterQuery(filter_clauses=self.filter_clauses).apply(limit_qry)
|
||||||
limit_qry = FilterQuery(filter_clauses=self.exclude_clauses, exclude=True).apply(
|
limit_qry = FilterQuery(filter_clauses=self.exclude_clauses,
|
||||||
|
exclude=True).apply(
|
||||||
limit_qry
|
limit_qry
|
||||||
)
|
)
|
||||||
limit_qry = limit_qry.group_by(qry_text)
|
limit_qry = limit_qry.group_by(qry_text)
|
||||||
# limit_qry = OrderQuery(sorted_orders=self.sorted_orders).apply(limit_qry)
|
limit_qry = OrderQuery(sorted_orders=self.sorted_orders).apply(limit_qry)
|
||||||
limit_qry = LimitQuery(limit_count=self.limit_count).apply(limit_qry)
|
limit_qry = LimitQuery(limit_count=self.limit_count).apply(limit_qry)
|
||||||
limit_qry = OffsetQuery(query_offset=self.query_offset).apply(limit_qry)
|
limit_qry = OffsetQuery(query_offset=self.query_offset).apply(limit_qry)
|
||||||
limit_action = FilterAction(
|
limit_qry = limit_qry.alias("limit_query")
|
||||||
filter_str=f"{pk_alias}__in", value=limit_qry, model_cls=self.model_cls
|
on_clause = sqlalchemy.text(
|
||||||
)
|
f"limit_query.{pk_alias}={self.table.name}.{pk_alias}")
|
||||||
self.filter_clauses.append(limit_action)
|
return limit_qry, on_clause
|
||||||
|
|
||||||
def _apply_expression_modifiers(
|
def _apply_expression_modifiers(
|
||||||
self, expr: sqlalchemy.sql.select
|
self, expr: sqlalchemy.sql.select
|
||||||
) -> sqlalchemy.sql.select:
|
) -> sqlalchemy.sql.select:
|
||||||
"""
|
"""
|
||||||
Receives the select query (might be join) and applies:
|
Receives the select query (might be join) and applies:
|
||||||
|
|||||||
Reference in New Issue
Block a user