# Working with Pydantic
# It just works 😎
Out of the box, Mayim works great with Pydantic (opens new window).
To get started, just annotate your executors with a Pydantic model.
import asyncio
from mayim import Mayim, SQLiteExecutor, query
from pydantic import BaseModel
class Person(BaseModel):
name: str
class PersonExecutor(SQLiteExecutor):
@query("SELECT $name as name")
async def select_person(self, name: str) -> Person:
...
async def run():
executor = PersonExecutor()
Mayim(db_path="./example.db")
print(await executor.select_person(name="Adam"))
asyncio.run(run())
(This script is complete, it should run "as is")
# Nested models
But, that is kind of boring and really misses one of the great features of Pydantic: it's ability to hydrate nested data. Going back to one of our earlier examples, what if we wanted to retrieve some data that looked like this:
class City(BaseModel):
id: int
name: str
district: str
population: int
class Country(BaseModel):
code: str
name: str
continent: str
region: str
capital: City
Our goal is to query for some countries, but we also want to capture the nested city information and have it available as country.capital
, which is shown in the models.
To do this, we can use SQL to generate rows that have nested JSON blobs as columns. When these queries are made, they will be output as nested dict
objects that Pydantic will easily convert to our usable form.
We start by making a .sql
file with our desired query:
-- ./queries/select_all_countries.sql
SELECT country.code,
country.name,
country.continent,
country.region,
(
SELECT row_to_json(q)
FROM (
SELECT city.id,
city.name,
city.district,
city.population
) q
) capital
FROM country
JOIN city ON country.capital = city.id
ORDER BY country.name ASC
LIMIT $limit OFFSET $offset;
Next, we make and run an executor like any other.
class CountryExecutor(PostgresExecutor):
async def select_all_countries(
self, limit: int = 4, offset: int = 0
) -> List[Country]:
...
async def run():
country_executor = CountryExecutor()
Mayim(dsn="postgres://postgres:postgres@localhost:5432/world")
print(
await country_executor.select_all_countries(50_000_000)
)
As you would expect, we now have nice nested models:
[
Country(
code="AFG",
name="Afghanistan",
continent="Asia",
region="Southern and Central Asia",
capital=City(id=1, name="Kabul", district="Kabol", population=1780000),
),
Country(
code="ALB",
name="Albania",
continent="Europe",
region="Southern Europe",
capital=City(id=34, name="Tirana", district="Tirana", population=270000),
),
Country(
code="DZA",
name="Algeria",
continent="Africa",
region="Northern Africa",
capital=City(id=35, name="Alger", district="Alger", population=2168000),
),
Country(
code="ASM",
name="American Samoa",
continent="Oceania",
region="Polynesia",
capital=City(id=54, name="Fagatogo", district="Tutuila", population=2323),
),
]