Using a decorator, we can map the model columns automatically to a dataclass which is used as a strawberry.type
.
- Automatically sets a type annotation for all supported types below
- Respects
nullable
on the column and will addtyping.Optional
if needed
Relationships are NOT supported automatically. You will have to add them yourself to the strawberry.type
dataclasses.
There is some code which tries to get the relationship from the mapper of the model, but this requires to know upfront how the models and dataclasses are mapped (e.g. BookModel -> Book). You could theoretically assume that all models end with ..Model, but I don't want to go that route.
- Allow a list of columns to be passed which should be excluded from the mapping
SQLAlchemy Column Type | Python Type | Mapping Supported |
---|---|---|
Integer |
int |
✔️ |
SmallInteger |
int |
✔️ |
BigInteger |
int |
✔️ |
Numeric |
decimal.Decimal |
✔️ |
Float |
float |
✔️ |
Boolean |
bool |
✔️ |
Enum |
enum.Enum |
❌ |
Date |
datetime.date |
✔️ |
Time |
datetime.time |
✔️ |
Interval |
datetime.Interval |
❌ |
DateTime |
datetime.datetime |
✔️ |
String |
str |
✔️ |
Text |
str |
✔️ |
Unicode |
str |
✔️ |
UnicodeText |
str |
✔️ |
PickleType |
- |
❌ |
LargeBinary |
bytes |
❌ |
To run the example, use strawberry server example
which will start a local GraphiQL server on localhost:8000/graphql.
The example explained below can be found in example.py
Using SQLAlchemy, we will create a sqlite3 database in memory.
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
engine = create_engine("sqlite:///")
session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
We will go for a simple example with two models: An author and their books.
from sqlalchemy import Column, String, Integer, ForeignKey,
from sqlalchemy.orm import relationship
class AuthorModel(Base):
__tablename__ = "author"
id = Column(Integer, primary_key=True, autoincrement=True)
first_name = Column(String, nullable=False)
last_name = Column(String, nullable=False)
books = relationship("BookModel", backref="author")
class BookModel(Base):
__tablename__ = "book"
book_id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String, nullable=False)
release_year = Column(Integer, nullable=False)
author_id = Column(Integer, ForeignKey("author.id"), nullable=False)
Using the @strawberry_dataclass_from_model(model)
decorator, we don't need to write all fields with their type hints again. The model will be inspected and annotations will be set accordingly.
from strawberry_sqlalchemy_mapper import strawberry_dataclass_from_model
import strawberry
import typing
@strawberry.type
@strawberry_dataclass_from_model(AuthorModel)
class Author:
books: typing.List["Book"]
@strawberry.type
@strawberry_dataclass_from_model(BookModel)
class Book:
author: "Author"
A simple strawberry query which allows us to query a single author or book by id or all authors and books.
@strawberry.type
class Query:
@strawberry.field
def get_author(self, id: strawberry.ID) -> Author:
return session.query(AuthorModel).get(id)
@strawberry.field
def get_all_authors(self) -> typing.List[Author]:
return session.query(AuthorModel).all()
@strawberry.field
def get_book(self, id: strawberry.ID) -> Book:
return session.query(BookModel).get(id)
@strawberry.field
def get_all_books(self) -> typing.List[Book]:
return session.query(BookModel).all()
query {
getAuthor(id: 1) {
firstName
lastName
books {
title
releaseYear
}
}
getAllAuthors {
firstName
lastName
books {
title
releaseYear
}
}
getBook(id: 1) {
title
releaseYear
author {
firstName
lastName
}
}
getAllBooks {
title
releaseYear
author {
firstName
lastName
}
}
}