Skip to main content

Rest framework for aiohttp web server

Project description

aiohttp-rest-framework

Codecov PyPI PyPI - Downloads PyPI - Python Version

Fully asynchronous rest framework for aiohttp web server, inspired by Django Rest Framework (DRF), powered by marshmallow and SQLAlchemy.

Currently supports only combination of postgres (thanks to databases library) and sqlalchemy (core). MySQL support will be shipped in the near future.

Installation

pip install aiohttp-rest-framework

Usage example

Consider we have the following SQLAlchemy ORM models:

import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import declarative_base

from app.utils import get_stringified_uuid

Base = declarative_base()
meta = Base.metadata


class User(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)


class Company(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.

import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import UUID

from app.utils import get_stringified_uuid

meta = 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's Schema:

from aiohttp_rest_framework import serializers

from app.models import User


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = "__all__"
        dump_only = ("id",)

Note: for more information about field declaration please refer to marshmallow

For SQLAlchemy ORM ModelSerializer supports generic typing:

class UserSerializer(serializers.ModelSerializer[User]):  # <- mention `User` here
    class Meta:
        model = User
        fields = "__all__"

Now type hints will be available for serializers methods like create(), update(), etc.

And, finally, now we can use our serializer in class based views:

from aiohttp_rest_framework import views

from app.serializers import UserSerializer


class UsersListCreateView(views.ListCreateAPIView):
    serializer_class = UserSerializer


class UsersRetrieveUpdateDestroyView(views.RetrieveUpdateDestroyAPIView):
    serializer_class = UserSerializer

Our simple app would look like this:

from aiohttp import web
from aiohttp_rest_framework import setup_rest_framework, create_connection
from aiohttp_rest_framework.utils import create_tables

from app import views, config, models


async def db_cleanup_context(app_: web.Application) -> None:
    app_["db"] = await create_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 asynchronous
    await create_tables(models.meta, app_["db"])
    yield 
    await app_["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 specify app_connection_property in config, passing to setup_rest_framework.

Example: setup_rest_framework(app, {"app_connection_property": "custom_db_prop"})

Mention setup_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 to documentation. For detailed aiohttp web app configuration please refer to their docs.

After starting the app, we can make a POST /users request to create a new user.

curl -H "Content-Type: application/json" 
     -d '{
            "name": "John Doe",
            "email": "john@mail.com",
            "phone": "+123456789"
         }'
     -X POST http://localhost:8080/users

And get the following HTTP 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. Making PATCH /users/aa392cc9-c734-44ff-9d7c-1602ecb4df2a request

curl -H "Content-Type: application/json" 
     -d '{"company_id": "0413de74-d9fb-494b-ba56-b56599261fb0"}'
     -X PATCH http://localhost:8080/users/a392cc9-c734-44ff-9d7c-1602ecb4df2a

HTTP 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 to documentation.

Requirements

Python >= 3.6

Dependencies:

  • aiohttp
  • databases[postgresql] (actually the fork of it with fixed sqlalchemy column defaults)
  • sqlalchemy
  • marshmallow

Documentation

setup_rest_framework(app, config)

Config is just a simple dict object. 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"

async def db_cleanup_context(app_: web.Application) -> None:
    app_[custom_db_prop] = await create_connection(config.db_url)
    yield 
    await app_[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 to aiohttp-rest-framework so framework can work with a database.

    Default: uses app[app_connection_property]

from app.utils import create_db_connection
custom_db_prop = "db_conn"

async def get_db_connection():
    return await create_db_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 from BaseDBManager imported from aiohttp_rest_framework.db.base). Usually you wouldn't need it because framework's built-in SAManager (if you use schema_type = "sa", which is default) already handles all CRUD operations and also has execute() method where you can pass custom sqlalchemy queries.

    Default: uses manager specific to the current schema_type.

Just a dumb useless example just to show how it can be used:

from aiohttp_rest_framework.db.sa import SAManager

class SAManagerWithLogging(SAManager):
    async def execute(self, query, *args, **kwargs):
        print(query)
        return await super().execute(query, *args, **kwargs)

app = web.Application()
setup_rest_framework(app, {"db_manager": SAManagerWithLogging})

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aiohttp_rest_framework-0.0.5.tar.gz (18.9 kB view details)

Uploaded Source

Built Distribution

aiohttp_rest_framework-0.0.5-py2.py3-none-any.whl (19.8 kB view details)

Uploaded Python 2 Python 3

File details

Details for the file aiohttp_rest_framework-0.0.5.tar.gz.

File metadata

  • Download URL: aiohttp_rest_framework-0.0.5.tar.gz
  • Upload date:
  • Size: 18.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/50.3.2 requests-toolbelt/0.9.1 tqdm/4.53.0 CPython/3.9.0

File hashes

Hashes for aiohttp_rest_framework-0.0.5.tar.gz
Algorithm Hash digest
SHA256 0af222e7f932b029cfc91d4c163ebdd0d66ab79c2504c1f89e91049c03306ce6
MD5 a33a2af34ca769777edbfefb368ce9f7
BLAKE2b-256 77adfb8d956458bbbb1f3176816d6f57c5c8e9f7bba9016ce724d4ef168e7f5e

See more details on using hashes here.

File details

Details for the file aiohttp_rest_framework-0.0.5-py2.py3-none-any.whl.

File metadata

  • Download URL: aiohttp_rest_framework-0.0.5-py2.py3-none-any.whl
  • Upload date:
  • Size: 19.8 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/50.3.2 requests-toolbelt/0.9.1 tqdm/4.53.0 CPython/3.9.0

File hashes

Hashes for aiohttp_rest_framework-0.0.5-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 7300d29b4a362ee45a2def98060c86c1fa5dd2d6d918c6fed91361a357229394
MD5 41245da3fb38e4acb1ca31fd1d264d1a
BLAKE2b-256 a7fb736cb6e5085ac6af98dc66af89186c2527c9031176124c2a41875461771a

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page