Package: aiohttp-rest-framework
aiohttp-rest-frameworkFully asynchronous rest framework for aiohttp web server, inspired byDjango Rest Framework(DRF), powered bymarshmallowandSQLAlchemy.Currently supports only combination of postgres and sqlalchemy (ORM and core). MySQL support will be shipped on demand.Installationpipinstallaiohttp-rest-frameworkUsage exampleConsider we have the following SQLAlchemy ORM models:models.pyimportsqlalchemyassafromsqlalchemy.dialects.postgresqlimportUUIDfromsqlalchemy.ormimportdeclarative_basefromapp.utilsimportget_stringified_uuidBase=declarative_base()meta=Base.metadataclassUser(Base):__tablename__="users"id=sa.Column(UUID,primary_key=True,default=get_stringified_uuid)name=sa.Column(sa.Text)email=sa.Column(sa.Text,nullable=False,unique=True)phone=sa.Column(sa.Text)company_id=sa.Column(sa.ForeignKey("companies.id"),nullable=True)classCompany(Base):__tablename__="companies"id=sa.Column(UUID,primary_key=True,default=get_stringified_uuid)name=sa.Column(sa.Text,nullable=False)SQLAlchemy Core tables are also supported.importsqlalchemyassafromsqlalchemy.dialects.postgresqlimportUUIDfromapp.utilsimportget_stringified_uuidmeta=sa.MetaData()User=sa.Table("users",meta,sa.Column("id",UUID,primary_key=True,default=get_stringified_uuid),sa.Column("name",sa.Text),sa.Column("email",sa.Text,unique=True),sa.Column("phone",sa.Text),sa.Column("company_id",sa.ForeignKey("companies.id"),nullable=True),)Company=sa.Table("companies",meta,sa.Column("id",UUID,primary_key=True,default=get_stringified_uuid),sa.Column("name",sa.Text),)Now we can use very familiar to us from DRF serializer, built on top of marshmalow'sSchema:serializers.pyfromaiohttp_rest_frameworkimportserializersfromapp.modelsimportUserclassUserSerializer(serializers.ModelSerializer):classMeta:model=Userfields="__all__"dump_only=("id",)Note: for more information about field declaration please refer tomarshmallowFor SQLAlchemy ORMModelSerializersupports generic typing:classUserSerializer(serializers.ModelSerializer[User]):# <- mention `User` hereclassMeta:model=Userfields="__all__"Now type hints will be available for serializers methods likecreate(),update(), etc.And, finally, now we can use our serializer in class based views:views.pyfromaiohttp_rest_frameworkimportviewsfromapp.serializersimportUserSerializerclassUsersListCreateView(views.ListCreateAPIView):serializer_class=UserSerializerclassUsersRetrieveUpdateDestroyView(views.RetrieveUpdateDestroyAPIView):serializer_class=UserSerializerOur simple app would look like this:main.pyfromaiohttpimportwebfromaiohttp_rest_frameworkimportsetup_rest_framework,create_connectionfromaiohttp_rest_framework.utilsimportcreate_tablesfromappimportviews,config,modelsasyncdefdb_cleanup_context(app_:web.Application)->None:app_["db"]=awaitcreate_connection(config.db_url)# in case you need to create tables in the database# for sqlalchemy this is the same as `meta.create_all()`, but asynchronousawaitcreate_tables(models.meta,app_["db"])yieldawaitapp_["db"].dispose()app=web.Application()app.cleanup_ctx.append(db_cleanup_context)app.router.add_view("/users",views.UsersListCreateView)app.router.add_view("/users/{id}",views.UsersRetrieveUpdateDestroyView)setup_rest_framework(app)web.run_app(app)Note:
If you want to use other property than "db", in order to application work you have to specifyapp_connection_propertyin config, passing tosetup_rest_framework.Example:setup_rest_framework(app, {"app_connection_property": "custom_db_prop"})Mentionsetup_rest_framework()function, it is required to call it to configure framework to work with your app.
For available rest framework's config options refer todocumentation.
For detailed aiohttp web app configuration please refer totheir docs.After starting the app, we can make aPOST /usersrequest to create a new user.curl-H"Content-Type: application/json"-d'{"name": "John Doe","email": "john@mail.com","phone": "+123456789"}'-XPOSThttp://localhost:8080/usersAnd get the followingHTTP 201 Response:{"id":"aa392cc9-c734-44ff-9d7c-1602ecb4df2a","name":"John Doe","email":"john@mail.com","phone":"+123456789","company_id":null}Let's try to update user's company. MakingPATCH /users/aa392cc9-c734-44ff-9d7c-1602ecb4df2arequestcurl-H"Content-Type: application/json"-d'{"company_id": "0413de74-d9fb-494b-ba56-b56599261fb0"}'-XPATCHhttp://localhost:8080/users/a392cc9-c734-44ff-9d7c-1602ecb4df2aHTTP 200 Response:{"id":"aa392cc9-c734-44ff-9d7c-1602ecb4df2a","name":"John Doe","email":"john@mail.com","phone":"+123456789","company_id":"0413de74-d9fb-494b-ba56-b56599261fb0"}For more examples and usages please refer todocumentation.RequirementsPython >= 3.6Dependencies:aiohttpsqlalchemymarshmallowIf using PostgreSQL (currently being installed by default since it's the only database supported):asyncpgpsycopg2Documentationsetup_rest_framework(app, config)Config is just a simpledictobject. Config can accept following parameters (everything is optional):schema_type: str- Specifies what combination of database and SQL toolkit to use. Currently supports only combination of SQLAlchemy and PostgreSQL.Default:"sa"app_connection_property: str- The property name of the database connection in your aiohttp application.Default:"db".custom_db_prop="db_conn"asyncdefdb_cleanup_context(app_:web.Application)->None:app_[custom_db_prop]=awaitcreate_connection(config.db_url)yieldawaitapp_[custom_db_prop].dispose()app=web.Application()app.cleanup_ctx.append(db_cleanup_context)setup_rest_framework(app,{"app_connection_property":custom_db_prop})get_connection: Callable[[], Awaitable]- An async callable that receive no arguments and returns database connection. You would only need it if you don't want to store your database connection in aiohttp application, then you have to provide it toaiohttp-rest-frameworkso framework can work with a database.Default:usesapp[app_connection_property]# somewhere in your code, function to get initialized db connectionfromapp.dbimportget_connection# this is just to show that is has to be asyncasyncdefget_db_connection():returnawaitget_connection()app=web.Application()setup_rest_framework(app,{"get_connection":get_db_connection})db_manager: BaseDBManager- Specifies what database manager to use.  You would need it if you want to use custom logic in database operations (class should be inherited fromBaseDBManagerimported fromaiohttp_rest_framework.db.base). Usually you wouldn't need it because framework's built-inSAManager(if you useschema_type = "sa", which is default) already handles all CRUD operations and also hasexecute()method where you can pass custom sqlalchemy queries.Default:uses manager specific to the currentschema_type.A totally useless example just to show you how it can be used:fromaiohttp_rest_framework.db.saimportSAManagerclassSAManagerWithPrintingQuery(SAManager):asyncdefexecute(self,query,*args,**kwargs):print(query)returnawaitsuper().execute(query,*args,**kwargs)app=web.Application()setup_rest_framework(app,{"db_manager":SAManagerWithPrintingQuery})