{"id":15288707,"url":"https://github.com/viachkon/dataclass-sqlalchemy-mixins","last_synced_at":"2026-03-17T14:50:41.411Z","repository":{"id":249894381,"uuid":"818021252","full_name":"ViAchKoN/dataclass-sqlalchemy-mixins","owner":"ViAchKoN","description":"Python package for easy sqlalchemy filtering databases using dataclasses or without them","archived":false,"fork":false,"pushed_at":"2024-11-26T19:36:48.000Z","size":43,"stargazers_count":15,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-10T21:03:49.446Z","etag":null,"topics":["dataclasses","fastapi","filtering","flask","pydantic","pydantic-models","pydantic-v2","python","python3","sqlalchemy"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ViAchKoN.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-20T23:52:36.000Z","updated_at":"2025-04-01T11:43:43.000Z","dependencies_parsed_at":null,"dependency_job_id":"7394fd0c-bfbc-4bf7-8374-1bfa911815e3","html_url":"https://github.com/ViAchKoN/dataclass-sqlalchemy-mixins","commit_stats":null,"previous_names":["viachkon/dataclass-sqlalchemy-mixins"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ViAchKoN%2Fdataclass-sqlalchemy-mixins","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ViAchKoN%2Fdataclass-sqlalchemy-mixins/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ViAchKoN%2Fdataclass-sqlalchemy-mixins/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ViAchKoN%2Fdataclass-sqlalchemy-mixins/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ViAchKoN","download_url":"https://codeload.github.com/ViAchKoN/dataclass-sqlalchemy-mixins/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248674658,"owners_count":21143760,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dataclasses","fastapi","filtering","flask","pydantic","pydantic-models","pydantic-v2","python","python3","sqlalchemy"],"created_at":"2024-09-30T15:52:25.361Z","updated_at":"2025-10-11T23:02:51.572Z","avatar_url":"https://github.com/ViAchKoN.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![test](https://github.com/ViAchKoN/dataclass-sqlalchemy-mixins/workflows/Test/badge.svg?query=branch%3Amaster+event%3Apush)](https://github.com/ViAchKoN/dataclass-sqlalchemy-mixins/actions?query=branch%3Amaster+event%3Apush+workflow%3ATest++)\n[![python supported versions](https://img.shields.io/pypi/pyversions/dataclass-sqlalchemy-mixins.svg?color=%2334D058)](https://pypi.org/project/dataclass-sqlalchemy-mixins)\n\n# Dataclass Sqlalchemy Mixins\n_____\n### Package requirements\n- `python \u003e= 3.8.1`\n- `pydantic \u003e= 1.9`\n- `sqlalchemy \u003e= 1.4.0`\n___\n### Installation\n```bash\n# without extras \npip install dataclass-sqlalchemy-mixins\n\n# with pydantic \npip install dataclass-sqlalchemy-mixins[pydantic]\n```\n___\n### Description\nThis package consists of the several important parts: \n1. Helper mixins which directly interacts with SQLAlchemy to apply filters and orderings to a query or get binary/unary SQLAlchemy expressions that can be applied when required\n2. Pydantic dataclasses mixins that are used as proxies for helper mixins\n\n\n\n___\n### Usage\n \nIt is important to set the `SQLAlchemy` model in the `ConverterConfig` that you are going to query in the dataclass you are creating. \nThis package supports sql operations: `eq`, `in`, `not_in`, `gt`, `lt`, `gte`, `lte`, `not`, `is`, `is_not`, `like`, `ilike`, `isnull`. \nTo apply a filter to a field, the filter should be formatted like `field_name__{sql_op}`. Filtering though a foreign key supported using `field_name__foreigh_key_field__{sql_op}`.\nTo perform sorting the value should be passed as `id` to ASC and `-name` to DESC. Inner joins will be applied automatically if using `pydantic` mixins.\n\n\n**Direct**\n\nStarting from version `0.3.0`, `apply_filters` and `apply_order_by` were added, \nallowing you to apply filters and ordering to a query without using dataclasses. \nThey are located in utils.\n\nFilter:\n```python\nfrom dataclass_sqlalchemy_mixins.base import utils\n\nfilters = {\n    'id__gte': 1,\n    'name__in': ['abc', 'def'],\n    'object__place': 1,\n}\n\nquery = utils.apply_filters(\n    query=query,\n    filters=filters,\n    model=SomeModel\n)\n```\n\nOrder by:\n```python\nfrom dataclass_sqlalchemy_mixins.base import utils\n\norder_by = ['id', '-name']\n\nquery = utils.apply_order_by(\n    query=query,\n    order_by=order_by,\n    model=SomeModel,\n)\n```\n\nStarting from version `0.2.0`, `get_binary_expressions` and `get_unary_expressions` were introduced, \nallowing filters and ordering to be obtained without inheriting from dataclasses. \nYou just need to pass `filters`/`order_by` and a model you want to apply them to.\nThey are located in `utils`.\n\nFilter:\n```python\nfrom dataclass_sqlalchemy_mixins.base import utils\n\nfilters = {\n    'id__gte': 1,\n    'name__in': ['abc', 'def'],\n    'object__place': 1,\n}\n\nbinary_expressions = utils.get_binary_expressions(\n    filters=filters,\n    model=SomeModel\n)\n\nquery = query.filter(*binary_expressions)\n```\n\nOrder by:\n```python\nfrom dataclass_sqlalchemy_mixins.base import utils\n\norder_by = ['id', '-name']\n\nunary_expressions = utils.get_unary_expressions(\n    order_by=order_by,\n    model=SomeModel,\n)\n\nquery = query.order_by(*unary_expressions)\n```\n\n**Custom dataclasses**\n\nIt is possible to apply mixins to custom dataclasses by inheriting from either `SqlAlchemyFilterConverterMixin` for filters or `SqlAlchemyOrderConverterMixin` for orderings.\n\nFilter:\n```python\nimport typing\n\nfrom dataclasses import dataclass, asdict\n\nfrom dataclass_sqlalchemy_mixins.base.mixins import SqlAlchemyFilterConverterMixin\n\n@dataclass\nclass CustomDataclass(SqlAlchemyFilterConverterMixin):\n    id__gte: int = None\n    name__in: typing.List[str] = None\n    object__place: int = None \n    \n    class ConverterConfig:\n        model = SomeModel \n    \n    def dict(self):\n        return {k: str(v) for k, v in asdict(self).items() if v is not None}\n\n\ncustom_dataclass = CustomDataclass(\n    id__gte=1,\n    name__in=['abc', 'def'],\n    object__place=1,\n)\n\nbinary_expressions = custom_dataclass.get_binary_expressions(custom_dataclass.dict())\n\nquery = query.filter(*binary_expressions)\n```\n\nOrder by:\n```python\nimport typing\n\nfrom dataclasses import dataclass\n\nfrom dataclass_sqlalchemy_mixins.base.mixins import SqlAlchemyOrderConverterMixin\n\n@dataclass\nclass CustomDataclass(SqlAlchemyOrderConverterMixin):\n    order_by: typing.Optional[typing.Union[str, typing.List[str]]] = None\n    \n    class ConverterConfig:\n        model = SomeModel \n\ncustom_dataclass = CustomDataclass(\n    order_by=['id', '-name']\n)\n\nunary_expressions = custom_dataclass.get_unary_expressions(custom_dataclass.order_by)\n\nquery = query.order_by(*unary_expressions)\n```\n\n**Pydantic**\n\nFilter:\n```python\nimport typing\n\nfrom dataclass_sqlalchemy_mixins.pydantic_mixins.sqlalchemy_base_models import SqlAlchemyFilterBaseModel\n\nclass CustomBaseModel(SqlAlchemyFilterBaseModel):\n    id__gte: int = None\n    name__in: typing.List[str] = None\n    object__place: int = None \n    \n    class ConverterConfig:\n        model = SomeModel \n    \n\ncustom_basemodel = CustomBaseModel(\n    id__gte=1,\n    name__in=['abc', 'def'],\n    object__place=1,\n)\n\nbinary_expressions = custom_basemodel.to_binary_expressions()\n\nquery = query.filter(*binary_expressions)\n\n# or\n\nquery = custom_basemodel.apply_filters(query=query)\n```\n\nSometimes, it is necessary to manipulate sent data before applying filters. \nFor example, a field should not be directly converted to a filter; instead, custom logic should be applied. \nAs of version `0.1.3`, the `to_binary_expressions` and `apply_filters` methods accept the `export_params` argument to address this situation. \nValues mentioned in the Pydantic dictionary [export section](https://docs.pydantic.dev/1.10/usage/exporting_models/ ) can be sent as `export_params`.\n\n```python\nimport typing\n\nfrom dataclass_sqlalchemy_mixins.pydantic_mixins.sqlalchemy_base_models import SqlAlchemyFilterBaseModel\n\nclass CustomBaseModel(SqlAlchemyFilterBaseModel):\n    id__gte: int = None\n    name__in: typing.List[str] = None\n    filter_to_exclude: typing.Any = None \n    \n    class ConverterConfig:\n        model = SomeModel \n    \n\ncustom_basemodel = CustomBaseModel(\n    id__gte=1,\n    name__in=['abc', 'def'],\n    filter_to_exclude=\"filter_value\",\n)\n\n# filter_to_exclude field will be excluded from converting basemodel to sqlalchemy filters\n\nbinary_expressions = custom_basemodel.to_binary_expressions(\n    export_params={'exclude': {'filter_to_exclude'}, }\n)\n\nquery = query.filter(*binary_expressions)\n\n# or\n\nquery = custom_basemodel.apply_filters(\n    query=query,\n    export_params={'exclude': {'filter_to_exclude'}, }\n)\n```\n\nOrder by:\n```python\nimport typing\n\nfrom dataclass_sqlalchemy_mixins.pydantic_mixins.sqlalchemy_base_models import SqlAlchemyOrderBaseModel\n\nclass CustomBaseModel(SqlAlchemyOrderBaseModel):\n    id__gte: int = None\n    name__in: typing.List[str] = None\n    object__place: int = None \n    \n    class ConverterConfig:\n        model = SomeModel \n    \ncustom_basemodel = CustomBaseModel(\n    order_by=['id', '-name']\n)\n\nunary_expressions = custom_basemodel.get_unary_expressions(custom_dataclass.order_by)\n\nquery = query.order_by(*unary_expressions)\n\n# or \n\nquery = custom_basemodel.apply_order_by(query)\n```\n____\n### FastApi support \nDataclasses inherited from `SqlAlchemyFilterBaseModel` or `SqlAlchemyOrderBaseModel` normally produce the correct documentation. \nHowever, there is one issue that should be mentioned: \n`FastAPI` has trouble creating documentation when a complex type is set as an annotation for `Query` parameters. \nThis includes lists.\n\nThe `extra` parameter was introduced to address these situations which can be set in `ConverterConfig`. \nCurrently, this parameter only accepts a dictionary with one key: `BaseModelConverterExtraParams.LIST_AS_STRING`. \nThis key instructs the converter to treat the passed string as a list in the context of filtering and ordering.\n\nFor example, a class defined like this will convert the value passed for `field__in` into a list when applying filters and orderings.\nThe value passed for `another_field__in` won't be treated a list because the field wasn't included in the `fields` set in `extra`. \n\nAnother parameter can be used is `expected_types`. \nIt is used to define which as which type should be elements of the list treated as when a str converted to a list. \nIf an expected type is not passed for a field it will be converted to a str.\n\n```python\nfrom fastapi import Query\n\nfrom dataclass_sqlalchemy_mixins.pydantic_mixins.sqlalchemy_base_models import BaseModelConverterExtraParams\nfrom dataclass_sqlalchemy_mixins.pydantic_mixins.sqlalchemy_base_models import SqlAlchemyFilterBaseModel\n\nclass SomeSqlAlchemyFilterModel(SqlAlchemyFilterBaseModel):\n    field__in: str = Query(None)\n    another_field__in: str = Query(None)\n\n    class ConverterConfig:\n        model = SomeModel\n        extra = {\n            BaseModelConverterExtraParams.LIST_AS_STRING: {\n                'fields': ['field__in', ],\n                'expected_types': {\n                    'field__in': int,\n                }\n            }\n        }\n```\n\nThe same applies to the classes inherited from `SqlAlchemyOrderBaseModel`,\nexcept that since the model accepts only the `order_by` field, it is not necessary to specify specific fields.\n\n```python\nfrom dataclass_sqlalchemy_mixins.pydantic_mixins.sqlalchemy_base_models import BaseModelConverterExtraParams\nfrom dataclass_sqlalchemy_mixins.pydantic_mixins.sqlalchemy_base_models import SqlAlchemyOrderBaseModel\n\nclass SomeSqlAlchemyOrderModel(SqlAlchemyOrderBaseModel):\n    order_by: str = None\n\n    class ConverterConfig:\n        model = SomeModel\n        extra = {\n            BaseModelConverterExtraParams.LIST_AS_STRING: True\n        }\n```\n\n**Another possible solution**\n\nAlso, it is possible not use `ConverterConfig` to correctly display lists in `Query` parameters using `FastApi`\n\n```python\nimport typing\n\nfrom fastapi import Query\n\nfrom dataclass_sqlalchemy_mixins.pydantic_mixins.sqlalchemy_base_models import SqlAlchemyFilterBaseModel\n\nclass SomeSqlAlchemyFilterModel(SqlAlchemyFilterBaseModel):\n    field__in: str\n\n    def __init__(self, field__in: typing.List[str] = Query(), **kwargs) -\u003e None:\n        super().__init__(field__in=field__in, **kwargs)\n```\n\n____\n### Docker Compose\nTo run tests on your local machine\n```bash\ncd tests\ndocker compose up\n```\n____\n### Links\n[Github](https://github.com/ViAchKoN/dataclass-sqlalchemy-mixins)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fviachkon%2Fdataclass-sqlalchemy-mixins","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fviachkon%2Fdataclass-sqlalchemy-mixins","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fviachkon%2Fdataclass-sqlalchemy-mixins/lists"}