update the dosc with split of queries, fix tests
This commit is contained in:
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
normal queries like `get`, `all` etc.)
|
||||
as well as `select_related` and `prefetch_related` models (with nested notation).
|
||||
!!!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,8 +105,9 @@ 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
|
||||
exclude a field it has to be nullable.
|
||||
!!!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,8 +117,9 @@ 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
|
||||
included.
|
||||
!!!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,11 +134,45 @@ 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
|
||||
you can chain them together
|
||||
!!!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
|
||||
normal queries like `get`, `all` etc.)
|
||||
as well as `select_related` and `prefetch_related` models (with nested notation).
|
||||
!!!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
|
||||
exclude a field it has to be nullable.
|
||||
!!!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
|
||||
excluded.
|
||||
!!!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
|
||||
you can chain them together
|
||||
!!!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
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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")
|
||||
|
||||
Reference in New Issue
Block a user