update the dosc with split of queries, fix tests

This commit is contained in:
collerek
2021-01-29 11:17:43 +01:00
parent 2f8645b1a2
commit 95385425fe
12 changed files with 729 additions and 195 deletions

View File

@ -121,6 +121,6 @@ Prevents insertion of value not present in the choices list.
Used in pydantic only.
[relations]: ../relations/index.md
[queries]: ../queries.md
[queries]: ../queries/index.md
[pydantic]: https://pydantic-docs.helpmanual.io/usage/types/#constrained-types
[server default]: https://docs.sqlalchemy.org/en/13/core/defaults.html#server-invoked-ddl-explicit-default-expressions

View File

@ -423,7 +423,7 @@ You can check if model is saved with `ModelInstance.saved` property
[fields]: ../fields/field-types.md
[relations]: ../relations/index.md
[queries]: ../queries.md
[queries]: ../queries/index.md
[pydantic]: https://pydantic-docs.helpmanual.io/
[sqlalchemy-core]: https://docs.sqlalchemy.org/en/latest/core/
[sqlalchemy-metadata]: https://docs.sqlalchemy.org/en/13/core/metadata.html

View File

@ -118,7 +118,7 @@ But you can specify the `follow=True` parameter to traverse through nested model
[fields]: ../fields.md
[relations]: ../relations/index.md
[queries]: ../queries.md
[queries]: ../queries/index.md
[pydantic]: https://pydantic-docs.helpmanual.io/
[sqlalchemy-core]: https://docs.sqlalchemy.org/en/latest/core/
[sqlalchemy-metadata]: https://docs.sqlalchemy.org/en/13/core/metadata.html

View File

@ -1,16 +1,40 @@
# Aggregation functions
`ormar` currently supports 2 aggregation functions:
Currently 2 aggregation functions are supported.
* `count() -> int`
* `exists() -> bool`
* `QuerysetProxy`
* `QuerysetProxy.count()` method
* `QuerysetProxy.exists()` method
## count
`count() -> int`
Returns number of rows matching the given criteria (i.e. applied with `filter` and `exclude`)
```python
class Book(ormar.Model):
class Meta:
tablename = "books"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
title: str = ormar.String(max_length=200)
author: str = ormar.String(max_length=100)
genre: str = ormar.String(
max_length=100,
default="Fiction",
choices=["Fiction", "Adventure", "Historic", "Fantasy"],
)
```
```python
# returns count of rows in db for Books model
no_of_books = await Book.objects.count()
@ -22,7 +46,49 @@ no_of_books = await Book.objects.count()
Returns a bool value to confirm if there are rows matching the given criteria (applied with `filter` and `exclude`)
```python
class Book(ormar.Model):
class Meta:
tablename = "books"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
title: str = ormar.String(max_length=200)
author: str = ormar.String(max_length=100)
genre: str = ormar.String(
max_length=100,
default="Fiction",
choices=["Fiction", "Adventure", "Historic", "Fantasy"],
)
```
```python
# returns a boolean value if given row exists
has_sample = await Book.objects.filter(title='Sample').exists()
```
## QuerysetProxy methods
When access directly the related `ManyToMany` field as well as `ReverseForeignKey`
returns the list of related models.
But at the same time it exposes a subset of QuerySet API, so you can filter, create,
select related etc related models directly from parent model.
### count
Works exactly the same as [count](./#count) function above but allows you to select columns from related
objects from other side of the relation.
!!!tip
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section
### exists
Works exactly the same as [exists](./#exists) function above but allows you to select columns from related
objects from other side of the relation.
!!!tip
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section

View File

@ -22,13 +22,39 @@ And following methods to sort the data (sql order by clause).
* `QuerysetProxy`
* `QuerysetProxy.order_by(columns:Union[List, str])` method
## filter
## Filtering
### filter
`filter(**kwargs) -> QuerySet`
Allows you to filter by any `Model` attribute/field as well as to fetch instances, with
a filter across an FK relationship.
```python
class Album(ormar.Model):
class Meta:
tablename = "albums"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
is_best_seller: bool = ormar.Boolean(default=False)
class Track(ormar.Model):
class Meta:
tablename = "tracks"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
album: Optional[Album] = ormar.ForeignKey(Album)
name: str = ormar.String(max_length=100)
position: int = ormar.Integer()
play_count: int = ormar.Integer(nullable=True)
```
```python
track = Track.objects.filter(name="The Bird").get()
# will return a track with name equal to 'The Bird'
@ -67,7 +93,7 @@ You can use special filter suffix to change the filter operands:
filters, it's added for you. If you include `%` in your search value it will be escaped
and treated as literal percentage sign inside the text.
## exclude
### exclude
`exclude(**kwargs) -> QuerySet`
@ -82,12 +108,79 @@ conditions.
`exclude(name='John', age>=35)` will become `where not (name='John' and age>=35)`
```python
class Album(ormar.Model):
class Meta:
tablename = "albums"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
is_best_seller: bool = ormar.Boolean(default=False)
class Track(ormar.Model):
class Meta:
tablename = "tracks"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
album: Optional[Album] = ormar.ForeignKey(Album)
name: str = ormar.String(max_length=100)
position: int = ormar.Integer()
play_count: int = ormar.Integer(nullable=True)
```
```python
notes = await Track.objects.exclude(position_gt=3).all()
# returns all tracks with position < 3
```
## QuerysetProxy methods
## get
`get(**kwargs) -> Model`
Get's the first row from the db meeting the criteria set by kwargs.
When any kwargs are passed it's a shortcut equivalent to calling `filter(**kwargs).get()`
!!!tip
To read more about `filter` go to [filter](./#filter).
To read more about `get` go to [read/get](../read/#get)
## get_or_create
`get_or_create(**kwargs) -> Model`
Combination of create and get methods.
When any kwargs are passed it's a shortcut equivalent to calling `filter(**kwargs).get_or_create()`
!!!tip
To read more about `filter` go to [filter](./#filter).
To read more about `get_or_create` go to [read/get_or_create](../read/#get_or_create)
!!!warning
When given item does not exist you need to pass kwargs for all required fields of the
model, including but not limited to primary_key column (unless it's autoincrement).
## all
`all(**kwargs) -> List[Optional["Model"]]`
Returns all rows from a database for given model for set filter options.
When any kwargs are passed it's a shortcut equivalent to calling `filter(**kwargs).all()`
!!!tip
To read more about `filter` go to [filter](./#filter).
To read more about `all` go to [read/all](../read/#all)
### QuerysetProxy methods
When access directly the related `ManyToMany` field as well as `ReverseForeignKey`
returns the list of related models.
@ -95,33 +188,51 @@ returns the list of related models.
But at the same time it exposes subset of QuerySet API, so you can filter, create,
select related etc related models directly from parent model.
### get
#### filter
Works exactly the same as [get](./#get) function above but allows you to fetch related
Works exactly the same as [filter](./#filter) function above but allows you to filter related
objects from other side of the relation.
!!!tip
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section
### get_or_create
#### exclude
Works exactly the same as [exclude](./#exclude) function above but allows you to filter related
objects from other side of the relation.
!!!tip
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section
#### get
Works exactly the same as [get](./#get) function above but allows you to filter related
objects from other side of the relation.
!!!tip
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section
#### get_or_create
Works exactly the same as [get_or_create](./#get_or_create) function above but allows
you to query or create related objects from other side of the relation.
you to filter related objects from other side of the relation.
!!!tip
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section
### all
#### all
Works exactly the same as [all](./#all) function above but allows you to query related
Works exactly the same as [all](./#all) function above but allows you to filter related
objects from other side of the relation.
!!!tip
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section
## Sorting
## order_by
### order_by
`order_by(columns: Union[List, str]) -> QuerySet`
@ -206,4 +317,21 @@ assert owner.toys[1].name == "Toy 1"
Something like `Track.object.select_related("album").filter(album__name="Malibu").offset(1).limit(1).all()`
### QuerysetProxy methods
When access directly the related `ManyToMany` field as well as `ReverseForeignKey`
returns the list of related models.
But at the same time it exposes subset of QuerySet API, so you can filter, create,
select related etc related models directly from parent model.
#### order_by
Works exactly the same as [order_by](./#order_by) function above but allows you to sort related
objects from other side of the relation.
!!!tip
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section
[querysetproxy]: ../relations/queryset-proxy.md

View File

@ -1,18 +1,38 @@
#Pagination and rows number
Following methods allow you to paginate and limit number of rows in queries.
* `paginate(page: int) -> QuerySet`
* `limit(limit_count: int) -> QuerySet`
* `offset(offset: int) -> QuerySet`
* `get(**kwargs): -> Model`
* `first(): -> Model`
* `get() -> Model`
* `first() -> Model`
* `QuerysetProxy`
* `QuerysetProxy.paginate(page: int)` method
* `QuerysetProxy.limit(limit_count: int)` method
* `QuerysetProxy.offset(offset: int)` method
## paginate
`paginate(page: int, page_size: int = 20) -> QuerySet`
Combines the `offset` and `limit` methods based on page number and size
```python
class Track(ormar.Model):
class Meta:
tablename = "track"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
album: Optional[Album] = ormar.ForeignKey(Album)
name: str = ormar.String(max_length=100)
position: int = ormar.Integer()
```
```python
tracks = await Track.objects.paginate(3).all()
# will return 20 tracks starting at row 41
@ -30,6 +50,19 @@ You can limit the results to desired number of parent models.
To limit the actual number of database query rows instead of number of main models
use the `limit_raw_sql` parameter flag, and set it to `True`.
```python
class Track(ormar.Model):
class Meta:
tablename = "track"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
album: Optional[Album] = ormar.ForeignKey(Album)
name: str = ormar.String(max_length=100)
position: int = ormar.Integer()
```
```python
tracks = await Track.objects.limit(1).all()
# will return just one Track
@ -51,6 +84,19 @@ You can also offset the results by desired number of main models.
To offset the actual number of database query rows instead of number of main models
use the `limit_raw_sql` parameter flag, and set it to `True`.
```python
class Track(ormar.Model):
class Meta:
tablename = "track"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
album: Optional[Album] = ormar.ForeignKey(Album)
name: str = ormar.String(max_length=100)
position: int = ormar.Integer()
```
```python
tracks = await Track.objects.offset(1).limit(1).all()
# will return just one Track, but this time the second one
@ -67,28 +113,57 @@ tracks = await Track.objects.offset(1).limit(1).all()
## get
`get(**kwargs): -> Model`
`get(**kwargs) -> Model`
Get's the first row from the db meeting the criteria set by kwargs.
If no criteria set it will return the last row in db sorted by pk.
If no criteria is set it will return the last row in db sorted by pk.
(The criteria cannot be set also with filter/exclude).
Passing a criteria is actually calling filter(**kwargs) method described below.
!!!tip
To read more about `get` visit [read/get](./read/#get)
```python
track = await Track.objects.get(name='The Bird')
# note that above is equivalent to await Track.objects.filter(name='The Bird').get()
track2 = track = await Track.objects.get()
track == track2 # True since it's the only row in db in our example
```
!!!warning
If no row meets the criteria `NoMatch` exception is raised.
If there are multiple rows meeting the criteria the `MultipleMatches` exception is raised.
## first
`first(): -> Model`
`first() -> Model`
Gets the first row from the db ordered by primary key column ascending.
!!!tip
To read more about `first` visit [read/first](./read/#first)
## QuerysetProxy methods
When access directly the related `ManyToMany` field as well as `ReverseForeignKey`
returns the list of related models.
But at the same time it exposes subset of QuerySet API, so you can filter, create,
select related etc related models directly from parent model.
### paginate
Works exactly the same as [paginate](./#paginate) function above but allows you to paginate related
objects from other side of the relation.
!!!tip
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section
### limit
Works exactly the same as [limit](./#limit) function above but allows you to paginate related
objects from other side of the relation.
!!!tip
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section
### offset
Works exactly the same as [offset](./#offset) function above but allows you to paginate related
objects from other side of the relation.
!!!tip
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section
[querysetproxy]: ../relations/queryset-proxy.md

View File

@ -1,22 +1,75 @@
# Selecting subset of columns
To select only chosen columns of your model you can use following functions.
* `fields(columns: Union[List, str, set, dict]) -> QuerySet`
* `exclude_fields(columns: Union[List, str, set, dict]) -> QuerySet`
* `QuerysetProxy`
* `QuerysetProxy.fields(columns: Union[List, str, set, dict])` method
* `QuerysetProxy.exclude_fields(columns: Union[List, str, set, dict])` method
## fields
`fields(columns: Union[List, str, set, dict]) -> QuerySet`
With `fields()` you can select subset of model columns to limit the data load.
!!!note Note that `fields()` and `exclude_fields()` works both for main models (on
!!!note
Note that `fields()` and `exclude_fields()` works both for main models (on
normal queries like `get`, `all` etc.)
as well as `select_related` and `prefetch_related` models (with nested notation).
Given a sample data like following:
```python
--8 < -- "../docs_src/queries/docs006.py"
import databases
import sqlalchemy
import ormar
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
class Company(ormar.Model):
class Meta:
tablename = "companies"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
founded: int = ormar.Integer(nullable=True)
class Car(ormar.Model):
class Meta:
tablename = "cars"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
manufacturer = ormar.ForeignKey(Company)
name: str = ormar.String(max_length=100)
year: int = ormar.Integer(nullable=True)
gearbox_type: str = ormar.String(max_length=20, nullable=True)
gears: int = ormar.Integer(nullable=True)
aircon_type: str = ormar.String(max_length=20, nullable=True)
# build some sample data
toyota = await Company.objects.create(name="Toyota", founded=1937)
await Car.objects.create(manufacturer=toyota, name="Corolla", year=2020, gearbox_type='Manual', gears=5,
aircon_type='Manual')
await Car.objects.create(manufacturer=toyota, name="Yaris", year=2019, gearbox_type='Manual', gears=5,
aircon_type='Manual')
await Car.objects.create(manufacturer=toyota, name="Supreme", year=2020, gearbox_type='Auto', gears=6,
aircon_type='Auto')
```
You can select specified fields by passing a `str, List[str], Set[str] or dict` with
@ -52,7 +105,8 @@ assert all_cars[0].manufacturer.name == 'Toyota'
assert all_cars[0].manufacturer.founded == 1937
```
!!!warning Mandatory fields cannot be excluded as it will raise `ValidationError`, to
!!!warning
Mandatory fields cannot be excluded as it will raise `ValidationError`, to
exclude a field it has to be nullable.
You cannot exclude mandatory model columns - `manufacturer__name` in this example.
@ -63,7 +117,8 @@ await Car.objects.select_related('manufacturer').fields(
# will raise pydantic ValidationError as company.name is required
```
!!!tip Pk column cannot be excluded - it's always auto added even if not explicitly
!!!tip
Pk column cannot be excluded - it's always auto added even if not explicitly
included.
You can also pass fields to include as dictionary or set.
@ -79,10 +134,44 @@ To include whole nested model specify model related field name and ellipsis.
Below you can see examples that are equivalent:
```python
--8 < -- "../docs_src/queries/docs009.py"
# 1. like in example above
await Car.objects.select_related('manufacturer').fields(['id', 'name', 'manufacturer__name']).all()
# 2. to mark a field as required use ellipsis
await Car.objects.select_related('manufacturer').fields({'id': ...,
'name': ...,
'manufacturer': {
'name': ...}
}).all()
# 3. to include whole nested model use ellipsis
await Car.objects.select_related('manufacturer').fields({'id': ...,
'name': ...,
'manufacturer': ...
}).all()
# 4. to specify fields at last nesting level you can also use set - equivalent to 2. above
await Car.objects.select_related('manufacturer').fields({'id': ...,
'name': ...,
'manufacturer': {'name'}
}).all()
# 5. of course set can have multiple fields
await Car.objects.select_related('manufacturer').fields({'id': ...,
'name': ...,
'manufacturer': {'name', 'founded'}
}).all()
# 6. you can include all nested fields but it will be equivalent of 3. above which is shorter
await Car.objects.select_related('manufacturer').fields({'id': ...,
'name': ...,
'manufacturer': {'id', 'name', 'founded'}
}).all()
```
!!!note All methods that do not return the rows explicitly returns a QueySet instance so
!!!note
All methods that do not return the rows explicitly returns a QueySet instance so
you can chain them together
So operations like `filter()`, `select_related()`, `limit()` and `offset()` etc. can be chained.
@ -102,25 +191,125 @@ are available.
Especially check above how you can pass also nested dictionaries and sets as a mask to
exclude fields from whole hierarchy.
!!!note Note that `fields()` and `exclude_fields()` works both for main models (on
!!!note
Note that `fields()` and `exclude_fields()` works both for main models (on
normal queries like `get`, `all` etc.)
as well as `select_related` and `prefetch_related` models (with nested notation).
Below you can find few simple examples:
```python hl_lines="47 48 60 61 67"
--8<-- "../docs_src/queries/docs008.py"
import databases
import sqlalchemy
import ormar
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
class Company(ormar.Model):
class Meta:
tablename = "companies"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
founded: int = ormar.Integer(nullable=True)
class Car(ormar.Model):
class Meta:
tablename = "cars"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
manufacturer = ormar.ForeignKey(Company)
name: str = ormar.String(max_length=100)
year: int = ormar.Integer(nullable=True)
gearbox_type: str = ormar.String(max_length=20, nullable=True)
gears: int = ormar.Integer(nullable=True)
aircon_type: str = ormar.String(max_length=20, nullable=True)
# build some sample data
toyota = await Company.objects.create(name="Toyota", founded=1937)
await Car.objects.create(manufacturer=toyota, name="Corolla", year=2020, gearbox_type='Manual', gears=5,
aircon_type='Manual')
await Car.objects.create(manufacturer=toyota, name="Yaris", year=2019, gearbox_type='Manual', gears=5,
aircon_type='Manual')
await Car.objects.create(manufacturer=toyota, name="Supreme", year=2020, gearbox_type='Auto', gears=6,
aircon_type='Auto')
# select manufacturer but only name - to include related models use notation {model_name}__{column}
all_cars = await Car.objects.select_related('manufacturer').exclude_fields(
['year', 'gearbox_type', 'gears', 'aircon_type', 'company__founded']).all()
for car in all_cars:
# excluded columns will yield None
assert all(getattr(car, x) is None for x in ['year', 'gearbox_type', 'gears', 'aircon_type'])
# included column on related models will be available, pk column is always included
# even if you do not include it in fields list
assert car.manufacturer.name == 'Toyota'
# also in the nested related models - you cannot exclude pk - it's always auto added
assert car.manufacturer.founded is None
# fields() can be called several times, building up the columns to select
# models selected in select_related but with no columns in fields list implies all fields
all_cars = await Car.objects.select_related('manufacturer').exclude_fields('year').exclude_fields(
['gear', 'gearbox_type']).all()
# all fiels from company model are selected
assert all_cars[0].manufacturer.name == 'Toyota'
assert all_cars[0].manufacturer.founded == 1937
# cannot exclude mandatory model columns - company__name in this example - note usage of dict/set this time
await Car.objects.select_related('manufacturer').exclude_fields([{'company': {'name'}}]).all()
# will raise pydantic ValidationError as company.name is required
```
!!!warning Mandatory fields cannot be excluded as it will raise `ValidationError`, to
!!!warning
Mandatory fields cannot be excluded as it will raise `ValidationError`, to
exclude a field it has to be nullable.
!!!tip Pk column cannot be excluded - it's always auto added even if explicitly
!!!tip
Pk column cannot be excluded - it's always auto added even if explicitly
excluded.
!!!note All methods that do not return the rows explicitly returns a QueySet instance so
!!!note
All methods that do not return the rows explicitly returns a QueySet instance so
you can chain them together
So operations like `filter()`, `select_related()`, `limit()` and `offset()` etc. can be chained.
Something like `Track.object.select_related("album").filter(album__name="Malibu").offset(1).limit(1).all()`
## QuerysetProxy methods
When access directly the related `ManyToMany` field as well as `ReverseForeignKey`
returns the list of related models.
But at the same time it exposes subset of QuerySet API, so you can filter, create,
select related etc related models directly from parent model.
### fields
Works exactly the same as [fields](./#fields) function above but allows you to select columns from related
objects from other side of the relation.
!!!tip
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section
### exclude_fields
Works exactly the same as [exclude_fields](./#exclude_fields) function above but allows you to select columns from related
objects from other side of the relation.
!!!tip
To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section
[querysetproxy]: ../relations/queryset-proxy.md

View File

@ -30,7 +30,9 @@ But at the same time it exposes subset of QuerySet API, so you can filter, creat
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).
## get
## Read data from database
### get
`get(**kwargs): -> Model`
@ -52,7 +54,16 @@ assert post.categories[0] == news
!!!tip
Read more in queries documentation [get][get]
## all
### get_or_create
`get_or_create(**kwargs) -> Model`
Tries to get a row meeting the criteria and if NoMatch exception is raised it creates a new one with given kwargs.
!!!tip
Read more in queries documentation [get_or_create][get_or_create]
### all
`all(**kwargs) -> List[Optional["Model"]]`
@ -73,7 +84,9 @@ assert news_posts[0].author == guido
!!!tip
Read more in queries documentation [all][all]
## create
## Insert/ update data into database
### create
`create(**kwargs): -> Model`
@ -91,113 +104,162 @@ assert len(await post.categories.all()) == 2
!!!tip
Read more in queries documentation [create][create]
## get_or_create
### get_or_create
`get_or_create(**kwargs) -> Model`
Tries to get a row meeting the criteria and if NoMatch exception is raised it creates a new one with given kwargs.
!!!tip
Read more in queries documentation [get_or_create][get_or_create]
## update_or_create
### update_or_create
`update_or_create(**kwargs) -> Model`
Updates the model, or in case there is no match in database creates a new one.
!!!tip
Read more in queries documentation [update_or_create][update_or_create]
## filter
## Filtering and sorting
### filter
`filter(**kwargs) -> QuerySet`
Allows you to filter by any Model attribute/field as well as to fetch instances, with a filter across an FK relationship.
!!!tip
Read more in queries documentation [filter][filter]
## exclude
### exclude
`exclude(**kwargs) -> QuerySet`
Works exactly the same as filter and all modifiers (suffixes) are the same, but returns a not condition.
!!!tip
Read more in queries documentation [exclude][exclude]
## select_related
`select_related(related: Union[List, str]) -> QuerySet`
!!!tip
Read more in queries documentation [select_related][select_related]
## prefetch_related
`prefetch_related(related: Union[List, str]) -> QuerySet`
!!!tip
Read more in queries documentation [prefetch_related][prefetch_related]
## limit
`limit(limit_count: int) -> QuerySet`
!!!tip
Read more in queries documentation [limit][limit]
## offset
`offset(offset: int) -> QuerySet`
!!!tip
Read more in queries documentation [offset][offset]
## count
`count() -> int`
!!!tip
Read more in queries documentation [count][count]
## exists
`exists() -> bool`
!!!tip
Read more in queries documentation [exists][exists]
## fields
`fields(columns: Union[List, str, set, dict]) -> QuerySet`
!!!tip
Read more in queries documentation [fields][fields]
## exclude_fields
`exclude_fields(columns: Union[List, str, set, dict]) -> QuerySet`
!!!tip
Read more in queries documentation [exclude_fields][exclude_fields]
## order_by
### order_by
`order_by(columns:Union[List, str]) -> QuerySet`
With order_by() you can order the results from database based on your choice of fields.
!!!tip
Read more in queries documentation [order_by][order_by]
## Joins and subqueries
[queries]: ../queries.md
[get]: ../queries.md#get
[all]: ../queries.md#all
[create]: ../queries.md#create
[get_or_create]: ../queries.md#get_or_create
[update_or_create]: ../queries.md#update_or_create
[filter]: ../queries.md#filter
[exclude]: ../queries.md#exclude
[select_related]: ../queries.md#select_related
[prefetch_related]: ../queries.md#prefetch_related
[limit]: ../queries.md#limit
[offset]: ../queries.md#offset
[count]: ../queries.md#count
[exists]: ../queries.md#exists
[fields]: ../queries.md#fields
[exclude_fields]: ../queries.md#exclude_fields
[order_by]: ../queries.md#order_by
### select_related
`select_related(related: Union[List, str]) -> QuerySet`
Allows to prefetch related models during the same query.
With select_related always only one query is run against the database, meaning that one (sometimes complicated) join is generated and later nested models are processed in python.
!!!tip
Read more in queries documentation [select_related][select_related]
### prefetch_related
`prefetch_related(related: Union[List, str]) -> QuerySet`
Allows to prefetch related models during query - but opposite to select_related each subsequent model is fetched in a separate database query.
With prefetch_related always one query per Model is run against the database, meaning that you will have multiple queries executed one after another.
!!!tip
Read more in queries documentation [prefetch_related][prefetch_related]
## Pagination and rows number
### paginate
`paginate(page: int, page_size: int = 20) -> QuerySet`
Combines the offset and limit methods based on page number and size.
!!!tip
Read more in queries documentation [paginate][paginate]
### limit
`limit(limit_count: int) -> QuerySet`
You can limit the results to desired number of parent models.
!!!tip
Read more in queries documentation [limit][limit]
### offset
`offset(offset: int) -> QuerySet`
You can offset the results by desired number of main models.
!!!tip
Read more in queries documentation [offset][offset]
## Selecting subset of columns
### fields
`fields(columns: Union[List, str, set, dict]) -> QuerySet`
With fields() you can select subset of model columns to limit the data load.
!!!tip
Read more in queries documentation [fields][fields]
### exclude_fields
`exclude_fields(columns: Union[List, str, set, dict]) -> QuerySet`
With exclude_fields() you can select subset of model columns that will be excluded to limit the data load.
!!!tip
Read more in queries documentation [exclude_fields][exclude_fields]
## Aggregated functions
### count
`count() -> int`
Returns number of rows matching the given criteria (i.e. applied with filter and exclude)
!!!tip
Read more in queries documentation [count][count]
### exists
`exists() -> bool`
Returns a bool value to confirm if there are rows matching the given criteria (applied with filter and exclude)
!!!tip
Read more in queries documentation [exists][exists]
[queries]: ../queries/index.md
[get]: ../queries/read.md#get
[all]: ../queries/read.md#all
[create]: ../queries/create.md#create
[get_or_create]: ../queries/read.md#get_or_create
[update_or_create]: ../queries/update.md#update_or_create
[filter]: ../queries/filter-and-sort.md#filter
[exclude]: ../queries/filter-and-sort.md#exclude
[select_related]: ../queries/joins-and-subqueries.md#select_related
[prefetch_related]: ../queries/joins-and-subqueries.md#prefetch_related
[limit]: ../queries/pagination-and-rows-number.md#limit
[offset]: ../queries/pagination-and-rows-number.md#offset
[paginate]: ../queries/pagination-and-rows-number.md#paginate
[count]: ../queries/aggregations.md#count
[exists]: ../queries/aggregations.md#exists
[fields]: ../queries/select-columns.md#fields
[exclude_fields]: ../queries/select-columns.md#exclude_fields
[order_by]: ../queries/filter-and-sort.md#order_by

View File

@ -84,9 +84,9 @@ nav:
- Exceptions: api/exceptions.md
repo_name: collerek/ormar
repo_url: https://github.com/collerek/ormar
#google_analytics:
# - UA-72514911-3
# - auto
google_analytics:
- UA-72514911-3
- auto
theme:
name: material
highlightjs: true

View File

@ -1,3 +1,4 @@
import sys
import uuid
from dataclasses import dataclass
from typing import Any, List, Optional, TYPE_CHECKING, Tuple, Type, Union
@ -14,6 +15,11 @@ if TYPE_CHECKING: # pragma no cover
from ormar.models import Model, NewBaseModel
from ormar.fields import ManyToManyField
if sys.version_info < (3, 7):
ToType = Type["Model"]
else:
ToType = Union[Type["Model"], "ForwardRef"]
def create_dummy_instance(fk: Type["Model"], pk: Any = None) -> "Model":
"""
@ -124,7 +130,7 @@ class ForeignKeyConstraint:
def ForeignKey( # noqa CFQ002
to: Union[Type["Model"], "ForwardRef"],
to: "ToType",
*,
name: str = None,
unique: bool = False,

View File

@ -1,3 +1,4 @@
import sys
from typing import Any, List, Optional, TYPE_CHECKING, Tuple, Type, Union
from pydantic.typing import ForwardRef, evaluate_forwardref
@ -8,6 +9,11 @@ from ormar.fields.foreign_key import ForeignKeyField
if TYPE_CHECKING: # pragma no cover
from ormar.models import Model
if sys.version_info < (3, 7):
ToType = Type["Model"]
else:
ToType = Union[Type["Model"], "ForwardRef"]
REF_PREFIX = "#/components/schemas/"
@ -36,8 +42,8 @@ def populate_m2m_params_based_on_to_model(
def ManyToMany(
to: Union[Type["Model"], ForwardRef],
through: Union[Type["Model"], ForwardRef],
to: "ToType",
through: "ToType",
*,
name: str = None,
unique: bool = False,

View File

@ -60,6 +60,8 @@ def create_test_database():
@pytest.mark.asyncio
async def test_double_relations():
async with db:
async with db.transaction(force_rollback=True):
t1 = await Teacher.objects.create(name="Mr. Jones")
t2 = await Teacher.objects.create(name="Ms. Smith")
t3 = await Teacher.objects.create(name="Mr. Quibble")