diff --git a/README.md b/README.md index 6ce8908..5dc94bd 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ assert len(tracks) == 1 * `count() -> int` * `exists() -> bool` * `fields(columns: Union[List, str]) -> QuerySet` - +* `order_by(columns:Union[List, str]) -> QuerySet` #### Relation types diff --git a/docs/index.md b/docs/index.md index 6ce8908..7a264da 100644 --- a/docs/index.md +++ b/docs/index.md @@ -164,6 +164,7 @@ assert len(tracks) == 1 * `count() -> int` * `exists() -> bool` * `fields(columns: Union[List, str]) -> QuerySet` +* `order_by(columns:Union[List, str]) -> QuerySet` #### Relation types diff --git a/docs/queries.md b/docs/queries.md index 77c6229..ad82d40 100644 --- a/docs/queries.md +++ b/docs/queries.md @@ -370,5 +370,86 @@ With `fields()` you can select subset of model columns to limit the data load. Something like `Track.object.select_related("album").filter(album__name="Malibu").offset(1).limit(1).all()` +### 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. + +You can provide a string with field name or list of strings with different fields. + +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 + +!!!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): + ``` + MODEL: 1 - Child Model - 3 + MODEL: 2 - Child Model - 2 + MODEL: 1 - Child Model - 1 + ``` + + will result in 2 rows of result: + ``` + MODEL: 1 - Child Models: [3, 1] # encountered first in result, all children rows combined + MODEL: 2 - Child Modles: [2] + ``` + + The main model will never duplicate in the result + +Given sample Models like following: + +```python +--8<-- "../docs_src/queries/docs007.py" +``` + +To order by main model field just provide a field name + +```python +toys = await Toy.objects.select_related("owner").order_by("name").all() +assert [x.name.replace("Toy ", "") for x in toys] == [ + str(x + 1) for x in range(6) +] +assert toys[0].owner == zeus +assert toys[1].owner == aphrodite +``` + +To sort on nested models separate field names with dunder '__'. + +You can sort this way across all relation types -> `ForeignKey`, reverse virtual FK and `ManyToMany` fields. + +```python +toys = await Toy.objects.select_related("owner").order_by("owner__name").all() +assert toys[0].owner.name == toys[1].owner.name == "Aphrodite" +assert toys[2].owner.name == toys[3].owner.name == "Hermes" +assert toys[4].owner.name == toys[5].owner.name == "Zeus" +``` + +To sort in descending order provide a hyphen in front of the field name + +```python +owner = ( + await Owner.objects.select_related("toys") + .order_by("-toys__name") + .filter(name="Zeus") + .get() +) +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 + + 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()` + + [models]: ./models.md [relations]: ./relations.md \ No newline at end of file diff --git a/docs_src/queries/docs007.py b/docs_src/queries/docs007.py new file mode 100644 index 0000000..7e8ddc0 --- /dev/null +++ b/docs_src/queries/docs007.py @@ -0,0 +1,42 @@ +import databases +import sqlalchemy + +import ormar +from tests.settings import DATABASE_URL + +database = databases.Database(DATABASE_URL, force_rollback=True) +metadata = sqlalchemy.MetaData() + + +class Owner(ormar.Model): + class Meta: + tablename = "owners" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + + +class Toy(ormar.Model): + class Meta: + tablename = "toys" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + owner: Owner = ormar.ForeignKey(Owner) + + +# build some sample data +aphrodite = await Owner.objects.create(name="Aphrodite") +hermes = await Owner.objects.create(name="Hermes") +zeus = await Owner.objects.create(name="Zeus") + +await Toy.objects.create(name="Toy 4", owner=zeus) +await Toy.objects.create(name="Toy 5", owner=hermes) +await Toy.objects.create(name="Toy 2", owner=aphrodite) +await Toy.objects.create(name="Toy 1", owner=zeus) +await Toy.objects.create(name="Toy 3", owner=aphrodite) +await Toy.objects.create(name="Toy 6", owner=hermes) diff --git a/ormar/__init__.py b/ormar/__init__.py index b121bb1..e5e9262 100644 --- a/ormar/__init__.py +++ b/ormar/__init__.py @@ -30,7 +30,7 @@ class UndefinedType: # pragma no cover Undefined = UndefinedType() -__version__ = "0.4.0" +__version__ = "0.4.1" __all__ = [ "Integer", "BigInteger",