diff --git a/docs/advanced/uuid.md b/docs/advanced/uuid.md
new file mode 100644
index 0000000000..56492636f6
--- /dev/null
+++ b/docs/advanced/uuid.md
@@ -0,0 +1,342 @@
+# UUID (Universally Unique Identifiers)
+
+We have discussed some data types like `str`, `int`, etc.
+
+There's another data type called `UUID` (Universally Unique Identifier).
+
+You might have seen **UUIDs**, for example in URLs. They look something like this:
+
+```
+4ff2dab7-bffe-414d-88a5-1826b9fea8df
+```
+
+UUIDs can be particularly useful as an alternative to auto-incrementing integers for **primary keys**.
+
+/// info
+
+Official support for UUIDs was added in SQLModel version `0.0.20`.
+
+///
+
+## About UUIDs
+
+UUIDs are numbers with 128 bits, that is, 16 bytes.
+
+They are normally seen as 32 hexadecimal characters separated by dashes.
+
+There are several versions of UUID, some versions include the current time in the bytes, but **UUIDs version 4** are mainly random, the way they are generated makes them virtually **unique**.
+
+### Distributed UUIDs
+
+You could generate one UUID in one computer, and someone else could generate another UUID in another computer, and it would be almost **impossible** for both UUIDs to be the **same**.
+
+This means that you don't have to wait for the DB to generate the ID for you, you can **generate it in code before sending it to the database**, because you can be quite certain it will be unique.
+
+/// note | Technical Details
+
+Because the number of possible UUIDs is so large (2^128), the probability of generating the same UUID version 4 (the random ones) twice is very low.
+
+If you had 103 trillion version 4 UUIDs stored in the database, the probability of generating a duplicated new one is one in a billion. 🤓
+
+///
+
+For the same reason, if you decided to migrate your database, combine it with another database and mix records, etc. you would most probably be able to **just use the same UUIDs** you had originally.
+
+/// warning
+
+There's still a chance you could have a collision, but it's very low. In most cases you could assume you wouldn't have it, but it would be good to be prepared for it.
+
+///
+
+### UUIDs Prevent Information Leakage
+
+Because UUIDs version 4 are **random**, you could give these IDs to the application users or to other systems, **without exposing information** about your application.
+
+When using **auto-incremented integers** for primary keys, you could implicitly expose information about your system. For example, someone could create a new hero, and by getting the hero ID `20` **they would know that you have 20 heroes** in your system (or even less, if some heroes were already deleted).
+
+### UUID Storage
+
+Because UUIDs are 16 bytes, they would **consume more space** in the database than a smaller auto-incremented integer (commonly 4 bytes).
+
+Depending on the database you use, UUIDs could have **better or worse performance**. If you are concerned about that, you should check the documentation for the specific database.
+
+SQLite doesn't have a specific UUID type, so it will store the UUID as a string. Other databases like Postgres have a specific UUID type which would result in better performance and space usage than strings.
+
+## Models with UUIDs
+
+To use UUIDs as primary keys we need to import `uuid`, which is part of the Python standard library (we don't have to install anything) and use `uuid.UUID` as the **type** for the ID field.
+
+We also want the Python code to **generate a new UUID** when creating a new instance, so we use `default_factory`.
+
+The parameter `default_factory` takes a function (or in general, a "callable"). This function will be **called when creating a new instance** of the model and the value returned by the function will be used as the default value for the field.
+
+For the function in `default_factory` we pass `uuid.uuid4`, which is a function that generates a **new UUID version 4**.
+
+/// tip
+
+We don't call `uuid.uuid4()` ourselves in the code (we don't put the parenthesis). Instead, we pass the function itself, just `uuid.uuid4`, so that SQLModel can call it every time we create a new instance.
+
+///
+
+This means that the UUID will be generated in the Python code, **before sending the data to the database**.
+
+//// tab | Python 3.10+
+
+```Python hl_lines="1 7"
+{!./docs_src/advanced/uuid/tutorial001_py310.py[ln:1-10]!}
+
+# Code below omitted 👇
+```
+
+////
+
+//// tab | Python 3.7+
+
+```Python hl_lines="1 8"
+{!./docs_src/advanced/uuid/tutorial001.py[ln:1-11]!}
+
+# Code below omitted 👇
+```
+
+////
+
+/// details | 👀 Full file preview
+
+//// tab | Python 3.10+
+
+```Python
+{!./docs_src/advanced/uuid/tutorial001_py310.py!}
+```
+
+////
+
+//// tab | Python 3.7+
+
+```Python
+{!./docs_src/advanced/uuid/tutorial001.py!}
+```
+
+////
+
+///
+
+Pydantic has support for `UUID` types.
+
+For the database, **SQLModel** internally uses SQLAlchemy's `Uuid` type.
+
+### Create a Record with a UUID
+
+When creating a `Hero` record, the `id` field will be **automatically populated** with a new UUID because we set `default_factory=uuid.uuid4`.
+
+As `uuid.uuid4` will be called when creating the model instance, even before sending it to the database, we can **access and use the ID right away**.
+
+And that **same ID (a UUID)** will be saved in the database.
+
+//// tab | Python 3.10+
+
+```Python hl_lines="5 7 9 14"
+# Code above omitted 👆
+
+{!./docs_src/advanced/uuid/tutorial001_py310.py[ln:23-34]!}
+
+# Code below omitted 👇
+```
+
+////
+
+//// tab | Python 3.7+
+
+```Python hl_lines="5 7 9 14"
+# Code above omitted 👆
+
+{!./docs_src/advanced/uuid/tutorial001.py[ln:24-35]!}
+
+# Code below omitted 👇
+```
+
+////
+
+/// details | 👀 Full file preview
+
+//// tab | Python 3.10+
+
+```Python
+{!./docs_src/advanced/uuid/tutorial001_py310.py!}
+```
+
+////
+
+//// tab | Python 3.7+
+
+```Python
+{!./docs_src/advanced/uuid/tutorial001.py!}
+```
+
+////
+
+///
+
+### Select a Hero
+
+We can do the same operations we could do with other fields.
+
+For example we can **select a hero by ID**:
+
+//// tab | Python 3.10+
+
+```Python hl_lines="15"
+# Code above omitted 👆
+
+{!./docs_src/advanced/uuid/tutorial001_py310.py[ln:37-54]!}
+
+# Code below omitted 👇
+```
+
+////
+
+//// tab | Python 3.7+
+
+```Python hl_lines="15"
+# Code above omitted 👆
+
+{!./docs_src/advanced/uuid/tutorial001.py[ln:38-55]!}
+
+# Code below omitted 👇
+```
+
+////
+
+/// details | 👀 Full file preview
+
+//// tab | Python 3.10+
+
+```Python
+{!./docs_src/advanced/uuid/tutorial001_py310.py!}
+```
+
+////
+
+//// tab | Python 3.7+
+
+```Python
+{!./docs_src/advanced/uuid/tutorial001.py!}
+```
+
+////
+
+///
+
+/// tip
+
+Even if a database like SQLite stores the UUID as a string, we can select and run comparisons using a Python UUID object and it will work.
+
+SQLModel (actually SQLAlchemy) will take care of making it work. ✨
+
+///
+
+#### Select with `session.get()`
+
+We could also select by ID with `session.get()`:
+
+//// tab | Python 3.10+
+
+```Python hl_lines="15"
+# Code above omitted 👆
+
+{!./docs_src/advanced/uuid/tutorial002_py310.py[ln:37-54]!}
+
+# Code below omitted 👇
+```
+
+////
+
+//// tab | Python 3.7+
+
+```Python hl_lines="15"
+# Code above omitted 👆
+
+{!./docs_src/advanced/uuid/tutorial002.py[ln:38-55]!}
+
+# Code below omitted 👇
+```
+
+////
+
+/// details | 👀 Full file preview
+
+//// tab | Python 3.10+
+
+```Python
+{!./docs_src/advanced/uuid/tutorial002_py310.py!}
+```
+
+////
+
+//// tab | Python 3.7+
+
+```Python
+{!./docs_src/advanced/uuid/tutorial002.py!}
+```
+
+////
+
+///
+
+The same way as with other fields, we could update, delete, etc. 🚀
+
+### Run the program
+
+If you run the program, you will see the **UUID** generated in the Python code, and then the record **saved in the database with the same UUID**.
+
+
+
+```console
+$ python app.py
+
+// Some boilerplate and previous output omitted 😉
+
+// In SQLite, the UUID will be stored as a string
+// other DBs like Postgres have a specific UUID type
+CREATE TABLE hero (
+ id CHAR(32) NOT NULL,
+ name VARCHAR NOT NULL,
+ secret_name VARCHAR NOT NULL,
+ age INTEGER,
+ PRIMARY KEY (id)
+)
+
+// Before saving in the DB we already have the UUID
+The hero before saving in the DB
+name='Deadpond' secret_name='Dive Wilson' id=UUID('0e44c1a6-88d3-4a35-8b8a-307faa2def28') age=None
+The hero ID was already set
+0e44c1a6-88d3-4a35-8b8a-307faa2def28
+
+// The SQL statement to insert the record uses our UUID
+INSERT INTO hero (id, name, secret_name, age) VALUES (?, ?, ?, ?)
+('0e44c1a688d34a358b8a307faa2def28', 'Deadpond', 'Dive Wilson', None)
+
+// And indeed, the record was saved with the UUID we created 😎
+After saving in the DB
+age=None id=UUID('0e44c1a6-88d3-4a35-8b8a-307faa2def28') name='Deadpond' secret_name='Dive Wilson'
+
+// Now we create a new hero (to select it in a bit)
+Created hero:
+age=None id=UUID('9d90d186-85db-4eaa-891a-def7b4ae2dab') name='Spider-Boy' secret_name='Pedro Parqueador'
+Created hero ID:
+9d90d186-85db-4eaa-891a-def7b4ae2dab
+
+// And now we select it
+Selected hero:
+age=None id=UUID('9d90d186-85db-4eaa-891a-def7b4ae2dab') name='Spider-Boy' secret_name='Pedro Parqueador'
+Selected hero ID:
+9d90d186-85db-4eaa-891a-def7b4ae2dab
+```
+
+
+
+## Learn More
+
+You can learn more about **UUIDs** in:
+
+* The official Python docs for UUID.
+* The Wikipedia for UUID.
diff --git a/docs_src/advanced/uuid/__init__.py b/docs_src/advanced/uuid/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/docs_src/advanced/uuid/tutorial001.py b/docs_src/advanced/uuid/tutorial001.py
new file mode 100644
index 0000000000..cfd3146b41
--- /dev/null
+++ b/docs_src/advanced/uuid/tutorial001.py
@@ -0,0 +1,65 @@
+import uuid
+from typing import Union
+
+from sqlmodel import Field, Session, SQLModel, create_engine, select
+
+
+class Hero(SQLModel, table=True):
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ name: str = Field(index=True)
+ secret_name: str
+ age: Union[int, None] = Field(default=None, index=True)
+
+
+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_hero():
+ with Session(engine) as session:
+ hero = Hero(name="Deadpond", secret_name="Dive Wilson")
+ print("The hero before saving in the DB")
+ print(hero)
+ print("The hero ID was already set")
+ print(hero.id)
+ session.add(hero)
+ session.commit()
+ session.refresh(hero)
+ print("After saving in the DB")
+ print(hero)
+
+
+def select_hero():
+ with Session(engine) as session:
+ hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
+ session.add(hero_2)
+ session.commit()
+ session.refresh(hero_2)
+ hero_id = hero_2.id
+ print("Created hero:")
+ print(hero_2)
+ print("Created hero ID:")
+ print(hero_id)
+
+ statement = select(Hero).where(Hero.id == hero_id)
+ selected_hero = session.exec(statement).one()
+ print("Selected hero:")
+ print(selected_hero)
+ print("Selected hero ID:")
+ print(selected_hero.id)
+
+
+def main() -> None:
+ create_db_and_tables()
+ create_hero()
+ select_hero()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs_src/advanced/uuid/tutorial001_py310.py b/docs_src/advanced/uuid/tutorial001_py310.py
new file mode 100644
index 0000000000..610ec6b0d4
--- /dev/null
+++ b/docs_src/advanced/uuid/tutorial001_py310.py
@@ -0,0 +1,64 @@
+import uuid
+
+from sqlmodel import Field, Session, SQLModel, create_engine, select
+
+
+class Hero(SQLModel, table=True):
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ name: str = Field(index=True)
+ secret_name: str
+ age: int | None = Field(default=None, index=True)
+
+
+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_hero():
+ with Session(engine) as session:
+ hero = Hero(name="Deadpond", secret_name="Dive Wilson")
+ print("The hero before saving in the DB")
+ print(hero)
+ print("The hero ID was already set")
+ print(hero.id)
+ session.add(hero)
+ session.commit()
+ session.refresh(hero)
+ print("After saving in the DB")
+ print(hero)
+
+
+def select_hero():
+ with Session(engine) as session:
+ hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
+ session.add(hero_2)
+ session.commit()
+ session.refresh(hero_2)
+ hero_id = hero_2.id
+ print("Created hero:")
+ print(hero_2)
+ print("Created hero ID:")
+ print(hero_id)
+
+ statement = select(Hero).where(Hero.id == hero_id)
+ selected_hero = session.exec(statement).one()
+ print("Selected hero:")
+ print(selected_hero)
+ print("Selected hero ID:")
+ print(selected_hero.id)
+
+
+def main() -> None:
+ create_db_and_tables()
+ create_hero()
+ select_hero()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs_src/advanced/uuid/tutorial002.py b/docs_src/advanced/uuid/tutorial002.py
new file mode 100644
index 0000000000..831725581b
--- /dev/null
+++ b/docs_src/advanced/uuid/tutorial002.py
@@ -0,0 +1,64 @@
+import uuid
+from typing import Union
+
+from sqlmodel import Field, Session, SQLModel, create_engine
+
+
+class Hero(SQLModel, table=True):
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ name: str = Field(index=True)
+ secret_name: str
+ age: Union[int, None] = Field(default=None, index=True)
+
+
+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_hero():
+ with Session(engine) as session:
+ hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
+ print("The hero before saving in the DB")
+ print(hero_1)
+ print("The hero ID was already set")
+ print(hero_1.id)
+ session.add(hero_1)
+ session.commit()
+ session.refresh(hero_1)
+ print("After saving in the DB")
+ print(hero_1)
+
+
+def select_hero():
+ with Session(engine) as session:
+ hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
+ session.add(hero_2)
+ session.commit()
+ session.refresh(hero_2)
+ hero_id = hero_2.id
+ print("Created hero:")
+ print(hero_2)
+ print("Created hero ID:")
+ print(hero_id)
+
+ selected_hero = session.get(Hero, hero_id)
+ print("Selected hero:")
+ print(selected_hero)
+ print("Selected hero ID:")
+ print(selected_hero.id)
+
+
+def main() -> None:
+ create_db_and_tables()
+ create_hero()
+ select_hero()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs_src/advanced/uuid/tutorial002_py310.py b/docs_src/advanced/uuid/tutorial002_py310.py
new file mode 100644
index 0000000000..3ec8c80fa0
--- /dev/null
+++ b/docs_src/advanced/uuid/tutorial002_py310.py
@@ -0,0 +1,63 @@
+import uuid
+
+from sqlmodel import Field, Session, SQLModel, create_engine
+
+
+class Hero(SQLModel, table=True):
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ name: str = Field(index=True)
+ secret_name: str
+ age: int | None = Field(default=None, index=True)
+
+
+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_hero():
+ with Session(engine) as session:
+ hero = Hero(name="Deadpond", secret_name="Dive Wilson")
+ print("The hero before saving in the DB")
+ print(hero)
+ print("The hero ID was already set")
+ print(hero.id)
+ session.add(hero)
+ session.commit()
+ session.refresh(hero)
+ print("After saving in the DB")
+ print(hero)
+
+
+def select_hero():
+ with Session(engine) as session:
+ hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
+ session.add(hero_2)
+ session.commit()
+ session.refresh(hero_2)
+ hero_id = hero_2.id
+ print("Created hero:")
+ print(hero_2)
+ print("Created hero ID:")
+ print(hero_id)
+
+ selected_hero = session.get(Hero, hero_id)
+ print("Selected hero:")
+ print(selected_hero)
+ print("Selected hero ID:")
+ print(selected_hero.id)
+
+
+def main() -> None:
+ create_db_and_tables()
+ create_hero()
+ select_hero()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/mkdocs.yml b/mkdocs.yml
index fa85062a8b..09c2b7f156 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -99,6 +99,7 @@ nav:
- Advanced User Guide:
- advanced/index.md
- advanced/decimal.md
+ - advanced/uuid.md
- alternatives.md
- help.md
- contributing.md
diff --git a/sqlmodel/__init__.py b/sqlmodel/__init__.py
index 61ae35f7eb..5983974d77 100644
--- a/sqlmodel/__init__.py
+++ b/sqlmodel/__init__.py
@@ -140,5 +140,4 @@
from .sql.expression import tuple_ as tuple_
from .sql.expression import type_coerce as type_coerce
from .sql.expression import within_group as within_group
-from .sql.sqltypes import GUID as GUID
from .sql.sqltypes import AutoString as AutoString
diff --git a/sqlmodel/main.py b/sqlmodel/main.py
index 505683f756..5755bbb4fd 100644
--- a/sqlmodel/main.py
+++ b/sqlmodel/main.py
@@ -51,7 +51,7 @@
from sqlalchemy.orm.decl_api import DeclarativeMeta
from sqlalchemy.orm.instrumentation import is_instrumented
from sqlalchemy.sql.schema import MetaData
-from sqlalchemy.sql.sqltypes import LargeBinary, Time
+from sqlalchemy.sql.sqltypes import LargeBinary, Time, Uuid
from typing_extensions import Literal, deprecated, get_origin
from ._compat import ( # type: ignore[attr-defined]
@@ -80,7 +80,7 @@
sqlmodel_init,
sqlmodel_validate,
)
-from .sql.sqltypes import GUID, AutoString
+from .sql.sqltypes import AutoString
if TYPE_CHECKING:
from pydantic._internal._model_construction import ModelMetaclass as ModelMetaclass
@@ -608,7 +608,7 @@ def get_sqlalchemy_type(field: Any) -> Any:
scale=getattr(metadata, "decimal_places", None),
)
if issubclass(type_, uuid.UUID):
- return GUID
+ return Uuid
raise ValueError(f"{type_} has no matching SQLAlchemy type")
diff --git a/sqlmodel/sql/sqltypes.py b/sqlmodel/sql/sqltypes.py
index 5a4bb04ef1..512daacbab 100644
--- a/sqlmodel/sql/sqltypes.py
+++ b/sqlmodel/sql/sqltypes.py
@@ -1,10 +1,7 @@
-import uuid
-from typing import Any, Optional, cast
+from typing import Any, cast
-from sqlalchemy import CHAR, types
-from sqlalchemy.dialects.postgresql import UUID
+from sqlalchemy import types
from sqlalchemy.engine.interfaces import Dialect
-from sqlalchemy.sql.type_api import TypeEngine
class AutoString(types.TypeDecorator): # type: ignore
@@ -17,43 +14,3 @@ def load_dialect_impl(self, dialect: Dialect) -> "types.TypeEngine[Any]":
if impl.length is None and dialect.name == "mysql":
return dialect.type_descriptor(types.String(self.mysql_default_length))
return super().load_dialect_impl(dialect)
-
-
-# Reference form SQLAlchemy docs: https://docs.sqlalchemy.org/en/14/core/custom_types.html#backend-agnostic-guid-type
-# with small modifications
-class GUID(types.TypeDecorator): # type: ignore
- """Platform-independent GUID type.
-
- Uses PostgreSQL's UUID type, otherwise uses
- CHAR(32), storing as stringified hex values.
-
- """
-
- impl = CHAR
- cache_ok = True
-
- def load_dialect_impl(self, dialect: Dialect) -> TypeEngine[Any]:
- if dialect.name == "postgresql":
- return dialect.type_descriptor(UUID())
- else:
- return dialect.type_descriptor(CHAR(32))
-
- def process_bind_param(self, value: Any, dialect: Dialect) -> Optional[str]:
- if value is None:
- return value
- elif dialect.name == "postgresql":
- return str(value)
- else:
- if not isinstance(value, uuid.UUID):
- return uuid.UUID(value).hex
- else:
- # hexstring
- return value.hex
-
- def process_result_value(self, value: Any, dialect: Dialect) -> Optional[uuid.UUID]:
- if value is None:
- return value
- else:
- if not isinstance(value, uuid.UUID):
- value = uuid.UUID(value)
- return cast(uuid.UUID, value)
diff --git a/tests/test_advanced/test_uuid/__init__.py b/tests/test_advanced/test_uuid/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/test_advanced/test_uuid/test_tutorial001.py b/tests/test_advanced/test_uuid/test_tutorial001.py
new file mode 100644
index 0000000000..405195f8e9
--- /dev/null
+++ b/tests/test_advanced/test_uuid/test_tutorial001.py
@@ -0,0 +1,71 @@
+from unittest.mock import patch
+
+from dirty_equals import IsUUID
+from sqlmodel import create_engine
+
+from ...conftest import get_testing_print_function
+
+
+def test_tutorial(clear_sqlmodel) -> None:
+ from docs_src.advanced.uuid import tutorial001 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()
+ first_uuid = calls[1][0]["id"]
+ assert first_uuid == IsUUID(4)
+
+ second_uuid = calls[7][0]["id"]
+ assert second_uuid == IsUUID(4)
+
+ assert first_uuid != second_uuid
+
+ assert calls == [
+ ["The hero before saving in the DB"],
+ [
+ {
+ "name": "Deadpond",
+ "secret_name": "Dive Wilson",
+ "id": first_uuid,
+ "age": None,
+ }
+ ],
+ ["The hero ID was already set"],
+ [first_uuid],
+ ["After saving in the DB"],
+ [
+ {
+ "name": "Deadpond",
+ "secret_name": "Dive Wilson",
+ "age": None,
+ "id": first_uuid,
+ }
+ ],
+ ["Created hero:"],
+ [
+ {
+ "name": "Spider-Boy",
+ "secret_name": "Pedro Parqueador",
+ "age": None,
+ "id": second_uuid,
+ }
+ ],
+ ["Created hero ID:"],
+ [second_uuid],
+ ["Selected hero:"],
+ [
+ {
+ "name": "Spider-Boy",
+ "secret_name": "Pedro Parqueador",
+ "age": None,
+ "id": second_uuid,
+ }
+ ],
+ ["Selected hero ID:"],
+ [second_uuid],
+ ]
diff --git a/tests/test_advanced/test_uuid/test_tutorial001_py310.py b/tests/test_advanced/test_uuid/test_tutorial001_py310.py
new file mode 100644
index 0000000000..ee8cb085df
--- /dev/null
+++ b/tests/test_advanced/test_uuid/test_tutorial001_py310.py
@@ -0,0 +1,72 @@
+from unittest.mock import patch
+
+from dirty_equals import IsUUID
+from sqlmodel import create_engine
+
+from ...conftest import get_testing_print_function, needs_py310
+
+
+@needs_py310
+def test_tutorial(clear_sqlmodel) -> None:
+ from docs_src.advanced.uuid import tutorial001_py310 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()
+ first_uuid = calls[1][0]["id"]
+ assert first_uuid == IsUUID(4)
+
+ second_uuid = calls[7][0]["id"]
+ assert second_uuid == IsUUID(4)
+
+ assert first_uuid != second_uuid
+
+ assert calls == [
+ ["The hero before saving in the DB"],
+ [
+ {
+ "name": "Deadpond",
+ "secret_name": "Dive Wilson",
+ "id": first_uuid,
+ "age": None,
+ }
+ ],
+ ["The hero ID was already set"],
+ [first_uuid],
+ ["After saving in the DB"],
+ [
+ {
+ "name": "Deadpond",
+ "secret_name": "Dive Wilson",
+ "age": None,
+ "id": first_uuid,
+ }
+ ],
+ ["Created hero:"],
+ [
+ {
+ "name": "Spider-Boy",
+ "secret_name": "Pedro Parqueador",
+ "age": None,
+ "id": second_uuid,
+ }
+ ],
+ ["Created hero ID:"],
+ [second_uuid],
+ ["Selected hero:"],
+ [
+ {
+ "name": "Spider-Boy",
+ "secret_name": "Pedro Parqueador",
+ "age": None,
+ "id": second_uuid,
+ }
+ ],
+ ["Selected hero ID:"],
+ [second_uuid],
+ ]
diff --git a/tests/test_advanced/test_uuid/test_tutorial002.py b/tests/test_advanced/test_uuid/test_tutorial002.py
new file mode 100644
index 0000000000..cefd95ba49
--- /dev/null
+++ b/tests/test_advanced/test_uuid/test_tutorial002.py
@@ -0,0 +1,71 @@
+from unittest.mock import patch
+
+from dirty_equals import IsUUID
+from sqlmodel import create_engine
+
+from ...conftest import get_testing_print_function
+
+
+def test_tutorial(clear_sqlmodel) -> None:
+ from docs_src.advanced.uuid import tutorial002 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()
+ first_uuid = calls[1][0]["id"]
+ assert first_uuid == IsUUID(4)
+
+ second_uuid = calls[7][0]["id"]
+ assert second_uuid == IsUUID(4)
+
+ assert first_uuid != second_uuid
+
+ assert calls == [
+ ["The hero before saving in the DB"],
+ [
+ {
+ "name": "Deadpond",
+ "secret_name": "Dive Wilson",
+ "id": first_uuid,
+ "age": None,
+ }
+ ],
+ ["The hero ID was already set"],
+ [first_uuid],
+ ["After saving in the DB"],
+ [
+ {
+ "name": "Deadpond",
+ "secret_name": "Dive Wilson",
+ "age": None,
+ "id": first_uuid,
+ }
+ ],
+ ["Created hero:"],
+ [
+ {
+ "name": "Spider-Boy",
+ "secret_name": "Pedro Parqueador",
+ "age": None,
+ "id": second_uuid,
+ }
+ ],
+ ["Created hero ID:"],
+ [second_uuid],
+ ["Selected hero:"],
+ [
+ {
+ "name": "Spider-Boy",
+ "secret_name": "Pedro Parqueador",
+ "age": None,
+ "id": second_uuid,
+ }
+ ],
+ ["Selected hero ID:"],
+ [second_uuid],
+ ]
diff --git a/tests/test_advanced/test_uuid/test_tutorial002_py310.py b/tests/test_advanced/test_uuid/test_tutorial002_py310.py
new file mode 100644
index 0000000000..96f85c5333
--- /dev/null
+++ b/tests/test_advanced/test_uuid/test_tutorial002_py310.py
@@ -0,0 +1,72 @@
+from unittest.mock import patch
+
+from dirty_equals import IsUUID
+from sqlmodel import create_engine
+
+from ...conftest import get_testing_print_function, needs_py310
+
+
+@needs_py310
+def test_tutorial(clear_sqlmodel) -> None:
+ from docs_src.advanced.uuid import tutorial002_py310 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()
+ first_uuid = calls[1][0]["id"]
+ assert first_uuid == IsUUID(4)
+
+ second_uuid = calls[7][0]["id"]
+ assert second_uuid == IsUUID(4)
+
+ assert first_uuid != second_uuid
+
+ assert calls == [
+ ["The hero before saving in the DB"],
+ [
+ {
+ "name": "Deadpond",
+ "secret_name": "Dive Wilson",
+ "id": first_uuid,
+ "age": None,
+ }
+ ],
+ ["The hero ID was already set"],
+ [first_uuid],
+ ["After saving in the DB"],
+ [
+ {
+ "name": "Deadpond",
+ "secret_name": "Dive Wilson",
+ "age": None,
+ "id": first_uuid,
+ }
+ ],
+ ["Created hero:"],
+ [
+ {
+ "name": "Spider-Boy",
+ "secret_name": "Pedro Parqueador",
+ "age": None,
+ "id": second_uuid,
+ }
+ ],
+ ["Created hero ID:"],
+ [second_uuid],
+ ["Selected hero:"],
+ [
+ {
+ "name": "Spider-Boy",
+ "secret_name": "Pedro Parqueador",
+ "age": None,
+ "id": second_uuid,
+ }
+ ],
+ ["Selected hero ID:"],
+ [second_uuid],
+ ]