plain.models 0.49.2__py3-none-any.whl → 0.50.0__py3-none-any.whl
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.
- plain/models/CHANGELOG.md +13 -0
- plain/models/aggregates.py +42 -19
- plain/models/backends/base/base.py +125 -105
- plain/models/backends/base/client.py +11 -3
- plain/models/backends/base/creation.py +22 -12
- plain/models/backends/base/features.py +10 -4
- plain/models/backends/base/introspection.py +29 -16
- plain/models/backends/base/operations.py +187 -91
- plain/models/backends/base/schema.py +267 -165
- plain/models/backends/base/validation.py +12 -3
- plain/models/backends/ddl_references.py +85 -43
- plain/models/backends/mysql/base.py +29 -26
- plain/models/backends/mysql/client.py +7 -2
- plain/models/backends/mysql/compiler.py +12 -3
- plain/models/backends/mysql/creation.py +5 -2
- plain/models/backends/mysql/features.py +24 -22
- plain/models/backends/mysql/introspection.py +22 -13
- plain/models/backends/mysql/operations.py +106 -39
- plain/models/backends/mysql/schema.py +48 -24
- plain/models/backends/mysql/validation.py +13 -6
- plain/models/backends/postgresql/base.py +41 -34
- plain/models/backends/postgresql/client.py +7 -2
- plain/models/backends/postgresql/creation.py +10 -5
- plain/models/backends/postgresql/introspection.py +15 -8
- plain/models/backends/postgresql/operations.py +109 -42
- plain/models/backends/postgresql/schema.py +85 -46
- plain/models/backends/sqlite3/_functions.py +151 -115
- plain/models/backends/sqlite3/base.py +37 -23
- plain/models/backends/sqlite3/client.py +7 -1
- plain/models/backends/sqlite3/creation.py +9 -5
- plain/models/backends/sqlite3/features.py +5 -3
- plain/models/backends/sqlite3/introspection.py +32 -16
- plain/models/backends/sqlite3/operations.py +125 -42
- plain/models/backends/sqlite3/schema.py +82 -58
- plain/models/backends/utils.py +52 -29
- plain/models/backups/cli.py +8 -6
- plain/models/backups/clients.py +16 -7
- plain/models/backups/core.py +24 -13
- plain/models/base.py +113 -74
- plain/models/cli.py +94 -63
- plain/models/config.py +1 -1
- plain/models/connections.py +23 -7
- plain/models/constraints.py +65 -47
- plain/models/database_url.py +1 -1
- plain/models/db.py +6 -2
- plain/models/deletion.py +66 -43
- plain/models/entrypoints.py +1 -1
- plain/models/enums.py +22 -11
- plain/models/exceptions.py +23 -8
- plain/models/expressions.py +440 -257
- plain/models/fields/__init__.py +253 -202
- plain/models/fields/json.py +120 -54
- plain/models/fields/mixins.py +12 -8
- plain/models/fields/related.py +284 -252
- plain/models/fields/related_descriptors.py +31 -22
- plain/models/fields/related_lookups.py +23 -11
- plain/models/fields/related_managers.py +81 -47
- plain/models/fields/reverse_related.py +58 -55
- plain/models/forms.py +89 -63
- plain/models/functions/comparison.py +71 -18
- plain/models/functions/datetime.py +79 -29
- plain/models/functions/math.py +43 -10
- plain/models/functions/mixins.py +24 -7
- plain/models/functions/text.py +104 -25
- plain/models/functions/window.py +12 -6
- plain/models/indexes.py +52 -28
- plain/models/lookups.py +228 -153
- plain/models/migrations/autodetector.py +86 -43
- plain/models/migrations/exceptions.py +7 -3
- plain/models/migrations/executor.py +33 -7
- plain/models/migrations/graph.py +79 -50
- plain/models/migrations/loader.py +45 -22
- plain/models/migrations/migration.py +23 -18
- plain/models/migrations/operations/base.py +37 -19
- plain/models/migrations/operations/fields.py +89 -42
- plain/models/migrations/operations/models.py +245 -143
- plain/models/migrations/operations/special.py +82 -25
- plain/models/migrations/optimizer.py +7 -2
- plain/models/migrations/questioner.py +58 -31
- plain/models/migrations/recorder.py +18 -11
- plain/models/migrations/serializer.py +50 -39
- plain/models/migrations/state.py +220 -133
- plain/models/migrations/utils.py +29 -13
- plain/models/migrations/writer.py +17 -14
- plain/models/options.py +63 -56
- plain/models/otel.py +16 -6
- plain/models/preflight.py +35 -12
- plain/models/query.py +323 -228
- plain/models/query_utils.py +93 -58
- plain/models/registry.py +34 -16
- plain/models/sql/compiler.py +146 -97
- plain/models/sql/datastructures.py +38 -25
- plain/models/sql/query.py +255 -169
- plain/models/sql/subqueries.py +32 -21
- plain/models/sql/where.py +54 -29
- plain/models/test/pytest.py +15 -11
- plain/models/test/utils.py +4 -2
- plain/models/transaction.py +20 -7
- plain/models/utils.py +13 -5
- {plain_models-0.49.2.dist-info → plain_models-0.50.0.dist-info}/METADATA +1 -1
- plain_models-0.50.0.dist-info/RECORD +122 -0
- plain_models-0.49.2.dist-info/RECORD +0 -122
- {plain_models-0.49.2.dist-info → plain_models-0.50.0.dist-info}/WHEEL +0 -0
- {plain_models-0.49.2.dist-info → plain_models-0.50.0.dist-info}/entry_points.txt +0 -0
- {plain_models-0.49.2.dist-info → plain_models-0.50.0.dist-info}/licenses/LICENSE +0 -0
plain/models/backups/clients.py
CHANGED
@@ -1,13 +1,20 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import gzip
|
2
4
|
import os
|
3
5
|
import subprocess
|
6
|
+
from pathlib import Path
|
7
|
+
from typing import TYPE_CHECKING
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from plain.models.backends.base.base import BaseDatabaseWrapper
|
4
11
|
|
5
12
|
|
6
13
|
class PostgresBackupClient:
|
7
|
-
def __init__(self, connection):
|
14
|
+
def __init__(self, connection: BaseDatabaseWrapper) -> None:
|
8
15
|
self.connection = connection
|
9
16
|
|
10
|
-
def get_env(self):
|
17
|
+
def get_env(self) -> dict[str, str]:
|
11
18
|
settings_dict = self.connection.settings_dict
|
12
19
|
options = settings_dict.get("OPTIONS", {})
|
13
20
|
env = {}
|
@@ -27,7 +34,7 @@ class PostgresBackupClient:
|
|
27
34
|
env["PGSSLKEY"] = str(options.get("sslkey"))
|
28
35
|
return env
|
29
36
|
|
30
|
-
def create_backup(self, backup_path, *, pg_dump="pg_dump"):
|
37
|
+
def create_backup(self, backup_path: Path, *, pg_dump: str = "pg_dump") -> None:
|
31
38
|
settings_dict = self.connection.settings_dict
|
32
39
|
|
33
40
|
args = pg_dump.split()
|
@@ -64,7 +71,9 @@ class PostgresBackupClient:
|
|
64
71
|
cmd, env={**os.environ, **self.get_env()}, check=True, shell=True
|
65
72
|
)
|
66
73
|
|
67
|
-
def restore_backup(
|
74
|
+
def restore_backup(
|
75
|
+
self, backup_path: Path, *, pg_restore: str = "pg_restore", psql: str = "psql"
|
76
|
+
) -> None:
|
68
77
|
settings_dict = self.connection.settings_dict
|
69
78
|
|
70
79
|
host = settings_dict.get("HOST")
|
@@ -118,17 +127,17 @@ class PostgresBackupClient:
|
|
118
127
|
|
119
128
|
|
120
129
|
class SQLiteBackupClient:
|
121
|
-
def __init__(self, connection):
|
130
|
+
def __init__(self, connection: BaseDatabaseWrapper) -> None:
|
122
131
|
self.connection = connection
|
123
132
|
|
124
|
-
def create_backup(self, backup_path):
|
133
|
+
def create_backup(self, backup_path: Path) -> None:
|
125
134
|
self.connection.ensure_connection()
|
126
135
|
src_conn = self.connection.connection
|
127
136
|
dump = "\n".join(src_conn.iterdump())
|
128
137
|
with gzip.open(backup_path, "wt") as f:
|
129
138
|
f.write(dump)
|
130
139
|
|
131
|
-
def restore_backup(self, backup_path):
|
140
|
+
def restore_backup(self, backup_path: Path) -> None:
|
132
141
|
with gzip.open(backup_path, "rt") as f:
|
133
142
|
sql = f.read()
|
134
143
|
|
plain/models/backups/core.py
CHANGED
@@ -1,18 +1,29 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import datetime
|
2
4
|
import os
|
5
|
+
from collections.abc import Generator
|
3
6
|
from pathlib import Path
|
7
|
+
from typing import TYPE_CHECKING, Any, cast
|
4
8
|
|
5
9
|
from plain.runtime import PLAIN_TEMP_PATH
|
6
10
|
|
7
|
-
from .. import db_connection
|
11
|
+
from .. import db_connection as _db_connection
|
8
12
|
from .clients import PostgresBackupClient, SQLiteBackupClient
|
9
13
|
|
14
|
+
if TYPE_CHECKING:
|
15
|
+
from plain.models.backends.base.base import BaseDatabaseWrapper
|
16
|
+
|
17
|
+
db_connection = cast("BaseDatabaseWrapper", _db_connection)
|
18
|
+
else:
|
19
|
+
db_connection = _db_connection
|
20
|
+
|
10
21
|
|
11
22
|
class DatabaseBackups:
|
12
|
-
def __init__(self):
|
23
|
+
def __init__(self) -> None:
|
13
24
|
self.path = PLAIN_TEMP_PATH / "backups"
|
14
25
|
|
15
|
-
def find_backups(self):
|
26
|
+
def find_backups(self) -> list[DatabaseBackup]:
|
16
27
|
if not self.path.exists():
|
17
28
|
return []
|
18
29
|
|
@@ -27,20 +38,20 @@ class DatabaseBackups:
|
|
27
38
|
|
28
39
|
return backups
|
29
40
|
|
30
|
-
def create(self, name, **create_kwargs):
|
41
|
+
def create(self, name: str, **create_kwargs: Any) -> Path:
|
31
42
|
backup = DatabaseBackup(name, backups_path=self.path)
|
32
43
|
if backup.exists():
|
33
44
|
raise Exception(f"Backup {name} already exists")
|
34
45
|
backup_dir = backup.create(**create_kwargs)
|
35
46
|
return backup_dir
|
36
47
|
|
37
|
-
def restore(self, name, **restore_kwargs):
|
48
|
+
def restore(self, name: str, **restore_kwargs: Any) -> None:
|
38
49
|
backup = DatabaseBackup(name, backups_path=self.path)
|
39
50
|
if not backup.exists():
|
40
51
|
raise Exception(f"Backup {name} not found")
|
41
52
|
backup.restore(**restore_kwargs)
|
42
53
|
|
43
|
-
def delete(self, name):
|
54
|
+
def delete(self, name: str) -> None:
|
44
55
|
backup = DatabaseBackup(name, backups_path=self.path)
|
45
56
|
if not backup.exists():
|
46
57
|
raise Exception(f"Backup {name} not found")
|
@@ -48,17 +59,17 @@ class DatabaseBackups:
|
|
48
59
|
|
49
60
|
|
50
61
|
class DatabaseBackup:
|
51
|
-
def __init__(self, name: str, *, backups_path: Path):
|
62
|
+
def __init__(self, name: str, *, backups_path: Path) -> None:
|
52
63
|
self.name = name
|
53
64
|
self.path = backups_path / name
|
54
65
|
|
55
66
|
if not self.name:
|
56
67
|
raise ValueError("Backup name is required")
|
57
68
|
|
58
|
-
def exists(self):
|
69
|
+
def exists(self) -> bool:
|
59
70
|
return self.path.exists()
|
60
71
|
|
61
|
-
def create(self, **create_kwargs):
|
72
|
+
def create(self, **create_kwargs: Any) -> Path:
|
62
73
|
self.path.mkdir(parents=True, exist_ok=True)
|
63
74
|
|
64
75
|
backup_path = self.path / "default.backup"
|
@@ -75,7 +86,7 @@ class DatabaseBackup:
|
|
75
86
|
|
76
87
|
return self.path
|
77
88
|
|
78
|
-
def iter_files(self):
|
89
|
+
def iter_files(self) -> Generator[Path, None, None]:
|
79
90
|
for backup_file in self.path.iterdir():
|
80
91
|
if not backup_file.is_file():
|
81
92
|
continue
|
@@ -83,7 +94,7 @@ class DatabaseBackup:
|
|
83
94
|
continue
|
84
95
|
yield backup_file
|
85
96
|
|
86
|
-
def restore(self, **restore_kwargs):
|
97
|
+
def restore(self, **restore_kwargs: Any) -> None:
|
87
98
|
for backup_file in self.iter_files():
|
88
99
|
if db_connection.vendor == "postgresql":
|
89
100
|
PostgresBackupClient(db_connection).restore_backup(
|
@@ -95,12 +106,12 @@ class DatabaseBackup:
|
|
95
106
|
else:
|
96
107
|
raise Exception("Unsupported database vendor")
|
97
108
|
|
98
|
-
def delete(self):
|
109
|
+
def delete(self) -> None:
|
99
110
|
for backup_file in self.iter_files():
|
100
111
|
backup_file.unlink()
|
101
112
|
|
102
113
|
self.path.rmdir()
|
103
114
|
|
104
|
-
def updated_at(self):
|
115
|
+
def updated_at(self) -> datetime.datetime:
|
105
116
|
mtime = os.path.getmtime(self.path)
|
106
117
|
return datetime.datetime.fromtimestamp(mtime)
|