simple-db-settings 1.0.0__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.
- simple_db_settings-1.0.0/.github/workflows/publish.yml +36 -0
- simple_db_settings-1.0.0/.gitignore +13 -0
- simple_db_settings-1.0.0/CHANGELOG.md +5 -0
- simple_db_settings-1.0.0/LICENSE +21 -0
- simple_db_settings-1.0.0/PKG-INFO +385 -0
- simple_db_settings-1.0.0/README.md +348 -0
- simple_db_settings-1.0.0/art/banner.svg +146 -0
- simple_db_settings-1.0.0/art/flow.svg +155 -0
- simple_db_settings-1.0.0/compose.tests.yml +27 -0
- simple_db_settings-1.0.0/pyproject.toml +56 -0
- simple_db_settings-1.0.0/src/simple_db_settings/__init__.py +41 -0
- simple_db_settings-1.0.0/src/simple_db_settings/audit.py +75 -0
- simple_db_settings-1.0.0/src/simple_db_settings/cache.py +70 -0
- simple_db_settings-1.0.0/src/simple_db_settings/cli.py +183 -0
- simple_db_settings-1.0.0/src/simple_db_settings/codec.py +67 -0
- simple_db_settings-1.0.0/src/simple_db_settings/exceptions.py +38 -0
- simple_db_settings-1.0.0/src/simple_db_settings/py.typed +0 -0
- simple_db_settings-1.0.0/src/simple_db_settings/schema.py +50 -0
- simple_db_settings-1.0.0/src/simple_db_settings/store.py +306 -0
- simple_db_settings-1.0.0/src/simple_db_settings/transfer.py +87 -0
- simple_db_settings-1.0.0/src/simple_db_settings/typed.py +86 -0
- simple_db_settings-1.0.0/src/simple_db_settings/upsert.py +42 -0
- simple_db_settings-1.0.0/tests/conftest.py +16 -0
- simple_db_settings-1.0.0/tests/fixtures/php_rows.json +95 -0
- simple_db_settings-1.0.0/tests/integration/test_dialects.py +93 -0
- simple_db_settings-1.0.0/tests/test_audit.py +110 -0
- simple_db_settings-1.0.0/tests/test_cache.py +124 -0
- simple_db_settings-1.0.0/tests/test_cli.py +130 -0
- simple_db_settings-1.0.0/tests/test_codec.py +97 -0
- simple_db_settings-1.0.0/tests/test_package.py +27 -0
- simple_db_settings-1.0.0/tests/test_parity.py +79 -0
- simple_db_settings-1.0.0/tests/test_php_compat.py +35 -0
- simple_db_settings-1.0.0/tests/test_schema.py +56 -0
- simple_db_settings-1.0.0/tests/test_store.py +153 -0
- simple_db_settings-1.0.0/tests/test_transfer.py +128 -0
- simple_db_settings-1.0.0/tests/test_typed.py +114 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.12"
|
|
16
|
+
- run: python -m pip install build
|
|
17
|
+
- run: python -m build
|
|
18
|
+
- uses: actions/upload-artifact@v4
|
|
19
|
+
with:
|
|
20
|
+
name: dist
|
|
21
|
+
path: dist/
|
|
22
|
+
|
|
23
|
+
publish:
|
|
24
|
+
needs: build
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
environment:
|
|
27
|
+
name: pypi
|
|
28
|
+
url: https://pypi.org/p/simple-db-settings
|
|
29
|
+
permissions:
|
|
30
|
+
id-token: write
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/download-artifact@v4
|
|
33
|
+
with:
|
|
34
|
+
name: dist
|
|
35
|
+
path: dist/
|
|
36
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Timur Turdyev
|
|
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,385 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: simple-db-settings
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: DB-backed settings manager on SQLAlchemy, table-compatible with timurturdyev/simple-settings
|
|
5
|
+
Project-URL: Homepage, https://github.com/TimurTurdyev/simple-settings-py
|
|
6
|
+
Project-URL: Repository, https://github.com/TimurTurdyev/simple-settings-py
|
|
7
|
+
Project-URL: Issues, https://github.com/TimurTurdyev/simple-settings-py/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/TimurTurdyev/simple-settings-py/blob/main/CHANGELOG.md
|
|
9
|
+
Author: Timur Turdyev
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: sqlalchemy>=2.0
|
|
23
|
+
Provides-Extra: cli
|
|
24
|
+
Requires-Dist: typer>=0.12; extra == 'cli'
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
28
|
+
Provides-Extra: pydantic
|
|
29
|
+
Requires-Dist: pydantic>=2.0; extra == 'pydantic'
|
|
30
|
+
Provides-Extra: redis
|
|
31
|
+
Requires-Dist: redis>=5.0; extra == 'redis'
|
|
32
|
+
Provides-Extra: test-db
|
|
33
|
+
Requires-Dist: cryptography>=42.0; extra == 'test-db'
|
|
34
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == 'test-db'
|
|
35
|
+
Requires-Dist: pymysql>=1.1; extra == 'test-db'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# Simple DB Settings
|
|
39
|
+
|
|
40
|
+
<p align="center">
|
|
41
|
+
<img src="art/banner.svg" alt="Simple DB Settings" width="100%">
|
|
42
|
+
</p>
|
|
43
|
+
|
|
44
|
+
Менеджер настроек в БД для Python на SQLAlchemy 2: группы, TTL-кэш, аудит изменений, типизация через pydantic. Идея взята из Laravel-пакета [timurturdyev/simple-settings](https://github.com/timurturdyev/simple-settings) и совместима с ним по таблице: приложения на Python и PHP могут работать с одними и теми же настройками.
|
|
45
|
+
|
|
46
|
+
[English](#english)
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Требования
|
|
51
|
+
|
|
52
|
+
- Python 3.11+
|
|
53
|
+
- SQLAlchemy 2.0+
|
|
54
|
+
- Любая СУБД с драйвером для SQLAlchemy: SQLite, PostgreSQL, MySQL/MariaDB
|
|
55
|
+
|
|
56
|
+
## Установка
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install simple-db-settings
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Дополнительные возможности ставятся экстрами:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install "simple-db-settings[pydantic]" # типизированные группы
|
|
66
|
+
pip install "simple-db-settings[cli]" # консольная команда
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Быстрый старт
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from sqlalchemy import create_engine
|
|
73
|
+
from simple_db_settings import SettingsStore
|
|
74
|
+
|
|
75
|
+
engine = create_engine("sqlite:///app.db")
|
|
76
|
+
store = SettingsStore(engine)
|
|
77
|
+
store.create_tables() # или создайте таблицы своей миграцией
|
|
78
|
+
|
|
79
|
+
site = store.group("site")
|
|
80
|
+
site["name"] = "My App"
|
|
81
|
+
site["per_page"] = 15
|
|
82
|
+
|
|
83
|
+
site["name"] # 'My App'
|
|
84
|
+
site.get("missing", "default") # 'default'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Группы
|
|
88
|
+
|
|
89
|
+
Настройки разделены по группам. Группа по умолчанию - `global`.
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
email = store.group("email")
|
|
93
|
+
email["host"] = "smtp.example.com"
|
|
94
|
+
|
|
95
|
+
store.group("site").get("host") # None - другая группа
|
|
96
|
+
store.groups() # ['email', 'site']
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`GroupView` ведет себя как обычный словарь, работают все привычные операции:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
"host" in email # True
|
|
103
|
+
len(email) # 1
|
|
104
|
+
dict(email) # {'host': 'smtp.example.com'}
|
|
105
|
+
del email["host"] # удалить ключ
|
|
106
|
+
email.clear() # удалить все настройки группы
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Типы данных
|
|
110
|
+
|
|
111
|
+
Значения сериализуются в JSON и восстанавливаются без ручного приведения:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
site["count"] = 42 # int
|
|
115
|
+
site["price"] = 9.99 # float
|
|
116
|
+
site["enabled"] = True # bool
|
|
117
|
+
site["tags"] = ["a", "b"] # list
|
|
118
|
+
site["meta"] = {"k": "v"} # dict
|
|
119
|
+
site["empty"] = None # None
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Массовая запись
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
site.update({
|
|
126
|
+
"name": "My App",
|
|
127
|
+
"url": "https://example.com",
|
|
128
|
+
"per_page": 15,
|
|
129
|
+
})
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Вся пачка уходит в БД одним upsert-запросом в одной транзакции. Валидация идет до записи: невалиден хоть один ключ - не сохранится ни один. Кэш сбрасывается один раз после коммита.
|
|
133
|
+
|
|
134
|
+
## Кэш
|
|
135
|
+
|
|
136
|
+
Чтение идет через TTL-кэш (по умолчанию 5 секунд), кэшируется карта группы целиком.
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from simple_db_settings import NullCache
|
|
140
|
+
|
|
141
|
+
store = SettingsStore(engine, cache_ttl=30) # свой TTL
|
|
142
|
+
store = SettingsStore(engine, cache=NullCache()) # без кэша
|
|
143
|
+
|
|
144
|
+
site.fresh() # прочитать группу из БД мимо кэша
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Кэш инвалидируется только после коммита транзакции, поэтому параллельный запрос не затянет в кэш еще не зафиксированные данные. При нескольких процессах (gunicorn, celery) устаревание ограничено TTL.
|
|
148
|
+
|
|
149
|
+
Свой бэкенд - любой объект с методами `get` / `set` / `invalidate` (протокол `CacheBackend`), например обертка над Redis.
|
|
150
|
+
|
|
151
|
+
## Транзакции
|
|
152
|
+
|
|
153
|
+
По умолчанию каждая операция - отдельная короткая транзакция. Чтобы записать настройки атомарно вместе со своими данными, передайте соединение:
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
with engine.begin() as conn:
|
|
157
|
+
conn.execute(orders_table.insert().values(user_id=1))
|
|
158
|
+
store.with_connection(conn).group("site")["last_order"] = 1
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`with_connection()` возвращает копию стора на внешнем соединении: она ничего не коммитит сама, коммит общий, кэш сбросится после него.
|
|
162
|
+
|
|
163
|
+
## Аудит
|
|
164
|
+
|
|
165
|
+
Опциональная история изменений в таблице `simple_setting_changes`:
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
from simple_db_settings import SettingsStore, causer
|
|
169
|
+
|
|
170
|
+
store = SettingsStore(engine, audit=True)
|
|
171
|
+
|
|
172
|
+
with causer("user", 42):
|
|
173
|
+
store.group("site")["per_page"] = 20
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Каждая запись и удаление кладет строку: `event` (`created` / `updated` / `deleted`), `old_payload`, `new_payload`, `causer_type`, `causer_id`, `created_at`. Строки аудита пишутся в той же транзакции, что и сами настройки: либо сохранилось все, либо ничего. Вне контекста `causer()` автор будет `NULL`.
|
|
177
|
+
|
|
178
|
+
Как и в PHP-пакете, `clear()` записей в истории не создает, а холостое удаление (ключа нет) не оставляет следов.
|
|
179
|
+
|
|
180
|
+
Чтение истории - обычный select:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from sqlalchemy import MetaData, select
|
|
184
|
+
from simple_db_settings.schema import make_changes_table
|
|
185
|
+
|
|
186
|
+
changes = make_changes_table(MetaData())
|
|
187
|
+
with engine.connect() as conn:
|
|
188
|
+
rows = conn.execute(
|
|
189
|
+
select(changes)
|
|
190
|
+
.where(changes.c.group == "site", changes.c.name == "per_page")
|
|
191
|
+
.order_by(changes.c.created_at.desc())
|
|
192
|
+
.limit(20)
|
|
193
|
+
).all()
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Типизированные настройки
|
|
197
|
+
|
|
198
|
+
Экстра `pydantic`. Дефолты живут в коде, БД хранит только отличия от них:
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from pydantic import BaseModel
|
|
202
|
+
|
|
203
|
+
class SiteSettings(BaseModel):
|
|
204
|
+
name: str = "My App"
|
|
205
|
+
per_page: int = 15
|
|
206
|
+
maintenance: bool = False
|
|
207
|
+
|
|
208
|
+
typed = store.typed(SiteSettings, group="site")
|
|
209
|
+
|
|
210
|
+
cfg = typed.load() # дефолты + оверрайды из БД
|
|
211
|
+
cfg.per_page = 50
|
|
212
|
+
typed.save(cfg) # в БД уйдет только per_page
|
|
213
|
+
|
|
214
|
+
typed.reset("per_page") # вернуть поле к дефолту
|
|
215
|
+
typed.reset() # вернуть все поля
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`save()` удаляет из БД поля, значение которых совпало с дефолтом, поэтому смена дефолта в коде сразу видна везде, где поле не переопределяли.
|
|
219
|
+
|
|
220
|
+
## Консольная команда
|
|
221
|
+
|
|
222
|
+
Экстра `cli`. URL базы передается флагом `--url` или переменной окружения `SIMPLE_DB_SETTINGS_URL`.
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
export SIMPLE_DB_SETTINGS_URL="sqlite:///app.db"
|
|
226
|
+
|
|
227
|
+
# Получить и установить (значение парсится как JSON, иначе строка)
|
|
228
|
+
simple-db-settings get site_name
|
|
229
|
+
simple-db-settings get host --group email
|
|
230
|
+
simple-db-settings set per_page 15
|
|
231
|
+
simple-db-settings set tags '["a","b"]'
|
|
232
|
+
|
|
233
|
+
# Список настроек и групп
|
|
234
|
+
simple-db-settings list
|
|
235
|
+
simple-db-settings list --group email
|
|
236
|
+
simple-db-settings groups
|
|
237
|
+
|
|
238
|
+
# Удаление
|
|
239
|
+
simple-db-settings delete site_name
|
|
240
|
+
simple-db-settings clear --group email
|
|
241
|
+
|
|
242
|
+
# Экспорт и импорт JSON
|
|
243
|
+
simple-db-settings export backup.json
|
|
244
|
+
simple-db-settings export --group email
|
|
245
|
+
simple-db-settings import backup.json
|
|
246
|
+
simple-db-settings import backup.json --replace
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Экспорт и импорт
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from simple_db_settings import export_json, import_json
|
|
253
|
+
|
|
254
|
+
dump = export_json(store) # все группы
|
|
255
|
+
dump = export_json(store, "email") # одна группа
|
|
256
|
+
|
|
257
|
+
import_json(store, dump) # merge: существующие ключи перезаписываются
|
|
258
|
+
import_json(store, dump, replace=True) # сначала очистить группы из файла
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Файл несет сырые `val` и `type`, поэтому типы переносятся без потерь. Формат совместим с `setting:export` / `setting:import` из PHP-пакета в обе стороны. Валидация идет до записи: битая запись в файле - и не импортируется ничего.
|
|
262
|
+
|
|
263
|
+
## Совместимость с Laravel-пакетом
|
|
264
|
+
|
|
265
|
+
Пакет работает с той же таблицей, что и timurturdyev/simple-settings v6:
|
|
266
|
+
|
|
267
|
+
- схема идентична: `(group, name, val, type, created_at, updated_at)`, составной первичный ключ `(group, name)`
|
|
268
|
+
- Python читает все типы, которые пишет PHP: `string`, `integer`, `float`, `boolean`, `array`, `object`, `null` (и legacy `double`)
|
|
269
|
+
- Python пишет `type = 'json'`; PHP читает его начиная с v6.1
|
|
270
|
+
- таблица аудита и формат export/import тоже общие
|
|
271
|
+
|
|
272
|
+
Одна таблица настроек - разные приложения на разных языках.
|
|
273
|
+
|
|
274
|
+
## Как это работает
|
|
275
|
+
|
|
276
|
+
<p align="center">
|
|
277
|
+
<img src="art/flow.svg" alt="Схема взаимодействия" width="100%">
|
|
278
|
+
</p>
|
|
279
|
+
|
|
280
|
+
**Точка входа** - `SettingsStore`: ему отдают engine и, по желанию, имя таблицы, кэш и флаг аудита. Стор сам ничего не хранит, он раздает `GroupView` - живые представления групп, через которые идет вся работа. CLI, типизированный слой и export/import - обертки над теми же `GroupView`, отдельных путей к БД у них нет.
|
|
281
|
+
|
|
282
|
+
**Чтение.** `site["per_page"]` сначала смотрит в кэш. Если карта группы там и не протухла - БД не трогается вообще. При промахе одним select-ом читается вся группа, каждая строка прогоняется через кодек (по колонке `type`) и готовая карта кладется в кэш. Следующие чтения любой настройки этой группы бесплатны до истечения TTL.
|
|
283
|
+
|
|
284
|
+
**Запись.** `site["x"] = 1` и `site.update({...})` собирают пачку, кодируют значения в JSON и отправляют одним upsert-запросом: insert с обработкой конфликта по `(group, name)` на диалекте вашей СУБД. Если включен аудит, в той же транзакции читаются старые значения и одним insert-ом пишутся строки истории. Кэш группы сбрасывается строго после коммита.
|
|
285
|
+
|
|
286
|
+
**Внешняя транзакция.** Стор, полученный через `with_connection()`, выполняет те же операции на вашем соединении и не коммитит: судьбу транзакции решает вызывающий код. Сброс кэша откладывается до реального коммита, а откат не оставляет в кэше мусора.
|
|
287
|
+
|
|
288
|
+
**Вторая сторона таблицы.** Laravel-приложение с пакетом simple-settings ходит в ту же таблицу со своим кэшем. Оба пакета переживают типы через колонку `type`, поэтому настройка, записанная одним, корректно читается другим.
|
|
289
|
+
|
|
290
|
+
## Лимиты значений
|
|
291
|
+
|
|
292
|
+
Колонка `val` создается как `TEXT` - на MySQL/MariaDB это 65 535 байт (~64 KB), на PostgreSQL и SQLite ограничения нет. Для типовых настроек этого с большим запасом. Если уперлись в лимит - это сигнал, что в одну настройку положили что-то не то (каталог товаров, лог, контент). Таким данным нужна своя таблица.
|
|
293
|
+
|
|
294
|
+
## Схема БД
|
|
295
|
+
|
|
296
|
+
```
|
|
297
|
+
simple_settings
|
|
298
|
+
group string
|
|
299
|
+
name string
|
|
300
|
+
val text
|
|
301
|
+
type char(20)
|
|
302
|
+
created_at
|
|
303
|
+
updated_at
|
|
304
|
+
|
|
305
|
+
PRIMARY KEY (group, name)
|
|
306
|
+
|
|
307
|
+
simple_setting_changes
|
|
308
|
+
id bigint PK
|
|
309
|
+
group string
|
|
310
|
+
name string
|
|
311
|
+
event string created / updated / deleted
|
|
312
|
+
old_payload text NULL
|
|
313
|
+
new_payload text NULL
|
|
314
|
+
causer_type string NULL
|
|
315
|
+
causer_id bigint NULL
|
|
316
|
+
created_at
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Справочник API
|
|
320
|
+
|
|
321
|
+
| Метод | Описание |
|
|
322
|
+
|-------|----------|
|
|
323
|
+
| `SettingsStore(engine, table_name=..., cache=..., cache_ttl=..., audit=..., changes_table_name=...)` | Создать стор |
|
|
324
|
+
| `store.group(name)` | `GroupView` для группы |
|
|
325
|
+
| `store.groups()` | Список всех групп |
|
|
326
|
+
| `store.rows(group=None)` | Сырые строки с фильтром по группе |
|
|
327
|
+
| `store.typed(ModelCls, group=...)` | Типизированная группа (pydantic) |
|
|
328
|
+
| `store.with_connection(conn)` | Копия стора на внешнем соединении |
|
|
329
|
+
| `store.create_tables()` | Создать таблицы |
|
|
330
|
+
| `view[key]` / `view.get(key, default)` | Получить значение |
|
|
331
|
+
| `view[key] = value` | Записать значение |
|
|
332
|
+
| `del view[key]` | Удалить ключ |
|
|
333
|
+
| `view.update(mapping)` | Записать пачку атомарно |
|
|
334
|
+
| `view.fresh()` | Прочитать группу мимо кэша |
|
|
335
|
+
| `view.clear()` | Удалить все настройки группы |
|
|
336
|
+
| `causer(type, id)` | Контекст автора изменений для аудита |
|
|
337
|
+
| `export_json(store, group=None)` | Экспорт в JSON-строку |
|
|
338
|
+
| `import_json(store, text, group=None, replace=False)` | Импорт из JSON-строки |
|
|
339
|
+
|
|
340
|
+
## Лицензия
|
|
341
|
+
|
|
342
|
+
MIT
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## English
|
|
347
|
+
|
|
348
|
+
DB-backed settings manager for Python on SQLAlchemy 2: group namespacing, TTL cache, change audit, typed groups via pydantic. Table-compatible with the Laravel package [timurturdyev/simple-settings](https://github.com/timurturdyev/simple-settings): Python and PHP apps can share the same settings table.
|
|
349
|
+
|
|
350
|
+
**Requirements:** Python 3.11+, SQLAlchemy 2.0+; SQLite, PostgreSQL or MySQL/MariaDB.
|
|
351
|
+
|
|
352
|
+
**Install:**
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
pip install simple-db-settings
|
|
356
|
+
pip install "simple-db-settings[pydantic,cli]" # optional extras
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**Basic usage:**
|
|
360
|
+
|
|
361
|
+
```python
|
|
362
|
+
from sqlalchemy import create_engine
|
|
363
|
+
from simple_db_settings import SettingsStore
|
|
364
|
+
|
|
365
|
+
store = SettingsStore(create_engine("sqlite:///app.db"))
|
|
366
|
+
store.create_tables()
|
|
367
|
+
|
|
368
|
+
site = store.group("site")
|
|
369
|
+
site["per_page"] = 15
|
|
370
|
+
site.get("missing", "default")
|
|
371
|
+
site.update({"a": 1, "b": 2}) # single upsert, all-or-nothing
|
|
372
|
+
site.fresh() # bypass cache
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Values are stored as JSON and restored automatically (int, float, bool, str, list, dict, None). Reads go through a per-group TTL cache invalidated only after commit. Pass a connection via `store.with_connection(conn)` to join your own transaction.
|
|
376
|
+
|
|
377
|
+
**Audit:** `SettingsStore(engine, audit=True)` records every create/update/delete into `simple_setting_changes` within the same transaction; wrap calls in `causer("user", 42)` to attach the author.
|
|
378
|
+
|
|
379
|
+
**Typed groups:** defaults live in code, the DB keeps only overrides; `typed.save()` deletes rows that returned to their defaults.
|
|
380
|
+
|
|
381
|
+
**CLI:** `simple-db-settings get/set/delete/list/groups/clear/export/import` with `--url` or `SIMPLE_DB_SETTINGS_URL`.
|
|
382
|
+
|
|
383
|
+
**Cross-language:** same table schema as the Laravel package v6; Python reads every PHP type and writes `type = 'json'`, which PHP reads since v6.1. Export files are interchangeable.
|
|
384
|
+
|
|
385
|
+
For full documentation see the Russian section above.
|