From e07c2f296b4a6a9f116e08349ac1c7d3aab5e973 Mon Sep 17 00:00:00 2001 From: collerek Date: Mon, 22 Mar 2021 17:20:16 +0100 Subject: [PATCH] add docs for select_all --- docs/queries/joins-and-subqueries.md | 93 ++++++++++++++++++++++++++++ docs/releases.md | 8 ++- 2 files changed, 98 insertions(+), 3 deletions(-) diff --git a/docs/queries/joins-and-subqueries.md b/docs/queries/joins-and-subqueries.md index a1b9f87..f295f1b 100644 --- a/docs/queries/joins-and-subqueries.md +++ b/docs/queries/joins-and-subqueries.md @@ -3,6 +3,7 @@ To join one table to another, so load also related models you can use following methods. * `select_related(related: Union[List, str]) -> QuerySet` +* `select_all(follow: bool = True) -> QuerySet` * `prefetch_related(related: Union[List, str]) -> QuerySet` @@ -12,6 +13,7 @@ To join one table to another, so load also related models you can use following * `QuerysetProxy` * `QuerysetProxy.select_related(related: Union[List, str])` method + * `QuerysetProxy.select_all(follow: bool=True)` method * `QuerysetProxy.prefetch_related(related: Union[List, str])` method ## select_related @@ -142,6 +144,88 @@ fields and the final `Models` are fetched for you. Something like `Track.object.select_related("album").filter(album__name="Malibu").offset(1).limit(1).all()` +## select_all + +`select_all(follow: bool = False) -> QuerySet` + +By default when you select `all()` none of the relations are loaded, likewise, +when `select_related()` is used you need to explicitly specify all relations that should +be loaded. If you want to include also nested relations this can be cumberstone. + +That's why `select_all()` was introduced, so by default load all relations of a model +(so kind of opposite as with `all()` approach). + +By default adds only directly related models of a parent model (from which the query is run). + +If `follow=True` is set it adds also related models of related models. + +!!!info + To not get stuck in an infinite loop as related models also keep a relation + to parent model visited models set is kept. + + That way already visited models that are nested are loaded, but the load do not + follow them inside. So Model A -> Model B -> Model C -> Model A -> Model X + will load second Model A but will never follow into Model X. + Nested relations of those kind need to be loaded manually. + +With sample date like follow: + +```python +database = databases.Database(DATABASE_URL, force_rollback=True) +metadata = sqlalchemy.MetaData() + + +class BaseMeta(ormar.ModelMeta): + database = database + metadata = metadata + + +class Address(ormar.Model): + class Meta(BaseMeta): + tablename = "addresses" + + id: int = ormar.Integer(primary_key=True) + street: str = ormar.String(max_length=100, nullable=False) + number: int = ormar.Integer(nullable=False) + post_code: str = ormar.String(max_length=20, nullable=False) + + + +class Branch(ormar.Model): + class Meta(BaseMeta): + tablename = "branches" + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100, nullable=False) + address = ormar.ForeignKey(Address) + + +class Company(ormar.Model): + class Meta(BaseMeta): + tablename = "companies" + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100, nullable=False, name="company_name") + founded: int = ormar.Integer(nullable=True) + branches = ormar.ManyToMany(Branch) +``` + +To select all `Companies` with all `Branches` and `Addresses` you can simply query: + +```python +companies = await Company.objects.select_all(follow=True).all() + +# which is equivalent to: +companies = await Company.objects.select_related('branches__address').all() +``` + +Of course in this case it's quite easy to issue explicit relation names in `select_related`, +but the benefit of `select_all()` shows when you have multiple relations. + +If for example `Company` would have 3 relations and all of those 3 relations have it's own +3 relations you would have to issue 9 relation strings to `select_related`, `select_all()` +is also resistant to change in names of relations. + ## prefetch_related `prefetch_related(related: Union[List, str]) -> QuerySet` @@ -404,6 +488,15 @@ objects from other side of the relation. !!!tip To read more about `QuerysetProxy` visit [querysetproxy][querysetproxy] section +### select_all + +Works exactly the same as [select_all](./#select_all) 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 diff --git a/docs/releases.md b/docs/releases.md index 1d3c5a9..9eaf097 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -10,13 +10,15 @@ ## Features * add `select_all(follow: bool = False)` method to `QuerySet` and `QuerysetProxy`. - It is an equivalent of the Model's `load_all()` method but can be used directly in a query. + It is kind of equivalent of the Model's `load_all()` method but can be used directly in a query. By default `select_all()` adds only directly related models, with `follow=True` also related models - of related models are added without loops in relations. + of related models are added without loops in relations. Note that it's not and end `async` model + so you still have to issue `get()`, `all()` etc. as `select_all()` returns a QuerySet (or proxy) + like `fields()` or `order_by()`. ## Internals -* `ormar` fields are no longer stored as Classes in `Meta.model_fields` dictionary +* `ormar` fields are no longer stored as classes in `Meta.model_fields` dictionary but instead they are stored as instances. # 0.9.9