Package: aio-strawberry-sqlalchemy-mapper
This fork is a heavily modified version ofstrawberry-sqlalchemy-mapperwith the following additions/changes:Implements relay pagination (usingthis sqlakeyset fork)Fully asyncUses SQLAlchmy 2.0 stylePydantic integration (generated types can also be pydantic models)strawberry-sqlalchemy-mapperStrawberry-sqlalchemy-mapper is the simplest way to implement autogenerated strawberry types for columns and relationships in SQLAlchemy models.Instead of manually listing every column and relationship in a SQLAlchemy model, strawberry-sqlalchemy-mapper
lets you decorate a class declaration and it will automatically generate the necessary strawberry fields
for all columns and relationships (subject to the limitations below) in the given model.Native support for most of SQLAlchemy's most common types.Extensible to arbitrary custom SQLAlchemy types.Automatic batching of queries, avoiding N+1 queries when getting relationshipsSupport for SQLAlchemy >=1.4.xLightweight and fast.Getting Startedstrawberry-sqlalchemy-mapper is available onPyPipip install strawberry-sqlalchemy-mapperFirst, define your sqlalchemy model:# models.pyfromsqlalchemyimportColumn,Integer,Stringfromsqlalchemy.ext.declarativeimportdeclarative_baseBase=declarative_base()classEmployee(Base):__tablename__='employee'id=Column(UUID,primary_key=True)name=Column(String,nullable=False)password_hash=Column(String,nullable=False)department_id=Column(UUID,ForeignKey('department.id'))department=relationship('Department',back_populates='employees')classDepartment(Base):__tablename__="department"id=Column(UUID,primary_key=True)name=Column(String,nullable=False)employees=relationship('Employee',back_populates='department')Next, decorate a type withstrawberry_sqlalchemy_mapper.type()to register it as a strawberry type for the given SQLAlchemy model.
This will automatically add fields for the model's columns, relationships, association proxies,
and hybrid properties. For example:# elsewhere# ...fromstrawberry_sqlalchemy_mapperimportStrawberrySQLAlchemyMapperstrawberry_sqlalchemy_mapper=StrawberrySQLAlchemyMapper()@strawberry_sqlalchemy_mapper.type(models.Employee)classEmployee:__exclude__=["password_hash"]@strawberry_sqlalchemy_mapper.type(models.Department)classDepartment:pass@strawberry.typeclassQuery:@strawberry.fielddefdepartments(self):returndb.session.scalars(select(models.Department)).all()# context is expected to have an instance of StrawberrySQLAlchemyLoaderclassCustomGraphQLView(GraphQLView):defget_context(self):return{"sqlalchemy_loader":StrawberrySQLAlchemyLoader(bind=YOUR_SESSION),}# call finalize() before using the schema:# (note that models that are related to models that are in the schema# are automatically mapped at this stage -- e.g., Department is mapped# because employee.department is a relationshp to Department)strawberry_sqlalchemy_mapper.finalize()# only needed if you have polymorphic typesadditional_types=list(strawberry_sqlalchemy_mapper.mapped_types.values())schema=strawberry.Schema(query=Query,mutation=Mutation,extensions=extensions,types=additional_types,)# You can now query, e.g.:"""query {departments {idnameemployees {edge {node {idnamedepartment {# Just an example of nested relationshipsidname}}}}}}"""LimitationsSQLAlchemy Models -> Strawberry Types and Interfaces are expected to have a consistent
(customizable) naming convention. These can be configured by passingmodel_to_type_nameandmodel_to_interface_namewhen constructing the mapper.Natively supports the following SQLAlchemy types:Integer:int,Float:float,BigInteger:int,Numeric:Decimal,DateTime:datetime,Date:date,Time:time,String:str,Text:str,Boolean:bool,Unicode:str,UnicodeText:str,SmallInteger:int,SQLAlchemyUUID:uuid.UUID,VARCHAR:str,ARRAY[T]:List[T]# PostgreSQL arrayEnum:(thePythonenumitismappedto,whichshouldbe@strawberry.enum-decorated)Additional types can be supported by passingextra_sqlalchemy_type_to_strawberry_type_map,
although support forTypeDecoratortypes is untested.Association proxies are expected to be of the formassociation_proxy('relationship1', 'relationship2'),
i.e., both properties are expected to be relationships.Roots of polymorphic hierarchiesare supported, but are also expected to be registered viastrawberry_sqlalchemy_mapper.interface(), and its concrete type and
its descendants are expected to inherit from the interface:classBook(Model):id=Column(UUID,primary_key=True)classNovel(Book):passclassShortStory(Book):pass# in another filestrawberry_sqlalchemy_mapper=StrawberrySQLAlchemyMapper()@strawberry_sqlalchemy_mapper.interface(models.Book)classBookInterface:pass@strawberry_sqlalchemy_mapper.type(models.Book)classBook:pass@strawberry_sqlalchemy_mapper.type(models.Novel)classNovel:pass@strawberry_sqlalchemy_mapper.type(models.ShortStory)classShortStory:passContributingWe encourage you to contribute to strawberry-sqlalchemy-mapper! Any contributions you make are greatly appreciated.If you have a suggestion that would make this better, please fork the repo and create a pull request. Don't forget to give the project a star! Thanks again!Fork the ProjectCreate your Feature Branch (git checkout -b feature)Commit your Changes (git commit -m 'Add some feature')Push to the Branch (git push origin feature)Open a Pull RequestPrerequisitesThis project usespre-commit_, please make sure to install it before making any
changes::pip install pre-commit
cd strawberry-sqlalchemy-mapper
pre-commit installIt is a good idea to update the hooks to the latest version::pre-commit autoupdateDon't forget to tell your contributors to also install and use pre-commit.Installationpipinstall-rrequirements.txtTestpytest⚖️ LICENSEMIT ©strawberry-sqlalchemy-mapper