Tutorials

This section will cover:

  • basic and advanced concepts creating a basic model with pydbantic
  • linking DataBaseModels with a database
  • integrating DataBaseModel within a FastAPI application and create some data
  • modifying our model to demonstrate migrations.

Creating a Model

Here we create a DataBaseModel which stores employee information. This model contains only standard types and typing extensions Optional indicating whether a null value is allowed

# models.py
class EmployeeInfo(DataBaseModel):
    ssn: str
    first_name: str
    last_name: str
    address: str
    address2: Optional[str]
    city: Optional[str]
    zip: Optional[int]

Creating Models with Relationships

Here we are extending our existing EmployeeInfo model with a new DataBaseModel name Employees which will populate data from EmployeeInfo based on an automatic relationship between Employees.id & EmployeeInfo.ssn

class Employees(DataBaseModel):
    id: str
    employee_info: EmployeeInfo
    salary: float
    is_employed: bool
    date_employed: Optional[str]

Linking Models with a database

Models are linked with a database by feeding a list of DataBaseModels into a pydbantic Database at creation.

import asyncio
from pydbantic import Database
from models import Employees

async def main():
    db = await Database.create(
        'sqlite:///test.db',
        tables=[Employees]
    )

if __name__ == '__main__':
    asyncio.run(main())

Note

The first parameter to Database.create is a URL DB connection string. This is needed for all database types and should contain relevant user / pw, host, port and other user requirements as necessary.

Using Models with a FastAPI application

As DataBaseModels are inherently pydantic BaseModels, they work seamlessly with the FastAPI openAPI schema, generating self documenting API's with expected input / response data structures.

Here we link our existing Employees model with a basic API that creates new employees, lists existing employees

# fastapi application
from typing import List
from fastapi import FastAPI
from pydbantic import Database
from tests.models import Employee

app = FastAPI()

@app.on_event('startup')
async def db_setup():
    db = await Database.create(
        'sqlite:///company.db',
        tables=[Employees],
    )

@app.post('/employee')
async def new_example(employee: Employee):
    return await employee.insert()

@app.get('/employees', response_model=List[Employees])
async def view_employees():
    return await Employee.all()

At this point we should be able to start our Application and feed in some data to create persistent Employees objects.

Note

When viewed from the application /docs, the view_employees endpoint should indicate the response should be a List of Employees.

Changing a DataBaseModel - Adding a new Dependent Model

Here we will add a new field to our existing Employees model which is also a DataBaseModel representing employee positions in a company.

## models.py
from enum import Enum
from pydbantic import DataBaseModel, PrimaryKey

class PositionNames(str, Enum):
    manager: str = 'manager'
    director: str 'director'
    engineer: str 'engineer'


class Positions(DataBaseModel):
    id: str = PrimaryKey()
    name: PositionNames


## Adding Positions to Employee
class Employees(DataBaseModel):
    id: str = PrimaryKey()
    employee_info: EmployeeInfo
    position: Positions = Position(id='p123', name='engineer')
    salary: float
    is_employed: bool
    date_employed: Optional[str]

The next time we start our application, the new schema will be detected and existing data migrated with new schema.

Tip

Default Values or Optional types are needed when adding a field to an existing model. Once migrated, the default value fields can be removed if undesired.