Skip to main content

Scalable data access patterns for rapid API development, using SQLAlchemy & Pydantic.

Project description

OnePattern

Scalable data access patterns for rapid API development, using SQLAlchemy & Pydantic.

Features

  • Focus on business logic: No need to write boilerplate code.
  • Scalable: You can use the provided abstractions and easily extend them.
  • Portable: Works with any database supported by SQLAlchemy.
  • Intuitive: Great editor support. Completion everywhere. Less time debugging.
  • Easy: Designed to be easy to use and learn. Less time reading docs.
  • Asynchronous: Built-in support for async/await.
  • Use anywhere: Use it with FastAPI, Aiogram, or any other framework.

Requirements

OnePattern stands on the shoulders of giants:

Installation

pip install onepattern

Concepts

1. Models

from datetime import datetime

from sqlalchemy import Identity
from sqlalchemy.orm import Mapped, mapped_column

from onepattern import DeclarativeBase, AlchemyEntity
from onepattern.alchemy import SoftDeletable, HasID, HasTimestamp


# Classic way
class User(DeclarativeBase):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(Identity(), primary_key=True)
    email: Mapped[str] = mapped_column(unique=True, index=True)
    password: Mapped[str]
    created_at: Mapped[datetime] = mapped_column(default=datetime.now)
    updated_at: Mapped[datetime] = mapped_column(
        default=datetime.now, onupdate=datetime.now
    )


# Using AlchemyEntity
class UserAlchemyEntity(AlchemyEntity):
    __tablename__ = "users"

    email: Mapped[str] = mapped_column(unique=True, index=True)
    password: Mapped[str]

    # These columns will be added automatically
    # id: Mapped[int] = mapped_column(Identity(), primary_key=True)
    # created_at: Mapped[datetime] = mapped_column(default=datetime.now)
    # updated_at: Mapped[datetime] = mapped_column(
    #     default=datetime.now, onupdate=datetime.now
    # )


# Using mixins
class UserMixins(HasID, HasTimestamp, SoftDeletable):
    __tablename__ = "users"

    email: Mapped[str] = mapped_column(unique=True, index=True)
    password: Mapped[str]
    # These columns will be added automatically
    # id: Mapped[int]
    # created_at: Mapped[datetime] = mapped_column(default=datetime.now)
    # updated_at: Mapped[datetime] = mapped_column(
    #     default=datetime.now, onupdate=datetime.now
    # )
    # deleted_at: Mapped[datetime | None] = None

2. Repositories

from onepattern import AlchemyRepository
from .models import User


class UserRepository(AlchemyRepository[User]):
    model_type = User

3. Unit of Work

from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker

from onepattern import AlchemyUOW
from .repositories import UserRepository


class UOW(AlchemyUOW):
    users: UserRepository

    async def on_open(self) -> None:
        self.users = UserRepository(self.session)


async_engine = create_async_engine("sqlite+aiosqlite:///memory")
async_session = async_sessionmaker(async_engine)

uow = UOW(async_session)

4. Services

from onepattern import AlchemyService
from .models import User
from .uow import UOW


class UserService(AlchemyService):
    uow: UOW

    async def create(self, email: str, password: str) -> User:
        user = User(email=email, password=password)
        self.uow.users.add(user)
        await self.uow.commit()

        return user

    async def get(self, user_id: int) -> User | None:
        return await self.uow.users.get(user_id)

    async def get_by_email(self, email: str) -> User | None:
        return await self.uow.users.get_by_where(where=[User.email == email])

    async def create_many(self, users: list[User]) -> list[User]:
        await self.uow.users.insert(*users)
        await self.uow.commit()

        return users

    async def get_by_domain(self, domain: str) -> list[User]:
        return await self.uow.users.get_many(where=[User.email.like(f"%@{domain}")])

    # And more...

5. Use Cases

from faker import Faker

from .models import User
from .service import UserService
from .uow import uow

fake = Faker()


def get_users(domains: list[str], number: int = 10) -> list[User]:
    users = [User(email=fake.email(domain=domain), password=fake.password()) for _ in range(number) for domain in
             domains]
    return users


async def main():
    async with uow:
        service = UserService(uow)

        user = await service.create("user@example.com", "qwerty12345")
        print(user)

        user = await service.get(user.id)
        print(user)

        user = await service.get_by_email("user@example.com")
        print(user)

        users = get_users(["gmail.com", "yahoo.com"])
        await service.create_many(*users)

        users = await service.get_by_domain("gmail.com")
        print(users)

Made with love ❤️

Download files

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

Source Distribution

onepattern-0.1.9.tar.gz (11.0 kB view details)

Uploaded Source

Built Distribution

onepattern-0.1.9-py3-none-any.whl (16.4 kB view details)

Uploaded Python 3

File details

Details for the file onepattern-0.1.9.tar.gz.

File metadata

  • Download URL: onepattern-0.1.9.tar.gz
  • Upload date:
  • Size: 11.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.2 CPython/3.12.3 Windows/11

File hashes

Hashes for onepattern-0.1.9.tar.gz
Algorithm Hash digest
SHA256 b655b3cb2cc78200f77a4682dd9ab80351ade3f7ef29ca938e266afd991b4f66
MD5 d9d71092fa64292826eb7ae76b050f72
BLAKE2b-256 f1826e208367c26d81046987e705927a221ce82aa17cecb635f2a2a2bbd54dd9

See more details on using hashes here.

File details

Details for the file onepattern-0.1.9-py3-none-any.whl.

File metadata

  • Download URL: onepattern-0.1.9-py3-none-any.whl
  • Upload date:
  • Size: 16.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.2 CPython/3.12.3 Windows/11

File hashes

Hashes for onepattern-0.1.9-py3-none-any.whl
Algorithm Hash digest
SHA256 5298f253de385a64cdfb48bec9946002945ca1b509aa377fde75441cb026c90b
MD5 a763e1a363aba041a582fe27ed5c999d
BLAKE2b-256 f82777473d857b5920444070008b4ff7946696be522abb1527793c7cd99a725e

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