sessionViewForClassApiSqlModel 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alvaro Martinez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,81 @@
1
+ Metadata-Version: 2.4
2
+ Name: sessionViewForClassApiSqlModel
3
+ Version: 0.1.1
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: classapi>=0.1.0.1
9
+ Requires-Dist: sqlmodel>=0.0.34
10
+ Dynamic: license-file
11
+
12
+ # SQLModel support for classApi
13
+
14
+ This project provides a simple way to use a SQLModel `Session` inside your classApi views.
15
+
16
+ First, create your SQLModel engine. Here is a basic example:
17
+
18
+ ```py
19
+ # engine.py
20
+ from sqlalchemy import create_engine
21
+ from sqlmodel import SQLModel
22
+
23
+ from .model import User # In this example, User has 3 fields: id, name, and email
24
+
25
+
26
+ sqlite_file_name = "database.db"
27
+ sqlite_url = f"sqlite:///{sqlite_file_name}"
28
+
29
+ engine = create_engine(sqlite_url, echo=True)
30
+
31
+ def create_db_and_tables():
32
+ SQLModel.metadata.create_all(engine)
33
+ ```
34
+
35
+ Then create a new base view using `make_session_view`:
36
+
37
+ ```py
38
+ # engine.py
39
+ from sqlalchemy import create_engine
40
+ from sqlmodel import SQLModel
41
+
42
+ from sqlmodelclassapi import make_session_view
43
+ from .model import User
44
+
45
+
46
+ sqlite_file_name = "database.db"
47
+ sqlite_url = f"sqlite:///{sqlite_file_name}"
48
+
49
+ engine = create_engine(sqlite_url, echo=True)
50
+
51
+ def create_db_and_tables():
52
+ SQLModel.metadata.create_all(engine)
53
+
54
+ SessionView = make_session_view(engine=engine) # New base view with session support
55
+ ```
56
+
57
+ Now, to create a view, inherit from `SessionView` instead of `BaseView`:
58
+
59
+ ```py
60
+ # views.py
61
+ class ExampleSessionView(SessionView):
62
+ methods = ["GET", "POST"]
63
+
64
+ def get(self, *args):
65
+ statement = select(User)
66
+ results = self.session.exec(statement).all()
67
+ return [
68
+ {"id": user.id, "name": user.name, "email": user.email}
69
+ for user in results
70
+ ]
71
+
72
+ def post(self, name: str):
73
+ new_user = User(name=name, email=f"{name.lower()}@example.com")
74
+ self.session.add(new_user)
75
+ self.session.commit()
76
+ self.session.refresh(new_user)
77
+
78
+ return {"message": f"User '{name}' created successfully!", "user": new_user}
79
+ ```
80
+
81
+ With this setup, all operations in your request run inside the same session, including `pre_{method}` hooks.
@@ -0,0 +1,70 @@
1
+ # SQLModel support for classApi
2
+
3
+ This project provides a simple way to use a SQLModel `Session` inside your classApi views.
4
+
5
+ First, create your SQLModel engine. Here is a basic example:
6
+
7
+ ```py
8
+ # engine.py
9
+ from sqlalchemy import create_engine
10
+ from sqlmodel import SQLModel
11
+
12
+ from .model import User # In this example, User has 3 fields: id, name, and email
13
+
14
+
15
+ sqlite_file_name = "database.db"
16
+ sqlite_url = f"sqlite:///{sqlite_file_name}"
17
+
18
+ engine = create_engine(sqlite_url, echo=True)
19
+
20
+ def create_db_and_tables():
21
+ SQLModel.metadata.create_all(engine)
22
+ ```
23
+
24
+ Then create a new base view using `make_session_view`:
25
+
26
+ ```py
27
+ # engine.py
28
+ from sqlalchemy import create_engine
29
+ from sqlmodel import SQLModel
30
+
31
+ from sqlmodelclassapi import make_session_view
32
+ from .model import User
33
+
34
+
35
+ sqlite_file_name = "database.db"
36
+ sqlite_url = f"sqlite:///{sqlite_file_name}"
37
+
38
+ engine = create_engine(sqlite_url, echo=True)
39
+
40
+ def create_db_and_tables():
41
+ SQLModel.metadata.create_all(engine)
42
+
43
+ SessionView = make_session_view(engine=engine) # New base view with session support
44
+ ```
45
+
46
+ Now, to create a view, inherit from `SessionView` instead of `BaseView`:
47
+
48
+ ```py
49
+ # views.py
50
+ class ExampleSessionView(SessionView):
51
+ methods = ["GET", "POST"]
52
+
53
+ def get(self, *args):
54
+ statement = select(User)
55
+ results = self.session.exec(statement).all()
56
+ return [
57
+ {"id": user.id, "name": user.name, "email": user.email}
58
+ for user in results
59
+ ]
60
+
61
+ def post(self, name: str):
62
+ new_user = User(name=name, email=f"{name.lower()}@example.com")
63
+ self.session.add(new_user)
64
+ self.session.commit()
65
+ self.session.refresh(new_user)
66
+
67
+ return {"message": f"User '{name}' created successfully!", "user": new_user}
68
+ ```
69
+
70
+ With this setup, all operations in your request run inside the same session, including `pre_{method}` hooks.
@@ -0,0 +1,10 @@
1
+ [project]
2
+ name = "sessionViewForClassApiSqlModel"
3
+ version = "0.1.1"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "classapi>=0.1.0.1",
9
+ "sqlmodel>=0.0.34",
10
+ ]
@@ -0,0 +1 @@
1
+ from main import make_session_view
@@ -0,0 +1,160 @@
1
+ from classapi import BaseView
2
+ import fastapi as _fastapi
3
+ from sqlmodel import Session
4
+
5
+ def make_session_view(engine = None):
6
+ class SessionView(BaseView):
7
+ def __init__(self):
8
+ super().__init__()
9
+ self.session = None # will be set in the endpoint wrapper
10
+
11
+ def _add_route(self, app: _fastapi.FastAPI, path: str = "/", **kwargs):
12
+ """Register handlers on `app` forwarding any FastAPI-compatible kwargs.
13
+
14
+ `kwargs` are passed directly to `app.add_api_route`, so you can provide
15
+ `response_model`, `dependencies`, `status_code`, etc.
16
+ """
17
+ # create FastAPI-compatible endpoint wrappers that preserve the
18
+ # original method signature (excluding `self`) by setting
19
+ # `__signature__` on the wrapper. FastAPI uses that for dependency
20
+ # injection and parameter parsing.
21
+ def _make_endpoint(method_name, http_methods):
22
+ import inspect
23
+ func = getattr(self, method_name)
24
+ sig_func = inspect.signature(func)
25
+ params_func = list(sig_func.parameters.values())
26
+ if params_func and params_func[0].name == "self":
27
+ params_func = params_func[1:]
28
+ # exclude *args and **kwargs from the public signature
29
+ params_func = [p for p in params_func if p.kind not in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD)]
30
+
31
+ # gather pre handlers: specific pre_<method> and general pre_process
32
+ pre_specific = getattr(self, f"pre_{method_name}", None)
33
+ pre_general = getattr(self, "pre_process", None)
34
+
35
+ params_pre_specific = []
36
+ params_pre_general = []
37
+ if pre_specific is not None:
38
+ sig_pre = inspect.signature(pre_specific)
39
+ params_pre_specific = list(sig_pre.parameters.values())
40
+ if params_pre_specific and params_pre_specific[0].name == "self":
41
+ params_pre_specific = params_pre_specific[1:]
42
+ # exclude *args and **kwargs from the public signature
43
+ params_pre_specific = [p for p in params_pre_specific if p.kind not in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD)]
44
+ # also filter accidental plain 'args'/'kwargs' parameters
45
+ params_pre_specific = [p for p in params_pre_specific if p.name not in ("args", "kwargs")]
46
+ if pre_general is not None:
47
+ sig_pre_g = inspect.signature(pre_general)
48
+ params_pre_general = list(sig_pre_g.parameters.values())
49
+ if params_pre_general and params_pre_general[0].name == "self":
50
+ params_pre_general = params_pre_general[1:]
51
+ # exclude *args and **kwargs from the public signature
52
+ params_pre_general = [p for p in params_pre_general if p.kind not in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD)]
53
+ # also filter accidental plain 'args'/'kwargs' parameters
54
+ params_pre_general = [p for p in params_pre_general if p.name not in ("args", "kwargs")]
55
+
56
+ # merge params: general pre, specific pre, then func params; skip duplicates by name
57
+ seen = set()
58
+ merged = []
59
+ for p in params_pre_general + params_pre_specific + params_func:
60
+ if p.name not in seen:
61
+ merged.append(p)
62
+ seen.add(p.name)
63
+
64
+ # Reorder merged params to satisfy Python's parameter ordering rules:
65
+ # positional-only, positional-or-keyword, var-positional, keyword-only, var-keyword
66
+ posonly = []
67
+ pos_or_kw = []
68
+ var_pos = None
69
+ kw_only = []
70
+ var_kw = None
71
+ for p in merged:
72
+ if p.kind == inspect.Parameter.POSITIONAL_ONLY:
73
+ posonly.append(p)
74
+ elif p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
75
+ pos_or_kw.append(p)
76
+ elif p.kind == inspect.Parameter.VAR_POSITIONAL:
77
+ if var_pos is None:
78
+ var_pos = p
79
+ elif p.kind == inspect.Parameter.KEYWORD_ONLY:
80
+ kw_only.append(p)
81
+ elif p.kind == inspect.Parameter.VAR_KEYWORD:
82
+ if var_kw is None:
83
+ var_kw = p
84
+
85
+ # Ensure parameters without defaults come before those with defaults
86
+ def split_by_default(params):
87
+ no_def = [p for p in params if p.default is inspect._empty]
88
+ has_def = [p for p in params if p.default is not inspect._empty]
89
+ return no_def + has_def
90
+
91
+ posonly = split_by_default(posonly)
92
+ pos_or_kw = split_by_default(pos_or_kw)
93
+ kw_only = split_by_default(kw_only)
94
+
95
+ ordered = posonly + pos_or_kw
96
+ if var_pos is not None:
97
+ ordered.append(var_pos)
98
+ ordered += kw_only
99
+ if var_kw is not None:
100
+ ordered.append(var_kw)
101
+
102
+ # normalize parameters: if a parameter has no annotation but its default
103
+ # is an `Annotated[T, metadata...]` object (commonly used incorrectly
104
+ # as a default), extract the annotation and the first metadata item
105
+ # (e.g. Header()) and set them on the parameter so FastAPI detects it.
106
+ import typing
107
+ normalized = []
108
+ for p in ordered:
109
+ ann = p.annotation
110
+ default = p.default
111
+ if (ann is inspect._empty and default is not inspect._empty
112
+ and typing.get_origin(default) is typing.Annotated):
113
+ args = typing.get_args(default)
114
+ if args:
115
+ new_ann = args[0]
116
+ new_default = args[1] if len(args) > 1 else inspect._empty
117
+ p = inspect.Parameter(p.name, p.kind, default=new_default, annotation=new_ann)
118
+ normalized.append(p)
119
+
120
+ new_sig = inspect.Signature(parameters=normalized)
121
+
122
+ def endpoint(**kwargs):
123
+ if engine is None:
124
+ raise RuntimeError(
125
+ "SessionView has no engine configured. "
126
+ "Create it with make_session_view(engine=your_engine)."
127
+ )
128
+ with Session(engine) as session:
129
+ self.session = session # make session available as an instance attribute for handlers and pre-processors
130
+ try:
131
+ # call general pre_process with its subset
132
+ if pre_general is not None:
133
+ pre_g_kwargs = {k: v for k, v in kwargs.items() if k in {p.name for p in params_pre_general}}
134
+ pre_general(**pre_g_kwargs)
135
+ # call specific pre_<method> with its subset
136
+ if pre_specific is not None:
137
+ pre_s_kwargs = {k: v for k, v in kwargs.items() if k in {p.name for p in params_pre_specific}}
138
+ pre_specific(**pre_s_kwargs)
139
+ # call actual handler with its subset
140
+ func_kwargs = {k: v for k, v in kwargs.items() if k in {p.name for p in params_func}}
141
+ return func(**func_kwargs)
142
+ finally:
143
+ self.session = None
144
+
145
+ endpoint.__signature__ = new_sig
146
+ endpoint.__name__ = f"{self.__class__.__name__}_{method_name}_endpoint"
147
+ return endpoint
148
+
149
+ if hasattr(self, "get") and ("GET" in self.methods or "get" in self.methods):
150
+ app.add_api_route(path, _make_endpoint("get", ["GET"]), methods=["GET"], **kwargs)
151
+ if hasattr(self, "post") and ("POST" in self.methods or "post" in self.methods):
152
+ app.add_api_route(path, _make_endpoint("post", ["POST"]), methods=["POST"], **kwargs)
153
+ if hasattr(self, "put") and ("PUT" in self.methods or "put" in self.methods):
154
+ app.add_api_route(path, _make_endpoint("put", ["PUT"]), methods=["PUT"], **kwargs)
155
+ if hasattr(self, "delete") and ("DELETE" in self.methods or "delete" in self.methods):
156
+ app.add_api_route(path, _make_endpoint("delete", ["DELETE"]), methods=["DELETE"], **kwargs)
157
+ if hasattr(self, "patch") and ("PATCH" in self.methods or "patch" in self.methods):
158
+ app.add_api_route(path, _make_endpoint("patch", ["PATCH"]), methods=["PATCH"], **kwargs)
159
+
160
+ return SessionView
@@ -0,0 +1,81 @@
1
+ Metadata-Version: 2.4
2
+ Name: sessionViewForClassApiSqlModel
3
+ Version: 0.1.1
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: classapi>=0.1.0.1
9
+ Requires-Dist: sqlmodel>=0.0.34
10
+ Dynamic: license-file
11
+
12
+ # SQLModel support for classApi
13
+
14
+ This project provides a simple way to use a SQLModel `Session` inside your classApi views.
15
+
16
+ First, create your SQLModel engine. Here is a basic example:
17
+
18
+ ```py
19
+ # engine.py
20
+ from sqlalchemy import create_engine
21
+ from sqlmodel import SQLModel
22
+
23
+ from .model import User # In this example, User has 3 fields: id, name, and email
24
+
25
+
26
+ sqlite_file_name = "database.db"
27
+ sqlite_url = f"sqlite:///{sqlite_file_name}"
28
+
29
+ engine = create_engine(sqlite_url, echo=True)
30
+
31
+ def create_db_and_tables():
32
+ SQLModel.metadata.create_all(engine)
33
+ ```
34
+
35
+ Then create a new base view using `make_session_view`:
36
+
37
+ ```py
38
+ # engine.py
39
+ from sqlalchemy import create_engine
40
+ from sqlmodel import SQLModel
41
+
42
+ from sqlmodelclassapi import make_session_view
43
+ from .model import User
44
+
45
+
46
+ sqlite_file_name = "database.db"
47
+ sqlite_url = f"sqlite:///{sqlite_file_name}"
48
+
49
+ engine = create_engine(sqlite_url, echo=True)
50
+
51
+ def create_db_and_tables():
52
+ SQLModel.metadata.create_all(engine)
53
+
54
+ SessionView = make_session_view(engine=engine) # New base view with session support
55
+ ```
56
+
57
+ Now, to create a view, inherit from `SessionView` instead of `BaseView`:
58
+
59
+ ```py
60
+ # views.py
61
+ class ExampleSessionView(SessionView):
62
+ methods = ["GET", "POST"]
63
+
64
+ def get(self, *args):
65
+ statement = select(User)
66
+ results = self.session.exec(statement).all()
67
+ return [
68
+ {"id": user.id, "name": user.name, "email": user.email}
69
+ for user in results
70
+ ]
71
+
72
+ def post(self, name: str):
73
+ new_user = User(name=name, email=f"{name.lower()}@example.com")
74
+ self.session.add(new_user)
75
+ self.session.commit()
76
+ self.session.refresh(new_user)
77
+
78
+ return {"message": f"User '{name}' created successfully!", "user": new_user}
79
+ ```
80
+
81
+ With this setup, all operations in your request run inside the same session, including `pre_{method}` hooks.
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ sessionViewForClassApiSqlModel/__init__.py
5
+ sessionViewForClassApiSqlModel/main.py
6
+ sessionViewForClassApiSqlModel.egg-info/PKG-INFO
7
+ sessionViewForClassApiSqlModel.egg-info/SOURCES.txt
8
+ sessionViewForClassApiSqlModel.egg-info/dependency_links.txt
9
+ sessionViewForClassApiSqlModel.egg-info/requires.txt
10
+ sessionViewForClassApiSqlModel.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ classapi>=0.1.0.1
2
+ sqlmodel>=0.0.34
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+