You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/customization.qmd
+30-3Lines changed: 30 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -41,7 +41,7 @@ To run the tests, use these commands:
41
41
The project uses type annotations and mypy for static type checking. To run mypy, use this command from the root directory:
42
42
43
43
```bash
44
-
mypy
44
+
mypy.
45
45
```
46
46
47
47
We find that mypy is an enormous time-saver, catching many errors early and greatly reducing time spent debugging unit tests. However, note that mypy requires you type annotate every variable, function, and method in your code base, so taking advantage of it requires a lifestyle change!
The session automatically handles transaction management, ensuring that database operations are atomic and consistent.
295
+
296
+
#### Cascade deletes
297
+
298
+
Cascade deletes (in which deleting a record from one table deletes related records from another table) can be handled at either the ORM level or the database level. This template handles cascade deletes at the ORM level, via SQLModel relationships. Inside a SQLModel `Relationship`, we set:
299
+
300
+
```python
301
+
sa_relationship_kwargs={
302
+
"cascade": "all, delete-orphan"
303
+
}
304
+
```
305
+
306
+
This tells SQLAlchemy to cascade all operations (e.g., `SELECT`, `INSERT`, `UPDATE`, `DELETE`) to the related table. Since this happens through the ORM, we need to be careful to do all our database operations through the ORM using supported syntax. That generally means loading database records into Python objects and then deleting those objects rather than deleting records in the database directly.
307
+
308
+
For example,
309
+
310
+
```python
311
+
session.exec(delete(Role))
312
+
```
313
+
314
+
will not trigger the cascade delete. Instead, we need to select the role objects and then delete them:
315
+
316
+
```python
317
+
for role in session.exec(select(Role)).all():
318
+
session.delete(role)
319
+
```
320
+
321
+
This is slower than deleting the records directly, but it makes [many-to-many relationships](https://sqlmodel.tiangolo.com/tutorial/many-to-many/create-models-with-link/#create-the-tables) much easier to manage.
Copy file name to clipboardExpand all lines: docs/static/llms.txt
+62-9Lines changed: 62 additions & 9 deletions
Original file line number
Diff line number
Diff line change
@@ -105,6 +105,8 @@ To use password recovery, register a [Resend](https://resend.com/) account, veri
105
105
106
106
### Start development database
107
107
108
+
To start the development database, run the following command in your terminal from the root directory:
109
+
108
110
``` bash
109
111
docker compose up -d
110
112
```
@@ -515,6 +517,8 @@ If you use VSCode with Docker to develop in a container, the following VSCode De
515
517
516
518
Simply create a `.devcontainer` folder in the root of the project and add a `devcontainer.json` file in the folder with the above content. VSCode may prompt you to install the Dev Container extension if you haven't already, and/or to open the project in a container. If not, you can manually select "Dev Containers: Reopen in Container" from View > Command Palette.
517
519
520
+
*IMPORTANT: If using this dev container configuration, you will need to set the `DB_HOST` environment variable to "host.docker.internal" in the `.env` file.*
521
+
518
522
## Install development dependencies manually
519
523
520
524
### Python and Docker
@@ -598,15 +602,32 @@ Set your desired database name, username, and password in the .env file.
598
602
599
603
To use password recovery, register a [Resend](https://resend.com/) account, verify a domain, get an API key, and paste the API key into the .env file.
600
604
605
+
If using the dev container configuration, you will need to set the `DB_HOST` environment variable to "host.docker.internal" in the .env file. Otherwise, set `DB_HOST` to "localhost" for local development. (In production, `DB_HOST` will be set to the hostname of the database server.)
606
+
601
607
## Start development database
602
608
609
+
To start the development database, run the following command in your terminal from the root directory:
610
+
603
611
``` bash
604
612
docker compose up -d
605
613
```
606
614
615
+
If at any point you change the environment variables in the .env file, you will need to stop the database service *and tear down the volume*:
616
+
617
+
``` bash
618
+
# Don't forget the -v flag to tear down the volume!
619
+
docker compose down -v
620
+
```
621
+
622
+
You may also need to restart the terminal session to pick up the new environment variables. You can also add the `--force-recreate` and `--build` flags to the startup command to ensure the container is rebuilt:
623
+
624
+
``` bash
625
+
docker compose up -d --force-recreate --build
626
+
```
627
+
607
628
## Run the development server
608
629
609
-
Make sure the development database is running and tables and default permissions/roles are created first.
630
+
Before running the development server, make sure the development database is running and tables and default permissions/roles are created first. Then run the following command in your terminal from the root directory:
@@ -646,7 +667,8 @@ The following fixtures, defined in `tests/conftest.py`, are available in the tes
646
667
- `set_up_database`: Sets up the test database before running the test suite by dropping all tables and recreating them to ensure a clean state.
647
668
- `session`: Provides a session for database operations in tests.
648
669
- `clean_db`: Cleans up the database tables before each test by deleting all entries in the `PasswordResetToken` and `User` tables.
649
-
- `client`: Provides a `TestClient` instance with the session fixture, overriding the `get_session` dependency to use the test session.
670
+
- `auth_client`: Provides a `TestClient` instance with access and refresh token cookies set, overriding the `get_session` dependency to use the `session` fixture.
671
+
- `unauth_client`: Provides a `TestClient` instance without authentication cookies set, overriding the `get_session` dependency to use the `session` fixture.
650
672
- `test_user`: Creates a test user in the database with a predefined name, email, and hashed password.
651
673
652
674
To run the tests, use these commands:
@@ -661,10 +683,10 @@ To run the tests, use these commands:
661
683
The project uses type annotations and mypy for static type checking. To run mypy, use this command from the root directory:
662
684
663
685
```bash
664
-
mypy
686
+
mypy .
665
687
```
666
688
667
-
We find that mypy is an enormous time-saver, catching many errors early and greatly reducing time spent debugging unit tests. However, note that mypy requires you type annotate every variable, function, and method in your code base, so taking advantage of it is a lifestyle change!
689
+
We find that mypy is an enormous time-saver, catching many errors early and greatly reducing time spent debugging unit tests. However, note that mypy requires you type annotate every variable, function, and method in your code base, so taking advantage of it requires a lifestyle change!
668
690
669
691
### Developing with LLMs
670
692
@@ -705,15 +727,19 @@ We also create POST endpoints, which accept form submissions so the user can cre
705
727
706
728
#### Routing patterns in this template
707
729
708
-
In this template, GET routes are defined in the main entry point for the application, `main.py`. POST routes are organized into separate modules within the `routers/` directory. We name our GET routes using the convention `read_<name>`, where `<name>` is the name of the page, to indicate that they are read-only endpoints that do not modify the database.
730
+
In this template, GET routes are defined in the main entry point for the application, `main.py`. POST routes are organized into separate modules within the `routers/` directory.
731
+
732
+
We name our GET routes using the convention `read_<name>`, where `<name>` is the name of the page, to indicate that they are read-only endpoints that do not modify the database.
709
733
710
734
We divide our GET routes into authenticated and unauthenticated routes, using commented section headers in our code that look like this:
711
735
712
736
```python
713
737
# -- Authenticated Routes --
714
738
```
715
739
716
-
Some of our routes take request parameters, which we pass as keyword arguments to the route handler. These parameters should be type annotated for validation purposes. Some parameters are shared across all authenticated or unauthenticated routes, so we define them in the `common_authenticated_parameters` and `common_unauthenticated_parameters` dependencies defined in `main.py`.
740
+
Some of our routes take request parameters, which we pass as keyword arguments to the route handler. These parameters should be type annotated for validation purposes.
741
+
742
+
Some parameters are shared across all authenticated or unauthenticated routes, so we define them in the `common_authenticated_parameters` and `common_unauthenticated_parameters` dependencies defined in `main.py`.
In this example, the `welcome.html` template will receive two pieces of context: the user's `request`, which is always passed automatically by FastAPI, and a `username` variable, which we specify as "Alice". We can then use the `{{ username }}` syntax in the `welcome.html` template (or any of its parent or child templates) to insert the value into the HTML.
763
+
In this example, the `welcome.html` template will receive two pieces of context: the user's `request`, which is always passed automatically by FastAPI, and a `username` variable, which we specify as "Alice". We can then use the `{{{ username }}}` syntax in the `welcome.html` template (or any of its parent or child templates) to insert the value into the HTML.
The session automatically handles transaction management, ensuring that database operations are atomic and consistent.
911
937
938
+
#### Cascade deletes
939
+
940
+
Cascade deletes (in which deleting a record from one table deletes related records from another table) can be handled at either the ORM level or the database level. This template handles cascade deletes at the ORM level, via SQLModel relationships. Inside a SQLModel `Relationship`, we set:
941
+
942
+
```python
943
+
sa_relationship_kwargs={
944
+
"cascade": "all, delete-orphan"
945
+
}
946
+
```
947
+
948
+
This tells SQLAlchemy to cascade all operations (e.g., `SELECT`, `INSERT`, `UPDATE`, `DELETE`) to the related table. Since this happens through the ORM, we need to be careful to do all our database operations through the ORM using supported syntax. That generally means loading database records into Python objects and then deleting those objects rather than deleting records in the database directly.
949
+
950
+
For example,
951
+
952
+
```python
953
+
session.exec(delete(Role))
954
+
```
955
+
956
+
will not trigger the cascade delete. Instead, we need to select the role objects and then delete them:
957
+
958
+
```python
959
+
for role in session.exec(select(Role)).all():
960
+
session.delete(role)
961
+
```
962
+
963
+
This is slower than deleting the records directly, but it makes [many-to-many relationships](https://sqlmodel.tiangolo.com/tutorial/many-to-many/create-models-with-link/#create-the-tables) much easier to manage.
0 commit comments