Skip to content

Commit

Permalink
Add additional documentation pages
Browse files Browse the repository at this point in the history
  • Loading branch information
BenMillar-MOJ committed Oct 29, 2024
1 parent 7634a2d commit b69cb78
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/source/documentation/case.html.md.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
title: Case model
---

# Cases

### Create a case
Expand Down
73 changes: 73 additions & 0 deletions docs/source/documentation/models.html.md.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
title: Models
---

# What are models?
Models are classes which define both the API and database schema.

Each field has Python type annotations which defines what data is accepted by the API
as well as the column type in the database.

Every object created from these classes is validated by Pydantic, which ensures that the types of every object matches
the type defined in the model, always.

View the SQLModel documentation [here](https://sqlmodel.tiangolo.com/).

## What data types can I use in models?
All datatypes supported by Pydantic can be used, and you can define your own if needed.

View the Pydantic documentation [here](https://docs.pydantic.dev/latest/concepts/types/#constrained-types).

To allow for a set of values you can define an enum and use this in your model.

```python
from enum import Enum
from sqlmodel import SQLModel, Field


class Month(str, Enum):
# jan will be stored in the database and "January" will be an acceptable input.
jan = "January"
fed = "February"
mar = "March"


class Birthday(SQLModel):
day: int = Field(gt=0, le=31) # A number greater than 0 but less than or equal to 31.
month: Month # Valid inputs would be "January", "February", "March"
```

## How do I create a database table?
By default, models will not be created as a table in the database.

This is because we use models to define more than the database, they also define request and response
models from the API.

To create a table with the schema defined by your model set the `table` argument on your class to True.

```python
from sqlmodel import SQLModel


class MyModel(SQLModel, table=True):
name: str
age: int
```

To reflect this new model in the database you will need to generate and run an Alembic migration.

## How do I use these models to define endpoint schemas?
By implementing them as part of your routers' method arguments they are automatically used to validate the user's input.

```python
from app.models.cases import Case, CaseRequest
from app.routers.case_information import router


@router.post("/", tags=["cases"], response_model=Case)
def create_case(request: CaseRequest):
pass
```

### Model documentation
Your models will automatically be added to the Swagger documentation when you use them as part of an API endpoint.
83 changes: 83 additions & 0 deletions docs/source/documentation/routers.html.md.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
title: Endpoints
---

# Creating new endpoints
To create new endpoints you need to define the URL route it belongs to and which request method it accepts.

## APIRouter
Each route needs to belong to an APIRouter. APIRouters are analogous to Flask Blueprints in that they group endpoints
together and can be assigned shared a prefix, tags and other attributes.

See [here](https://fastapi.tiangolo.com/reference/apirouter/) for the documentation on the APIRouter class.

```python
from fastapi import APIRouter
from sqlmodel import SQLModel
from app.models.types.categories import Categories

router = APIRouter(prefix="/case",
tags=["cases"])


@router.get("/{case_id}")
async def read_case(case_id: int):
# We will discuss how to read from the database below.
case = {"id": 1,
"name": "test",
"category": "Housing"}
return case


class Case(SQLModel):
name: str # Pydantic ensures the name is always a string
category: Categories | None = None # This means that the category must be of the type Categories or None


@router.post("/")
async def create_case(case: Case):
return case
```
These create_case route only accepts post requests and requires the request body to be of the form defined by
the Case model.

This means the body needs to contain a name which can be encoded as a string and a category which
matches one of the categories of law defined in the models enum class.


## Reading and writing to the database
Database connections are managed with SQLModel sessions, these are inherited from SQLAlchemy sessions.

If your endpoint depends on a database session you can pass this into your routing function using the
FastAPI `Depends()` method.

```python
from fastapi import APIRouter, HTTPException, Depends
from app.models.cases import CaseRequest, Case
from sqlmodel import Session, select
from app.db import get_session
from datetime import datetime

router = APIRouter(
prefix="/cases",
tags=["cases"],
responses={404: {"description": "Not found"}},
)


@router.get("/{case_id}", tags=["cases"])
async def read_case(case_id: str, session: Session = Depends(get_session)):
case = session.get(Case, case_id)
if not case:
raise HTTPException(status_code=404, detail="Case not found")
return case


@router.post("/", tags=["cases"], response_model=Case)
def create_case(request: CaseRequest, session: Session = Depends(get_session)):
case = Case(category=request.category, time=datetime.now(), name=request.name, id=1)
session.add(case)
session.commit()
session.refresh(case)
return case
```

0 comments on commit b69cb78

Please sign in to comment.