Merge pull request #589 from erichaydel/588-fix-queryset-count-bug
Fix collerek/ormar#588 Bug in queryset count() method
This commit is contained in:
@ -601,7 +601,7 @@ metadata.drop_all(engine)
|
|||||||
* `prefetch_related(related: Union[List, str]) -> QuerySet`
|
* `prefetch_related(related: Union[List, str]) -> QuerySet`
|
||||||
* `limit(limit_count: int) -> QuerySet`
|
* `limit(limit_count: int) -> QuerySet`
|
||||||
* `offset(offset: int) -> QuerySet`
|
* `offset(offset: int) -> QuerySet`
|
||||||
* `count() -> int`
|
* `count(distinct: bool = True) -> int`
|
||||||
* `exists() -> bool`
|
* `exists() -> bool`
|
||||||
* `max(columns: List[str]) -> Any`
|
* `max(columns: List[str]) -> Any`
|
||||||
* `min(columns: List[str]) -> Any`
|
* `min(columns: List[str]) -> Any`
|
||||||
|
|||||||
@ -499,11 +499,19 @@ Returns a bool value to confirm if there are rows matching the given criteria
|
|||||||
#### count
|
#### count
|
||||||
|
|
||||||
```python
|
```python
|
||||||
| async count() -> int
|
| async count(distinct: bool = True) -> int
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns number of rows matching the given criteria
|
Returns number of rows matching the given criteria
|
||||||
(applied with `filter` and `exclude` if set before).
|
(applied with `filter` and `exclude` if set before).
|
||||||
|
If `distinct` is `True` (the default), this will return the number of primary rows selected. If `False`,
|
||||||
|
the count will be the total number of rows returned
|
||||||
|
(including extra rows for `one-to-many` or `many-to-many` left `select_related` table joins).
|
||||||
|
`False` is the legacy (buggy) behavior for workflows that depend on it.
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `distinct` (`bool`): flag if the primary table rows should be distinct or not
|
||||||
|
|
||||||
**Returns**:
|
**Returns**:
|
||||||
|
|
||||||
@ -865,4 +873,3 @@ Bulk operations do not send signals.
|
|||||||
|
|
||||||
- `objects` (`List[Model]`): list of ormar models
|
- `objects` (`List[Model]`): list of ormar models
|
||||||
- `columns` (`List[str]`): list of columns to update
|
- `columns` (`List[str]`): list of columns to update
|
||||||
|
|
||||||
|
|||||||
@ -150,14 +150,22 @@ Actual call delegated to QuerySet.
|
|||||||
#### count
|
#### count
|
||||||
|
|
||||||
```python
|
```python
|
||||||
| async count() -> int
|
| async count(distinct: bool = True) -> int
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns number of rows matching the given criteria
|
Returns number of rows matching the given criteria
|
||||||
(applied with `filter` and `exclude` if set before).
|
(applied with `filter` and `exclude` if set before).
|
||||||
|
If `distinct` is `True` (the default), this will return the number of primary rows selected. If `False`,
|
||||||
|
the count will be the total number of rows returned
|
||||||
|
(including extra rows for `one-to-many` or `many-to-many` left `select_related` table joins).
|
||||||
|
`False` is the legacy (buggy) behavior for workflows that depend on it.
|
||||||
|
|
||||||
Actual call delegated to QuerySet.
|
Actual call delegated to QuerySet.
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `distinct` (`bool`): flag if the primary table rows should be distinct or not
|
||||||
|
|
||||||
**Returns**:
|
**Returns**:
|
||||||
|
|
||||||
`int`: number of rows
|
`int`: number of rows
|
||||||
@ -773,4 +781,3 @@ Actual call delegated to QuerySet.
|
|||||||
**Returns**:
|
**Returns**:
|
||||||
|
|
||||||
`QuerysetProxy`: QuerysetProxy
|
`QuerysetProxy`: QuerysetProxy
|
||||||
|
|
||||||
|
|||||||
@ -610,7 +610,7 @@ metadata.drop_all(engine)
|
|||||||
* `prefetch_related(related: Union[List, str]) -> QuerySet`
|
* `prefetch_related(related: Union[List, str]) -> QuerySet`
|
||||||
* `limit(limit_count: int) -> QuerySet`
|
* `limit(limit_count: int) -> QuerySet`
|
||||||
* `offset(offset: int) -> QuerySet`
|
* `offset(offset: int) -> QuerySet`
|
||||||
* `count() -> int`
|
* `count(distinct: bool = True) -> int`
|
||||||
* `exists() -> bool`
|
* `exists() -> bool`
|
||||||
* `max(columns: List[str]) -> Any`
|
* `max(columns: List[str]) -> Any`
|
||||||
* `min(columns: List[str]) -> Any`
|
* `min(columns: List[str]) -> Any`
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
Currently 6 aggregation functions are supported.
|
Currently 6 aggregation functions are supported.
|
||||||
|
|
||||||
|
|
||||||
* `count() -> int`
|
* `count(distinct: bool = True) -> int`
|
||||||
* `exists() -> bool`
|
* `exists() -> bool`
|
||||||
* `sum(columns) -> Any`
|
* `sum(columns) -> Any`
|
||||||
* `avg(columns) -> Any`
|
* `avg(columns) -> Any`
|
||||||
@ -12,7 +12,7 @@ Currently 6 aggregation functions are supported.
|
|||||||
|
|
||||||
|
|
||||||
* `QuerysetProxy`
|
* `QuerysetProxy`
|
||||||
* `QuerysetProxy.count()` method
|
* `QuerysetProxy.count(distinct=True)` method
|
||||||
* `QuerysetProxy.exists()` method
|
* `QuerysetProxy.exists()` method
|
||||||
* `QuerysetProxy.sum(columns)` method
|
* `QuerysetProxy.sum(columns)` method
|
||||||
* `QuerysetProxy.avg(columns)` method
|
* `QuerysetProxy.avg(columns)` method
|
||||||
@ -22,9 +22,13 @@ Currently 6 aggregation functions are supported.
|
|||||||
|
|
||||||
## count
|
## count
|
||||||
|
|
||||||
`count() -> int`
|
`count(distinct: bool = True) -> int`
|
||||||
|
|
||||||
Returns number of rows matching the given criteria (i.e. applied with `filter` and `exclude`)
|
Returns number of rows matching the given criteria (i.e. applied with `filter` and `exclude`).
|
||||||
|
If `distinct` is `True` (the default), this will return the number of primary rows selected. If `False`,
|
||||||
|
the count will be the total number of rows returned
|
||||||
|
(including extra rows for `one-to-many` or `many-to-many` left `select_related` table joins).
|
||||||
|
`False` is the legacy (buggy) behavior for workflows that depend on it.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Book(ormar.Model):
|
class Book(ormar.Model):
|
||||||
@ -322,4 +326,3 @@ objects from other side of the relation.
|
|||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section
|
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section
|
||||||
|
|
||||||
|
|||||||
@ -188,12 +188,12 @@ Instead of ormar models return raw data in form list of dictionaries or tuples.
|
|||||||
|
|
||||||
### [Aggregated functions](./aggregations.md)
|
### [Aggregated functions](./aggregations.md)
|
||||||
|
|
||||||
* `count() -> int`
|
* `count(distinct: bool = True) -> int`
|
||||||
* `exists() -> bool`
|
* `exists() -> bool`
|
||||||
|
|
||||||
|
|
||||||
* `QuerysetProxy`
|
* `QuerysetProxy`
|
||||||
* `QuerysetProxy.count()` method
|
* `QuerysetProxy.count(distinct=True)` method
|
||||||
* `QuerysetProxy.exists()` method
|
* `QuerysetProxy.exists()` method
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
|
|||||||
@ -274,7 +274,7 @@ With exclude_fields() you can select subset of model columns that will be exclud
|
|||||||
|
|
||||||
### count
|
### count
|
||||||
|
|
||||||
`count() -> int`
|
`count(distinct: bool = True) -> int`
|
||||||
|
|
||||||
Returns number of rows matching the given criteria (i.e. applied with filter and exclude)
|
Returns number of rows matching the given criteria (i.e. applied with filter and exclude)
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@ class QuerySetProtocol(Protocol): # pragma: nocover
|
|||||||
async def exists(self) -> bool:
|
async def exists(self) -> bool:
|
||||||
...
|
...
|
||||||
|
|
||||||
async def count(self) -> int:
|
async def count(self, distinct: bool = True) -> int:
|
||||||
...
|
...
|
||||||
|
|
||||||
async def clear(self) -> int:
|
async def clear(self) -> int:
|
||||||
|
|||||||
@ -678,16 +678,25 @@ class QuerySet(Generic[T]):
|
|||||||
expr = sqlalchemy.exists(expr).select()
|
expr = sqlalchemy.exists(expr).select()
|
||||||
return await self.database.fetch_val(expr)
|
return await self.database.fetch_val(expr)
|
||||||
|
|
||||||
async def count(self) -> int:
|
async def count(self, distinct: bool = True) -> int:
|
||||||
"""
|
"""
|
||||||
Returns number of rows matching the given criteria
|
Returns number of rows matching the given criteria
|
||||||
(applied with `filter` and `exclude` if set before).
|
(applied with `filter` and `exclude` if set before).
|
||||||
|
If `distinct` is `True` (the default), this will return the number of primary rows selected. If `False`,
|
||||||
|
the count will be the total number of rows returned
|
||||||
|
(including extra rows for `one-to-many` or `many-to-many` left `select_related` table joins).
|
||||||
|
`False` is the legacy (buggy) behavior for workflows that depend on it.
|
||||||
|
|
||||||
|
:param distinct: flag if the primary table rows should be distinct or not
|
||||||
|
|
||||||
:return: number of rows
|
:return: number of rows
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
expr = self.build_select_expression().alias("subquery_for_count")
|
expr = self.build_select_expression().alias("subquery_for_count")
|
||||||
expr = sqlalchemy.func.count().select().select_from(expr)
|
expr = sqlalchemy.func.count().select().select_from(expr)
|
||||||
|
if distinct:
|
||||||
|
expr_distinct = expr.group_by(self.model_meta.pkname).alias("subquery_for_group")
|
||||||
|
expr = sqlalchemy.func.count().select().select_from(expr_distinct)
|
||||||
return await self.database.fetch_val(expr)
|
return await self.database.fetch_val(expr)
|
||||||
|
|
||||||
async def _query_aggr_function(self, func_name: str, columns: List) -> Any:
|
async def _query_aggr_function(self, func_name: str, columns: List) -> Any:
|
||||||
|
|||||||
@ -193,17 +193,22 @@ class QuerysetProxy(Generic[T]):
|
|||||||
"""
|
"""
|
||||||
return await self.queryset.exists()
|
return await self.queryset.exists()
|
||||||
|
|
||||||
async def count(self) -> int:
|
async def count(self, distinct: bool = True) -> int:
|
||||||
"""
|
"""
|
||||||
Returns number of rows matching the given criteria
|
Returns number of rows matching the given criteria
|
||||||
(applied with `filter` and `exclude` if set before).
|
(applied with `filter` and `exclude` if set before).
|
||||||
|
If `distinct` is `True` (the default), this will return the number of primary rows selected. If `False`,
|
||||||
|
the count will be the total number of rows returned
|
||||||
|
(including extra rows for `one-to-many` or `many-to-many` left `select_related` table joins).
|
||||||
|
`False` is the legacy (buggy) behavior for workflows that depend on it.
|
||||||
|
|
||||||
Actual call delegated to QuerySet.
|
Actual call delegated to QuerySet.
|
||||||
|
|
||||||
|
:param distinct: flag if the primary table rows should be distinct or not
|
||||||
:return: number of rows
|
:return: number of rows
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
return await self.queryset.count()
|
return await self.queryset.count(distinct=distinct)
|
||||||
|
|
||||||
async def max(self, columns: Union[str, List[str]]) -> Any: # noqa: A003
|
async def max(self, columns: Union[str, List[str]]) -> Any: # noqa: A003
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -175,3 +175,15 @@ async def test_queryset_method():
|
|||||||
assert await author.books.max(["year", "title"]) == dict(
|
assert await author.books.max(["year", "title"]) == dict(
|
||||||
year=1930, title="Book 3"
|
year=1930, title="Book 3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_count_method():
|
||||||
|
async with database:
|
||||||
|
await sample_data()
|
||||||
|
|
||||||
|
count = await Author.objects.select_related("books").count()
|
||||||
|
assert count == 1
|
||||||
|
|
||||||
|
# The legacy functionality
|
||||||
|
count = await Author.objects.select_related("books").count(distinct=False)
|
||||||
|
assert count == 3
|
||||||
|
|||||||
Reference in New Issue
Block a user