surrealdb-orm 0.1.2__tar.gz → 0.1.4__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.
Potentially problematic release.
This version of surrealdb-orm might be problematic. Click here for more details.
- {surrealdb_orm-0.1.2 → surrealdb_orm-0.1.4}/.gitignore +0 -1
- {surrealdb_orm-0.1.2 → surrealdb_orm-0.1.4}/Makefile +6 -2
- {surrealdb_orm-0.1.2 → surrealdb_orm-0.1.4}/PKG-INFO +2 -2
- {surrealdb_orm-0.1.2 → surrealdb_orm-0.1.4}/README.md +1 -1
- {surrealdb_orm-0.1.2 → surrealdb_orm-0.1.4}/pyproject.toml +5 -4
- surrealdb_orm-0.1.4/src/surreal_orm/__init__.py +12 -0
- surrealdb_orm-0.1.4/src/surreal_orm/model_base.py +202 -0
- {surrealdb_orm-0.1.2 → surrealdb_orm-0.1.4}/src/surreal_orm/query_set.py +0 -1
- surrealdb_orm-0.1.2/src/surreal_orm/__init__.py +0 -6
- surrealdb_orm-0.1.2/src/surreal_orm/model_base.py +0 -229
- surrealdb_orm-0.1.2/tests/test_e2e.py +0 -250
- surrealdb_orm-0.1.2/tests/test_manager.py +0 -162
- surrealdb_orm-0.1.2/tests/test_unit.py +0 -104
- {surrealdb_orm-0.1.2 → surrealdb_orm-0.1.4}/LICENSE +0 -0
- {surrealdb_orm-0.1.2 → surrealdb_orm-0.1.4}/src/__init__.py +0 -0
- {surrealdb_orm-0.1.2 → surrealdb_orm-0.1.4}/src/surreal_orm/connection_manager.py +0 -0
- {surrealdb_orm-0.1.2 → surrealdb_orm-0.1.4}/src/surreal_orm/constants.py +0 -0
- {surrealdb_orm-0.1.2 → surrealdb_orm-0.1.4}/src/surreal_orm/enum.py +0 -0
- /surrealdb_orm-0.1.2/tests/__init__.py → /surrealdb_orm-0.1.4/src/surreal_orm/py.typed +0 -0
- {surrealdb_orm-0.1.2 → surrealdb_orm-0.1.4}/src/surreal_orm/utils.py +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
.DEFAULT_GOAL := all
|
|
2
|
-
sources = src
|
|
2
|
+
sources = src
|
|
3
3
|
|
|
4
4
|
.PHONY: .uv # Check that uv is installed
|
|
5
5
|
.uv:
|
|
@@ -20,6 +20,10 @@ lint:
|
|
|
20
20
|
uv run ruff format --check
|
|
21
21
|
uv run ruff check
|
|
22
22
|
|
|
23
|
+
.PHONY: mypy
|
|
24
|
+
mypy:
|
|
25
|
+
uv run python -m mypy $(sources)
|
|
26
|
+
|
|
23
27
|
.PHONY: typecheck
|
|
24
28
|
typecheck:
|
|
25
29
|
uv run pyright
|
|
@@ -41,4 +45,4 @@ html: test-all-python
|
|
|
41
45
|
uv run coverage html -d htmlcov
|
|
42
46
|
|
|
43
47
|
.PHONY: all
|
|
44
|
-
all: format lint typecheck test-all-python
|
|
48
|
+
all: format mypy lint typecheck test-all-python
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: surrealdb-orm
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: SurrealDB ORM as 'DJango style' for Python with async support. Works with pydantic validation.
|
|
5
5
|
Project-URL: Homepage, https://github.com/EulogySnowfall/SurrealDB-ORM
|
|
6
6
|
Project-URL: Documentation, https://github.com/EulogySnowfall/SurrealDB-ORM
|
|
@@ -70,7 +70,7 @@ Description-Content-Type: text/markdown
|
|
|
70
70
|
|
|
71
71
|
## ✅ Version
|
|
72
72
|
|
|
73
|
-
Alpha 0.1.
|
|
73
|
+
Alpha 0.1.4
|
|
74
74
|
|
|
75
75
|
---
|
|
76
76
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "surrealdb-orm"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.4"
|
|
4
4
|
description = "SurrealDB ORM as 'DJango style' for Python with async support. Works with pydantic validation."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -32,7 +32,7 @@ Repository = "https://github.com/EulogySnowfall/SurrealDB-ORM.git"
|
|
|
32
32
|
Issues = "https://github.com/EulogySnowfall/SurrealDB-ORM/issues"
|
|
33
33
|
|
|
34
34
|
[tool.setuptools]
|
|
35
|
-
packages = ["src
|
|
35
|
+
packages = ["src/surreal_orm"]
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
[build-system]
|
|
@@ -42,6 +42,7 @@ build-backend = "hatchling.build"
|
|
|
42
42
|
[tool.ruff]
|
|
43
43
|
line-length = 128
|
|
44
44
|
target-version = "py311"
|
|
45
|
+
exclude = ["examples"]
|
|
45
46
|
|
|
46
47
|
[tool.mypy]
|
|
47
48
|
python_version = "3.11"
|
|
@@ -87,7 +88,7 @@ lint = [
|
|
|
87
88
|
|
|
88
89
|
|
|
89
90
|
[tool.hatch.build.targets.wheel]
|
|
90
|
-
packages = ["src
|
|
91
|
+
packages = ["src/surreal_orm"]
|
|
91
92
|
|
|
92
93
|
[tool.hatch.build.targets.sdist]
|
|
93
|
-
include = ["
|
|
94
|
+
include = ["README.md", "Makefile", "/src"]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .model_base import BaseSurrealModel, SurrealConfigDict
|
|
2
|
+
from .connection_manager import SurrealDBConnectionManager
|
|
3
|
+
from .query_set import QuerySet
|
|
4
|
+
from .enum import OrderBy
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"SurrealDBConnectionManager",
|
|
8
|
+
"BaseSurrealModel",
|
|
9
|
+
"QuerySet",
|
|
10
|
+
"OrderBy",
|
|
11
|
+
"SurrealConfigDict",
|
|
12
|
+
]
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
from typing import Any, Self
|
|
2
|
+
from pydantic import BaseModel, ConfigDict, model_validator
|
|
3
|
+
from .connection_manager import SurrealDBConnectionManager
|
|
4
|
+
from surrealdb import RecordID, SurrealDbError
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SurrealConfigDict(ConfigDict):
|
|
13
|
+
"""
|
|
14
|
+
SurrealConfigDict is a configuration dictionary for SurrealDB models.
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
primary_key (str | None): The primary key field name for the model.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
primary_key: str | None
|
|
21
|
+
" The primary key field name for the model. "
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class BaseSurrealModel(BaseModel):
|
|
25
|
+
"""
|
|
26
|
+
Base class for models interacting with SurrealDB.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def get_table_name(cls) -> str:
|
|
31
|
+
"""
|
|
32
|
+
Get the table name for the model.
|
|
33
|
+
"""
|
|
34
|
+
return cls.__name__
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def get_index_primary_key(cls) -> str | None:
|
|
38
|
+
"""
|
|
39
|
+
Get the primary key field name for the model.
|
|
40
|
+
"""
|
|
41
|
+
if hasattr(cls, "model_config"): # pragma: no cover
|
|
42
|
+
primary_key = cls.model_config.get("primary_key", None)
|
|
43
|
+
if isinstance(primary_key, str):
|
|
44
|
+
return primary_key
|
|
45
|
+
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
def get_id(self) -> None | str | RecordID:
|
|
49
|
+
"""
|
|
50
|
+
Get the ID of the model instance.
|
|
51
|
+
"""
|
|
52
|
+
if hasattr(self, "id"):
|
|
53
|
+
id_value = getattr(self, "id")
|
|
54
|
+
return str(id_value) if id_value is not None else None
|
|
55
|
+
|
|
56
|
+
if hasattr(self, "model_config"):
|
|
57
|
+
primary_key = self.model_config.get("primary_key", None)
|
|
58
|
+
if isinstance(primary_key, str) and hasattr(self, primary_key):
|
|
59
|
+
primary_key_value = getattr(self, primary_key)
|
|
60
|
+
return str(primary_key_value) if primary_key_value is not None else None
|
|
61
|
+
|
|
62
|
+
return None # pragma: no cover
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def from_db(cls, record: dict | list) -> Self | list[Self]:
|
|
66
|
+
"""
|
|
67
|
+
Create an instance from a SurrealDB record.
|
|
68
|
+
"""
|
|
69
|
+
if isinstance(record, list):
|
|
70
|
+
return [cls.from_db(rs) for rs in record] # type: ignore
|
|
71
|
+
|
|
72
|
+
return cls(**record)
|
|
73
|
+
|
|
74
|
+
@model_validator(mode="before")
|
|
75
|
+
@classmethod
|
|
76
|
+
def set_data(cls, data: Any) -> Any:
|
|
77
|
+
"""
|
|
78
|
+
Set the ID of the model instance.
|
|
79
|
+
"""
|
|
80
|
+
if isinstance(data, dict): # pragma: no cover
|
|
81
|
+
if "id" in data and isinstance(data["id"], RecordID):
|
|
82
|
+
data["id"] = str(data["id"]).split(":")[1]
|
|
83
|
+
return data
|
|
84
|
+
|
|
85
|
+
async def refresh(self) -> None:
|
|
86
|
+
"""
|
|
87
|
+
Refresh the model instance from the database.
|
|
88
|
+
"""
|
|
89
|
+
if not self.get_id():
|
|
90
|
+
raise SurrealDbError("Can't refresh data, not recorded yet.") # pragma: no cover
|
|
91
|
+
|
|
92
|
+
client = await SurrealDBConnectionManager.get_client()
|
|
93
|
+
record = await client.select(f"{self.get_table_name()}:{self.get_id()}")
|
|
94
|
+
|
|
95
|
+
if record is None:
|
|
96
|
+
raise SurrealDbError("Can't refresh data, no record found.") # pragma: no cover
|
|
97
|
+
|
|
98
|
+
self.from_db(record)
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
async def save(self) -> Self:
|
|
102
|
+
"""
|
|
103
|
+
Save the model instance to the database.
|
|
104
|
+
"""
|
|
105
|
+
client = await SurrealDBConnectionManager.get_client()
|
|
106
|
+
data = self.model_dump(exclude={"id"})
|
|
107
|
+
id = self.get_id()
|
|
108
|
+
table = self.get_table_name()
|
|
109
|
+
|
|
110
|
+
if id is not None:
|
|
111
|
+
thing = f"{table}:{id}"
|
|
112
|
+
await client.create(thing, data)
|
|
113
|
+
return self
|
|
114
|
+
|
|
115
|
+
# Auto-generate the ID
|
|
116
|
+
record = await client.create(table, data) # pragma: no cover
|
|
117
|
+
|
|
118
|
+
if isinstance(record, list):
|
|
119
|
+
raise SurrealDbError("Can't save data, multiple records returned.") # pragma: no cover
|
|
120
|
+
|
|
121
|
+
if record is None:
|
|
122
|
+
raise SurrealDbError("Can't save data, no record returned.") # pragma: no cover
|
|
123
|
+
|
|
124
|
+
obj = self.from_db(record)
|
|
125
|
+
if isinstance(obj, type(self)):
|
|
126
|
+
self = obj
|
|
127
|
+
return self
|
|
128
|
+
|
|
129
|
+
raise SurrealDbError("Can't save data, no record returned.") # pragma: no cover
|
|
130
|
+
|
|
131
|
+
async def update(self) -> Any:
|
|
132
|
+
"""
|
|
133
|
+
Update the model instance to the database.
|
|
134
|
+
"""
|
|
135
|
+
client = await SurrealDBConnectionManager.get_client()
|
|
136
|
+
|
|
137
|
+
data = self.model_dump(exclude={"id"})
|
|
138
|
+
id = self.get_id()
|
|
139
|
+
if id is not None:
|
|
140
|
+
thing = f"{self.__class__.__name__}:{id}"
|
|
141
|
+
test = await client.update(thing, data)
|
|
142
|
+
return test
|
|
143
|
+
raise SurrealDbError("Can't update data, no id found.")
|
|
144
|
+
|
|
145
|
+
async def merge(self, **data: Any) -> Any:
|
|
146
|
+
"""
|
|
147
|
+
Update the model instance to the database.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
client = await SurrealDBConnectionManager.get_client()
|
|
151
|
+
data_set = {key: value for key, value in data.items()}
|
|
152
|
+
|
|
153
|
+
id = self.get_id()
|
|
154
|
+
if id:
|
|
155
|
+
thing = f"{self.get_table_name()}:{id}"
|
|
156
|
+
|
|
157
|
+
await client.merge(thing, data_set)
|
|
158
|
+
await self.refresh()
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
raise SurrealDbError(f"No Id for the data to merge: {data}")
|
|
162
|
+
|
|
163
|
+
async def delete(self) -> None:
|
|
164
|
+
"""
|
|
165
|
+
Delete the model instance from the database.
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
client = await SurrealDBConnectionManager.get_client()
|
|
169
|
+
|
|
170
|
+
id = self.get_id()
|
|
171
|
+
|
|
172
|
+
thing = f"{self.get_table_name()}:{id}"
|
|
173
|
+
|
|
174
|
+
deleted = await client.delete(thing)
|
|
175
|
+
|
|
176
|
+
if not deleted:
|
|
177
|
+
raise SurrealDbError(f"Can't delete Record id -> '{id}' not found!")
|
|
178
|
+
|
|
179
|
+
logger.info(f"Record deleted -> {deleted}.")
|
|
180
|
+
del self
|
|
181
|
+
|
|
182
|
+
@model_validator(mode="after")
|
|
183
|
+
def check_config(self) -> Self:
|
|
184
|
+
"""
|
|
185
|
+
Check the model configuration.
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
if not self.get_index_primary_key() and not hasattr(self, "id"):
|
|
189
|
+
raise SurrealDbError( # pragma: no cover
|
|
190
|
+
"Can't create model, the model need either 'id' field or primirary_key in 'model_config'."
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return self
|
|
194
|
+
|
|
195
|
+
@classmethod
|
|
196
|
+
def objects(cls) -> Any:
|
|
197
|
+
"""
|
|
198
|
+
Return a QuerySet for the model class.
|
|
199
|
+
"""
|
|
200
|
+
from .query_set import QuerySet
|
|
201
|
+
|
|
202
|
+
return QuerySet(cls)
|
|
@@ -301,7 +301,6 @@ class QuerySet:
|
|
|
301
301
|
results = await self._execute_query(query)
|
|
302
302
|
try:
|
|
303
303
|
data = cast(dict, results[0])
|
|
304
|
-
|
|
305
304
|
return self.model.from_db(data["result"])
|
|
306
305
|
except ValidationError as e:
|
|
307
306
|
logger.info(f"Pydantic invalid format for the class, returning dict value: {e}")
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
from typing import Any, Type, Self
|
|
2
|
-
from pydantic import BaseModel, create_model, ConfigDict
|
|
3
|
-
from .connection_manager import SurrealDBConnectionManager
|
|
4
|
-
from surrealdb import RecordID, SurrealDbError
|
|
5
|
-
|
|
6
|
-
import warnings
|
|
7
|
-
import logging
|
|
8
|
-
|
|
9
|
-
warnings.filterwarnings("ignore", message="fields may not start with an underscore", category=RuntimeWarning)
|
|
10
|
-
|
|
11
|
-
logger = logging.getLogger(__name__)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class BaseSurrealModel(BaseModel):
|
|
15
|
-
"""
|
|
16
|
-
Base class for models interacting with SurrealDB.
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
__pydantic_model_cache__: Type[BaseModel] | None = None
|
|
20
|
-
|
|
21
|
-
def __init__(self, **data: Any):
|
|
22
|
-
model_cls = self._init_model()
|
|
23
|
-
instance = model_cls(**data)
|
|
24
|
-
object.__setattr__(self, "_data", instance.model_dump())
|
|
25
|
-
object.__setattr__(self, "_table_name", self.__class__.__name__)
|
|
26
|
-
|
|
27
|
-
def __getattr__(self, item: str) -> Any:
|
|
28
|
-
"""
|
|
29
|
-
If the item is a field in _data, return it,
|
|
30
|
-
otherwise, let the normal mechanism raise AttributeError.
|
|
31
|
-
"""
|
|
32
|
-
_data = object.__getattribute__(self, "_data")
|
|
33
|
-
if item in _data:
|
|
34
|
-
return _data[item]
|
|
35
|
-
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{item}'.")
|
|
36
|
-
|
|
37
|
-
def __str__(self) -> str:
|
|
38
|
-
return f"{self._data}"
|
|
39
|
-
|
|
40
|
-
def __setattr__(self, key: str, value: Any) -> None:
|
|
41
|
-
"""
|
|
42
|
-
If we want to allow updates, reinstantiate a Pydantic model
|
|
43
|
-
with the new value.
|
|
44
|
-
"""
|
|
45
|
-
if key in ("_data",): # and other internal attributes
|
|
46
|
-
object.__setattr__(self, key, value)
|
|
47
|
-
else:
|
|
48
|
-
# Update the dict, validate via Pydantic, etc.
|
|
49
|
-
current_data = dict(object.__getattribute__(self, "_data"))
|
|
50
|
-
current_data[key] = value
|
|
51
|
-
instance = self._init_model()(**current_data)
|
|
52
|
-
object.__setattr__(self, "_data", instance.model_dump())
|
|
53
|
-
|
|
54
|
-
@classmethod
|
|
55
|
-
def from_db(cls, record: dict | list) -> Any:
|
|
56
|
-
"""
|
|
57
|
-
Create an instance from a SurrealDB record.
|
|
58
|
-
"""
|
|
59
|
-
if isinstance(record, list):
|
|
60
|
-
return [cls.from_db(rs) for rs in record]
|
|
61
|
-
|
|
62
|
-
record = cls.__set_data(record)
|
|
63
|
-
|
|
64
|
-
return cls(**record)
|
|
65
|
-
|
|
66
|
-
def to_db_dict(self) -> dict[str, Any]:
|
|
67
|
-
"""
|
|
68
|
-
Return a dictionary ready to be inserted into the database.
|
|
69
|
-
"""
|
|
70
|
-
data_set = {key: value for key, value in self._data.items() if not key.startswith("_") and key != "id"}
|
|
71
|
-
return data_set
|
|
72
|
-
|
|
73
|
-
def show_config(self) -> ConfigDict:
|
|
74
|
-
# Accès depuis une méthode d'instance
|
|
75
|
-
return type(self).model_config
|
|
76
|
-
|
|
77
|
-
def get_id(self) -> str | RecordID | None:
|
|
78
|
-
if "id" in self._data:
|
|
79
|
-
return self._data["id"]
|
|
80
|
-
|
|
81
|
-
config = self.show_config()
|
|
82
|
-
pk_field = config.get("primary_key", "id")
|
|
83
|
-
return self._data.get(pk_field, None)
|
|
84
|
-
|
|
85
|
-
@staticmethod
|
|
86
|
-
def __set_data(data: Any) -> dict:
|
|
87
|
-
"""
|
|
88
|
-
Set the model instance data.
|
|
89
|
-
"""
|
|
90
|
-
if isinstance(data, dict): # pragma: no cover
|
|
91
|
-
if "id" in data and isinstance(data["id"], RecordID): # pragma: no cover
|
|
92
|
-
data["id"] = str(data["id"]).split(":")[1]
|
|
93
|
-
return data
|
|
94
|
-
|
|
95
|
-
raise TypeError("Data must be a dictionary.") # pragma: no cover
|
|
96
|
-
|
|
97
|
-
async def refresh(self) -> None:
|
|
98
|
-
"""
|
|
99
|
-
Refresh the model instance from the database.
|
|
100
|
-
"""
|
|
101
|
-
client = await SurrealDBConnectionManager.get_client()
|
|
102
|
-
record = None
|
|
103
|
-
|
|
104
|
-
id = self.get_id()
|
|
105
|
-
record = await client.select(f"{self._table_name}:{id}")
|
|
106
|
-
|
|
107
|
-
self._data = self.__set_data(record)
|
|
108
|
-
|
|
109
|
-
async def save(self) -> Self:
|
|
110
|
-
"""
|
|
111
|
-
Save the model instance to the database.
|
|
112
|
-
"""
|
|
113
|
-
client = await SurrealDBConnectionManager.get_client()
|
|
114
|
-
|
|
115
|
-
data = self.to_db_dict()
|
|
116
|
-
id = self.get_id()
|
|
117
|
-
if id:
|
|
118
|
-
thing = f"{self._table_name}:{id}"
|
|
119
|
-
await client.create(thing, data)
|
|
120
|
-
return self
|
|
121
|
-
# Auto-generate the ID
|
|
122
|
-
record = await client.create(self._table_name, data) # pragma: no cover
|
|
123
|
-
if isinstance(record, dict): # pragma: no cover
|
|
124
|
-
self._data = self.__set_data(record)
|
|
125
|
-
|
|
126
|
-
return self
|
|
127
|
-
|
|
128
|
-
async def update(self) -> Any:
|
|
129
|
-
"""
|
|
130
|
-
Update the model instance to the database.
|
|
131
|
-
"""
|
|
132
|
-
client = await SurrealDBConnectionManager.get_client()
|
|
133
|
-
|
|
134
|
-
data = self.to_db_dict()
|
|
135
|
-
id = self.get_id()
|
|
136
|
-
if id:
|
|
137
|
-
thing = f"{self._table_name}:{id}"
|
|
138
|
-
return await client.update(thing, data)
|
|
139
|
-
|
|
140
|
-
raise SurrealDbError("Can't update data, no id found.")
|
|
141
|
-
|
|
142
|
-
async def merge(self, **data: Any) -> Any:
|
|
143
|
-
"""
|
|
144
|
-
Update the model instance to the database.
|
|
145
|
-
"""
|
|
146
|
-
|
|
147
|
-
client = await SurrealDBConnectionManager.get_client()
|
|
148
|
-
data_set = {key: value for key, value in data.items()}
|
|
149
|
-
|
|
150
|
-
id = self.get_id()
|
|
151
|
-
if id:
|
|
152
|
-
thing = f"{self._table_name}:{id}"
|
|
153
|
-
|
|
154
|
-
await client.merge(thing, data_set)
|
|
155
|
-
await self.refresh()
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
raise SurrealDbError(f"No Id for the data to merge: {data}")
|
|
159
|
-
|
|
160
|
-
async def delete(self) -> None:
|
|
161
|
-
"""
|
|
162
|
-
Delete the model instance from the database.
|
|
163
|
-
"""
|
|
164
|
-
|
|
165
|
-
client = await SurrealDBConnectionManager.get_client()
|
|
166
|
-
|
|
167
|
-
id = self.get_id()
|
|
168
|
-
|
|
169
|
-
thing = f"{self._table_name}:{id}"
|
|
170
|
-
|
|
171
|
-
deleted = await client.delete(thing)
|
|
172
|
-
|
|
173
|
-
if not deleted:
|
|
174
|
-
raise SurrealDbError(f"Can't delete Record id -> '{id}' not found!")
|
|
175
|
-
|
|
176
|
-
logger.info(f"Record deleted -> {deleted}.")
|
|
177
|
-
self._data = {}
|
|
178
|
-
del self
|
|
179
|
-
|
|
180
|
-
@classmethod
|
|
181
|
-
def _init_model(cls) -> Any:
|
|
182
|
-
"""
|
|
183
|
-
Generate a real Pydantic model only once (per subclass)
|
|
184
|
-
from the fields annotated in the class inheriting from BaseSurrealModel.
|
|
185
|
-
"""
|
|
186
|
-
if cls.__pydantic_model_cache__ is not None:
|
|
187
|
-
return cls.__pydantic_model_cache__
|
|
188
|
-
|
|
189
|
-
# Retrieve the annotations declared in the class (e.g., ModelTest)
|
|
190
|
-
hints: dict[str, Any] = {}
|
|
191
|
-
config_dict = None
|
|
192
|
-
for base in reversed(cls.__mro__): # To capture all annotations
|
|
193
|
-
hints.update(getattr(base, "__annotations__", {}))
|
|
194
|
-
# Optionally, check if the class has 'model_config' to inject it
|
|
195
|
-
if hasattr(base, "model_config"):
|
|
196
|
-
config_dict = getattr(base, "model_config")
|
|
197
|
-
|
|
198
|
-
# Create the Pydantic model (dynamically)
|
|
199
|
-
fields = {}
|
|
200
|
-
for field_name, field_type in hints.items():
|
|
201
|
-
# Read the object already defined in the class (if Field(...))
|
|
202
|
-
default_val = getattr(cls, field_name, ...)
|
|
203
|
-
fields[field_name] = (field_type, default_val)
|
|
204
|
-
|
|
205
|
-
# Create model
|
|
206
|
-
if config_dict:
|
|
207
|
-
pyd_model = create_model( # type: ignore
|
|
208
|
-
f"{cls.__name__}PydModel",
|
|
209
|
-
__config__=config_dict,
|
|
210
|
-
**fields,
|
|
211
|
-
)
|
|
212
|
-
else:
|
|
213
|
-
pyd_model = create_model( # type: ignore
|
|
214
|
-
f"{cls.__name__}PydModel",
|
|
215
|
-
__base__=BaseModel,
|
|
216
|
-
**fields,
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
cls.__pydantic_model_cache__ = pyd_model
|
|
220
|
-
return pyd_model
|
|
221
|
-
|
|
222
|
-
@classmethod
|
|
223
|
-
def objects(cls) -> Any:
|
|
224
|
-
"""
|
|
225
|
-
Return a QuerySet for the model class.
|
|
226
|
-
"""
|
|
227
|
-
from .query_set import QuerySet
|
|
228
|
-
|
|
229
|
-
return QuerySet(cls)
|
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from pydantic import ConfigDict, Field
|
|
3
|
-
from src import surreal_orm
|
|
4
|
-
from surrealdb import RecordID
|
|
5
|
-
from surrealdb.errors import SurrealDbError
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
SURREALDB_URL = "http://localhost:8000"
|
|
9
|
-
SURREALDB_USER = "root"
|
|
10
|
-
SURREALDB_PASS = "root"
|
|
11
|
-
SURREALDB_NAMESPACE = "ns"
|
|
12
|
-
SURREALDB_DATABASE = "db"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class ModelTest(surreal_orm.BaseSurrealModel):
|
|
16
|
-
model_config = ConfigDict(extra="allow")
|
|
17
|
-
id: str | RecordID | None = Field(default=None)
|
|
18
|
-
name: str = Field(..., max_length=100)
|
|
19
|
-
age: int = Field(..., ge=0, le=125)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class ModelTestEmpty(surreal_orm.BaseSurrealModel):
|
|
23
|
-
model_config = ConfigDict(extra="allow")
|
|
24
|
-
id: str | RecordID | None = Field(default=None)
|
|
25
|
-
name: str = Field(..., max_length=100)
|
|
26
|
-
age: int = Field(..., ge=0, le=125)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@pytest.fixture(scope="module", autouse=True)
|
|
30
|
-
def setup_surrealdb() -> None:
|
|
31
|
-
# Initialiser SurrealDB
|
|
32
|
-
surreal_orm.SurrealDBConnectionManager.set_connection(
|
|
33
|
-
SURREALDB_URL,
|
|
34
|
-
SURREALDB_USER,
|
|
35
|
-
SURREALDB_PASS,
|
|
36
|
-
SURREALDB_NAMESPACE,
|
|
37
|
-
SURREALDB_DATABASE,
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@pytest.mark.filterwarnings("ignore:fields may not")
|
|
42
|
-
async def test_save_model() -> None:
|
|
43
|
-
model = ModelTest(id="1", name="Test", age=32)
|
|
44
|
-
await model.save()
|
|
45
|
-
|
|
46
|
-
# Vérification de l'insertion
|
|
47
|
-
client = await surreal_orm.SurrealDBConnectionManager.get_client()
|
|
48
|
-
result = await client.select("ModelTest")
|
|
49
|
-
test_id = RecordID(table_name="ModelTest", identifier=1)
|
|
50
|
-
assert len(result) == 1
|
|
51
|
-
|
|
52
|
-
assert result[0] == {"id": test_id, "name": "Test", "age": 32}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
async def test_merge_model() -> None:
|
|
56
|
-
item = await ModelTest.objects().get(1)
|
|
57
|
-
assert item.name == "Test"
|
|
58
|
-
assert item.age == 32
|
|
59
|
-
await item.merge(age=23) # Also test whole refresh() method
|
|
60
|
-
item.age = 23
|
|
61
|
-
item.name = "Test"
|
|
62
|
-
item.id = "1"
|
|
63
|
-
|
|
64
|
-
item2 = await ModelTest.objects().filter(name="Test").get()
|
|
65
|
-
assert item2.age == 23
|
|
66
|
-
assert item2.name == "Test"
|
|
67
|
-
assert item2.id == "1"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
async def test_update_model() -> None:
|
|
71
|
-
item = await ModelTest.objects().get(1)
|
|
72
|
-
assert item.name == "Test"
|
|
73
|
-
assert item.age == 23
|
|
74
|
-
item.age = 25
|
|
75
|
-
await item.update()
|
|
76
|
-
item2 = await ModelTest.objects().get(1)
|
|
77
|
-
assert item2.age == 25
|
|
78
|
-
|
|
79
|
-
item2 = await ModelTest.objects().filter(name="Test").get()
|
|
80
|
-
assert item2.age == 25
|
|
81
|
-
assert item2.name == "Test"
|
|
82
|
-
assert item2.id == "1"
|
|
83
|
-
|
|
84
|
-
item3 = ModelTest(id=None, name="TestNone", age=17)
|
|
85
|
-
|
|
86
|
-
with pytest.raises(SurrealDbError) as exc1:
|
|
87
|
-
await item3.update()
|
|
88
|
-
|
|
89
|
-
assert str(exc1.value) == "Can't update data, no id found."
|
|
90
|
-
|
|
91
|
-
with pytest.raises(SurrealDbError) as exc2:
|
|
92
|
-
await item3.merge(age=19)
|
|
93
|
-
|
|
94
|
-
assert str(exc2.value) == "No Id for the data to merge: {'age': 19}"
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
async def test_first_model() -> None:
|
|
98
|
-
model = await ModelTest.objects().filter(name="Test").first()
|
|
99
|
-
if isinstance(model, ModelTest):
|
|
100
|
-
assert model.name == "Test"
|
|
101
|
-
assert model.age == 25
|
|
102
|
-
assert model.id == "1"
|
|
103
|
-
else:
|
|
104
|
-
assert False
|
|
105
|
-
|
|
106
|
-
with pytest.raises(SurrealDbError) as exc1:
|
|
107
|
-
await ModelTest.objects().filter(name="NotExist").first()
|
|
108
|
-
|
|
109
|
-
assert str(exc1.value) == "No result found."
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
async def test_filter_model() -> None:
|
|
113
|
-
item3 = ModelTest(id=None, name="Test2", age=17)
|
|
114
|
-
await item3.save()
|
|
115
|
-
|
|
116
|
-
models = await ModelTest.objects().filter(age__lt=30).exec() # Test from_db isinstance(record["id"], RecordID)
|
|
117
|
-
assert len(models) == 2
|
|
118
|
-
for model in models:
|
|
119
|
-
assert model.age < 30
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
async def test_delete_model() -> None:
|
|
123
|
-
model = ModelTest(id="2", name="Test2", age=34)
|
|
124
|
-
await model.save()
|
|
125
|
-
client = await surreal_orm.SurrealDBConnectionManager.get_client()
|
|
126
|
-
result = await client.select("ModelTest")
|
|
127
|
-
assert len(result) == 3
|
|
128
|
-
|
|
129
|
-
await model.delete()
|
|
130
|
-
result = await client.select("ModelTest")
|
|
131
|
-
assert len(result) == 2
|
|
132
|
-
|
|
133
|
-
model2 = ModelTest(id="345", name="Test2", age=34)
|
|
134
|
-
|
|
135
|
-
with pytest.raises(SurrealDbError) as exc1:
|
|
136
|
-
await model2.delete() # Test delete() without saved()
|
|
137
|
-
|
|
138
|
-
assert str(exc1.value) == "Can't delete Record id -> '345' not found!"
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
async def test_query_model() -> None:
|
|
142
|
-
# Utiliser test_model pour exécuter la requête
|
|
143
|
-
results = await ModelTest.objects().filter(name="Test").exec()
|
|
144
|
-
assert len(results) == 1
|
|
145
|
-
assert results[0].name == "Test"
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
async def test_multi_select() -> None:
|
|
149
|
-
await ModelTest(id=None, name="Ian", age=23).save()
|
|
150
|
-
await ModelTest(id=None, name="Yan", age=32).save()
|
|
151
|
-
await ModelTest(id=None, name="Isa", age=32).save()
|
|
152
|
-
|
|
153
|
-
result = await ModelTest.objects().all()
|
|
154
|
-
|
|
155
|
-
assert len(result) == 5
|
|
156
|
-
|
|
157
|
-
result2 = await ModelTest.objects().filter(name__in=["Ian", "Yan"]).exec()
|
|
158
|
-
|
|
159
|
-
assert len(result2) == 2
|
|
160
|
-
for item in result2:
|
|
161
|
-
assert item.name in ["Yan", "Ian"]
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
async def test_order_by() -> None:
|
|
165
|
-
result1 = await ModelTest.objects().order_by("name").exec()
|
|
166
|
-
assert len(result1) == 5
|
|
167
|
-
assert result1[0].name == "Ian"
|
|
168
|
-
|
|
169
|
-
result2 = await ModelTest.objects().order_by("name", surreal_orm.OrderBy.DESC).exec()
|
|
170
|
-
assert len(result2) == 5
|
|
171
|
-
assert result2[0].name == "Yan"
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
async def test_offset() -> None:
|
|
175
|
-
result = await ModelTest.objects().offset(2).exec()
|
|
176
|
-
assert len(result) == 3
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
async def test_limit() -> None:
|
|
180
|
-
result = await ModelTest.objects().limit(2).exec()
|
|
181
|
-
assert len(result) == 2
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
async def test_select_field() -> None:
|
|
185
|
-
result = await ModelTest.objects().select("name", "age").exec()
|
|
186
|
-
assert len(result) == 5
|
|
187
|
-
assert isinstance(result[0], dict)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
async def test_select_with_variable() -> None:
|
|
191
|
-
result = await ModelTest.objects().filter(age__lte="$max_age").variables(max_age=25).exec()
|
|
192
|
-
assert len(result) == 3
|
|
193
|
-
for res in result:
|
|
194
|
-
assert res.age <= 25
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
async def test_query() -> None:
|
|
198
|
-
result = await ModelTest.objects().query("SELECT * FROM ModelTest WHERE age > 25")
|
|
199
|
-
assert len(result) == 2
|
|
200
|
-
for res in result:
|
|
201
|
-
assert res.age > 25
|
|
202
|
-
|
|
203
|
-
result2 = await ModelTest.objects().query("SELECT * FROM ModelTest WHERE age > $age", {"age": 19})
|
|
204
|
-
assert len(result2) == 4
|
|
205
|
-
|
|
206
|
-
with pytest.raises(SurrealDbError) as exc:
|
|
207
|
-
await ModelTest.objects().query("SELECT * FROM NoTable WHERE age > 34")
|
|
208
|
-
|
|
209
|
-
assert str(exc.value) == "The query must include 'FROM ModelTest' to reference the correct table."
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
async def test_error_on_get_multi() -> None:
|
|
213
|
-
with pytest.raises(SurrealDbError) as exc1:
|
|
214
|
-
await ModelTest.objects().get()
|
|
215
|
-
|
|
216
|
-
assert str(exc1.value) == "More than one result found."
|
|
217
|
-
|
|
218
|
-
with pytest.raises(SurrealDbError) as exc2:
|
|
219
|
-
await ModelTestEmpty.objects().get()
|
|
220
|
-
|
|
221
|
-
assert str(exc2.value) == "No result found."
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
@pytest.mark.filterwarnings("ignore:fields may not")
|
|
225
|
-
async def test_with_primary_key() -> None:
|
|
226
|
-
class ModelTest2(surreal_orm.BaseSurrealModel):
|
|
227
|
-
model_config = ConfigDict(extra="allow", primary_key="email") # type: ignore
|
|
228
|
-
name: str = Field(..., max_length=100)
|
|
229
|
-
age: int = Field(..., ge=0, le=125)
|
|
230
|
-
email: str = Field(..., max_length=100)
|
|
231
|
-
|
|
232
|
-
model = ModelTest2(name="Test", age=32, email="test@test.com")
|
|
233
|
-
await model.save()
|
|
234
|
-
|
|
235
|
-
fletch = await ModelTest2.objects().get("test@test.com")
|
|
236
|
-
if isinstance(fletch, ModelTest2):
|
|
237
|
-
assert fletch.name == "Test"
|
|
238
|
-
assert fletch.age == 32
|
|
239
|
-
assert fletch.email == "test@test.com"
|
|
240
|
-
else:
|
|
241
|
-
assert False
|
|
242
|
-
|
|
243
|
-
deleted = await ModelTest2.objects().delete_table()
|
|
244
|
-
assert deleted is True
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
async def test_delete_table() -> None:
|
|
248
|
-
# Suppression de la table via test_model
|
|
249
|
-
result = await ModelTest.objects().delete_table()
|
|
250
|
-
assert result is True
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from typing import AsyncGenerator, Any
|
|
3
|
-
from src.surreal_orm import SurrealDBConnectionManager
|
|
4
|
-
from surrealdb.errors import SurrealDbConnectionError
|
|
5
|
-
|
|
6
|
-
SURREALDB_URL = "http://localhost:8000"
|
|
7
|
-
SURREALDB_USER = "root"
|
|
8
|
-
SURREALDB_PASS = "root"
|
|
9
|
-
SURREALDB_NAMESPACE = "ns"
|
|
10
|
-
SURREALDB_DATABASE = "db"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@pytest.fixture
|
|
14
|
-
async def setup_connection_manager() -> AsyncGenerator[Any, Any]:
|
|
15
|
-
SurrealDBConnectionManager.set_connection(
|
|
16
|
-
SURREALDB_URL,
|
|
17
|
-
SURREALDB_USER,
|
|
18
|
-
SURREALDB_PASS,
|
|
19
|
-
SURREALDB_NAMESPACE,
|
|
20
|
-
SURREALDB_DATABASE,
|
|
21
|
-
)
|
|
22
|
-
yield
|
|
23
|
-
await SurrealDBConnectionManager.close_connection()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def test_set_connection() -> None:
|
|
27
|
-
assert SurrealDBConnectionManager.is_connection_set() is True
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
async def test_get_client() -> None:
|
|
31
|
-
client = await SurrealDBConnectionManager.get_client()
|
|
32
|
-
assert client is not None
|
|
33
|
-
assert SurrealDBConnectionManager.is_connected() is True
|
|
34
|
-
await SurrealDBConnectionManager.close_connection()
|
|
35
|
-
assert SurrealDBConnectionManager.is_connected() is False
|
|
36
|
-
await SurrealDBConnectionManager.set_user("wrong_user")
|
|
37
|
-
|
|
38
|
-
with pytest.raises(SurrealDbConnectionError) as exc1:
|
|
39
|
-
await SurrealDBConnectionManager.get_client()
|
|
40
|
-
|
|
41
|
-
await SurrealDBConnectionManager.unset_connection()
|
|
42
|
-
with pytest.raises(ValueError) as exc2:
|
|
43
|
-
await SurrealDBConnectionManager.get_client()
|
|
44
|
-
|
|
45
|
-
assert str(exc1.value) == "Can't connect to the database."
|
|
46
|
-
assert str(exc2.value) == "Connection not been set."
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
async def test_close_connection() -> None:
|
|
50
|
-
await SurrealDBConnectionManager.close_connection()
|
|
51
|
-
assert SurrealDBConnectionManager.is_connected() is False
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
async def test_get_connection_string(setup_connection_manager: AsyncGenerator[Any, Any]) -> None:
|
|
55
|
-
connection_string = SurrealDBConnectionManager.get_connection_string()
|
|
56
|
-
assert connection_string == SURREALDB_URL
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def test_get_connection_kwargs(setup_connection_manager: AsyncGenerator[Any, Any]) -> None:
|
|
60
|
-
kwargs = SurrealDBConnectionManager.get_connection_kwargs()
|
|
61
|
-
assert kwargs == {
|
|
62
|
-
"url": SURREALDB_URL,
|
|
63
|
-
"user": SURREALDB_USER,
|
|
64
|
-
"namespace": SURREALDB_NAMESPACE,
|
|
65
|
-
"database": SURREALDB_DATABASE,
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def test_is_connection_set(setup_connection_manager: AsyncGenerator[Any, Any]) -> None:
|
|
70
|
-
kwargs = SurrealDBConnectionManager.is_connection_set()
|
|
71
|
-
assert kwargs is True
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
async def test_set_url(setup_connection_manager: AsyncGenerator[Any, Any]) -> None:
|
|
75
|
-
new_url = "http://localhost:8001"
|
|
76
|
-
assert await SurrealDBConnectionManager.set_url(new_url) is True
|
|
77
|
-
assert SurrealDBConnectionManager.get_url() == new_url
|
|
78
|
-
assert await SurrealDBConnectionManager.set_url(new_url, True) is False # Cover validate_connection to False
|
|
79
|
-
|
|
80
|
-
await SurrealDBConnectionManager.unset_connection()
|
|
81
|
-
with pytest.raises(ValueError) as exc:
|
|
82
|
-
await SurrealDBConnectionManager.set_url(new_url)
|
|
83
|
-
|
|
84
|
-
assert str(exc.value) == "You can't change the URL when the others setting are not already set."
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
async def test_set_user(setup_connection_manager: AsyncGenerator[Any, Any]) -> None:
|
|
88
|
-
new_user = "admin"
|
|
89
|
-
assert await SurrealDBConnectionManager.set_user(new_user) is True
|
|
90
|
-
assert SurrealDBConnectionManager.get_user() == new_user
|
|
91
|
-
assert await SurrealDBConnectionManager.set_user(new_user, True) is False # Cover validate_connection to False
|
|
92
|
-
assert SurrealDBConnectionManager.get_user() is None
|
|
93
|
-
await SurrealDBConnectionManager.unset_connection()
|
|
94
|
-
with pytest.raises(ValueError) as exc:
|
|
95
|
-
await SurrealDBConnectionManager.set_user(new_user)
|
|
96
|
-
|
|
97
|
-
assert str(exc.value) == "You can't change the User when the others setting are not already set."
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
async def test_set_password(setup_connection_manager: AsyncGenerator[Any, Any]) -> None:
|
|
101
|
-
new_password = "new_pass"
|
|
102
|
-
assert await SurrealDBConnectionManager.set_password(new_password) is True
|
|
103
|
-
assert SurrealDBConnectionManager.is_password_set()
|
|
104
|
-
assert await SurrealDBConnectionManager.set_password(new_password, True) is False # Cover validate_connection to False
|
|
105
|
-
assert SurrealDBConnectionManager.is_password_set() is False
|
|
106
|
-
assert SurrealDBConnectionManager.is_connection_set() is False
|
|
107
|
-
|
|
108
|
-
await SurrealDBConnectionManager.unset_connection()
|
|
109
|
-
with pytest.raises(ValueError) as exc:
|
|
110
|
-
await SurrealDBConnectionManager.set_password(new_password)
|
|
111
|
-
|
|
112
|
-
assert str(exc.value) == "You can't change the password when the others setting are not already set."
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
async def test_set_namespace(setup_connection_manager: AsyncGenerator[Any, Any]) -> None:
|
|
116
|
-
new_namespace = "new_ns"
|
|
117
|
-
assert await SurrealDBConnectionManager.set_namespace(new_namespace) is True
|
|
118
|
-
assert SurrealDBConnectionManager.get_namespace() == new_namespace
|
|
119
|
-
assert await SurrealDBConnectionManager.set_namespace(new_namespace, True) is True
|
|
120
|
-
await SurrealDBConnectionManager.set_password("wrong_pass")
|
|
121
|
-
assert await SurrealDBConnectionManager.set_namespace(new_namespace, True) is False # Cover validate_connection to False
|
|
122
|
-
|
|
123
|
-
await SurrealDBConnectionManager.unset_connection()
|
|
124
|
-
with pytest.raises(ValueError) as exc:
|
|
125
|
-
await SurrealDBConnectionManager.set_namespace(new_namespace)
|
|
126
|
-
|
|
127
|
-
assert str(exc.value) == "You can't change the namespace when the others setting are not already set."
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
async def test_set_database(setup_connection_manager: AsyncGenerator[Any, Any]) -> None:
|
|
131
|
-
new_database = "new_db"
|
|
132
|
-
assert await SurrealDBConnectionManager.set_database(new_database) is True
|
|
133
|
-
assert SurrealDBConnectionManager.get_database() == new_database
|
|
134
|
-
assert await SurrealDBConnectionManager.set_database(new_database, True) is True
|
|
135
|
-
await SurrealDBConnectionManager.set_password("wrong_pass")
|
|
136
|
-
assert await SurrealDBConnectionManager.set_database(new_database, True) is False # Cover validate_connection to False
|
|
137
|
-
|
|
138
|
-
await SurrealDBConnectionManager.unset_connection()
|
|
139
|
-
with pytest.raises(ValueError) as exc:
|
|
140
|
-
await SurrealDBConnectionManager.set_database(new_database)
|
|
141
|
-
|
|
142
|
-
assert str(exc.value) == "You can't change the database when the others setting are not already set."
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
async def test_unset() -> None:
|
|
146
|
-
await SurrealDBConnectionManager.unset_connection()
|
|
147
|
-
assert SurrealDBConnectionManager.is_connected() is False
|
|
148
|
-
assert SurrealDBConnectionManager.is_connection_set() is False
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
async def test_reconnect(setup_connection_manager: AsyncGenerator[Any, Any]) -> None:
|
|
152
|
-
await SurrealDBConnectionManager.close_connection()
|
|
153
|
-
assert SurrealDBConnectionManager.is_connected() is False
|
|
154
|
-
await SurrealDBConnectionManager.reconnect()
|
|
155
|
-
assert SurrealDBConnectionManager.is_connected() is True
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
async def test_context_manager(setup_connection_manager: AsyncGenerator[Any, Any]) -> None:
|
|
159
|
-
async with SurrealDBConnectionManager():
|
|
160
|
-
assert SurrealDBConnectionManager.is_connected() is True
|
|
161
|
-
|
|
162
|
-
assert SurrealDBConnectionManager.is_connected() is False
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from pydantic import ConfigDict, Field
|
|
3
|
-
from src import surreal_orm
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
@pytest.fixture(scope="module", autouse=True)
|
|
7
|
-
def setup_model() -> surreal_orm.BaseSurrealModel:
|
|
8
|
-
class TestModel(surreal_orm.BaseSurrealModel):
|
|
9
|
-
model_config = ConfigDict(extra="allow")
|
|
10
|
-
id: str = Field(...)
|
|
11
|
-
name: str = Field(..., max_length=100)
|
|
12
|
-
age: int = Field(..., ge=0)
|
|
13
|
-
|
|
14
|
-
return TestModel(id="1", name="Test", age=45)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@pytest.mark.filterwarnings("ignore:fields may not")
|
|
18
|
-
def test_model_get_query_set(setup_model: surreal_orm.BaseSurrealModel) -> None:
|
|
19
|
-
query = setup_model.objects()
|
|
20
|
-
assert isinstance(query, surreal_orm.QuerySet)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def test_model_get_id(setup_model: surreal_orm.BaseSurrealModel) -> None:
|
|
24
|
-
assert setup_model.get_id() == "1" # cover _data.get("id") is True
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def test_model_to_db_dict(setup_model: surreal_orm.BaseSurrealModel) -> None:
|
|
28
|
-
assert setup_model.to_db_dict() == {"name": "Test", "age": 45}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def test_queryset_select() -> None:
|
|
32
|
-
qs = surreal_orm.BaseSurrealModel.objects().select("id", "name")
|
|
33
|
-
assert qs.select_item == ["id", "name"]
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def test_queryset_filter() -> None:
|
|
37
|
-
qs = surreal_orm.BaseSurrealModel.objects().filter(name="Test", age__gt=18)
|
|
38
|
-
assert qs._filters == [("name", "exact", "Test"), ("age", "gt", 18)]
|
|
39
|
-
qs = surreal_orm.BaseSurrealModel.objects().filter(name__in=["Test", "Test2"], age__gte=18)
|
|
40
|
-
qs = surreal_orm.BaseSurrealModel.objects().filter(age__lte=45)
|
|
41
|
-
assert qs._filters == [("age", "lte", 45)]
|
|
42
|
-
qs = surreal_orm.BaseSurrealModel.objects().filter(age__lt=45)
|
|
43
|
-
assert qs._filters == [("age", "lt", 45)]
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def test_queryset_variables(setup_model: surreal_orm.BaseSurrealModel) -> None:
|
|
47
|
-
qs = setup_model.objects().variables(name="Test")
|
|
48
|
-
assert qs._variables == {"name": "Test"}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def test_queryset_limit(setup_model: surreal_orm.BaseSurrealModel) -> None:
|
|
52
|
-
qs = setup_model.objects().limit(100)
|
|
53
|
-
assert qs._limit == 100
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def test_queryset_offset(setup_model: surreal_orm.BaseSurrealModel) -> None:
|
|
57
|
-
qs = setup_model.objects().offset(100)
|
|
58
|
-
assert qs._offset == 100
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def test_queryset_order_by(setup_model: surreal_orm.BaseSurrealModel) -> None:
|
|
62
|
-
qs = setup_model.objects().order_by("name")
|
|
63
|
-
assert qs._order_by == "name ASC"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def test_getattr(setup_model: surreal_orm.BaseSurrealModel) -> None:
|
|
67
|
-
assert setup_model.name == "Test"
|
|
68
|
-
assert setup_model.age == 45
|
|
69
|
-
assert setup_model.id == "1"
|
|
70
|
-
|
|
71
|
-
with pytest.raises(AttributeError) as exc:
|
|
72
|
-
setup_model.no_attribut
|
|
73
|
-
|
|
74
|
-
assert str(exc.value) == "'TestModel' object has no attribute 'no_attribut'."
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def test_str_dunnder(setup_model: surreal_orm.BaseSurrealModel) -> None:
|
|
78
|
-
assert str(setup_model) == "{'id': '1', 'name': 'Test', 'age': 45}"
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
@pytest.mark.filterwarnings("ignore:fields may not")
|
|
82
|
-
def test_class_without_config(setup_model: surreal_orm.BaseSurrealModel) -> None:
|
|
83
|
-
class TestModel2(surreal_orm.BaseSurrealModel):
|
|
84
|
-
id: str = Field(...)
|
|
85
|
-
name: str = Field(..., max_length=100)
|
|
86
|
-
age: int = Field(..., ge=0)
|
|
87
|
-
|
|
88
|
-
assert TestModel2(id="1", name="Test", age=45).to_db_dict() == {
|
|
89
|
-
"name": "Test",
|
|
90
|
-
"age": 45,
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
@pytest.mark.filterwarnings("ignore:fields may not")
|
|
95
|
-
def test_class_with_key_specify(setup_model: surreal_orm.BaseSurrealModel) -> None:
|
|
96
|
-
class TestModel3(surreal_orm.BaseSurrealModel):
|
|
97
|
-
model_config = ConfigDict(extra="allow", primary_key="email") # type: ignore
|
|
98
|
-
name: str = Field(..., max_length=100)
|
|
99
|
-
age: int = Field(..., ge=0)
|
|
100
|
-
email: str = Field(..., max_length=100, alias="id")
|
|
101
|
-
|
|
102
|
-
model = TestModel3(name="Test", age=45, email="test@test.com") # type: ignore
|
|
103
|
-
|
|
104
|
-
assert model.get_id() == "test@test.com" # type: ignore
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|