Skip to content

Commit bad2040

Browse files
V1 (#11)
Basic implementation provided. 100% tested.
1 parent 9c6ec51 commit bad2040

35 files changed

+1812
-451
lines changed

.github/workflows/publish.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Build and publish python package to Test PyPI
2+
3+
on:
4+
release:
5+
types: [ published ]
6+
7+
jobs:
8+
publish-package:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: write
12+
steps:
13+
- name: Publish PyPi package
14+
uses: code-specialist/[email protected]
15+
with:
16+
PYTHON_VERSION: "3.10"
17+
PACKAGE_DIRECTORY: './sqlmodel_repository/'
18+
ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19+
PUBLISH_REGISTRY_PASSWORD: ${{ secrets.PYPI_TOKEN }}
20+
BRANCH: "main"

.github/workflows/test.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@ jobs:
4444
run: poetry run pytest
4545
shell: bash
4646

47-
- name: Linting
48-
run: poetry run pylint .
49-
shell: bash
50-
5147
- name: Teardown Test Infrastructure
5248
if: always()
5349
run: |

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
**/test.db
66
.coverage
77
.venv
8+
.vscode/PythonImportHelper-v2-Completion.json

.pre-commit-config.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# See https://pre-commit.com for more information
2+
# See https://pre-commit.com/hooks.html for more hooks
3+
repos:
4+
- repo: https://github.com/pre-commit/pre-commit-hooks
5+
rev: v4.4.0
6+
hooks:
7+
- id: trailing-whitespace
8+
- id: end-of-file-fixer
9+
- id: check-yaml
10+
- id: check-added-large-files
11+
- id: check-ast
12+
- id: check-builtin-literals
13+
- id: debug-statements
14+
- repo: https://github.com/psf/black
15+
rev: 23.1.0
16+
hooks:
17+
- id: black
18+
language_version: python3.11
19+
- repo: https://github.com/executablebooks/mdformat
20+
rev: 0.7.16
21+
hooks:
22+
- id: mdformat

.vscode/launch.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Python: Debug Tests",
6+
"type": "python",
7+
"request": "launch",
8+
"program": "${file}",
9+
"purpose": [
10+
"debug-test"
11+
],
12+
"console": "internalConsole",
13+
"justMyCode": false,
14+
"presentation": {
15+
"hidden": true
16+
},
17+
"env": {
18+
"PYTEST_ADDOPTS": "--no-cov"
19+
}
20+
}
21+
]
22+
}

.vscode/settings.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"python.testing.pytestArgs": [
3+
"."
4+
],
5+
"python.testing.unittestEnabled": false,
6+
"python.testing.pytestEnabled": true,
7+
"python.analysis.typeCheckingMode": "basic",
8+
"python.formatting.provider": "none",
9+
"[python]": {
10+
"editor.defaultFormatter": "ms-python.black-formatter"
11+
},
12+
"dotenv.enableAutocloaking": false
13+
}

README.md

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# SQLModel Repository - Python Repository Pattern Implementation for SQLModel
2+
3+
[![CodeQL](https://github.com/code-specialist/python-repository/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/code-specialist/python-repository/actions/workflows/github-code-scanning/codeql) [![Tests](https://github.com/code-specialist/python-repository/actions/workflows/test.yaml/badge.svg)](https://github.com/code-specialist/python-repository/actions/workflows/test.yaml)
4+
5+
SQLModel Repository implements the repository pattern and provides simple, robust and reliable CRUD operations for [SQLModels](https://sqlmodel.tiangolo.com/). The repository pattern is a great way to encapsulate the logic of your application and to separate the business logic from the data access layer.
6+
7+
Any contributions are welcome. But we do not accept any pull requests that do not come with tests.
8+
9+
## Installation
10+
11+
```bash
12+
pip install sqlmodel-repository
13+
```
14+
15+
## Usage
16+
17+
### 1. Create a Repository
18+
19+
To create a repository you need to inherit from the `Repository` class and pass the entity type as a generic type argument.
20+
21+
```python
22+
from typing import TypeVar
23+
from sqlalchemy.orm import Session
24+
from sqlmodel_repository import SQLModelEntity, Repository
25+
26+
ExampleEntity = TypeVar("ExampleEntity", bound=SQLModelEntity)
27+
28+
29+
class AbstractRepository(Repository[ExampleEntity]):
30+
"""Example base class for all repositories"""
31+
32+
def get_session(self) -> Session:
33+
"""Provides a session to work with"""
34+
# TODO: Implement a method to provide a session here
35+
```
36+
37+
In this example we use a `TypeVar` to pass the generic type downwards. You have to implement the `get_session` method to provide a session to the repository.
38+
39+
### 2. Create Entities and Relationships
40+
41+
```python
42+
from enum import Enum
43+
from sqlmodel import Relationship, Field
44+
from sqlmodel_repository import SQLModelEntity
45+
46+
47+
class PetType(Enum):
48+
"""Enum that describes the type of pet"""
49+
50+
DOG = "dog"
51+
CAT = "cat"
52+
FISH = "fish"
53+
54+
55+
class Pet(SQLModelEntity, table=True):
56+
"""Pet model"""
57+
58+
id: int = Field(index=True, default=None, primary_key=True)
59+
60+
name: str
61+
age: int
62+
type: PetType
63+
shelter_id: int = Field(foreign_key="shelter.id")
64+
shelter: "Shelter" = Relationship(back_populates="pets")
65+
66+
class Shelter(SQLModelEntity, table=True):
67+
"""Shelter model"""
68+
69+
id: int = Field(index=True, default=None, primary_key=True)
70+
71+
name: str
72+
pets: list[Pet] = Relationship(back_populates="shelter", sa_relationship_kwargs={"cascade": "all, delete-orphan"})
73+
```
74+
75+
### 3. Inherit from the Repository
76+
77+
Now you can inherit from your `AbstractRepository` and tell it to manage the a specific entity. e.g. `Pet` and `Shelter`:
78+
79+
```python
80+
class PetRepository(AbstractRepository[Pet]):
81+
"""Repository to manage pets"""
82+
83+
class ShelterRepository(AbstractRepository[Shelter]):
84+
"""Repository to manage shelters"""
85+
```
86+
87+
Done 🚀 You can now use the repository to perform the operations on your entities. e.g.:
88+
89+
```python
90+
from sqlmodel import col
91+
92+
# Create a new shelter
93+
shelter = ShelterRepository().create(Shelter(name="Shelter 1"))
94+
95+
# Create some pets
96+
fido = PetRepository().create(Pet(name="Fido", age=3, type=PetType.DOG, shelter_id=1))
97+
fifi = PetRepository().create(Pet(name="Fifi", age=2, type=PetType.CAT, shelter_id=1))
98+
99+
# Find all pets that belong to the shelter
100+
PetRepository().find(shelter=shelter)
101+
```
102+
103+
No more session passing, no more boilerplate code. Just use the repository to perform the operations on your entities 🎉
104+
105+
## Methods
106+
107+
### Repository
108+
109+
Each `Repository` comes with a set of **typed methods** to perform common CRUD operations on your entities:
110+
111+
- `create`: Create a new record of an entity
112+
- `create_batch`: Create a batch of records of an entity
113+
114+
______________________________________________________________________
115+
116+
- `find`: Find all records of an entity that match the given filters
117+
118+
______________________________________________________________________
119+
120+
- `get_by_id`: Get a single record by its ID
121+
- `get_batch`: Get all records of an entity that match the given filters
122+
- `get_batch_by_ids`: Get a batch of records by their IDs
123+
- `get_all`: Get all records of an entity
124+
125+
______________________________________________________________________
126+
127+
- `update`: Update an entity instance
128+
- `update_by_id`: Update an entity by its ID
129+
- `update_batch`: Update a batch of entity instances with the same values
130+
- `update_batch_by_ids`: Update a batch of entities by their IDs
131+
132+
______________________________________________________________________
133+
134+
- `delete`: Delete an entity instance
135+
- `delete_by_id`: Delete an entity by its ID
136+
- `delete_batch`: Delete a batch of entity instances
137+
- `delete_batch_by_ids`: Delete a batch of entities by their IDs
138+
139+
### BaseRepository
140+
141+
If you require more flexibility, you may also use the `BaseRepository` which provides more granular operations. The `BaseRepository` provides the following methods:
142+
143+
- `_create`: Create a new record of an entity
144+
- `_create_batch`: Create a batch of records of an entity
145+
- `_update`: Update an entity instance
146+
- `_update_batch`: Update a batch of entity instances with the same values
147+
- `_get`: Get a single record by its ID
148+
- `_get_batch`: Get all records of an entity that match the given filters
149+
- `_delete`: Delete an entity instance
150+
- `_delete_batch`: Delete a batch of entity instances
151+
152+
## Examples
153+
154+
For a more detailed example, check out our [tests/integration/scenarios](tests/integration/scenarios) directory. We do not currently offer a full example application.

0 commit comments

Comments
 (0)