ns-orm 0.0.1__tar.gz → 0.0.2__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.
Files changed (46) hide show
  1. {ns_orm-0.0.1/src/ns_orm.egg-info → ns_orm-0.0.2}/PKG-INFO +60 -21
  2. ns_orm-0.0.2/README.md +108 -0
  3. {ns_orm-0.0.1 → ns_orm-0.0.2}/doc/async.md +11 -6
  4. ns_orm-0.0.2/doc/database.md +126 -0
  5. {ns_orm-0.0.1 → ns_orm-0.0.2}/doc/index.md +13 -0
  6. {ns_orm-0.0.1 → ns_orm-0.0.2}/doc/migrations.md +9 -6
  7. {ns_orm-0.0.1 → ns_orm-0.0.2}/pyproject.toml +9 -3
  8. ns_orm-0.0.2/src/ns_orm/database.py +601 -0
  9. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/fields.py +57 -4
  10. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/model.py +3 -3
  11. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/query.py +28 -9
  12. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/schema.py +6 -1
  13. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/typing.py +5 -5
  14. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/utils.py +13 -2
  15. {ns_orm-0.0.1 → ns_orm-0.0.2/src/ns_orm.egg-info}/PKG-INFO +60 -21
  16. ns_orm-0.0.2/src/ns_orm.egg-info/requires.txt +14 -0
  17. ns_orm-0.0.1/README.md +0 -70
  18. ns_orm-0.0.1/doc/database.md +0 -60
  19. ns_orm-0.0.1/src/ns_orm/database.py +0 -292
  20. ns_orm-0.0.1/src/ns_orm.egg-info/requires.txt +0 -8
  21. {ns_orm-0.0.1 → ns_orm-0.0.2}/LICENSE +0 -0
  22. {ns_orm-0.0.1 → ns_orm-0.0.2}/MANIFEST.in +0 -0
  23. {ns_orm-0.0.1 → ns_orm-0.0.2}/doc/models.md +0 -0
  24. {ns_orm-0.0.1 → ns_orm-0.0.2}/doc/queryset.md +0 -0
  25. {ns_orm-0.0.1 → ns_orm-0.0.2}/doc/relations.md +0 -0
  26. {ns_orm-0.0.1 → ns_orm-0.0.2}/doc/release.md +0 -0
  27. {ns_orm-0.0.1 → ns_orm-0.0.2}/doc/schema.md +0 -0
  28. {ns_orm-0.0.1 → ns_orm-0.0.2}/setup.cfg +0 -0
  29. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/__init__.py +0 -0
  30. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/cli.py +0 -0
  31. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/dialects.py +0 -0
  32. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/exceptions.py +0 -0
  33. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/expressions.py +0 -0
  34. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/manager.py +0 -0
  35. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/migrations/__init__.py +0 -0
  36. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/migrations/autodetector.py +0 -0
  37. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/migrations/executor.py +0 -0
  38. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/migrations/loader.py +0 -0
  39. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/migrations/migration.py +0 -0
  40. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/migrations/operations.py +0 -0
  41. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/migrations/state.py +0 -0
  42. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm/migrations/writer.py +0 -0
  43. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm.egg-info/SOURCES.txt +0 -0
  44. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm.egg-info/dependency_links.txt +0 -0
  45. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm.egg-info/entry_points.txt +0 -0
  46. {ns_orm-0.0.1 → ns_orm-0.0.2}/src/ns_orm.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: ns-orm
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: A ORM built on Pydantic models.
5
5
  Author: ns-orm contributors
6
6
  License: Apache License
@@ -210,12 +210,13 @@ Description-Content-Type: text/markdown
210
210
  License-File: LICENSE
211
211
  Requires-Dist: pydantic
212
212
  Requires-Dist: lesscode-database
213
- Requires-Dist: typing-extensions>=4.0
213
+ Requires-Dist: typing-extensions
214
214
  Provides-Extra: dev
215
- Requires-Dist: pytest>=8; extra == "dev"
216
- Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
217
- Requires-Dist: ruff>=0.5; extra == "dev"
218
- Dynamic: license-file
215
+ Requires-Dist: pytest<8,>=7; python_version < "3.8" and extra == "dev"
216
+ Requires-Dist: pytest>=8; python_version >= "3.8" and extra == "dev"
217
+ Requires-Dist: pytest-asyncio<0.21.2,>=0.20; python_version < "3.8" and extra == "dev"
218
+ Requires-Dist: pytest-asyncio>=0.23; python_version >= "3.8" and extra == "dev"
219
+ Requires-Dist: ruff>=0.5; python_version >= "3.8" and extra == "dev"
219
220
 
220
221
  # ns-orm
221
222
 
@@ -240,44 +241,82 @@ from typing import Optional
240
241
 
241
242
  from typing_extensions import Annotated
242
243
 
243
- from ns_orm import Database, ForeignKey, Int, Model, String, create_all
244
- from ns_orm.dialects import dialect_from_url
244
+ from lesscode_database.connection_info import ConnectionInfo
245
+ from lesscode_database.db_options import db_options
246
+
247
+ from ns_orm import ForeignKey, Int, Model, String, create_all, get_connection
245
248
 
246
249
 
247
250
  class User(Model):
251
+ class Meta:
252
+ connect_name = "default"
253
+
248
254
  id: Annotated[Optional[int], Int(primary_key=True)] = None
249
255
  name: Annotated[str, String(max_length=50)]
250
256
 
251
257
 
252
258
  class Post(Model):
259
+ class Meta:
260
+ connect_name = "default"
261
+
253
262
  id: Annotated[Optional[int], Int(primary_key=True)] = None
254
263
  user_id: Annotated[int, ForeignKey("User", on_delete="CASCADE")]
255
264
  title: Annotated[str, String(max_length=200)]
256
265
 
257
266
 
258
- executor = ... # 由 lesscode-database 创建
259
- db = Database(executor=executor, dialect=dialect_from_url("sqlite"))
267
+ db_options.conn_list = [
268
+ ConnectionInfo(dialect="sqlite3", name="default", dsn="test.db", async_enable=False),
269
+ ]
270
+
271
+ db = get_connection("default")
260
272
  create_all(db, [User, Post])
261
273
 
262
- u = User.objects.using(db).create(name="alice")
263
- Post.objects.using(db).create(user_id=u.id, title="hello")
274
+ u = User.objects.create(name="alice")
275
+ Post.objects.create(user_id=u.id, title="hello")
264
276
 
265
- posts = Post.objects.using(db).prefetch_related("user").all()
277
+ posts = Post.objects.prefetch_related("user").all()
266
278
  print(posts[0].user.name)
279
+
280
+ connect, _connect_info = getattr(db_options, "default")
281
+ if not hasattr(connect, "cursor") and hasattr(connect, "connection"):
282
+ connect = connect.connection()
283
+
284
+ qs = User.objects.filter(id=u.id)
285
+ sql, params = qs._select_sql()
286
+ prepared = db.dialect.prepare(sql, params)
287
+ cursor = connect.cursor()
288
+ cursor.execute(prepared.sql, prepared.params)
289
+ row = cursor.fetchone()
290
+ print(row)
267
291
  ```
268
292
 
269
293
  ## 快速开始(异步)
270
294
 
271
295
  ```python
272
- from ns_orm import AsyncDatabase, acreate_all
273
- from ns_orm.dialects import dialect_from_url
296
+ import asyncio
297
+
298
+ from lesscode_database.connection_info import ConnectionInfo
299
+ from lesscode_database.db_options import db_options
300
+
301
+ from ns_orm import acreate_all, get_connection
302
+
303
+
304
+ async def main():
305
+ db_options.conn_list = [
306
+ ConnectionInfo(dialect="sqlite3", name="default_async", dsn="test_async.db", async_enable=True),
307
+ ]
308
+
309
+ db = get_connection("default_async")
310
+ await acreate_all(db, [User, Post])
311
+
312
+ u = await User.objects.using("default_async").acreate(name="alice")
313
+ posts = await (
314
+ Post.objects.using("default_async").filter(user_id=u.id).prefetch_related("user").all()
315
+ )
316
+ print(posts[0].user.name)
274
317
 
275
- executor = ... # 由 lesscode-database 创建(异步执行器)
276
- db = AsyncDatabase(executor=executor, dialect=dialect_from_url("sqlite"))
277
- await acreate_all(db, [User, Post])
278
318
 
279
- u = await User.objects.using(db).acreate(name="alice")
280
- posts = await Post.objects.using(db).filter(user_id=u.id).prefetch_related("user").all()
319
+ asyncio.run(main())
281
320
  ```
282
321
 
283
322
  ## 迁移命令
ns_orm-0.0.2/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # ns-orm
2
+
3
+ 一个参考 Django ORM 设计的轻量级 ORM(不依赖 SQLAlchemy),以 Pydantic 作为模型层,支持同步/异步、CRUD、关联预取与最小建表能力。
4
+
5
+ 数据库连接/连接池由 lesscode-database 负责,ns-orm 仅消费“执行器”来执行 SQL。
6
+
7
+ 更多功能与完整用法见 [doc/index.md](doc/index.md)。
8
+
9
+ ## 安装
10
+
11
+ ```bash
12
+ pip install ns-orm
13
+ ```
14
+
15
+ 数据库驱动与连接方式由 lesscode-database 决定。
16
+
17
+ ## 快速开始(同步)
18
+
19
+ ```python
20
+ from typing import Optional
21
+
22
+ from typing_extensions import Annotated
23
+
24
+ from lesscode_database.connection_info import ConnectionInfo
25
+ from lesscode_database.db_options import db_options
26
+
27
+ from ns_orm import ForeignKey, Int, Model, String, create_all, get_connection
28
+
29
+
30
+ class User(Model):
31
+ class Meta:
32
+ connect_name = "default"
33
+
34
+ id: Annotated[Optional[int], Int(primary_key=True)] = None
35
+ name: Annotated[str, String(max_length=50)]
36
+
37
+
38
+ class Post(Model):
39
+ class Meta:
40
+ connect_name = "default"
41
+
42
+ id: Annotated[Optional[int], Int(primary_key=True)] = None
43
+ user_id: Annotated[int, ForeignKey("User", on_delete="CASCADE")]
44
+ title: Annotated[str, String(max_length=200)]
45
+
46
+
47
+ db_options.conn_list = [
48
+ ConnectionInfo(dialect="sqlite3", name="default", dsn="test.db", async_enable=False),
49
+ ]
50
+
51
+ db = get_connection("default")
52
+ create_all(db, [User, Post])
53
+
54
+ u = User.objects.create(name="alice")
55
+ Post.objects.create(user_id=u.id, title="hello")
56
+
57
+ posts = Post.objects.prefetch_related("user").all()
58
+ print(posts[0].user.name)
59
+
60
+ connect, _connect_info = getattr(db_options, "default")
61
+ if not hasattr(connect, "cursor") and hasattr(connect, "connection"):
62
+ connect = connect.connection()
63
+
64
+ qs = User.objects.filter(id=u.id)
65
+ sql, params = qs._select_sql()
66
+ prepared = db.dialect.prepare(sql, params)
67
+ cursor = connect.cursor()
68
+ cursor.execute(prepared.sql, prepared.params)
69
+ row = cursor.fetchone()
70
+ print(row)
71
+ ```
72
+
73
+ ## 快速开始(异步)
74
+
75
+ ```python
76
+ import asyncio
77
+
78
+ from lesscode_database.connection_info import ConnectionInfo
79
+ from lesscode_database.db_options import db_options
80
+
81
+ from ns_orm import acreate_all, get_connection
82
+
83
+
84
+ async def main():
85
+ db_options.conn_list = [
86
+ ConnectionInfo(dialect="sqlite3", name="default_async", dsn="test_async.db", async_enable=True),
87
+ ]
88
+
89
+ db = get_connection("default_async")
90
+ await acreate_all(db, [User, Post])
91
+
92
+ u = await User.objects.using("default_async").acreate(name="alice")
93
+ posts = await (
94
+ Post.objects.using("default_async").filter(user_id=u.id).prefetch_related("user").all()
95
+ )
96
+ print(posts[0].user.name)
97
+
98
+
99
+ asyncio.run(main())
100
+ ```
101
+
102
+ ## 迁移命令
103
+
104
+ 见 [doc/migrations.md](doc/migrations.md)。
105
+
106
+ ## 打包与发布
107
+
108
+ 见 [doc/release.md](doc/release.md)。
@@ -3,15 +3,20 @@
3
3
  ## AsyncDatabase 与异步 CRUD
4
4
 
5
5
  ```python
6
- from ns_orm import AsyncDatabase, acreate_all
7
- from ns_orm.dialects import dialect_from_url
6
+ from lesscode_database.connection_info import ConnectionInfo
7
+ from lesscode_database.db_options import db_options
8
8
 
9
- executor = ... # lesscode-database 创建(异步执行器)
10
- db = AsyncDatabase(executor=executor, dialect=dialect_from_url("sqlite"))
9
+ from ns_orm import acreate_all, get_connection
10
+
11
+ db_options.conn_list = [
12
+ ConnectionInfo(dialect="sqlite3", name="default_async", dsn="test_async.db", async_enable=True),
13
+ ]
14
+
15
+ db = get_connection("default_async")
11
16
  await acreate_all(db, [User, Post])
12
17
 
13
- u = await User.objects.using(db).acreate(name="alice")
14
- posts = await Post.objects.using(db).filter(user_id=u.id).all()
18
+ u = await User.objects.using("default_async").acreate(name="alice")
19
+ posts = await Post.objects.using("default_async").filter(user_id=u.id).all()
15
20
  ```
16
21
 
17
22
  ## 异步 QuerySet
@@ -0,0 +1,126 @@
1
+ # 数据库与执行器(lesscode-database)
2
+
3
+ ## 与 db_options 集成(connect_name)
4
+
5
+ ns-orm 默认会从 `lesscode_database.db_options.db_options` 读取连接:
6
+
7
+ - `connect, connect_info = getattr(db_options, connect_name)`
8
+ - `connect_info.dialect` 用于选择 ns-orm dialect
9
+
10
+ 示例(SQLite):
11
+
12
+ ```python
13
+ from lesscode_database.connection_info import ConnectionInfo
14
+ from lesscode_database.db_options import db_options
15
+
16
+ from ns_orm import create_all, get_connection
17
+
18
+ db_options.conn_list = [
19
+ ConnectionInfo(dialect="sqlite3", name="default", dsn="test.db", async_enable=False),
20
+ ]
21
+
22
+ db = get_connection("default")
23
+ create_all(db, [User, Post])
24
+ ```
25
+
26
+ 说明:
27
+
28
+ - `sqlite3` 连接池在 lesscode-database 中依赖 `DBUtils`(未安装会报错)
29
+ - 异步 `sqlite3` 连接依赖 `aiosqlite3`(未安装会报错)
30
+ - 某些情况下 `connect` 可能是连接池对象而不是 DB-API 连接;如果你需要直接 `cursor()`,可使用 `connect.connection()` 获取真实连接
31
+
32
+ 使用 `connect.cursor()` 执行 ORM 生成的 SQL:
33
+
34
+ ```python
35
+ connect_name = "default"
36
+ connect, _connect_info = getattr(db_options, connect_name)
37
+ if not hasattr(connect, "cursor") and hasattr(connect, "connection"):
38
+ connect = connect.connection()
39
+
40
+ qs = User.objects.filter(id=1)
41
+ sql, params = qs._select_sql()
42
+ prepared = db.dialect.prepare(sql, params)
43
+
44
+ cursor = connect.cursor()
45
+ cursor.execute(prepared.sql, prepared.params)
46
+ row = cursor.fetchone()
47
+ print(row)
48
+ ```
49
+
50
+ 示例(SQLite 异步):
51
+
52
+ ```python
53
+ from lesscode_database.connection_info import ConnectionInfo
54
+ from lesscode_database.db_options import db_options
55
+
56
+ from ns_orm import acreate_all, get_connection
57
+
58
+ db_options.conn_list = [
59
+ ConnectionInfo(dialect="sqlite3", name="default_async", dsn="test_async.db", async_enable=True),
60
+ ]
61
+
62
+ db = get_connection("default_async")
63
+ await acreate_all(db, [User, Post])
64
+
65
+ u = await User.objects.using("default_async").acreate(name="alice")
66
+ posts = await Post.objects.using("default_async").filter(user_id=u.id).prefetch_related("user").all()
67
+ ```
68
+
69
+ ## Database / AsyncDatabase(自定义 executor)
70
+
71
+ 当你希望绕开 `db_options`(例如在框架外自行管理连接/执行器)时,也可以直接构造:
72
+
73
+ 同步:
74
+
75
+ ```python
76
+ from ns_orm import Database
77
+ from ns_orm.dialects import dialect_from_url
78
+
79
+ executor = ... # 由 lesscode-database 创建
80
+ db = Database(executor=executor, dialect=dialect_from_url("sqlite"))
81
+ ```
82
+
83
+ 异步:
84
+
85
+ ```python
86
+ from ns_orm import AsyncDatabase
87
+ from ns_orm.dialects import dialect_from_url
88
+
89
+ executor = ... # 由 lesscode-database 创建(异步执行器)
90
+ db = AsyncDatabase(executor=executor, dialect=dialect_from_url("postgresql"))
91
+ ```
92
+
93
+ ## 事务
94
+
95
+ 同步:
96
+
97
+ ```python
98
+ with db.transaction():
99
+ User.objects.using(db).create(name="alice")
100
+ ```
101
+
102
+ 异步:
103
+
104
+ ```python
105
+ async with db.transaction():
106
+ await User.objects.using(db).acreate(name="alice")
107
+ ```
108
+
109
+ ## 方言(dialect)
110
+
111
+ 当前方言用于:
112
+
113
+ - 标识符引用(引号)
114
+ - DDL 类型映射
115
+ - 参数风格转换(`:name` -> driver paramstyle)
116
+
117
+ 常用值(可直接使用 URL scheme 的前缀):
118
+
119
+ - `sqlite`
120
+ - `clickhouse`
121
+ - `postgres` / `postgresql`
122
+ - `mysql`(也可传 `mariadb` / `tidb`)
123
+ - `mssql` / `sqlserver`
124
+ - `oracle`
125
+
126
+ 驱动安装与连接配置请参考 lesscode-database。
@@ -20,3 +20,16 @@ ns-orm 的目标是提供接近 Django ORM 的开发体验:
20
20
  - 以 Database/AsyncDatabase 作为数据库适配层(不同驱动、参数风格转换)
21
21
 
22
22
  如果你需要在框架未内置的数据库上使用,请优先选择该数据库的 DB-API 2.0 驱动(同步)或提供等价的异步驱动。
23
+
24
+ ## 开发与测试
25
+
26
+ 安装开发依赖:
27
+
28
+ ```bash
29
+ pip install -e ".[dev]"
30
+ ```
31
+
32
+ 说明:
33
+
34
+ - Python 3.7:`pytest<8`、较老版本的 `pytest-asyncio`,不安装 ruff
35
+ - Python >=3.8:`pytest>=8`、`pytest-asyncio>=0.23`、`ruff>=0.5`
@@ -36,15 +36,18 @@ ns-orm makemigrations \
36
36
 
37
37
  ```python
38
38
  # project/db.py
39
- from ns_orm import Database
40
- from ns_orm.dialects import dialect_from_url
39
+ from lesscode_database.connection_info import ConnectionInfo
40
+ from lesscode_database.db_options import db_options
41
+
42
+ from ns_orm import get_connection
41
43
 
42
44
 
43
45
  def get_db(dialect):
44
- # 这里应使用 lesscode-database 创建执行器(示意)
45
- # executor = lesscode_database.get_executor(...)
46
- executor = ...
47
- return Database(executor=executor, dialect=dialect)
46
+ _ = dialect
47
+ db_options.conn_list = [
48
+ ConnectionInfo(dialect="sqlite3", name="default", dsn="test.db", async_enable=False),
49
+ ]
50
+ return get_connection("default")
48
51
  ```
49
52
 
50
53
  执行迁移:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ns-orm"
7
- version = "0.0.1"
7
+ version = "0.0.2"
8
8
  description = "A ORM built on Pydantic models."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.7"
@@ -13,11 +13,17 @@ authors = [{ name = "ns-orm contributors" }]
13
13
  dependencies = [
14
14
  "pydantic",
15
15
  "lesscode-database",
16
- "typing-extensions>=4.0",
16
+ "typing-extensions",
17
17
  ]
18
18
 
19
19
  [project.optional-dependencies]
20
- dev = ["pytest>=8", "pytest-asyncio>=0.23", "ruff>=0.5"]
20
+ dev = [
21
+ "pytest>=7,<8; python_version<'3.8'",
22
+ "pytest>=8; python_version>='3.8'",
23
+ "pytest-asyncio>=0.20,<0.21.2; python_version<'3.8'",
24
+ "pytest-asyncio>=0.23; python_version>='3.8'",
25
+ "ruff>=0.5; python_version>='3.8'",
26
+ ]
21
27
 
22
28
  [project.scripts]
23
29
  ns-orm = "ns_orm.cli:main"