djanquiltdb-plugin-postgres-objects 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.
- djanquiltdb_plugin_postgres_objects-1.0.0/CHANGELOG.rst +4 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/LICENSE +23 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/MANIFEST.in +18 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/PKG-INFO +135 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/README.rst +90 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/pyproject.toml +91 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/setup.cfg +4 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/src/djanquiltdb_plugin_postgres_objects/__init__.py +34 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/src/djanquiltdb_plugin_postgres_objects/decorators.py +250 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/src/djanquiltdb_plugin_postgres_objects.egg-info/PKG-INFO +135 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/src/djanquiltdb_plugin_postgres_objects.egg-info/SOURCES.txt +39 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/src/djanquiltdb_plugin_postgres_objects.egg-info/dependency_links.txt +1 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/src/djanquiltdb_plugin_postgres_objects.egg-info/entry_points.txt +2 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/src/djanquiltdb_plugin_postgres_objects.egg-info/requires.txt +21 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/src/djanquiltdb_plugin_postgres_objects.egg-info/top_level.txt +1 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/config/__init__.py +0 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/config/settings.py +63 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/declared/__init__.py +0 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/declared/migrations/__init__.py +0 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/declared/test_migrations_declared_objects/0001_declared_objects.py +68 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/declared/test_migrations_declared_objects/__init__.py +0 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/declared/test_migrations_pre_plugin/0001_pre_plugin.py +31 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/declared/test_migrations_pre_plugin/__init__.py +0 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/declared/test_migrations_recalculation/0001_generated_column.py +52 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/declared/test_migrations_recalculation/0002_recalculate.py +46 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/declared/test_migrations_recalculation/__init__.py +0 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/example/__init__.py +0 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/example/db_views.py +57 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/example/functions.py +71 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/example/migrations/0001_initial.py +37 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/example/migrations/__init__.py +0 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/example/models.py +36 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/manage.py +10 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/plugin_tests/__init__.py +0 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/plugin_tests/declared_migrations.py +97 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/plugin_tests/functions.py +237 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/plugin_tests/generated_fields.py +371 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/plugin_tests/installation.py +36 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/plugin_tests/migration_state.py +51 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/plugin_tests/recalculation_migrations.py +122 -0
- djanquiltdb_plugin_postgres_objects-1.0.0/tests/plugin_tests/views.py +574 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Copyright (c) 2026, DjanQuiltDB Project
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
|
4
|
+
modification, are permitted provided that the following conditions are met:
|
|
5
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
6
|
+
list of conditions and the following disclaimer.
|
|
7
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
8
|
+
this list of conditions and the following disclaimer in the documentation
|
|
9
|
+
and/or other materials provided with the distribution.
|
|
10
|
+
* Neither the name of the copyright holder nor the names of its
|
|
11
|
+
contributors may be used to endorse or promote products derived from
|
|
12
|
+
this software without specific prior written permission.
|
|
13
|
+
|
|
14
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
15
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
16
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
17
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
18
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
19
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
20
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
21
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
22
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
23
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# The sdist is what downstream packagers build and test from, so it carries the whole test project, not just the
|
|
2
|
+
# importable package. Without this, setuptools' defaults ship none of the fixture apps or settings the suite imports,
|
|
3
|
+
# and the suite in the sdist cannot run.
|
|
4
|
+
#
|
|
5
|
+
# The documentation is not here: docs/ lives at the repository root and covers the core library and the in-tree
|
|
6
|
+
# plugins together, so it cannot be grafted into either package. The Documentation URL points at it instead.
|
|
7
|
+
|
|
8
|
+
include CHANGELOG.rst
|
|
9
|
+
include LICENSE
|
|
10
|
+
include README.rst
|
|
11
|
+
|
|
12
|
+
graft tests
|
|
13
|
+
|
|
14
|
+
global-exclude *.py[cod]
|
|
15
|
+
global-exclude __pycache__
|
|
16
|
+
# A local test run leaves its coverage data in the test project; it is not part of the release.
|
|
17
|
+
global-exclude .coverage
|
|
18
|
+
global-exclude .coverage.*
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: djanquiltdb-plugin-postgres-objects
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: DjanQuiltDB decorators for functions/views as declared by django-postgres-objects
|
|
5
|
+
Author-email: DjanQuiltDB Project <djanquiltdb@portal42.net>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/DjanQuiltDB/djanquiltdb
|
|
8
|
+
Project-URL: Documentation, https://djanquiltdb.readthedocs.io/en/latest/plugins/postgres-objects/index.html
|
|
9
|
+
Project-URL: Source, https://github.com/DjanQuiltDB/djanquiltdb/tree/master/packages/djanquiltdb-plugin-postgres-objects
|
|
10
|
+
Project-URL: Issues, https://github.com/DjanQuiltDB/djanquiltdb/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/DjanQuiltDB/djanquiltdb/blob/master/packages/djanquiltdb-plugin-postgres-objects/CHANGELOG.rst
|
|
12
|
+
Keywords: django,database,sharding,postgresql,migrations
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Environment :: Web Environment
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Framework :: Django
|
|
21
|
+
Classifier: Framework :: Django :: 6.0
|
|
22
|
+
Classifier: Framework :: Django :: 6.1
|
|
23
|
+
Classifier: Topic :: Database
|
|
24
|
+
Requires-Python: >=3.14
|
|
25
|
+
Description-Content-Type: text/x-rst
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: django<7.0,>=6.0
|
|
28
|
+
Requires-Dist: djanquiltdb<5.0,>=4.0
|
|
29
|
+
Requires-Dist: django-postgres-objects<2.0,>=1.0
|
|
30
|
+
Provides-Extra: lint
|
|
31
|
+
Requires-Dist: ruff==0.15.21; extra == "lint"
|
|
32
|
+
Provides-Extra: test
|
|
33
|
+
Requires-Dist: coverage; extra == "test"
|
|
34
|
+
Requires-Dist: psycopg[binary]>=3.0.0; extra == "test"
|
|
35
|
+
Requires-Dist: dj-database-url; extra == "test"
|
|
36
|
+
Provides-Extra: docs
|
|
37
|
+
Requires-Dist: sphinx<10,>=8; extra == "docs"
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: djanquiltdb-plugin-postgres-objects[docs,lint,test]; extra == "dev"
|
|
40
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
41
|
+
Requires-Dist: tox>=4.21; extra == "dev"
|
|
42
|
+
Requires-Dist: build; extra == "dev"
|
|
43
|
+
Requires-Dist: twine; extra == "dev"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
.. The logo needs an absolute URL: this README is the PyPI long description, where a relative path renders broken.
|
|
47
|
+
|
|
48
|
+
.. image:: https://raw.githubusercontent.com/DjanQuiltDB/djanquiltdb/master/assets/icon-128.png
|
|
49
|
+
:alt: DjanQuiltDB
|
|
50
|
+
:width: 128
|
|
51
|
+
|
|
52
|
+
===================================
|
|
53
|
+
djanquiltdb-plugin-postgres-objects
|
|
54
|
+
===================================
|
|
55
|
+
|
|
56
|
+
.. image:: https://img.shields.io/pypi/v/djanquiltdb-plugin-postgres-objects.svg
|
|
57
|
+
:target: https://pypi.org/project/djanquiltdb-plugin-postgres-objects/
|
|
58
|
+
:alt: PyPI
|
|
59
|
+
|
|
60
|
+
.. image:: https://img.shields.io/pypi/pyversions/djanquiltdb-plugin-postgres-objects.svg
|
|
61
|
+
:target: https://pypi.org/project/djanquiltdb-plugin-postgres-objects/
|
|
62
|
+
:alt: Supported Python versions
|
|
63
|
+
|
|
64
|
+
.. image:: https://img.shields.io/pypi/frameworkversions/django/djanquiltdb-plugin-postgres-objects.svg
|
|
65
|
+
:target: https://pypi.org/project/djanquiltdb-plugin-postgres-objects/
|
|
66
|
+
:alt: Supported Django versions
|
|
67
|
+
|
|
68
|
+
.. image:: https://img.shields.io/badge/postgres-17%20%7C%2018-4169e1?logo=postgresql&logoColor=white
|
|
69
|
+
:target: https://www.postgresql.org/
|
|
70
|
+
:alt: Supported PostgreSQL versions
|
|
71
|
+
|
|
72
|
+
.. image:: https://github.com/DjanQuiltDB/djanquiltdb/actions/workflows/ci.yml/badge.svg
|
|
73
|
+
:target: https://github.com/DjanQuiltDB/djanquiltdb/actions/workflows/ci.yml
|
|
74
|
+
:alt: CI
|
|
75
|
+
|
|
76
|
+
.. image:: https://readthedocs.org/projects/djanquiltdb/badge/?version=latest
|
|
77
|
+
:target: https://djanquiltdb.readthedocs.io/en/latest/plugins/postgres-objects/index.html
|
|
78
|
+
:alt: Documentation
|
|
79
|
+
|
|
80
|
+
.. image:: https://img.shields.io/pypi/l/djanquiltdb-plugin-postgres-objects.svg
|
|
81
|
+
:target: https://github.com/DjanQuiltDB/djanquiltdb/blob/master/LICENSE
|
|
82
|
+
:alt: BSD-3-Clause licence
|
|
83
|
+
|
|
84
|
+
`django-postgres-objects <https://github.com/djanquiltdb/django-postgres-objects>`_ declares Postgres functions and
|
|
85
|
+
views as classes and lets ``makemigrations`` manage them. `DjanQuiltDB <https://github.com/djanquiltdb/djanquiltdb>`_
|
|
86
|
+
shards a database across schemas and nodes. This library serves as a compatibility layer between them, allowing you to
|
|
87
|
+
mark declarative functions and views as sharded::
|
|
88
|
+
|
|
89
|
+
# example/functions.py
|
|
90
|
+
from djanquiltdb.decorators import public_function
|
|
91
|
+
from postgres_objects import Function
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@public_function()
|
|
95
|
+
class AllUppercase(Function):
|
|
96
|
+
arguments = 'input TEXT'
|
|
97
|
+
returns = 'TEXT'
|
|
98
|
+
volatility = 'IMMUTABLE'
|
|
99
|
+
strict = True
|
|
100
|
+
parallel = 'SAFE'
|
|
101
|
+
body = """
|
|
102
|
+
BEGIN
|
|
103
|
+
RETURN UPPER(input);
|
|
104
|
+
END;
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
Install it through DjanQuiltDB's extra::
|
|
108
|
+
|
|
109
|
+
pip install djanquiltdb[postgres-objects]
|
|
110
|
+
|
|
111
|
+
The decorators are importable from ``djanquiltdb.decorators`` beside the model ones, as above.
|
|
112
|
+
|
|
113
|
+
Functions get ``@public_function``, ``@mirrored_function`` and ``@sharded_function``; views get ``@public_view``,
|
|
114
|
+
``@mirrored_view`` and ``@sharded_view``. An unannotated declaration is placed in the public schema, so declarations
|
|
115
|
+
written for a single-database project keep working once that project is sharded.
|
|
116
|
+
|
|
117
|
+
Full documentation is at https://djanquiltdb.readthedocs.io/en/latest/plugins/postgres-objects/index.html.
|
|
118
|
+
|
|
119
|
+
Development
|
|
120
|
+
===========
|
|
121
|
+
|
|
122
|
+
This package is developed in the `DjanQuiltDB repository <https://github.com/DjanQuiltDB/djanquiltdb>`_ beside
|
|
123
|
+
``djanquiltdb`` itself. Tests run through tox against the Postgres containers in the repository's
|
|
124
|
+
``docker-compose.yml``; see ``DOCKER.md`` at the repository root::
|
|
125
|
+
|
|
126
|
+
docker compose run --rm test tox
|
|
127
|
+
|
|
128
|
+
Requirements
|
|
129
|
+
------------
|
|
130
|
+
|
|
131
|
+
* Python 3.14
|
|
132
|
+
* Django 6.0 or 6.1
|
|
133
|
+
* PostgreSQL 17 or 18
|
|
134
|
+
* djanquiltdb 4.x
|
|
135
|
+
* django-postgres-objects 1.x
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
.. The logo needs an absolute URL: this README is the PyPI long description, where a relative path renders broken.
|
|
2
|
+
|
|
3
|
+
.. image:: https://raw.githubusercontent.com/DjanQuiltDB/djanquiltdb/master/assets/icon-128.png
|
|
4
|
+
:alt: DjanQuiltDB
|
|
5
|
+
:width: 128
|
|
6
|
+
|
|
7
|
+
===================================
|
|
8
|
+
djanquiltdb-plugin-postgres-objects
|
|
9
|
+
===================================
|
|
10
|
+
|
|
11
|
+
.. image:: https://img.shields.io/pypi/v/djanquiltdb-plugin-postgres-objects.svg
|
|
12
|
+
:target: https://pypi.org/project/djanquiltdb-plugin-postgres-objects/
|
|
13
|
+
:alt: PyPI
|
|
14
|
+
|
|
15
|
+
.. image:: https://img.shields.io/pypi/pyversions/djanquiltdb-plugin-postgres-objects.svg
|
|
16
|
+
:target: https://pypi.org/project/djanquiltdb-plugin-postgres-objects/
|
|
17
|
+
:alt: Supported Python versions
|
|
18
|
+
|
|
19
|
+
.. image:: https://img.shields.io/pypi/frameworkversions/django/djanquiltdb-plugin-postgres-objects.svg
|
|
20
|
+
:target: https://pypi.org/project/djanquiltdb-plugin-postgres-objects/
|
|
21
|
+
:alt: Supported Django versions
|
|
22
|
+
|
|
23
|
+
.. image:: https://img.shields.io/badge/postgres-17%20%7C%2018-4169e1?logo=postgresql&logoColor=white
|
|
24
|
+
:target: https://www.postgresql.org/
|
|
25
|
+
:alt: Supported PostgreSQL versions
|
|
26
|
+
|
|
27
|
+
.. image:: https://github.com/DjanQuiltDB/djanquiltdb/actions/workflows/ci.yml/badge.svg
|
|
28
|
+
:target: https://github.com/DjanQuiltDB/djanquiltdb/actions/workflows/ci.yml
|
|
29
|
+
:alt: CI
|
|
30
|
+
|
|
31
|
+
.. image:: https://readthedocs.org/projects/djanquiltdb/badge/?version=latest
|
|
32
|
+
:target: https://djanquiltdb.readthedocs.io/en/latest/plugins/postgres-objects/index.html
|
|
33
|
+
:alt: Documentation
|
|
34
|
+
|
|
35
|
+
.. image:: https://img.shields.io/pypi/l/djanquiltdb-plugin-postgres-objects.svg
|
|
36
|
+
:target: https://github.com/DjanQuiltDB/djanquiltdb/blob/master/LICENSE
|
|
37
|
+
:alt: BSD-3-Clause licence
|
|
38
|
+
|
|
39
|
+
`django-postgres-objects <https://github.com/djanquiltdb/django-postgres-objects>`_ declares Postgres functions and
|
|
40
|
+
views as classes and lets ``makemigrations`` manage them. `DjanQuiltDB <https://github.com/djanquiltdb/djanquiltdb>`_
|
|
41
|
+
shards a database across schemas and nodes. This library serves as a compatibility layer between them, allowing you to
|
|
42
|
+
mark declarative functions and views as sharded::
|
|
43
|
+
|
|
44
|
+
# example/functions.py
|
|
45
|
+
from djanquiltdb.decorators import public_function
|
|
46
|
+
from postgres_objects import Function
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@public_function()
|
|
50
|
+
class AllUppercase(Function):
|
|
51
|
+
arguments = 'input TEXT'
|
|
52
|
+
returns = 'TEXT'
|
|
53
|
+
volatility = 'IMMUTABLE'
|
|
54
|
+
strict = True
|
|
55
|
+
parallel = 'SAFE'
|
|
56
|
+
body = """
|
|
57
|
+
BEGIN
|
|
58
|
+
RETURN UPPER(input);
|
|
59
|
+
END;
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
Install it through DjanQuiltDB's extra::
|
|
63
|
+
|
|
64
|
+
pip install djanquiltdb[postgres-objects]
|
|
65
|
+
|
|
66
|
+
The decorators are importable from ``djanquiltdb.decorators`` beside the model ones, as above.
|
|
67
|
+
|
|
68
|
+
Functions get ``@public_function``, ``@mirrored_function`` and ``@sharded_function``; views get ``@public_view``,
|
|
69
|
+
``@mirrored_view`` and ``@sharded_view``. An unannotated declaration is placed in the public schema, so declarations
|
|
70
|
+
written for a single-database project keep working once that project is sharded.
|
|
71
|
+
|
|
72
|
+
Full documentation is at https://djanquiltdb.readthedocs.io/en/latest/plugins/postgres-objects/index.html.
|
|
73
|
+
|
|
74
|
+
Development
|
|
75
|
+
===========
|
|
76
|
+
|
|
77
|
+
This package is developed in the `DjanQuiltDB repository <https://github.com/DjanQuiltDB/djanquiltdb>`_ beside
|
|
78
|
+
``djanquiltdb`` itself. Tests run through tox against the Postgres containers in the repository's
|
|
79
|
+
``docker-compose.yml``; see ``DOCKER.md`` at the repository root::
|
|
80
|
+
|
|
81
|
+
docker compose run --rm test tox
|
|
82
|
+
|
|
83
|
+
Requirements
|
|
84
|
+
------------
|
|
85
|
+
|
|
86
|
+
* Python 3.14
|
|
87
|
+
* Django 6.0 or 6.1
|
|
88
|
+
* PostgreSQL 17 or 18
|
|
89
|
+
* djanquiltdb 4.x
|
|
90
|
+
* django-postgres-objects 1.x
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
# The PEP 639 license expression below needs 77, which is also new enough not to want `wheel` declared here.
|
|
3
|
+
requires = ["setuptools>=77"]
|
|
4
|
+
build-backend = "setuptools.build_meta"
|
|
5
|
+
|
|
6
|
+
[project]
|
|
7
|
+
name = "djanquiltdb-plugin-postgres-objects"
|
|
8
|
+
dynamic = ["version"]
|
|
9
|
+
description = "DjanQuiltDB decorators for functions/views as declared by django-postgres-objects"
|
|
10
|
+
readme = "README.rst"
|
|
11
|
+
requires-python = ">=3.14"
|
|
12
|
+
license = "BSD-3-Clause"
|
|
13
|
+
license-files = ["LICENSE"]
|
|
14
|
+
authors = [
|
|
15
|
+
{ name = "DjanQuiltDB Project", email = "djanquiltdb@portal42.net" },
|
|
16
|
+
]
|
|
17
|
+
keywords = [
|
|
18
|
+
"django",
|
|
19
|
+
"database",
|
|
20
|
+
"sharding",
|
|
21
|
+
"postgresql",
|
|
22
|
+
"migrations",
|
|
23
|
+
]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 5 - Production/Stable",
|
|
26
|
+
"Environment :: Web Environment",
|
|
27
|
+
"Intended Audience :: Developers",
|
|
28
|
+
"Operating System :: OS Independent",
|
|
29
|
+
"Programming Language :: Python",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3.14",
|
|
32
|
+
"Framework :: Django",
|
|
33
|
+
"Framework :: Django :: 6.0",
|
|
34
|
+
"Framework :: Django :: 6.1",
|
|
35
|
+
"Topic :: Database",
|
|
36
|
+
]
|
|
37
|
+
dependencies = [
|
|
38
|
+
"django>=6.0,<7.0",
|
|
39
|
+
"djanquiltdb>=4.0,<5.0",
|
|
40
|
+
"django-postgres-objects>=1.0,<2.0",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.entry-points."djanquiltdb.plugins"]
|
|
44
|
+
postgres_objects = "djanquiltdb_plugin_postgres_objects:plugin"
|
|
45
|
+
|
|
46
|
+
[project.urls]
|
|
47
|
+
Homepage = "https://github.com/DjanQuiltDB/djanquiltdb"
|
|
48
|
+
Documentation = "https://djanquiltdb.readthedocs.io/en/latest/plugins/postgres-objects/index.html"
|
|
49
|
+
Source = "https://github.com/DjanQuiltDB/djanquiltdb/tree/master/packages/djanquiltdb-plugin-postgres-objects"
|
|
50
|
+
Issues = "https://github.com/DjanQuiltDB/djanquiltdb/issues"
|
|
51
|
+
Changelog = "https://github.com/DjanQuiltDB/djanquiltdb/blob/master/packages/djanquiltdb-plugin-postgres-objects/CHANGELOG.rst"
|
|
52
|
+
|
|
53
|
+
[project.optional-dependencies]
|
|
54
|
+
lint = [
|
|
55
|
+
"ruff==0.15.21",
|
|
56
|
+
]
|
|
57
|
+
test = [
|
|
58
|
+
"coverage",
|
|
59
|
+
"psycopg[binary]>=3.0.0",
|
|
60
|
+
"dj-database-url",
|
|
61
|
+
]
|
|
62
|
+
docs = [
|
|
63
|
+
"sphinx>=8,<10",
|
|
64
|
+
]
|
|
65
|
+
dev = [
|
|
66
|
+
"djanquiltdb-plugin-postgres-objects[test,lint,docs]",
|
|
67
|
+
"pre-commit",
|
|
68
|
+
# Native TOML configuration, so that tox.ini is not needed, landed in 4.21.
|
|
69
|
+
"tox>=4.21",
|
|
70
|
+
"build",
|
|
71
|
+
"twine",
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
[tool.setuptools.dynamic]
|
|
75
|
+
version = { attr = "djanquiltdb_plugin_postgres_objects.__version__" }
|
|
76
|
+
|
|
77
|
+
[tool.setuptools.packages.find]
|
|
78
|
+
where = ["src"]
|
|
79
|
+
|
|
80
|
+
[tool.coverage.run]
|
|
81
|
+
source = ["djanquiltdb_plugin_postgres_objects"]
|
|
82
|
+
branch = true
|
|
83
|
+
|
|
84
|
+
[tool.coverage.report]
|
|
85
|
+
show_missing = true
|
|
86
|
+
fail_under = 100
|
|
87
|
+
|
|
88
|
+
# Shared settings live in ruff.toml at the repository root; only the src roots are per package.
|
|
89
|
+
[tool.ruff]
|
|
90
|
+
extend = "../../ruff.toml"
|
|
91
|
+
src = ["src", "tests"]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The plugin object DjanQuiltDB discovers through the ``djanquiltdb.plugins`` entry-point group.
|
|
3
|
+
|
|
4
|
+
It exposes two things. ``decorators`` is the module DjanQuiltDB serves from ``djanquiltdb.decorators``, so the
|
|
5
|
+
placement decorators read the same as the model ones. ``install()`` runs when the app registry is ready, and gives
|
|
6
|
+
declared objects their default placement.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from types import SimpleNamespace
|
|
10
|
+
|
|
11
|
+
from djanquiltdb_plugin_postgres_objects import decorators
|
|
12
|
+
|
|
13
|
+
__version__ = '1.0.0'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def install():
|
|
17
|
+
"""
|
|
18
|
+
Give declared database objects a default placement.
|
|
19
|
+
|
|
20
|
+
django-postgres-objects manages Postgres objects that are not tables, and has no notion of placement: a declaration
|
|
21
|
+
carries no routing hints unless something puts them there, which is exactly right for the single-database projects
|
|
22
|
+
it is also meant to serve. Inside a sharded project the sensible default is the public schema, since that is where
|
|
23
|
+
a function called by a stored generated column belongs and it stays reachable from every shard's search path.
|
|
24
|
+
|
|
25
|
+
The placement decorators set the hints per declaration and so override this.
|
|
26
|
+
"""
|
|
27
|
+
from djanquiltdb import ShardingMode
|
|
28
|
+
from postgres_objects.base import DeclarativeObject
|
|
29
|
+
|
|
30
|
+
if not DeclarativeObject.router_hints:
|
|
31
|
+
DeclarativeObject.router_hints = {'sharding_mode': ShardingMode.PUBLIC}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
plugin = SimpleNamespace(decorators=decorators, install=install)
|
djanquiltdb_plugin_postgres_objects-1.0.0/src/djanquiltdb_plugin_postgres_objects/decorators.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
from djanquiltdb import ShardingMode
|
|
2
|
+
from djanquiltdb.postgresql_backend.base import PUBLIC_SCHEMA_NAME
|
|
3
|
+
from djanquiltdb.router import get_active_connection
|
|
4
|
+
from djanquiltdb.utils import get_all_databases, transaction_for_every_node, use_shard
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _annotate_refresh(cls, sharding_mode):
|
|
8
|
+
"""
|
|
9
|
+
Point a declared materialized view's refresh at the correct copy.
|
|
10
|
+
|
|
11
|
+
Left alone, django-postgres-objects sends a refresh to the connection its own routing picks, which for a raw-sql
|
|
12
|
+
declaration is the default one: the wrong copy for any view that does not live on the public schema of the default
|
|
13
|
+
node. ``db_for_refresh`` is the hook it documents for exactly this, so the active connection goes there instead.
|
|
14
|
+
Which schema that is resolves through the search path, since the view is named unqualified, and a view is either
|
|
15
|
+
sharded or public and never both.
|
|
16
|
+
|
|
17
|
+
A mirrored view is the one mode no single connection answers for: its copies sit on the public schema of every
|
|
18
|
+
node. Its refresh is wrapped to visit them all inside one cascading transaction, the way
|
|
19
|
+
``atomic_write_to_every_node`` propagates any other mirrored write, so either every copy moves or none does.
|
|
20
|
+
Naming a connection with ``using`` still pins it to one.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def db_for_refresh(declaration):
|
|
24
|
+
return get_active_connection()
|
|
25
|
+
|
|
26
|
+
cls.db_for_refresh = classmethod(db_for_refresh)
|
|
27
|
+
|
|
28
|
+
if sharding_mode is not ShardingMode.MIRRORED:
|
|
29
|
+
# Re-annotating away from MIRRORED, either on the declaration itself or on a subclass of a mirrored one, has to
|
|
30
|
+
# drop the fan-out the mirrored annotation wraps around refresh.
|
|
31
|
+
base = getattr(cls.refresh.__func__, '__wrapped_refresh__', None)
|
|
32
|
+
if base is not None:
|
|
33
|
+
cls.refresh = classmethod(base)
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
# The plain function rather than the bound classmethod, so a subclassed declaration refreshes its own view and not
|
|
37
|
+
# its parent's, and unwrapped first, so annotating an already annotated declaration cannot nest the loop.
|
|
38
|
+
base = getattr(cls.refresh.__func__, '__wrapped_refresh__', cls.refresh.__func__)
|
|
39
|
+
|
|
40
|
+
def refresh(declaration, concurrently=False, using=None):
|
|
41
|
+
if using is not None:
|
|
42
|
+
return base(declaration, concurrently=concurrently, using=using)
|
|
43
|
+
|
|
44
|
+
with transaction_for_every_node():
|
|
45
|
+
for node_name in get_all_databases():
|
|
46
|
+
with use_shard(node_name=node_name, schema_name=PUBLIC_SCHEMA_NAME):
|
|
47
|
+
base(declaration, concurrently=concurrently)
|
|
48
|
+
|
|
49
|
+
refresh.__wrapped_refresh__ = base
|
|
50
|
+
cls.refresh = classmethod(refresh)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _annotate_object(cls, sharding_mode):
|
|
54
|
+
"""
|
|
55
|
+
Mark a declared database object as belonging to a sharding mode.
|
|
56
|
+
|
|
57
|
+
django-postgres-objects hands ``router_hints`` to ``allow_migrate`` for every operation on the object, so putting
|
|
58
|
+
the sharding mode there routes it exactly the way a model of that mode is routed. It knows nothing about sharding
|
|
59
|
+
itself; this is the whole of the seam between the two libraries.
|
|
60
|
+
"""
|
|
61
|
+
cls.router_hints = {'sharding_mode': sharding_mode}
|
|
62
|
+
|
|
63
|
+
if hasattr(cls, 'db_for_refresh'):
|
|
64
|
+
_annotate_refresh(cls, sharding_mode)
|
|
65
|
+
|
|
66
|
+
return cls
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def mirrored_function():
|
|
70
|
+
"""
|
|
71
|
+
A decorator for marking a declared Postgres function as being mirrored across the various nodes.
|
|
72
|
+
|
|
73
|
+
The function is created on the public schema of every node, which is where ``@public_function()`` puts it too: a
|
|
74
|
+
function holds no data, so there is nothing for the two modes to differ by. Pick whichever names the intent, the
|
|
75
|
+
way the model decorators do.
|
|
76
|
+
|
|
77
|
+
:Example:
|
|
78
|
+
.. code-block:: python
|
|
79
|
+
|
|
80
|
+
from djanquiltdb.decorators import mirrored_function
|
|
81
|
+
from postgres_objects import Function
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@mirrored_function()
|
|
85
|
+
class AllUppercase(Function):
|
|
86
|
+
arguments = 'input TEXT'
|
|
87
|
+
returns = 'TEXT'
|
|
88
|
+
volatility = 'IMMUTABLE'
|
|
89
|
+
body = '''
|
|
90
|
+
BEGIN
|
|
91
|
+
RETURN UPPER(input);
|
|
92
|
+
END;
|
|
93
|
+
'''
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
def configure(cls):
|
|
97
|
+
return _annotate_object(cls, ShardingMode.MIRRORED)
|
|
98
|
+
|
|
99
|
+
return configure
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def public_function():
|
|
103
|
+
"""
|
|
104
|
+
A decorator for marking a declared Postgres function as living in the public schema.
|
|
105
|
+
|
|
106
|
+
This is what a function used by a stored generated column normally wants. The search path of a shard covers the
|
|
107
|
+
shard's own schema and the public schema, so an unqualified call from inside a shard resolves.
|
|
108
|
+
|
|
109
|
+
The function is created on the public schema of every node, since that is where every shard's search path can
|
|
110
|
+
reach it. That is the same placement ``@mirrored_function()`` gives, and for a function the two are equivalent;
|
|
111
|
+
what a mode records here is intent rather than a different set of schemas.
|
|
112
|
+
|
|
113
|
+
:Example:
|
|
114
|
+
.. code-block:: python
|
|
115
|
+
|
|
116
|
+
from djanquiltdb.decorators import public_function
|
|
117
|
+
from postgres_objects import Function
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@public_function()
|
|
121
|
+
class AllUppercase(Function):
|
|
122
|
+
arguments = 'input TEXT'
|
|
123
|
+
returns = 'TEXT'
|
|
124
|
+
volatility = 'IMMUTABLE'
|
|
125
|
+
strict = True
|
|
126
|
+
parallel = 'SAFE'
|
|
127
|
+
body = '''
|
|
128
|
+
BEGIN
|
|
129
|
+
RETURN UPPER(input);
|
|
130
|
+
END;
|
|
131
|
+
'''
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
def configure(cls):
|
|
135
|
+
return _annotate_object(cls, ShardingMode.PUBLIC)
|
|
136
|
+
|
|
137
|
+
return configure
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def sharded_function():
|
|
141
|
+
"""
|
|
142
|
+
A decorator for marking a declared Postgres function as being sharded.
|
|
143
|
+
|
|
144
|
+
The function is created on the template schema and on every shard, and not on the public schema. Note that a
|
|
145
|
+
``PUBLIC`` function can already read sharded tables: as long as the table names in its body are not
|
|
146
|
+
schema-qualified, they resolve at execution time to the tables of whichever schema the caller is pointed at. Reach
|
|
147
|
+
for a sharded function when the *function itself* has to differ per shard.
|
|
148
|
+
|
|
149
|
+
:Example:
|
|
150
|
+
.. code-block:: python
|
|
151
|
+
|
|
152
|
+
from djanquiltdb.decorators import sharded_function
|
|
153
|
+
from postgres_objects import Function
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@sharded_function()
|
|
157
|
+
class ShardTotal(Function):
|
|
158
|
+
returns = 'BIGINT'
|
|
159
|
+
body = '''
|
|
160
|
+
BEGIN
|
|
161
|
+
RETURN (SELECT COUNT(*) FROM example_cake);
|
|
162
|
+
END;
|
|
163
|
+
'''
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
def configure(cls):
|
|
167
|
+
return _annotate_object(cls, ShardingMode.SHARDED)
|
|
168
|
+
|
|
169
|
+
return configure
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def mirrored_view():
|
|
173
|
+
"""
|
|
174
|
+
A decorator for marking a declared Postgres view as being mirrored across the various nodes.
|
|
175
|
+
|
|
176
|
+
The view is created on the public schema of every node, which is where a view over mirrored tables belongs. A
|
|
177
|
+
``@public_view()`` reaches those same schemas; what MIRRORED adds is that the copies are kept in step, so a
|
|
178
|
+
materialized one refreshes on every node at once rather than on the connection in context.
|
|
179
|
+
|
|
180
|
+
:Example:
|
|
181
|
+
.. code-block:: python
|
|
182
|
+
|
|
183
|
+
from djanquiltdb.decorators import mirrored_view
|
|
184
|
+
from postgres_objects import View
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@mirrored_view()
|
|
188
|
+
class ActiveTypes(View):
|
|
189
|
+
sql = 'SELECT id, name FROM example_type WHERE active'
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
def configure(cls):
|
|
193
|
+
return _annotate_object(cls, ShardingMode.MIRRORED)
|
|
194
|
+
|
|
195
|
+
return configure
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def public_view():
|
|
199
|
+
"""
|
|
200
|
+
A decorator for marking a declared Postgres view as living in the public schema.
|
|
201
|
+
|
|
202
|
+
A view is created on the public schema of every node and reads the tables that live there. Those are the same
|
|
203
|
+
schemas a mirrored view reaches; what PUBLIC says is that each copy stands on its own, over sources that may differ
|
|
204
|
+
per node, and that a materialized one is refreshed on the connection in context rather than everywhere at once.
|
|
205
|
+
|
|
206
|
+
Note that unlike a function, a public view is *not* a way to read sharded tables: a view's body is resolved when it
|
|
207
|
+
is created, not when it is queried, so the tables it names are pinned to the schema it was created in. A view over
|
|
208
|
+
sharded tables has to be sharded itself.
|
|
209
|
+
|
|
210
|
+
:Example:
|
|
211
|
+
.. code-block:: python
|
|
212
|
+
|
|
213
|
+
from djanquiltdb.decorators import public_view
|
|
214
|
+
from postgres_objects import View
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@public_view()
|
|
218
|
+
class CakeTypeNames(View):
|
|
219
|
+
sql = 'SELECT id, name FROM example_caketype'
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
def configure(cls):
|
|
223
|
+
return _annotate_object(cls, ShardingMode.PUBLIC)
|
|
224
|
+
|
|
225
|
+
return configure
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def sharded_view():
|
|
229
|
+
"""
|
|
230
|
+
A decorator for marking a declared Postgres view as being sharded.
|
|
231
|
+
|
|
232
|
+
The view is created on the template schema and on every shard, and not on the public schema. This is what a view
|
|
233
|
+
over sharded tables needs: each shard gets its own copy, reading the tables of the schema it was created in.
|
|
234
|
+
|
|
235
|
+
:Example:
|
|
236
|
+
.. code-block:: python
|
|
237
|
+
|
|
238
|
+
from djanquiltdb.decorators import sharded_view
|
|
239
|
+
from postgres_objects import View
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
@sharded_view()
|
|
243
|
+
class LoudCakes(View):
|
|
244
|
+
sql = 'SELECT id, upper(name) AS name FROM example_cake'
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
def configure(cls):
|
|
248
|
+
return _annotate_object(cls, ShardingMode.SHARDED)
|
|
249
|
+
|
|
250
|
+
return configure
|