From 1dbc273e13f9858b94788da41c07c6d5b761aa4b Mon Sep 17 00:00:00 2001 From: Pau Ramon Revilla Date: Thu, 13 Jul 2023 10:17:01 +0200 Subject: [PATCH 1/5] Document IN() --- docs/tutorial/where.md | 30 +++++++++- docs_src/tutorial/where/tutorial0065.py | 59 +++++++++++++++++++ .../test_where/test_tutorial0065.py | 28 +++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 docs_src/tutorial/where/tutorial0065.py create mode 100644 tests/test_tutorial/test_where/test_tutorial0065.py diff --git a/docs/tutorial/where.md b/docs/tutorial/where.md index ca85a4dd00..e5406cbab8 100644 --- a/docs/tutorial/where.md +++ b/docs/tutorial/where.md @@ -665,7 +665,7 @@ age=32 id=4 name='Tarantula' secret_name='Natalia Roman-on' ### Less Than or Equal -Finally, we can use `<=` to get the rows where a column is **less than or equal** to a value: +We can also use `<=` to get the rows where a column is **less than or equal** to a value: ```Python hl_lines="5" # Code above omitted 👆 @@ -694,6 +694,34 @@ age=35 id=5 name='Black Lion' secret_name='Trevor Challa' !!! tip We get `Black Lion` here too because although the age is not *strictly* less than `35` it is *equal* to `35`. +### In + +Finally, we can use `in_ to get the rows where a column is a member of a collection of values: + +```Python hl_lines="5" +# Code above omitted 👆 + +{!./docs_src/tutorial/where/tutorial0066.py[ln:44-49]!} + +# Code below omitted 👇 +``` + +
+👀 Full file preview + +```Python +{!./docs_src/tutorial/where/tutorial0065.py!} +``` + +
+ +In this case, we match `Deadpond` since it's part of the collections of names. +We don't have any hero called `Ratman`, so it does not match any hero. + +!!! tip + You need to wrap your attribute with `col()` to use `in_`. + You can read more about it in the (Type annotations and errors)[#type-annotations-and-errors] section. + ### Benefits of Expressions Here's a good moment to see that being able to use these pure Python expressions instead of keyword arguments can help a lot. ✨ diff --git a/docs_src/tutorial/where/tutorial0065.py b/docs_src/tutorial/where/tutorial0065.py new file mode 100644 index 0000000000..deb0b23025 --- /dev/null +++ b/docs_src/tutorial/where/tutorial0065.py @@ -0,0 +1,59 @@ +from typing import Optional + +from sqlmodel import Field, Session, SQLModel, col, create_engine, select + + +class Hero(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str + secret_name: str + age: Optional[int] = None + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +engine = create_engine(sqlite_url, echo=True) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def create_heroes(): + hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") + hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(col(Hero.name).in_(["Deadpond", "Ratman"])) + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/tests/test_tutorial/test_where/test_tutorial0065.py b/tests/test_tutorial/test_where/test_tutorial0065.py new file mode 100644 index 0000000000..675b3fdc2f --- /dev/null +++ b/tests/test_tutorial/test_where/test_tutorial0065.py @@ -0,0 +1,28 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function + + +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.where import tutorial0065 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [ + { + "name": "Deadpond", + "secret_name": "Dive Wilson", + "age": None, + "id": 1, + } + ] + ] From 41eeeb6187951fd6df4312fdd5c13b300e88073a Mon Sep 17 00:00:00 2001 From: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com> Date: Fri, 22 Aug 2025 22:11:41 +0200 Subject: [PATCH 2/5] Update "includes" and "tip" formatting in `where.md` --- docs/tutorial/where.md | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/docs/tutorial/where.md b/docs/tutorial/where.md index e7a812328e..3e5636cb2e 100644 --- a/docs/tutorial/where.md +++ b/docs/tutorial/where.md @@ -583,29 +583,17 @@ We get `Black Lion` here too because although the age is not *strictly* less tha Finally, we can use `in_ to get the rows where a column is a member of a collection of values: -```Python hl_lines="5" -# Code above omitted 👆 - -{!./docs_src/tutorial/where/tutorial0066.py[ln:44-49]!} - -# Code below omitted 👇 -``` - -
-👀 Full file preview - -```Python -{!./docs_src/tutorial/where/tutorial0065.py!} -``` - -
+{* ./docs_src/tutorial/where/tutorial0065.py ln[44:49] hl[46] *} In this case, we match `Deadpond` since it's part of the collections of names. We don't have any hero called `Ratman`, so it does not match any hero. -!!! tip - You need to wrap your attribute with `col()` to use `in_`. - You can read more about it in the (Type annotations and errors)[#type-annotations-and-errors] section. +/// tip + +You need to wrap your attribute with `col()` to use `in_`. +You can read more about it in the (Type annotations and errors)[#type-annotations-and-errors] section. + +/// ### Benefits of Expressions From fe123ba42eedb6b5215b5b305a973eeb9f1f2038 Mon Sep 17 00:00:00 2001 From: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com> Date: Fri, 22 Aug 2025 22:14:45 +0200 Subject: [PATCH 3/5] Fix code block formatting in `where.md` --- docs/tutorial/where.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/where.md b/docs/tutorial/where.md index 3e5636cb2e..be8a64efbc 100644 --- a/docs/tutorial/where.md +++ b/docs/tutorial/where.md @@ -581,7 +581,7 @@ We get `Black Lion` here too because although the age is not *strictly* less tha ### In -Finally, we can use `in_ to get the rows where a column is a member of a collection of values: +Finally, we can use `in_` to get the rows where a column is a member of a collection of values: {* ./docs_src/tutorial/where/tutorial0065.py ln[44:49] hl[46] *} From bd737cc1889c3fcbdbc397a0f7989ec42d827d30 Mon Sep 17 00:00:00 2001 From: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com> Date: Fri, 22 Aug 2025 22:22:36 +0200 Subject: [PATCH 4/5] Fix link in `where.md` --- docs/tutorial/where.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/where.md b/docs/tutorial/where.md index be8a64efbc..8d9105c336 100644 --- a/docs/tutorial/where.md +++ b/docs/tutorial/where.md @@ -591,7 +591,7 @@ We don't have any hero called `Ratman`, so it does not match any hero. /// tip You need to wrap your attribute with `col()` to use `in_`. -You can read more about it in the (Type annotations and errors)[#type-annotations-and-errors] section. +You can read more about it in the [Type annotations and errors](#type-annotations-and-errors){.internal-link target=_blank} section. /// From e6777bcf7837ee4c8f5971d5f1c28bdf1f5ba09a Mon Sep 17 00:00:00 2001 From: Yurii Motov Date: Thu, 8 Jan 2026 16:18:48 +0100 Subject: [PATCH 5/5] Rename code example, add `_py310` version --- docs/tutorial/where.md | 2 +- docs_src/tutorial/where/tutorial006b_py310.py | 57 +++++++++++++++++++ .../{tutorial0065.py => tutorial006b_py39.py} | 0 .../test_where/test_tutorial0065.py | 28 --------- .../test_where/test_tutorial006b.py | 35 ++++++++++++ 5 files changed, 93 insertions(+), 29 deletions(-) create mode 100644 docs_src/tutorial/where/tutorial006b_py310.py rename docs_src/tutorial/where/{tutorial0065.py => tutorial006b_py39.py} (100%) delete mode 100644 tests/test_tutorial/test_where/test_tutorial0065.py create mode 100644 tests/test_tutorial/test_where/test_tutorial006b.py diff --git a/docs/tutorial/where.md b/docs/tutorial/where.md index 8d9105c336..120874a235 100644 --- a/docs/tutorial/where.md +++ b/docs/tutorial/where.md @@ -583,7 +583,7 @@ We get `Black Lion` here too because although the age is not *strictly* less tha Finally, we can use `in_` to get the rows where a column is a member of a collection of values: -{* ./docs_src/tutorial/where/tutorial0065.py ln[44:49] hl[46] *} +{* ./docs_src/tutorial/where/tutorial006b_py310.py ln[42:47] hl[44] *} In this case, we match `Deadpond` since it's part of the collections of names. We don't have any hero called `Ratman`, so it does not match any hero. diff --git a/docs_src/tutorial/where/tutorial006b_py310.py b/docs_src/tutorial/where/tutorial006b_py310.py new file mode 100644 index 0000000000..21a4e28c0f --- /dev/null +++ b/docs_src/tutorial/where/tutorial006b_py310.py @@ -0,0 +1,57 @@ +from sqlmodel import Field, Session, SQLModel, col, create_engine, select + + +class Hero(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str + secret_name: str + age: int | None = None + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +engine = create_engine(sqlite_url, echo=True) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def create_heroes(): + hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") + hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(col(Hero.name).in_(["Deadpond", "Ratman"])) + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/where/tutorial0065.py b/docs_src/tutorial/where/tutorial006b_py39.py similarity index 100% rename from docs_src/tutorial/where/tutorial0065.py rename to docs_src/tutorial/where/tutorial006b_py39.py diff --git a/tests/test_tutorial/test_where/test_tutorial0065.py b/tests/test_tutorial/test_where/test_tutorial0065.py deleted file mode 100644 index 675b3fdc2f..0000000000 --- a/tests/test_tutorial/test_where/test_tutorial0065.py +++ /dev/null @@ -1,28 +0,0 @@ -from unittest.mock import patch - -from sqlmodel import create_engine - -from ...conftest import get_testing_print_function - - -def test_tutorial(clear_sqlmodel): - from docs_src.tutorial.where import tutorial0065 as mod - - mod.sqlite_url = "sqlite://" - mod.engine = create_engine(mod.sqlite_url) - calls = [] - - new_print = get_testing_print_function(calls) - - with patch("builtins.print", new=new_print): - mod.main() - assert calls == [ - [ - { - "name": "Deadpond", - "secret_name": "Dive Wilson", - "age": None, - "id": 1, - } - ] - ] diff --git a/tests/test_tutorial/test_where/test_tutorial006b.py b/tests/test_tutorial/test_where/test_tutorial006b.py new file mode 100644 index 0000000000..237618afea --- /dev/null +++ b/tests/test_tutorial/test_where/test_tutorial006b.py @@ -0,0 +1,35 @@ +import importlib +from types import ModuleType + +import pytest +from sqlmodel import create_engine + +from ...conftest import PrintMock, needs_py310 + + +@pytest.fixture( + name="mod", + params=[ + pytest.param("tutorial006b_py39"), + pytest.param("tutorial006b_py310", marks=needs_py310), + ], +) +def get_module(request: pytest.FixtureRequest) -> ModuleType: + mod = importlib.import_module(f"docs_src.tutorial.where.{request.param}") + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + return mod + + +def test_tutorial(print_mock: PrintMock, mod: ModuleType): + mod.main() + assert print_mock.calls == [ + [ + { + "name": "Deadpond", + "secret_name": "Dive Wilson", + "age": None, + "id": 1, + } + ] + ]