From 1b861f9ff44f9073471b1acfdf510f20c618e4cc Mon Sep 17 00:00:00 2001 From: Igor Magalhaes Date: Fri, 3 Nov 2023 00:57:26 -0300 Subject: [PATCH] schema_to_select now also accepts a list of column names, docs updated --- README.md | 35 +++++++++++++++-------------------- src/app/crud/crud_base.py | 8 ++++---- src/app/crud/helper.py | 10 +++++++--- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index f64cad8..750ce01 100644 --- a/README.md +++ b/README.md @@ -33,24 +33,25 @@ ## 0. About **FastAPI boilerplate** creates an extendable async API using FastAPI, Pydantic V2, SQLAlchemy 2.0 and PostgreSQL: - [`FastAPI`](https://fastapi.tiangolo.com): modern Python web framework for building APIs -- [`Pydantic V2`](https://docs.pydantic.dev/2.4/): the most widely used data validation library for Python, rewritten in Rust [`(5x-50x faster)`](https://docs.pydantic.dev/latest/blog/pydantic-v2-alpha/) +- [`Pydantic V2`](https://docs.pydantic.dev/2.4/): the most widely used data Python validation library, rewritten in Rust [`(5x-50x faster)`](https://docs.pydantic.dev/latest/blog/pydantic-v2-alpha/) - [`SQLAlchemy 2.0`](https://docs.sqlalchemy.org/en/20/changelog/whatsnew_20.html): Python SQL toolkit and Object Relational Mapper - [`PostgreSQL`](https://www.postgresql.org): The World's Most Advanced Open Source Relational Database -- [`Redis`](https://redis.io): Open source, in-memory data store used by millions as a database, cache, streaming engine, and message broker +- [`Redis`](https://redis.io): Open source, in-memory data store used by millions as a cache, message broker and more. - [`ARQ`](https://arq-docs.helpmanual.io) Job queues and RPC in python with asyncio and redis. - [`Docker Compose`](https://docs.docker.com/compose/) With a single command, create and start all the services from your configuration. ## 1. Features -- Fully async -- Pydantic V2 and SQLAlchemy 2.0 -- User authentication with JWT -- Easy redis caching -- Easy client-side caching -- ARQ integration for task queue -- Efficient querying (only queries what's needed) -- Easily extendable -- Flexible -- Easy running with docker compose +- ⚡️ Fully async +- 🚀 Pydantic V2 and SQLAlchemy 2.0 +- 🔐 User authentication with JWT +- 🏬 Easy redis caching +- 👜 Easy client-side caching +- 🚦 ARQ integration for task queue +- ⚙️ Efficient querying (only queries what's needed) +- 👮 FastAPI docs behind authentication and hidden based on the environment +- 🦾 Easily extendable +- 🤸‍♂️ Flexible +- 🚚 Easy running with docker compose ### 1.1 To Do #### API @@ -64,13 +65,7 @@ #### Features - [ ] Add a Rate Limiter decorator - [ ] Add mongoDB support -- [ ] Add support in schema_to_select for dict as well as Pydantic Schema - -#### Security -- [x] FastAPI docs behind authentication and hidden based on the environment - -#### Structure -- [x] Remove python-decouple in favor of starlette.config +- [x] Add support in schema_to_select for a list of column names #### Tests - [ ] Add Ruff linter @@ -634,7 +629,7 @@ crud_users.db_delete(db=db, username="myusername") ``` #### More Efficient Selecting -For the `get` and `get_multi` methods we have the option to define a `schema_to_select` attribute, which is what actually makes the queries more efficient. When you pass a pydantic schema in `schema_to_select` to the `get` or `get_multi` methods, only the attributes in the schema will be selected. +For the `get` and `get_multi` methods we have the option to define a `schema_to_select` attribute, which is what actually makes the queries more efficient. When you pass a `pydantic schema` (preferred) or a list of the names of the attributes in `schema_to_select` to the `get` or `get_multi` methods, only the attributes in the schema will be selected. ```python from app.schemas.user import UserRead # Here it's selecting all of the user's data diff --git a/src/app/crud/crud_base.py b/src/app/crud/crud_base.py index 5106afa..d766b6c 100644 --- a/src/app/crud/crud_base.py +++ b/src/app/crud/crud_base.py @@ -55,7 +55,7 @@ async def create( async def get( self, db: AsyncSession, - schema_to_select: Type[BaseModel] | None = None, + schema_to_select: Union[Type[BaseModel], List, None] = None, **kwargs ) -> Row | None: """ @@ -65,7 +65,7 @@ async def get( ---------- db : AsyncSession The SQLAlchemy async session. - schema_to_select : Type[BaseModel] | None, optional + schema_to_select : Union[Type[BaseModel], List, None], optional Pydantic schema for selecting specific columns. Default is None to select all columns. kwargs : dict Filters to apply to the query. @@ -87,7 +87,7 @@ async def get_multi( db: AsyncSession, offset: int = 0, limit: int = 100, - schema_to_select: Type[BaseModel] | None = None, + schema_to_select: Union[Type[BaseModel], List, None] = None, **kwargs ) -> List[Row]: """ @@ -101,7 +101,7 @@ async def get_multi( Number of rows to skip before fetching. Default is 0. limit : int, optional Maximum number of rows to fetch. Default is 100. - schema_to_select : Type[BaseModel] | None, optional + schema_to_select : Union[Type[BaseModel], List, None], optional Pydantic schema for selecting specific columns. Default is None to select all columns. kwargs : dict Filters to apply to the query. diff --git a/src/app/crud/helper.py b/src/app/crud/helper.py index 45a74bc..9423943 100644 --- a/src/app/crud/helper.py +++ b/src/app/crud/helper.py @@ -1,10 +1,10 @@ -from typing import Any, List, Type +from typing import Any, List, Type, Union, Dict from pydantic import BaseModel from app.core.database import Base -def _extract_matching_columns_from_schema(model: Type[Base], schema: Type[BaseModel] | None) -> List[Any]: +def _extract_matching_columns_from_schema(model: Type[Base], schema: Union[Type[BaseModel], List, None]) -> List[Any]: """ Retrieves a list of ORM column objects from a SQLAlchemy model that match the field names in a given Pydantic schema. @@ -22,7 +22,11 @@ def _extract_matching_columns_from_schema(model: Type[Base], schema: Type[BaseMo """ column_list = list(model.__table__.columns) if schema is not None: - schema_fields = schema.model_fields.keys() + if isinstance(schema, list): + schema_fields = schema + else: + schema_fields = schema.model_fields.keys() + column_list = [] for column_name in schema_fields: if hasattr(model, column_name):