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:
@ -334,7 +334,7 @@ async def delete():
|
|||||||
|
|
||||||
async def joins():
|
async def joins():
|
||||||
# Tho join two models use select_related
|
# Tho join two models use select_related
|
||||||
|
|
||||||
# Django style
|
# Django style
|
||||||
book = await Book.objects.select_related("author").get(title="The Hobbit")
|
book = await Book.objects.select_related("author").get(title="The Hobbit")
|
||||||
# Python style
|
# Python style
|
||||||
@ -348,7 +348,7 @@ async def joins():
|
|||||||
# By default you also get a second side of the relation
|
# By default you also get a second side of the relation
|
||||||
# constructed as lowercase source model name +'s' (books in this case)
|
# constructed as lowercase source model name +'s' (books in this case)
|
||||||
# you can also provide custom name with parameter related_name
|
# you can also provide custom name with parameter related_name
|
||||||
|
|
||||||
# Django style
|
# Django style
|
||||||
author = await Author.objects.select_related("books").all(name="J.R.R. Tolkien")
|
author = await Author.objects.select_related("books").all(name="J.R.R. Tolkien")
|
||||||
# Python style
|
# Python style
|
||||||
@ -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
|
||||||
@ -260,11 +268,11 @@ List of related models is cleared before the call.
|
|||||||
|
|
||||||
**Arguments**:
|
**Arguments**:
|
||||||
|
|
||||||
- `kwargs`:
|
- `kwargs`:
|
||||||
|
|
||||||
**Returns**:
|
**Returns**:
|
||||||
|
|
||||||
`_asyncio.Future`:
|
`_asyncio.Future`:
|
||||||
|
|
||||||
<a name="relations.querysetproxy.QuerysetProxy.get_or_none"></a>
|
<a name="relations.querysetproxy.QuerysetProxy.get_or_none"></a>
|
||||||
#### get\_or\_none
|
#### get\_or\_none
|
||||||
@ -773,4 +781,3 @@ Actual call delegated to QuerySet.
|
|||||||
**Returns**:
|
**Returns**:
|
||||||
|
|
||||||
`QuerysetProxy`: QuerysetProxy
|
`QuerysetProxy`: QuerysetProxy
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
### Overview
|
### Overview
|
||||||
|
|
||||||
The `ormar` package is an async mini ORM for Python, with support for **Postgres,
|
The `ormar` package is an async mini ORM for Python, with support for **Postgres,
|
||||||
MySQL**, and **SQLite**.
|
MySQL**, and **SQLite**.
|
||||||
|
|
||||||
The main benefits of using `ormar` are:
|
The main benefits of using `ormar` are:
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ The main benefits of using `ormar` are:
|
|||||||
|
|
||||||
The goal was to create a simple ORM that can be **used directly (as request and response models) with [`fastapi`][fastapi]** that bases it's data validation on pydantic.
|
The goal was to create a simple ORM that can be **used directly (as request and response models) with [`fastapi`][fastapi]** that bases it's data validation on pydantic.
|
||||||
|
|
||||||
Ormar - apart from the obvious "ORM" in name - gets its name from _ormar_ in Swedish which means _snakes_, and _ormar(e)_ in Croatian which means _cabinet_.
|
Ormar - apart from the obvious "ORM" in name - gets its name from _ormar_ in Swedish which means _snakes_, and _ormar(e)_ in Croatian which means _cabinet_.
|
||||||
|
|
||||||
And what's a better name for python ORM than snakes cabinet :)
|
And what's a better name for python ORM than snakes cabinet :)
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ Ormar is built with:
|
|||||||
|
|
||||||
`ormar` is built as open-source software and will remain completely free (MIT license).
|
`ormar` is built as open-source software and will remain completely free (MIT license).
|
||||||
|
|
||||||
As I write open-source code to solve everyday problems in my work or to promote and build strong python
|
As I write open-source code to solve everyday problems in my work or to promote and build strong python
|
||||||
community you can say thank you and buy me a coffee or sponsor me with a monthly amount to help ensure my work remains free and maintained.
|
community you can say thank you and buy me a coffee or sponsor me with a monthly amount to help ensure my work remains free and maintained.
|
||||||
|
|
||||||
<a aria-label="Sponsor collerek" href="https://github.com/sponsors/collerek" style="text-decoration: none; color: #c9d1d9 !important;">
|
<a aria-label="Sponsor collerek" href="https://github.com/sponsors/collerek" style="text-decoration: none; color: #c9d1d9 !important;">
|
||||||
@ -99,7 +99,7 @@ community you can say thank you and buy me a coffee or sponsor me with a monthly
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
line-height: 0px;
|
line-height: 0px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
">
|
">
|
||||||
<svg aria-hidden="true" viewBox="0 0 16 16" height="16" width="16" style="fill: #db61a2">
|
<svg aria-hidden="true" viewBox="0 0 16 16" height="16" width="16" style="fill: #db61a2">
|
||||||
<path fill-rule="evenodd" d="M4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.565 20.565 0 008 13.393a20.561 20.561 0 003.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.75.75 0 01-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5zM8 14.25l-.345.666-.002-.001-.006-.003-.018-.01a7.643 7.643 0 01-.31-.17 22.075 22.075 0 01-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.08 22.08 0 01-3.744 2.584l-.018.01-.006.003h-.002L8 14.25zm0 0l.345.666a.752.752 0 01-.69 0L8 14.25z"></path>
|
<path fill-rule="evenodd" d="M4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.565 20.565 0 008 13.393a20.561 20.561 0 003.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.75.75 0 01-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5zM8 14.25l-.345.666-.002-.001-.006-.003-.018-.01a7.643 7.643 0 01-.31-.17 22.075 22.075 0 01-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.08 22.08 0 01-3.744 2.584l-.018.01-.006.003h-.002L8 14.25zm0 0l.345.666a.752.752 0 01-.69 0L8 14.25z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
@ -141,30 +141,30 @@ engine = sqlalchemy.create_engine(DATABASE_URL)
|
|||||||
metadata.create_all(engine)
|
metadata.create_all(engine)
|
||||||
```
|
```
|
||||||
|
|
||||||
For a sample configuration of alembic and more information regarding migrations and
|
For a sample configuration of alembic and more information regarding migrations and
|
||||||
database creation visit [migrations][migrations] documentation section.
|
database creation visit [migrations][migrations] documentation section.
|
||||||
|
|
||||||
### Package versions
|
### Package versions
|
||||||
**ormar is still under development:**
|
**ormar is still under development:**
|
||||||
We recommend pinning any dependencies (with i.e. `ormar~=0.9.1`)
|
We recommend pinning any dependencies (with i.e. `ormar~=0.9.1`)
|
||||||
|
|
||||||
`ormar` also follows the release numeration that breaking changes bump the major number,
|
`ormar` also follows the release numeration that breaking changes bump the major number,
|
||||||
while other changes and fixes bump minor number, so with the latter you should be safe to
|
while other changes and fixes bump minor number, so with the latter you should be safe to
|
||||||
update, yet always read the [releases][releases] docs before.
|
update, yet always read the [releases][releases] docs before.
|
||||||
`example: (0.5.2 -> 0.6.0 - breaking, 0.5.2 -> 0.5.3 - non breaking)`.
|
`example: (0.5.2 -> 0.6.0 - breaking, 0.5.2 -> 0.5.3 - non breaking)`.
|
||||||
|
|
||||||
### Asynchronous Python
|
### Asynchronous Python
|
||||||
|
|
||||||
Note that `ormar` is an asynchronous ORM, which means that you have to `await` the calls to
|
Note that `ormar` is an asynchronous ORM, which means that you have to `await` the calls to
|
||||||
the methods, that are scheduled for execution in an event loop. Python has a builtin module
|
the methods, that are scheduled for execution in an event loop. Python has a builtin module
|
||||||
[`asyncio`][asyncio] that allows you to do just that.
|
[`asyncio`][asyncio] that allows you to do just that.
|
||||||
|
|
||||||
Note that most "normal" python interpreters do not allow execution of `await`
|
Note that most "normal" python interpreters do not allow execution of `await`
|
||||||
outside of a function (because you actually schedule this function for delayed execution
|
outside of a function (because you actually schedule this function for delayed execution
|
||||||
and don't get the result immediately).
|
and don't get the result immediately).
|
||||||
|
|
||||||
In a modern web framework (like `fastapi`), the framework will handle this for you, but if
|
In a modern web framework (like `fastapi`), the framework will handle this for you, but if
|
||||||
you plan to do this on your own you need to perform this manually like described in the
|
you plan to do this on your own you need to perform this manually like described in the
|
||||||
quick start below.
|
quick start below.
|
||||||
|
|
||||||
### Quick Start
|
### Quick Start
|
||||||
@ -343,7 +343,7 @@ async def delete():
|
|||||||
|
|
||||||
async def joins():
|
async def joins():
|
||||||
# Tho join two models use select_related
|
# Tho join two models use select_related
|
||||||
|
|
||||||
# Django style
|
# Django style
|
||||||
book = await Book.objects.select_related("author").get(title="The Hobbit")
|
book = await Book.objects.select_related("author").get(title="The Hobbit")
|
||||||
# Python style
|
# Python style
|
||||||
@ -357,7 +357,7 @@ async def joins():
|
|||||||
# By default you also get a second side of the relation
|
# By default you also get a second side of the relation
|
||||||
# constructed as lowercase source model name +'s' (books in this case)
|
# constructed as lowercase source model name +'s' (books in this case)
|
||||||
# you can also provide custom name with parameter related_name
|
# you can also provide custom name with parameter related_name
|
||||||
|
|
||||||
# Django style
|
# Django style
|
||||||
author = await Author.objects.select_related("books").all(name="J.R.R. Tolkien")
|
author = await Author.objects.select_related("books").all(name="J.R.R. Tolkien")
|
||||||
# Python style
|
# Python style
|
||||||
@ -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`
|
||||||
@ -670,10 +670,10 @@ All fields are required unless one of the following is set:
|
|||||||
* `sql_nullable` - Used to set different setting for pydantic and the database. Sets the default to `nullable` value. Read the fields common parameters for details.
|
* `sql_nullable` - Used to set different setting for pydantic and the database. Sets the default to `nullable` value. Read the fields common parameters for details.
|
||||||
* `default` - Set a default value for the field. **Not available for relation fields**
|
* `default` - Set a default value for the field. **Not available for relation fields**
|
||||||
* `server_default` - Set a default value for the field on server side (like sqlalchemy's `func.now()`). **Not available for relation fields**
|
* `server_default` - Set a default value for the field on server side (like sqlalchemy's `func.now()`). **Not available for relation fields**
|
||||||
* `primary key` with `autoincrement` - When a column is set to primary key and autoincrement is set on this column.
|
* `primary key` with `autoincrement` - When a column is set to primary key and autoincrement is set on this column.
|
||||||
Autoincrement is set by default on int primary keys.
|
Autoincrement is set by default on int primary keys.
|
||||||
* `pydantic_only` - Field is available only as normal pydantic field, not stored in the database.
|
* `pydantic_only` - Field is available only as normal pydantic field, not stored in the database.
|
||||||
|
|
||||||
### Available signals
|
### Available signals
|
||||||
|
|
||||||
Signals allow to trigger your function for a given event on a given Model.
|
Signals allow to trigger your function for a given event on a given Model.
|
||||||
|
|||||||
@ -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,19 +12,23 @@ 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
|
||||||
* `QuerysetProxy.min(column)` method
|
* `QuerysetProxy.min(column)` method
|
||||||
* `QuerysetProxy.max(columns)` method
|
* `QuerysetProxy.max(columns)` method
|
||||||
|
|
||||||
|
|
||||||
## 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):
|
||||||
@ -84,7 +88,7 @@ Returns sum value of columns for rows matching the given criteria (applied with
|
|||||||
|
|
||||||
You can pass one or many column names including related columns.
|
You can pass one or many column names including related columns.
|
||||||
|
|
||||||
As of now each column passed is aggregated separately (so `sum(col1+col2)` is not possible,
|
As of now each column passed is aggregated separately (so `sum(col1+col2)` is not possible,
|
||||||
you can have `sum(col1, col2)` and later add 2 returned sums in python)
|
you can have `sum(col1, col2)` and later add 2 returned sums in python)
|
||||||
|
|
||||||
You cannot `sum` non numeric columns.
|
You cannot `sum` non numeric columns.
|
||||||
@ -138,7 +142,7 @@ Returns avg value of columns for rows matching the given criteria (applied with
|
|||||||
|
|
||||||
You can pass one or many column names including related columns.
|
You can pass one or many column names including related columns.
|
||||||
|
|
||||||
As of now each column passed is aggregated separately (so `sum(col1+col2)` is not possible,
|
As of now each column passed is aggregated separately (so `sum(col1+col2)` is not possible,
|
||||||
you can have `sum(col1, col2)` and later add 2 returned sums in python)
|
you can have `sum(col1, col2)` and later add 2 returned sums in python)
|
||||||
|
|
||||||
You cannot `avg` non numeric columns.
|
You cannot `avg` non numeric columns.
|
||||||
@ -193,7 +197,7 @@ Returns min value of columns for rows matching the given criteria (applied with
|
|||||||
|
|
||||||
You can pass one or many column names including related columns.
|
You can pass one or many column names including related columns.
|
||||||
|
|
||||||
As of now each column passed is aggregated separately (so `sum(col1+col2)` is not possible,
|
As of now each column passed is aggregated separately (so `sum(col1+col2)` is not possible,
|
||||||
you can have `sum(col1, col2)` and later add 2 returned sums in python)
|
you can have `sum(col1, col2)` and later add 2 returned sums in python)
|
||||||
|
|
||||||
If you aggregate on one column, the single value is directly returned as a result
|
If you aggregate on one column, the single value is directly returned as a result
|
||||||
@ -241,7 +245,7 @@ Returns min value of columns for rows matching the given criteria (applied with
|
|||||||
|
|
||||||
You can pass one or many column names including related columns.
|
You can pass one or many column names including related columns.
|
||||||
|
|
||||||
As of now each column passed is aggregated separately (so `sum(col1+col2)` is not possible,
|
As of now each column passed is aggregated separately (so `sum(col1+col2)` is not possible,
|
||||||
you can have `sum(col1, col2)` and later add 2 returned sums in python)
|
you can have `sum(col1, col2)` and later add 2 returned sums in python)
|
||||||
|
|
||||||
If you aggregate on one column, the single value is directly returned as a result
|
If you aggregate on one column, the single value is directly returned as a result
|
||||||
@ -292,7 +296,7 @@ select related etc related models directly from parent model.
|
|||||||
Works exactly the same as [count](./#count) function above but allows you to select columns from related
|
Works exactly the same as [count](./#count) function above but allows you to select columns from related
|
||||||
objects from other side of the relation.
|
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
|
||||||
|
|
||||||
### exists
|
### exists
|
||||||
@ -320,6 +324,5 @@ objects from other side of the relation.
|
|||||||
Works exactly the same as [max](./#max) function above but allows you to select maximum of columns from related
|
Works exactly the same as [max](./#max) function above but allows you to select maximum of columns from related
|
||||||
objects from other side of the relation.
|
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
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ and it's options.
|
|||||||
Most of the methods are also available through many to many relations and on reverse
|
Most of the methods are also available through many to many relations and on reverse
|
||||||
foreign key relations through `QuerysetProxy` interface.
|
foreign key relations through `QuerysetProxy` interface.
|
||||||
|
|
||||||
!!!info
|
!!!info
|
||||||
To see which relations are supported and how to construct relations
|
To see which relations are supported and how to construct relations
|
||||||
visit [relations][relations].
|
visit [relations][relations].
|
||||||
|
|
||||||
@ -34,12 +34,12 @@ To read more about any specific section or function please refer to the details
|
|||||||
* `Model.upsert()` method
|
* `Model.upsert()` method
|
||||||
* `Model.save_related()` method
|
* `Model.save_related()` method
|
||||||
|
|
||||||
|
|
||||||
* `QuerysetProxy`
|
* `QuerysetProxy`
|
||||||
* `QuerysetProxy.create(**kwargs)` method
|
* `QuerysetProxy.create(**kwargs)` method
|
||||||
* `QuerysetProxy.get_or_create(**kwargs)` method
|
* `QuerysetProxy.get_or_create(**kwargs)` method
|
||||||
* `QuerysetProxy.update_or_create(**kwargs)` method
|
* `QuerysetProxy.update_or_create(**kwargs)` method
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
To read more about any or all of those functions visit [create](./create.md) section.
|
To read more about any or all of those functions visit [create](./create.md) section.
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ To read more about any specific section or function please refer to the details
|
|||||||
* `QuerysetProxy.get_or_create(**kwargs)` method
|
* `QuerysetProxy.get_or_create(**kwargs)` method
|
||||||
* `QuerysetProxy.first()` method
|
* `QuerysetProxy.first()` method
|
||||||
* `QuerysetProxy.all(**kwargs)` method
|
* `QuerysetProxy.all(**kwargs)` method
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
To read more about any or all of those functions visit [read](./read.md) section.
|
To read more about any or all of those functions visit [read](./read.md) section.
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ Instead of ormar models return raw data in form list of dictionaries or tuples.
|
|||||||
|
|
||||||
* `QuerysetProxy`
|
* `QuerysetProxy`
|
||||||
* `QuerysetProxy.update_or_create(**kwargs)` method
|
* `QuerysetProxy.update_or_create(**kwargs)` method
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
To read more about any or all of those functions visit [update](./update.md) section.
|
To read more about any or all of those functions visit [update](./update.md) section.
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ Instead of ormar models return raw data in form list of dictionaries or tuples.
|
|||||||
* `QuerysetProxy`
|
* `QuerysetProxy`
|
||||||
* `QuerysetProxy.remove()` method
|
* `QuerysetProxy.remove()` method
|
||||||
* `QuerysetProxy.clear()` method
|
* `QuerysetProxy.clear()` method
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
To read more about any or all of those functions visit [delete](./delete.md) section.
|
To read more about any or all of those functions visit [delete](./delete.md) section.
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ Instead of ormar models return raw data in form list of dictionaries or tuples.
|
|||||||
* `QuerysetProxy`
|
* `QuerysetProxy`
|
||||||
* `QuerysetProxy.select_related(related: Union[List, str])` method
|
* `QuerysetProxy.select_related(related: Union[List, str])` method
|
||||||
* `QuerysetProxy.prefetch_related(related: Union[List, str])` method
|
* `QuerysetProxy.prefetch_related(related: Union[List, str])` method
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
To read more about any or all of those functions visit [joins and subqueries](./joins-and-subqueries.md) section.
|
To read more about any or all of those functions visit [joins and subqueries](./joins-and-subqueries.md) section.
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ Instead of ormar models return raw data in form list of dictionaries or tuples.
|
|||||||
* `QuerysetProxy.get_or_none(**kwargs)` method
|
* `QuerysetProxy.get_or_none(**kwargs)` method
|
||||||
* `QuerysetProxy.get_or_create(**kwargs)` method
|
* `QuerysetProxy.get_or_create(**kwargs)` method
|
||||||
* `QuerysetProxy.all(**kwargs)` method
|
* `QuerysetProxy.all(**kwargs)` method
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
To read more about any or all of those functions visit [filtering and sorting](./filter-and-sort.md) section.
|
To read more about any or all of those functions visit [filtering and sorting](./filter-and-sort.md) section.
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ Instead of ormar models return raw data in form list of dictionaries or tuples.
|
|||||||
* `QuerysetProxy`
|
* `QuerysetProxy`
|
||||||
* `QuerysetProxy.fields(columns: Union[List, str, set, dict])` method
|
* `QuerysetProxy.fields(columns: Union[List, str, set, dict])` method
|
||||||
* `QuerysetProxy.exclude_fields(columns: Union[List, str, set, dict])` method
|
* `QuerysetProxy.exclude_fields(columns: Union[List, str, set, dict])` method
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
To read more about any or all of those functions visit [selecting columns](./select-columns.md) section.
|
To read more about any or all of those functions visit [selecting columns](./select-columns.md) section.
|
||||||
|
|
||||||
@ -182,22 +182,22 @@ Instead of ormar models return raw data in form list of dictionaries or tuples.
|
|||||||
* `QuerysetProxy.paginate(page: int)` method
|
* `QuerysetProxy.paginate(page: int)` method
|
||||||
* `QuerysetProxy.limit(limit_count: int)` method
|
* `QuerysetProxy.limit(limit_count: int)` method
|
||||||
* `QuerysetProxy.offset(offset: int)` method
|
* `QuerysetProxy.offset(offset: int)` method
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
To read more about any or all of those functions visit [pagination](./pagination-and-rows-number.md) section.
|
To read more about any or all of those functions visit [pagination](./pagination-and-rows-number.md) section.
|
||||||
|
|
||||||
### [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
|
||||||
To read more about any or all of those functions visit [aggregations](./aggregations.md) section.
|
To read more about any or all of those functions visit [aggregations](./aggregations.md) section.
|
||||||
|
|
||||||
|
|
||||||
[relations]: ../relations/index.md
|
[relations]: ../relations/index.md
|
||||||
|
|||||||
@ -6,35 +6,35 @@ But at the same time it exposes subset of QuerySet API, so you can filter, creat
|
|||||||
|
|
||||||
!!!note
|
!!!note
|
||||||
By default exposed QuerySet is already filtered to return only `Models` related to parent `Model`.
|
By default exposed QuerySet is already filtered to return only `Models` related to parent `Model`.
|
||||||
|
|
||||||
So if you issue `post.categories.all()` you will get all categories related to that post, not all in table.
|
So if you issue `post.categories.all()` you will get all categories related to that post, not all in table.
|
||||||
|
|
||||||
!!!note
|
!!!note
|
||||||
Note that when accessing QuerySet API methods through QuerysetProxy you don't
|
Note that when accessing QuerySet API methods through QuerysetProxy you don't
|
||||||
need to use `objects` attribute like in normal queries.
|
need to use `objects` attribute like in normal queries.
|
||||||
|
|
||||||
So note that it's `post.categories.all()` and **not** `post.categories.objects.all()`.
|
So note that it's `post.categories.all()` and **not** `post.categories.objects.all()`.
|
||||||
|
|
||||||
To learn more about available QuerySet methods visit [queries][queries]
|
To learn more about available QuerySet methods visit [queries][queries]
|
||||||
|
|
||||||
!!!warning
|
!!!warning
|
||||||
Querying related models from ManyToMany cleans list of related models loaded on parent model:
|
Querying related models from ManyToMany cleans list of related models loaded on parent model:
|
||||||
|
|
||||||
Example: `post.categories.first()` will set post.categories to list of 1 related model -> the one returned by first()
|
Example: `post.categories.first()` will set post.categories to list of 1 related model -> the one returned by first()
|
||||||
|
|
||||||
Example 2: if post has 4 categories so `len(post.categories) == 4` calling `post.categories.limit(2).all()`
|
Example 2: if post has 4 categories so `len(post.categories) == 4` calling `post.categories.limit(2).all()`
|
||||||
-> will load only 2 children and now `assert len(post.categories) == 2`
|
-> will load only 2 children and now `assert len(post.categories) == 2`
|
||||||
|
|
||||||
This happens for all QuerysetProxy methods returning data: `get`, `all` and `first` and in `get_or_create` if model already exists.
|
This happens for all QuerysetProxy methods returning data: `get`, `all` and `first` and in `get_or_create` if model already exists.
|
||||||
|
|
||||||
Note that value returned by `create` or created in `get_or_create` and `update_or_create`
|
Note that value returned by `create` or created in `get_or_create` and `update_or_create`
|
||||||
if model does not exist will be added to relation list (not clearing it).
|
if model does not exist will be added to relation list (not clearing it).
|
||||||
|
|
||||||
## Read data from database
|
## Read data from database
|
||||||
|
|
||||||
### get
|
### get
|
||||||
|
|
||||||
`get(**kwargs): -> Model`
|
`get(**kwargs): -> Model`
|
||||||
|
|
||||||
To grab just one of related models filtered by name you can use `get(**kwargs)` method.
|
To grab just one of related models filtered by name you can use `get(**kwargs)` method.
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ Tries to get a row meeting the criteria and if NoMatch exception is raised it cr
|
|||||||
|
|
||||||
`all(**kwargs) -> List[Optional["Model"]]`
|
`all(**kwargs) -> List[Optional["Model"]]`
|
||||||
|
|
||||||
To get a list of related models use `all()` method.
|
To get a list of related models use `all()` method.
|
||||||
|
|
||||||
Note that you can filter the queryset, select related, exclude fields etc. like in normal query.
|
Note that you can filter the queryset, select related, exclude fields etc. like in normal query.
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ assert news_posts[0].author == guido
|
|||||||
|
|
||||||
### create
|
### create
|
||||||
|
|
||||||
`create(**kwargs): -> Model`
|
`create(**kwargs): -> Model`
|
||||||
|
|
||||||
Create related `Model` directly from parent `Model`.
|
Create related `Model` directly from parent `Model`.
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ assert len(await post.categories.all()) == 2
|
|||||||
Read more in queries documentation [create][create]
|
Read more in queries documentation [create][create]
|
||||||
|
|
||||||
For `ManyToMany` relations there is an additional functionality of passing parameters
|
For `ManyToMany` relations there is an additional functionality of passing parameters
|
||||||
that will be used to create a through model if you declared additional fields on explicitly
|
that will be used to create a through model if you declared additional fields on explicitly
|
||||||
provided Through model.
|
provided Through model.
|
||||||
|
|
||||||
Given sample like this:
|
Given sample like this:
|
||||||
@ -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)
|
||||||
|
|
||||||
@ -309,4 +309,4 @@ Returns a bool value to confirm if there are rows matching the given criteria (a
|
|||||||
[exists]: ../queries/aggregations.md#exists
|
[exists]: ../queries/aggregations.md#exists
|
||||||
[fields]: ../queries/select-columns.md#fields
|
[fields]: ../queries/select-columns.md#fields
|
||||||
[exclude_fields]: ../queries/select-columns.md#exclude_fields
|
[exclude_fields]: ../queries/select-columns.md#exclude_fields
|
||||||
[order_by]: ../queries/filter-and-sort.md#order_by
|
[order_by]: ../queries/filter-and-sort.md#order_by
|
||||||
|
|||||||
@ -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