From 2f8645b1a274aab65028a5609c75d8ef46425610 Mon Sep 17 00:00:00 2001 From: collerek Date: Thu, 28 Jan 2021 17:48:32 +0100 Subject: [PATCH] work on docs in queries --- docs/models/methods.md | 2 +- docs/queries.md | 0 docs/queries/create.md | 130 ++++++++++++++++-- docs/queries/delete.md | 134 +++++++++++++++++- docs/queries/filter-and-sort.md | 94 ++++++++++--- docs/queries/index.md | 39 +++--- docs/queries/joins-and-subqueries.md | 195 ++++++++++++++++++++++++++- docs/queries/read.md | 178 +++++++++++++++++++++--- docs/queries/update.md | 79 +++++++++-- mkdocs.yml | 6 +- 10 files changed, 766 insertions(+), 91 deletions(-) delete mode 100644 docs/queries.md diff --git a/docs/models/methods.md b/docs/models/methods.md index 31a0b21..a16369e 100644 --- a/docs/models/methods.md +++ b/docs/models/methods.md @@ -65,7 +65,7 @@ await track.update(name='The Bird Strikes Again') `upsert(**kwargs) -> self` -It's an proxy to either `save()` or `update(**kwargs)` methods described above. +It's a proxy to either `save()` or `update(**kwargs)` methods described above. If the primary key is set -> the `update` method will be called. diff --git a/docs/queries.md b/docs/queries.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/queries/create.md b/docs/queries/create.md index ca85137..560ccae 100644 --- a/docs/queries/create.md +++ b/docs/queries/create.md @@ -1,11 +1,23 @@ -# Create / Insert data into database +# Insert data into database -* `create(**kwargs): -> Model` +Following methods allow you to insert data into the database. + +* `create(**kwargs) -> Model` * `get_or_create(**kwargs) -> Model` * `update_or_create(**kwargs) -> Model` * `bulk_create(objects: List[Model]) -> None` -* `Model.save()` method -* `Model.upsert()` method + + +* `Model` + * `Model.save()` method + * `Model.upsert()` method + * `Model.save_related()` method + + +* `QuerysetProxy` + * `QuerysetProxy.create(**kwargs)` method + * `QuerysetProxy.get_or_create(**kwargs)` method + * `QuerysetProxy.update_or_create(**kwargs)` method ## create @@ -16,6 +28,17 @@ Creates the model instance, saves it in a database and returns the updates model The allowed kwargs are `Model` fields names and proper value types. +```python +class Album(ormar.Model): + class Meta: + tablename = "album" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) +``` + ```python malibu = await Album.objects.create(name="Malibu") await Track.objects.create(album=malibu, title="The Bird", position=1) @@ -28,7 +51,8 @@ malibu = Album(name="Malibu") await malibu.save() ``` -!!!tip Check other `Model` methods in [models][models] +!!!tip + Check other `Model` methods in [models][models] ## get_or_create @@ -39,6 +63,17 @@ Combination of create and get methods. Tries to get a row meeting the criteria and if `NoMatch` exception is raised it creates a new one with given kwargs. +```python +class Album(ormar.Model): + class Meta: + tablename = "album" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) +``` + ```python album = await Album.objects.get_or_create(name='The Cat') # object is created as it does not exist @@ -47,13 +82,15 @@ assert album == album2 # return True as the same db row is returned ``` -!!!warning Despite being a equivalent row from database the `album` and `album2` in -example above are 2 different python objects! -Updating one of them will not refresh the second one until you excplicitly load() the -fresh data from db. +!!!warning + Despite being a equivalent row from database the `album` and `album2` in + example above are 2 different python objects! + Updating one of them will not refresh the second one until you excplicitly load() the + fresh data from db. -!!!note Note that if you want to create a new object you either have to pass pk column -value or pk column has to be set as autoincrement +!!!note + Note that if you want to create a new object you either have to pass pk column + value or pk column has to be set as autoincrement ## update_or_create @@ -65,8 +102,9 @@ Updates the model, or in case there is no match in database creates a new one. --8<-- "../docs_src/queries/docs003.py" ``` -!!!note Note that if you want to create a new object you either have to pass pk column -value or pk column has to be set as autoincrement +!!!note + Note that if you want to create a new object you either have to pass pk column + value or pk column has to be set as autoincrement ## bulk_create @@ -80,4 +118,68 @@ A valid list of `Model` objects needs to be passed. --8<-- "../docs_src/queries/docs004.py" ``` -## Model method \ No newline at end of file +## Model methods + +Each model instance have a set of methods to `save`, `update` or `load` itself. + +###save + +You can create new models by using `QuerySet.create()` method or by initializing your model as a normal pydantic model +and later calling `save()` method. + +!!!tip + Read more about `save()` method in [models-save][models-save] + +###upsert + +It's a proxy to either `save()` or `update(**kwargs)` methods of a Model. +If the pk is not set the `save()` method will be called. + +!!!tip + Read more about `upsert()` method in [models-upsert][models-upsert] + +###save_related + +Method goes through all relations of the `Model` on which the method is called, +and calls `upsert()` method on each model that is **not** saved. + +!!!tip + Read more about `save_related()` method in [models-save-related][models-save-related] + +## 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. + +### create + +Works exactly the same as [create](./#create) function above but allows you to create 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. + +!!!tip + To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section + + +### update_or_create + +Works exactly the same as [update_or_create](./#update_or_create) function above but allows you to update or create related objects +from other side of the relation. + +!!!tip + To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section + +[models]: ../models/methods.md +[models-save]: ../models/methods.md#save +[models-upsert]: ../models/methods.md#upsert +[models-save-related]: ../models/methods.md#save_related +[querysetproxy]: ../relations/queryset-proxy.md \ No newline at end of file diff --git a/docs/queries/delete.md b/docs/queries/delete.md index 8d122a9..aec5171 100644 --- a/docs/queries/delete.md +++ b/docs/queries/delete.md @@ -1,7 +1,17 @@ -# Delete/ remove data from database +# Delete data from database + +Following methods allow you to delete data from the database. * `delete(each: bool = False, **kwargs) -> int` -* `Model.delete()` method + + +* `Model` + * `Model.delete()` method + + +* `QuerysetProxy` + * `QuerysetProxy.remove()` method + * `QuerysetProxy.clear()` method ## delete @@ -20,4 +30,122 @@ Return number of rows deleted. --8<-- "../docs_src/queries/docs005.py" ``` -## Model method \ No newline at end of file +## Model methods + +Each model instance have a set of methods to `save`, `update` or `load` itself. + +### delete + +You can delete model instance by calling `delete()` method on it. + +!!!tip + Read more about `delete()` method in [models methods](../models/methods.md#delete) + +## 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. + +### remove + +Removal of the related model one by one. + +Removes the relation in the database. + +If you specify the keep_reversed flag to `False` `ormar` will also delete the related model from the database. + +```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) + title: str = ormar.String(max_length=100) + position: int = ormar.Integer() + play_count: int = ormar.Integer(nullable=True) +``` + +```python +album = await Album(name="Malibu").save() +track1 = await Track( + album=album, title="The Bird", position=1, play_count=30, +).save() +# remove through proxy from reverse side of relation +await album.tracks.remove(track1, keep_reversed=False) + +# the track was also deleted +tracks = await Track.objects.all() +assert len(tracks) == 0 +``` + +### clear + +Removal of all related models in one call. + +Removes also the relation in the database. + +If you specify the keep_reversed flag to `False` `ormar` will also delete the related model from the database. + +```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) + title: str = ormar.String(max_length=100) + position: int = ormar.Integer() + play_count: int = ormar.Integer(nullable=True) +``` + +```python +album = await Album(name="Malibu").save() +track1 = await Track( + album=album, + title="The Bird", + position=1, + play_count=30, +).save() +track2 = await Track( + album=album, + title="Heart don't stand a chance", + position=2, + play_count=20, +).save() + +# removes the relation only -> clears foreign keys on tracks +await album.tracks.clear() + +# removes also the tracks +await album.tracks.clear(keep_reversed=False) +``` + +[querysetproxy]: ../relations/queryset-proxy.md \ No newline at end of file diff --git a/docs/queries/filter-and-sort.md b/docs/queries/filter-and-sort.md index 2b93f9d..14b48db 100644 --- a/docs/queries/filter-and-sort.md +++ b/docs/queries/filter-and-sort.md @@ -1,8 +1,26 @@ # Filtering and sorting data -* `filter(**kwargs) -> QuerySet` -* `exclude(**kwargs) -> QuerySet` -* `order_by(columns:Union[List, str]) -> QuerySet` +You can use following methods to filter the data (sql where clause). + +* `filter(**kwargs) -> QuerySet` +* `exclude(**kwargs) -> QuerySet` +* `get(**kwargs) -> Model` +* `get_or_create(**kwargs) -> Model` +* `all(**kwargs) -> List[Optional[Model]]` + + +* `QuerysetProxy` + * `QuerysetProxy.filter(**kwargs)` method + * `QuerysetProxy.exclude(**kwargs)` method + * `QuerysetProxy.get(**kwargs)` method + * `QuerysetProxy.get_or_create(**kwargs)` method + * `QuerysetProxy.all(**kwargs)` method + +And following methods to sort the data (sql order by clause). + +* `order_by(columns:Union[List, str]) -> QuerySet` +* `QuerysetProxy` + * `QuerysetProxy.order_by(columns:Union[List, str])` method ## filter @@ -36,18 +54,20 @@ You can use special filter suffix to change the filter operands: * endswith - like `album__name__endswith='ibu'` (exact end match) * iendswith - like `album__name__iendswith='IBU'` (exact end match case insensitive) -!!!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()` -!!!warning Note that you do not have to specify the `%` wildcard in contains and other -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. +!!!warning + Note that you do not have to specify the `%` wildcard in contains and other + 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` @@ -67,7 +87,41 @@ notes = await Track.objects.exclude(position_gt=3).all() # returns all tracks with position < 3 ``` -### order_by +## 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. + +### get + +Works exactly the same as [get](./#get) function above but allows you to fetch 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. + +!!!tip + To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section + +### all + +Works exactly the same as [all](./#all) function above but allows you to query related +objects from other side of the relation. + +!!!tip + To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section + + + +## order_by `order_by(columns: Union[List, str]) -> QuerySet` @@ -78,12 +132,14 @@ You can provide a string with field name or list of strings with different field Ordering in sql will be applied in order of names you provide in order_by. -!!!tip By default if you do not provide ordering `ormar` explicitly orders by all -primary keys +!!!tip + By default if you do not provide ordering `ormar` explicitly orders by all + primary keys -!!!warning If you are sorting by nested models that causes that the result rows are -unsorted by the main model -`ormar` will combine those children rows into one main model. +!!!warning + If you are sorting by nested models that causes that the result rows are + unsorted by the main model + `ormar` will combine those children rows into one main model. Sample raw database rows result (sort by child model desc): ``` @@ -103,7 +159,7 @@ unsorted by the main model Given sample Models like following: ```python ---8 < -- "../docs_src/queries/docs007.py" +--8 < -- "../../docs_src/queries/docs007.py" ``` To order by main model field just provide a field name @@ -142,10 +198,12 @@ assert owner.toys[0].name == "Toy 4" assert owner.toys[1].name == "Toy 1" ``` -!!!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]: ../relations/queryset-proxy.md \ No newline at end of file diff --git a/docs/queries/index.md b/docs/queries/index.md index fa00eec..eb112c5 100644 --- a/docs/queries/index.md +++ b/docs/queries/index.md @@ -8,8 +8,9 @@ and it's options. Most of the methods are also available through many to many relations and on reverse foreign key relations through `QuerysetProxy` interface. -!!!info To see which one are supported and how to construct relations -visit [relations][relations]. +!!!info + To see which relations are supported and how to construct relations + visit [relations][relations]. For simplicity available methods to fetch and save the data into the database are divided into categories according to the function they fulfill. @@ -20,7 +21,7 @@ For complicity also Models and relations methods are listed. To read more about any specific section or function please refer to the details subpage. -### Create +###[Insert data into database](./create.md) * `create(**kwargs) -> Model` * `get_or_create(**kwargs) -> Model` @@ -29,17 +30,17 @@ To read more about any specific section or function please refer to the details * `Model` - * `Model.save()` method - * `Model.upsert()` method - * `Model.save_related()` method - + * `Model.save()` method + * `Model.upsert()` method + * `Model.save_related()` method + * `QuerysetProxy` - * `QuerysetProxy.create(**kwargs)` method - * `QuerysetProxy.get_or_create(**kwargs)` method - * `QuerysetProxy.update_or_create(**kwargs)` method + * `QuerysetProxy.create(**kwargs)` method + * `QuerysetProxy.get_or_create(**kwargs)` method + * `QuerysetProxy.update_or_create(**kwargs)` method -### Read +### [Read data from database](./read.md) * `get(**kwargs) -> Model` * `get_or_create(**kwargs) -> Model` @@ -57,7 +58,7 @@ To read more about any specific section or function please refer to the details * `QuerysetProxy.first()` method * `QuerysetProxy.all(**kwargs)` method -### Update +### [Update data in database](./update.md) * `update(each: bool = False, **kwargs) -> int` * `update_or_create(**kwargs) -> Model` @@ -73,7 +74,7 @@ To read more about any specific section or function please refer to the details * `QuerysetProxy` * `QuerysetProxy.update_or_create(**kwargs)` method -### Delete +### [Delete data from database](./delete.md) * `delete(each: bool = False, **kwargs) -> int` @@ -86,7 +87,7 @@ To read more about any specific section or function please refer to the details * `QuerysetProxy.remove()` method * `QuerysetProxy.clear()` method -### Joins and subqueries +### [Joins and subqueries](./joins-and-subqueries.md) * `select_related(related: Union[List, str]) -> QuerySet` * `prefetch_related(related: Union[List, str]) -> QuerySet` @@ -100,7 +101,7 @@ To read more about any specific section or function please refer to the details * `QuerysetProxy.select_related(related: Union[List, str])` method * `QuerysetProxy.prefetch_related(related: Union[List, str])` method -### Filtering and sorting +### [Filtering and sorting](./filter-and-sort.md) * `filter(**kwargs) -> QuerySet` * `exclude(**kwargs) -> QuerySet` @@ -118,7 +119,7 @@ To read more about any specific section or function please refer to the details * `QuerysetProxy.get_or_create(**kwargs)` method * `QuerysetProxy.all(**kwargs)` method -### Selecting columns +### [Selecting columns](./select-columns.md) * `fields(columns: Union[List, str, set, dict]) -> QuerySet` * `exclude_fields(columns: Union[List, str, set, dict]) -> QuerySet` @@ -128,7 +129,7 @@ To read more about any specific section or function please refer to the details * `QuerysetProxy.fields(columns: Union[List, str, set, dict])` method * `QuerysetProxy.exclude_fields(columns: Union[List, str, set, dict])` method -### Pagination and rows number +### [Pagination and rows number](./pagination-and-rows-number.md) * `paginate(page: int) -> QuerySet` * `limit(limit_count: int) -> QuerySet` @@ -142,7 +143,7 @@ To read more about any specific section or function please refer to the details * `QuerysetProxy.limit(limit_count: int)` method * `QuerysetProxy.offset(offset: int)` method -### Aggregated functions +### [Aggregated functions](./aggregations.md) * `count() -> int` * `exists() -> bool` @@ -153,4 +154,4 @@ To read more about any specific section or function please refer to the details * `QuerysetProxy.exists()` method -[relations]: ./relations/index.md \ No newline at end of file +[relations]: ../relations/index.md \ No newline at end of file diff --git a/docs/queries/joins-and-subqueries.md b/docs/queries/joins-and-subqueries.md index a2635a7..a1b9f87 100644 --- a/docs/queries/joins-and-subqueries.md +++ b/docs/queries/joins-and-subqueries.md @@ -1,6 +1,18 @@ # Joins and subqueries +To join one table to another, so load also related models you can use following methods. +* `select_related(related: Union[List, str]) -> QuerySet` +* `prefetch_related(related: Union[List, str]) -> QuerySet` + + +* `Model` + * `Model.load()` method + + +* `QuerysetProxy` + * `QuerysetProxy.select_related(related: Union[List, str])` method + * `QuerysetProxy.prefetch_related(related: Union[List, str])` method ## select_related @@ -30,6 +42,30 @@ To chain related `Models` relation use double underscores between names. !!!tip To control order of models (both main or nested) use `order_by()` method. +```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) + title: str = ormar.String(max_length=100) + position: int = ormar.Integer() + play_count: int = ormar.Integer(nullable=True) +``` + ```python album = await Album.objects.select_related("tracks").all() # will return album will all columns tracks @@ -37,6 +73,52 @@ album = await Album.objects.select_related("tracks").all() You can provide a string or a list of strings +```python +class SchoolClass(ormar.Model): + class Meta: + tablename = "schoolclasses" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + department: Optional[Department] = ormar.ForeignKey(Department, nullable=False) + + +class Category(ormar.Model): + class Meta: + tablename = "categories" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + + +class Student(ormar.Model): + class Meta: + tablename = "students" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + schoolclass: Optional[SchoolClass] = ormar.ForeignKey(SchoolClass) + category: Optional[Category] = ormar.ForeignKey(Category, nullable=True) + + +class Teacher(ormar.Model): + class Meta: + tablename = "teachers" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + schoolclass: Optional[SchoolClass] = ormar.ForeignKey(SchoolClass) + category: Optional[Category] = ormar.ForeignKey(Category, nullable=True) +``` + ```python classes = await SchoolClass.objects.select_related( ["teachers__category", "students"]).all() @@ -81,12 +163,82 @@ To chain related `Models` relation use double underscores between names. !!!tip To control order of models (both main or nested) use `order_by()` method. +```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) + title: str = ormar.String(max_length=100) + position: int = ormar.Integer() + play_count: int = ormar.Integer(nullable=True) +``` + ```python album = await Album.objects.prefetch_related("tracks").all() # will return album will all columns tracks ``` -You can provide a string or a list of strings +You can provide a string, or a list of strings + +```python +class SchoolClass(ormar.Model): + class Meta: + tablename = "schoolclasses" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + department: Optional[Department] = ormar.ForeignKey(Department, nullable=False) + + +class Category(ormar.Model): + class Meta: + tablename = "categories" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + + +class Student(ormar.Model): + class Meta: + tablename = "students" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + schoolclass: Optional[SchoolClass] = ormar.ForeignKey(SchoolClass) + category: Optional[Category] = ormar.ForeignKey(Category, nullable=True) + + +class Teacher(ormar.Model): + class Meta: + tablename = "teachers" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + schoolclass: Optional[SchoolClass] = ormar.ForeignKey(SchoolClass) + category: Optional[Category] = ormar.ForeignKey(Category, nullable=True) +``` ```python classes = await SchoolClass.objects.prefetch_related( @@ -221,3 +373,44 @@ That means that in `prefetch_related` example above if there are 3 distinct mode table B and 2 in table C, there will be only 5 children nested models shared between all model A instances. That also means that if you update any attribute it will be updated on all parents as they share the same child object. + + +## Model methods + +Each model instance have a set of methods to `save`, `update` or `load` itself. + +### load + +You can load the `ForeignKey` related model by calling `load()` method. + +`load()` can be used to refresh the model from the database (if it was changed by some other process). + +!!!tip + Read more about `load()` method in [models methods](../models/methods.md#load) + +## 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. + +### select_related + +Works exactly the same as [select_related](./#select_related) function above but allows you to fetch related +objects from other side of the relation. + +!!!tip + To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section + +### prefetch_related + +Works exactly the same as [prefetch_related](./#prefetch_related) function above but allows you to fetch related +objects from other side of the relation. + +!!!tip + To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section + + +[querysetproxy]: ../relations/queryset-proxy.md \ No newline at end of file diff --git a/docs/queries/read.md b/docs/queries/read.md index b761537..f973507 100644 --- a/docs/queries/read.md +++ b/docs/queries/read.md @@ -1,29 +1,57 @@ -# Read/ Load data from database +# Read data from database -* `get(**kwargs): -> Model` -* `get_or_create(**kwargs) -> Model` -* `first(): -> Model` -* `all(**kwargs) -> List[Optional[Model]]` -* `Model.load() method` +Following methods allow you to load data from the database. + +* `get(**kwargs) -> Model` +* `get_or_create(**kwargs) -> Model` +* `first() -> Model` +* `all(**kwargs) -> List[Optional[Model]]` + + +* `Model` + * `Model.load()` method + + +* `QuerysetProxy` + * `QuerysetProxy.get(**kwargs)` method + * `QuerysetProxy.get_or_create(**kwargs)` method + * `QuerysetProxy.first()` method + * `QuerysetProxy.all(**kwargs)` method ## 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 set it will return the last row in db sorted by pk column. Passing a criteria is actually calling filter(**kwargs) method described below. +```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 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 +track == track2 +# True since it's the only row in db in our example +# and get without arguments return first row by pk column desc ``` -!!!warning If no row meets the criteria `NoMatch` exception is raised. +!!!warning + If no row meets the criteria `NoMatch` exception is raised. If there are multiple rows meeting the criteria the `MultipleMatches` exception is raised. @@ -36,6 +64,17 @@ Combination of create and get methods. Tries to get a row meeting the criteria and if `NoMatch` exception is raised it creates a new one with given kwargs. +```python +class Album(ormar.Model): + class Meta: + tablename = "album" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) +``` + ```python album = await Album.objects.get_or_create(name='The Cat') # object is created as it does not exist @@ -44,20 +83,41 @@ assert album == album2 # return True as the same db row is returned ``` -!!!warning Despite being a equivalent row from database the `album` and `album2` in -example above are 2 different python objects! -Updating one of them will not refresh the second one until you excplicitly load() the -fresh data from db. +!!!warning + Despite being an equivalent row from database the `album` and `album2` in + example above are 2 different python objects! + Updating one of them will not refresh the second one until you excplicitly load() the + fresh data from db. -!!!note Note that if you want to create a new object you either have to pass pk column -value or pk column has to be set as autoincrement +!!!note + Note that if you want to create a new object you either have to pass pk column + value or pk column has to be set as autoincrement ## first -`first(): -> Model` +`first() -> Model` Gets the first row from the db ordered by primary key column ascending. +```python +class Album(ormar.Model): + class Meta: + tablename = "album" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) +``` + +```python +await Album.objects.create(name='The Cat') +await Album.objects.create(name='The Dog') +album = await Album.objects.first() +# first row by primary_key column asc +assert album.name == 'The Cat' +``` + ## all `all(**kwargs) -> List[Optional["Model"]]` @@ -69,12 +129,90 @@ Passing kwargs is a shortcut and equals to calling `filter(**kwrags).all()`. If there are no rows meeting the criteria an empty list is returned. ```python -tracks = await Track.objects.select_related("album").all(title='Sample') -# will return a list of all Tracks with title Sample +class Album(ormar.Model): + class Meta: + tablename = "album" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + + +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) + title: str = ormar.String(max_length=100) + position: int = ormar.Integer() +``` + +```python +tracks = await Track.objects.select_related("album").all(album__title='Sample') +# will return a list of all Tracks for album Sample +# for more on joins visit joining and subqueries section tracks = await Track.objects.all() # will return a list of all Tracks in database ``` -## Model method +## Model methods + +Each model instance have a set of methods to `save`, `update` or `load` itself. + +### load + +You can load the `ForeignKey` related model by calling `load()` method. + +`load()` can be used to refresh the model from the database (if it was changed by some other process). + +!!!tip + Read more about `load()` method in [models methods](../models/methods.md#load) + +## 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. + +### get + +Works exactly the same as [get](./#get) function above but allows you to fetch 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. + +!!!tip + To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section + +### first + +Works exactly the same as [first](./#first) function above but allows you to query +related objects from other side of the relation. + +!!!tip + To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section + +### all + +Works exactly the same as [all](./#all) function above but allows you to query related +objects from other side of the relation. + +!!!tip + To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section + + +[querysetproxy]: ../relations/queryset-proxy.md \ No newline at end of file diff --git a/docs/queries/update.md b/docs/queries/update.md index 255b15f..642f044 100644 --- a/docs/queries/update.md +++ b/docs/queries/update.md @@ -1,11 +1,20 @@ -# Update +# Update data in database -* `update(each: bool = False, **kwargs) -> int` -* `update_or_create(**kwargs) -> Model` -* `bulk_update(objects: List[Model], columns: List[str] = None) -> None` -* `Model.update() method` -* `Model.upsert() method` -* `Model.save_related() method` +Following methods and functions allow updating existing data in the database. + +* `update(each: bool = False, **kwargs) -> int` +* `update_or_create(**kwargs) -> Model` +* `bulk_update(objects: List[Model], columns: List[str] = None) -> None` + + +* `Model` + * `Model.update()` method + * `Model.upsert()` method + * `Model.save_related()` method + + +* `QuerysetProxy` + * `QuerysetProxy.update_or_create(**kwargs)` method ## update @@ -24,8 +33,8 @@ Return number of rows updated. --8<-- "../docs_src/queries/docs002.py" ``` -!!!warning Queryset needs to be filtered before updating to prevent accidental -overwrite. +!!!warning + Queryset needs to be filtered before updating to prevent accidental overwrite. To update whole database table `each=True` needs to be provided as a safety switch @@ -39,8 +48,9 @@ Updates the model, or in case there is no match in database creates a new one. --8<-- "../docs_src/queries/docs003.py" ``` -!!!note Note that if you want to create a new object you either have to pass pk column -value or pk column has to be set as autoincrement +!!!note + Note that if you want to create a new object you either have to pass pk column + value or pk column has to be set as autoincrement ## bulk_update @@ -67,5 +77,50 @@ completed = await ToDo.objects.filter(completed=False).all() assert len(completed) == 3 ``` -## Model method +## Model methods +Each model instance have a set of methods to `save`, `update` or `load` itself. + +###update + +You can update models by updating your model attributes (fields) and calling `update()` method. + +If you try to update a model without a primary key set a `ModelPersistenceError` exception will be thrown. + + +!!!tip + Read more about `update()` method in [models-update](../models/methods.md#update) + +###upsert + +It's a proxy to either `save()` or `update(**kwargs)` methods of a Model. +If the pk is set the `update()` method will be called. + +!!!tip + Read more about `upsert()` method in [models-upsert][models-upsert] + +###save_related + +Method goes through all relations of the `Model` on which the method is called, +and calls `upsert()` method on each model that is **not** saved. + +!!!tip + Read more about `save_related()` method in [models-save-related][models-save-related] + +## 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. + +### update_or_create + +Works exactly the same as [update_or_create](./#update_or_create) function above but allows you to update or create related objects +from other side of the relation. + +!!!tip + To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section + +[querysetproxy]: ../relations/queryset-proxy.md +[models-upsert]: ../models/methods.md#upsert +[models-save-related]: ../models/methods.md#save_related \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index c6379f1..8a5aa6a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -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