diff --git a/docs/advanced/constraints.md b/docs/advanced/constraints.md
new file mode 100644
index 0000000000..a76244e40f
--- /dev/null
+++ b/docs/advanced/constraints.md
@@ -0,0 +1,90 @@
+# Database Constraints
+
+In some cases you might want to enforce rules about your data directly at the **database level**. For example, making sure that a hero's name is unique, or that their age is never negative. π¦ΈββοΈ
+
+These rules are called **constraints**, and because they live in the database, they work regardless of which application is inserting the data. This is particularly important for data consistency in production systems.
+
+/// info
+
+**SQLModel** uses SQLAlchemy's constraint system under the hood, so you have access to all the powerful constraint options available in SQLAlchemy.
+
+///
+
+## Unique Constraints
+
+Let's say you want to make sure that no two heroes can have the same name. The simplest way to do this is with the `unique` parameter in `Field()`:
+
+{* ./docs_src/advanced/constraints/tutorial001_py310.py ln[4:8] hl[6] *}
+
+Now the `name` field must be unique across all heroes. If you try to insert a hero with a name that already exists, the database will raise an error.
+
+So two heroes named "Deadpond" and "Spider-Boy" would work fine, but trying to add a second "Deadpond" would fail.
+
+## Multi-Column Unique Constraints
+
+Sometimes you don't need each individual field to be unique, but you want a **combination** of fields to be unique. For example, you might allow multiple heroes named "Spider-Boy" as long as they have different ages.
+
+You can do this using `__table_args__` with a `UniqueConstraint`:
+
+{* ./docs_src/advanced/constraints/tutorial002_py310.py ln[5:11] hl[6] *}
+
+With this setup, "Spider-Boy" aged 16 and "Spider-Boy" aged 25 are both allowed, because the **combination** of name and age is different. But two heroes both named "Spider-Boy" and both aged 16 would be rejected.
+
+/// tip
+
+You can include as many fields as needed in a `UniqueConstraint`. For example, `UniqueConstraint("name", "age", "team")` would require the combination of all three fields to be unique.
+
+///
+
+## Check Constraints
+
+Check constraints let you define custom validation rules using SQL expressions. This is handy for enforcing business rules, like making sure a hero's age is never negative:
+
+{* ./docs_src/advanced/constraints/tutorial003_py310.py ln[5:11] hl[6] *}
+
+Here we're saying that `age` must be greater than or equal to zero. The `name` parameter gives the constraint a descriptive label, which makes error messages much easier to understand.
+
+So heroes with age 0, 16, or 100 would all be fine, but trying to insert a hero with age -5 would fail.
+
+## Combining Multiple Constraints
+
+You can mix different types of constraints in the same model by adding multiple constraint objects to `__table_args__`:
+
+{* ./docs_src/advanced/constraints/tutorial004_py310.py ln[5:15] hl[6:10] *}
+
+This model has three constraints working together: the combination of `name` and `age` must be unique, age cannot be negative, and the name must be at least 2 characters long. All constraints must be satisfied for data to be inserted successfully.
+
+## What Happens When a Constraint is Violated?
+
+If you try to insert data that breaks a constraint, the database will raise an error. SQLAlchemy wraps this as an `IntegrityError`. Here's what that looks like in practice:
+
+{* ./docs_src/advanced/constraints/tutorial005_py310.py ln[25:38] hl[32:37] *}
+
+When you run this code, you'll see that the first hero is created successfully, but the attempt to create a duplicate fails with a clear error message.
+
+
+
+```console
+$ python app.py
+
+// Some boilerplate and previous output omitted π
+
+β
Created hero: id=1 age=48 secret_name='Dive Wilson' name='Deadpond'
+π« Constraint violation caught:
+ Error: (sqlite3.IntegrityError) UNIQUE constraint failed: hero.name
+[SQL: INSERT INTO hero (name, age, secret_name) VALUES (?, ?, ?)]
+[parameters: ('Deadpond', 25, 'Wade Wilson')]
+(Background on this error at: https://sqlalche.me/e/20/gkpj)
+```
+
+
+
+This error handling lets you gracefully manage constraint violations in your application instead of having your program crash unexpectedly. π‘οΈ
+
+/// warning
+
+Not all databases support all types of constraints equally. In particular, **SQLite** has limitations with some complex SQL expressions in check constraints. Make sure to test your constraints with your target database.
+
+Most other SQL databases like **PostgreSQL** and **MySQL** have full or near-full support. π
+
+///
diff --git a/docs_src/advanced/constraints/__init__.py b/docs_src/advanced/constraints/__init__.py
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/docs_src/advanced/constraints/__init__.py
@@ -0,0 +1 @@
+
diff --git a/docs_src/advanced/constraints/tutorial001_py310.py b/docs_src/advanced/constraints/tutorial001_py310.py
new file mode 100644
index 0000000000..9ff0c8c34c
--- /dev/null
+++ b/docs_src/advanced/constraints/tutorial001_py310.py
@@ -0,0 +1,42 @@
+from sqlmodel import Field, Session, SQLModel, create_engine
+
+
+class Hero(SQLModel, table=True):
+ id: int | None = Field(default=None, primary_key=True)
+ name: str = Field(unique=True)
+ age: int
+ secret_name: str
+
+
+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", age=48, secret_name="Dive Wilson")
+ hero_2 = Hero(name="Spider-Boy", age=16, secret_name="Pedro Parqueador")
+
+ with Session(engine) as session:
+ session.add(hero_1)
+ session.add(hero_2)
+ session.commit()
+ session.refresh(hero_1)
+ session.refresh(hero_2)
+
+ print("Created hero:", hero_1)
+ print("Created hero:", hero_2)
+
+
+def main():
+ create_db_and_tables()
+ create_heroes()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs_src/advanced/constraints/tutorial002_py310.py b/docs_src/advanced/constraints/tutorial002_py310.py
new file mode 100644
index 0000000000..a3cd30ed20
--- /dev/null
+++ b/docs_src/advanced/constraints/tutorial002_py310.py
@@ -0,0 +1,45 @@
+from sqlalchemy import UniqueConstraint
+from sqlmodel import Field, Session, SQLModel, create_engine
+
+
+class Hero(SQLModel, table=True):
+ __table_args__ = (UniqueConstraint("name", "age"),)
+
+ id: int | None = Field(default=None, primary_key=True)
+ name: str
+ age: int
+ secret_name: str
+
+
+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="Spider-Boy", age=16, secret_name="Pedro Parqueador")
+ hero_2 = Hero(name="Spider-Boy", age=25, secret_name="Different Person")
+
+ with Session(engine) as session:
+ session.add(hero_1)
+ session.add(hero_2)
+ session.commit()
+ session.refresh(hero_1)
+ session.refresh(hero_2)
+
+ print("Created hero:", hero_1)
+ print("Created hero:", hero_2)
+
+
+def main():
+ create_db_and_tables()
+ create_heroes()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs_src/advanced/constraints/tutorial003_py310.py b/docs_src/advanced/constraints/tutorial003_py310.py
new file mode 100644
index 0000000000..7e81a66e06
--- /dev/null
+++ b/docs_src/advanced/constraints/tutorial003_py310.py
@@ -0,0 +1,45 @@
+from sqlalchemy import CheckConstraint
+from sqlmodel import Field, Session, SQLModel, create_engine
+
+
+class Hero(SQLModel, table=True):
+ __table_args__ = (CheckConstraint("age >= 0", name="age_non_negative"),)
+
+ id: int | None = Field(default=None, primary_key=True)
+ name: str
+ age: int
+ secret_name: str
+
+
+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="Spider-Boy", age=16, secret_name="Pedro Parqueador")
+ hero_2 = Hero(name="Baby Hero", age=0, secret_name="Little One")
+
+ with Session(engine) as session:
+ session.add(hero_1)
+ session.add(hero_2)
+ session.commit()
+ session.refresh(hero_1)
+ session.refresh(hero_2)
+
+ print("Created hero:", hero_1)
+ print("Created hero:", hero_2)
+
+
+def main():
+ create_db_and_tables()
+ create_heroes()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs_src/advanced/constraints/tutorial004_py310.py b/docs_src/advanced/constraints/tutorial004_py310.py
new file mode 100644
index 0000000000..cafb4dcc86
--- /dev/null
+++ b/docs_src/advanced/constraints/tutorial004_py310.py
@@ -0,0 +1,49 @@
+from sqlalchemy import CheckConstraint, UniqueConstraint
+from sqlmodel import Field, Session, SQLModel, create_engine
+
+
+class Hero(SQLModel, table=True):
+ __table_args__ = (
+ UniqueConstraint("name", "age"),
+ CheckConstraint("age >= 0", name="age_non_negative"),
+ CheckConstraint("LENGTH(name) >= 2", name="name_min_length"),
+ )
+
+ id: int | None = Field(default=None, primary_key=True)
+ name: str
+ age: int
+ secret_name: str
+
+
+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="Spider-Boy", age=16, secret_name="Pedro Parqueador")
+ hero_2 = Hero(name="Captain Marvel", age=25, secret_name="Carol Danvers")
+
+ with Session(engine) as session:
+ session.add(hero_1)
+ session.add(hero_2)
+ session.commit()
+ session.refresh(hero_1)
+ session.refresh(hero_2)
+
+ print("Created hero:", hero_1)
+ print("Created hero:", hero_2)
+
+
+def main():
+ create_db_and_tables()
+ create_heroes()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs_src/advanced/constraints/tutorial005_py310.py b/docs_src/advanced/constraints/tutorial005_py310.py
new file mode 100644
index 0000000000..63296343c9
--- /dev/null
+++ b/docs_src/advanced/constraints/tutorial005_py310.py
@@ -0,0 +1,50 @@
+from sqlalchemy.exc import IntegrityError
+from sqlmodel import Field, Session, SQLModel, create_engine
+
+
+class Hero(SQLModel, table=True):
+ id: int | None = Field(default=None, primary_key=True)
+ name: str = Field(unique=True)
+ age: int
+ secret_name: str
+
+
+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", age=48, secret_name="Dive Wilson")
+
+ with Session(engine) as session:
+ session.add(hero_1)
+ session.commit()
+ session.refresh(hero_1)
+ print("β
Created hero:", hero_1)
+
+ # Now try to create another hero with the same name
+ duplicate_hero = Hero(name="Deadpond", age=25, secret_name="Wade Wilson")
+ session.add(duplicate_hero)
+
+ try:
+ session.commit()
+ print("β This shouldn't happen - duplicate was allowed!")
+ except IntegrityError as e:
+ session.rollback()
+ print("π« Constraint violation caught:")
+ print(f" Error: {e}")
+
+
+def main():
+ create_db_and_tables()
+ create_heroes()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/mkdocs.yml b/mkdocs.yml
index b89516e024..12ed13acd1 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -127,6 +127,7 @@ nav:
- tutorial/fastapi/tests.md
- Advanced User Guide:
- advanced/index.md
+ - advanced/constraints.md
- advanced/decimal.md
- advanced/uuid.md
- Resources: