django-postgres-denormalize 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.
Files changed (81) hide show
  1. django_postgres_denormalize-1.0.0/CHANGELOG.rst +9 -0
  2. django_postgres_denormalize-1.0.0/LICENSE +23 -0
  3. django_postgres_denormalize-1.0.0/MANIFEST.in +20 -0
  4. django_postgres_denormalize-1.0.0/PKG-INFO +154 -0
  5. django_postgres_denormalize-1.0.0/README.rst +107 -0
  6. django_postgres_denormalize-1.0.0/assets/icon-128.png +0 -0
  7. django_postgres_denormalize-1.0.0/assets/icon-16.png +0 -0
  8. django_postgres_denormalize-1.0.0/assets/icon-256.png +0 -0
  9. django_postgres_denormalize-1.0.0/assets/icon-32.png +0 -0
  10. django_postgres_denormalize-1.0.0/assets/icon-48.png +0 -0
  11. django_postgres_denormalize-1.0.0/assets/icon-512.png +0 -0
  12. django_postgres_denormalize-1.0.0/assets/icon-64.png +0 -0
  13. django_postgres_denormalize-1.0.0/assets/icon-small.svg +16 -0
  14. django_postgres_denormalize-1.0.0/assets/icon.svg +16 -0
  15. django_postgres_denormalize-1.0.0/assets/social-preview.png +0 -0
  16. django_postgres_denormalize-1.0.0/assets/social-preview.svg +21 -0
  17. django_postgres_denormalize-1.0.0/docs/Makefile +14 -0
  18. django_postgres_denormalize-1.0.0/docs/_static/custom.css +96 -0
  19. django_postgres_denormalize-1.0.0/docs/_templates/layout.html +30 -0
  20. django_postgres_denormalize-1.0.0/docs/conf.py +96 -0
  21. django_postgres_denormalize-1.0.0/docs/getting_started/alternatives.rst +125 -0
  22. django_postgres_denormalize-1.0.0/docs/getting_started/installation.rst +39 -0
  23. django_postgres_denormalize-1.0.0/docs/getting_started/overview.rst +114 -0
  24. django_postgres_denormalize-1.0.0/docs/getting_started/when_to_use.rst +87 -0
  25. django_postgres_denormalize-1.0.0/docs/guides/batch_updates.rst +82 -0
  26. django_postgres_denormalize-1.0.0/docs/guides/drift.rst +101 -0
  27. django_postgres_denormalize-1.0.0/docs/guides/exclusions.rst +81 -0
  28. django_postgres_denormalize-1.0.0/docs/guides/fields.rst +179 -0
  29. django_postgres_denormalize-1.0.0/docs/guides/indexing.rst +107 -0
  30. django_postgres_denormalize-1.0.0/docs/guides/migrations.rst +86 -0
  31. django_postgres_denormalize-1.0.0/docs/guides/triggers.rst +195 -0
  32. django_postgres_denormalize-1.0.0/docs/index.rst +52 -0
  33. django_postgres_denormalize-1.0.0/docs/integrations/djanquiltdb.rst +17 -0
  34. django_postgres_denormalize-1.0.0/docs/reference/api.rst +65 -0
  35. django_postgres_denormalize-1.0.0/docs/reference/changelog.rst +5 -0
  36. django_postgres_denormalize-1.0.0/docs/reference/checks.rst +78 -0
  37. django_postgres_denormalize-1.0.0/docs/reference/settings.rst +122 -0
  38. django_postgres_denormalize-1.0.0/pyproject.toml +198 -0
  39. django_postgres_denormalize-1.0.0/setup.cfg +4 -0
  40. django_postgres_denormalize-1.0.0/src/django_postgres_denormalize.egg-info/PKG-INFO +154 -0
  41. django_postgres_denormalize-1.0.0/src/django_postgres_denormalize.egg-info/SOURCES.txt +79 -0
  42. django_postgres_denormalize-1.0.0/src/django_postgres_denormalize.egg-info/dependency_links.txt +1 -0
  43. django_postgres_denormalize-1.0.0/src/django_postgres_denormalize.egg-info/requires.txt +22 -0
  44. django_postgres_denormalize-1.0.0/src/django_postgres_denormalize.egg-info/top_level.txt +1 -0
  45. django_postgres_denormalize-1.0.0/src/postgres_denormalize/__init__.py +36 -0
  46. django_postgres_denormalize-1.0.0/src/postgres_denormalize/apps.py +18 -0
  47. django_postgres_denormalize-1.0.0/src/postgres_denormalize/batch.py +130 -0
  48. django_postgres_denormalize-1.0.0/src/postgres_denormalize/chains.py +121 -0
  49. django_postgres_denormalize-1.0.0/src/postgres_denormalize/checks.py +381 -0
  50. django_postgres_denormalize-1.0.0/src/postgres_denormalize/conf.py +170 -0
  51. django_postgres_denormalize-1.0.0/src/postgres_denormalize/fields.py +716 -0
  52. django_postgres_denormalize-1.0.0/src/postgres_denormalize/operations.py +139 -0
  53. django_postgres_denormalize-1.0.0/src/postgres_denormalize/py.typed +0 -0
  54. django_postgres_denormalize-1.0.0/src/postgres_denormalize/triggers.py +597 -0
  55. django_postgres_denormalize-1.0.0/tests/catalog/__init__.py +0 -0
  56. django_postgres_denormalize-1.0.0/tests/catalog/apps.py +5 -0
  57. django_postgres_denormalize-1.0.0/tests/catalog/exclusions.py +6 -0
  58. django_postgres_denormalize-1.0.0/tests/catalog/migrations/0001_initial.py +292 -0
  59. django_postgres_denormalize-1.0.0/tests/catalog/migrations/__init__.py +0 -0
  60. django_postgres_denormalize-1.0.0/tests/catalog/models.py +59 -0
  61. django_postgres_denormalize-1.0.0/tests/config/__init__.py +0 -0
  62. django_postgres_denormalize-1.0.0/tests/config/settings.py +55 -0
  63. django_postgres_denormalize-1.0.0/tests/isolated/__init__.py +5 -0
  64. django_postgres_denormalize-1.0.0/tests/isolated/apps.py +5 -0
  65. django_postgres_denormalize-1.0.0/tests/isolated/test_checks.py +320 -0
  66. django_postgres_denormalize-1.0.0/tests/isolated/test_fields.py +870 -0
  67. django_postgres_denormalize-1.0.0/tests/isolated/test_operations.py +30 -0
  68. django_postgres_denormalize-1.0.0/tests/isolated/test_triggers.py +241 -0
  69. django_postgres_denormalize-1.0.0/tests/manage.py +10 -0
  70. django_postgres_denormalize-1.0.0/tests/tenancy/__init__.py +0 -0
  71. django_postgres_denormalize-1.0.0/tests/tenancy/apps.py +5 -0
  72. django_postgres_denormalize-1.0.0/tests/tenancy/migrations/0001_initial.py +232 -0
  73. django_postgres_denormalize-1.0.0/tests/tenancy/migrations/__init__.py +0 -0
  74. django_postgres_denormalize-1.0.0/tests/tenancy/models.py +52 -0
  75. django_postgres_denormalize-1.0.0/tests/test_batch.py +225 -0
  76. django_postgres_denormalize-1.0.0/tests/test_chains.py +55 -0
  77. django_postgres_denormalize-1.0.0/tests/test_checks.py +233 -0
  78. django_postgres_denormalize-1.0.0/tests/test_conf.py +221 -0
  79. django_postgres_denormalize-1.0.0/tests/test_fields.py +337 -0
  80. django_postgres_denormalize-1.0.0/tests/test_operations.py +215 -0
  81. django_postgres_denormalize-1.0.0/tests/test_triggers.py +667 -0
@@ -0,0 +1,9 @@
1
+ v1.0.0 (2026-10-18)
2
+ -------------------
3
+ Added:
4
+ * ``postgres_denormalize.fields.DenormalizedForeignKey``, a foreign key that stores a copy of the foreign key at the
5
+ end of a lookup path.
6
+ * ``postgres_denormalize.fields.DenormalizedField``, a column that stores a copy of any other column at the end of a
7
+ lookup path.
8
+ * Backfill tools as a migration operation (``postgres_denormalize.operations.BackfillDenormalized``) and a function
9
+ (``postgres_denormalize.operations.backfill_denormalized``).
@@ -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,20 @@
1
+ # The sdist is what downstream packagers build and test from, so it carries the whole test project and the docs, not
2
+ # just the importable package. Without this, setuptools' defaults ship tests/test_*.py but none of the fixture apps or
3
+ # settings they import, and the suite in the sdist cannot run.
4
+
5
+ include CHANGELOG.rst
6
+ include LICENSE
7
+ include README.rst
8
+
9
+ recursive-include src/postgres_denormalize py.typed
10
+
11
+ graft docs
12
+ graft tests
13
+
14
+ # docs/conf.py points html_logo and html_favicon at ../assets, so a docs build from the sdist fails without these -- and
15
+ # .readthedocs.yaml sets fail_on_warning, which turns a missing logo into a build failure rather than a shrug.
16
+ graft assets
17
+
18
+ prune docs/_build
19
+ global-exclude *.py[cod]
20
+ global-exclude __pycache__
@@ -0,0 +1,154 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-postgres-denormalize
3
+ Version: 1.0.0
4
+ Summary: Trigger-maintained copies of ancestor columns for Django on PostgreSQL.
5
+ Author-email: DjanQuiltDB Project <djanquiltdb@portal42.net>
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/djanquiltdb/django-postgres-denormalize
8
+ Project-URL: Documentation, https://django-postgres-denormalize.readthedocs.io/
9
+ Project-URL: Source, https://github.com/djanquiltdb/django-postgres-denormalize
10
+ Project-URL: Issues, https://github.com/djanquiltdb/django-postgres-denormalize/issues
11
+ Project-URL: Changelog, https://github.com/djanquiltdb/django-postgres-denormalize/blob/master/CHANGELOG.rst
12
+ Keywords: django,database,postgresql,denormalization,triggers
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
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.14
26
+ Description-Content-Type: text/x-rst
27
+ License-File: LICENSE
28
+ Requires-Dist: django<7.0,>=6.0
29
+ Requires-Dist: django-pgtrigger<5,>=4.17
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
+ Requires-Dist: sphinx-rtd-theme<4,>=3.1; extra == "docs"
39
+ Requires-Dist: psycopg[binary]>=3.0.0; extra == "docs"
40
+ Provides-Extra: dev
41
+ Requires-Dist: django-postgres-denormalize[docs,lint,test]; extra == "dev"
42
+ Requires-Dist: pre-commit; extra == "dev"
43
+ Requires-Dist: tox>=4.21; extra == "dev"
44
+ Requires-Dist: build; extra == "dev"
45
+ Requires-Dist: twine; extra == "dev"
46
+ Dynamic: license-file
47
+
48
+ .. image:: https://raw.githubusercontent.com/DjanQuiltDB/django-postgres-denormalize/master/assets/icon-128.png
49
+ :alt: django-postgres-denormalize
50
+ :width: 128
51
+
52
+ ===========================
53
+ django-postgres-denormalize
54
+ ===========================
55
+
56
+ .. image:: https://img.shields.io/pypi/v/django-postgres-denormalize.svg
57
+ :target: https://pypi.org/project/django-postgres-denormalize/
58
+ :alt: PyPI
59
+
60
+ .. image:: https://img.shields.io/pypi/pyversions/django-postgres-denormalize.svg
61
+ :target: https://pypi.org/project/django-postgres-denormalize/
62
+ :alt: Supported Python versions
63
+
64
+ .. image:: https://img.shields.io/pypi/frameworkversions/django/django-postgres-denormalize.svg
65
+ :target: https://pypi.org/project/django-postgres-denormalize/
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/django-postgres-denormalize/actions/workflows/ci.yml/badge.svg
73
+ :target: https://github.com/djanquiltdb/django-postgres-denormalize/actions/workflows/ci.yml
74
+ :alt: CI
75
+
76
+ .. image:: https://readthedocs.org/projects/django-postgres-denormalize/badge/?version=latest
77
+ :target: https://django-postgres-denormalize.readthedocs.io/
78
+ :alt: Documentation
79
+
80
+ .. image:: https://img.shields.io/pypi/l/django-postgres-denormalize.svg
81
+ :target: https://github.com/djanquiltdb/django-postgres-denormalize/blob/master/LICENSE
82
+ :alt: BSD-3-Clause licence
83
+
84
+ PostgreSQL cannot index, sort or constrain on a value that is stored in another table. A value that is several joins
85
+ away, such as the tenant that a row belongs to through its parent, cannot be part of an index or a unique constraint on
86
+ the row's table, and every query that filters or orders by it pays for the joins.
87
+
88
+ This package lets a model store a copy of such a value in an ordinary column, and keeps the copy correct with
89
+ PostgreSQL triggers. That works for every write, whether it comes from the ORM, a bulk update, raw SQL or the previous
90
+ release that is still serving during a rolling deploy. The model declares the lookup path and nothing else;
91
+ ``makemigrations`` writes the column, the triggers and their changes.
92
+
93
+ Full documentation is at https://django-postgres-denormalize.readthedocs.io/.
94
+
95
+ .. code-block:: python
96
+
97
+ from django.db import models
98
+ from postgres_denormalize.fields import DenormalizedField, DenormalizedForeignKey
99
+
100
+
101
+ class Task(models.Model):
102
+ project = models.ForeignKey('Project', on_delete=models.CASCADE)
103
+ title = models.CharField(max_length=100)
104
+ # A copy of project.tenant: a real foreign key, its target and on_delete rule inferred from the path.
105
+ tenant = DenormalizedForeignKey('project__tenant')
106
+ # A copy of project.name: a column of the same type as the source.
107
+ project_name = DenormalizedField('project__name')
108
+
109
+ class Meta:
110
+ ordering = ['project_name', 'title']
111
+ constraints = [models.UniqueConstraint(fields=['tenant', 'title'], name='unique_task_title_per_tenant')]
112
+ indexes = [models.Index(fields=['tenant', 'project_name', 'title'], name='task_listing')]
113
+
114
+ From then on, saving a task computes its copies in the database and reads them back onto the instance. Moving a
115
+ project to another tenant, or renaming it, rewrites the copies of its tasks in the same transaction, but only the
116
+ copies that change.
117
+
118
+ **Use it for** values that an index or a unique constraint needs, and values that most queries sort or filter by across
119
+ several joins. **Do not use it** to save a join for rendering, because ``select_related`` does that for free. Do not use
120
+ it either for values whose source is often updated in bulk by code that you cannot wrap in one of its batch helpers.
121
+ See `when to use it, and when not to
122
+ <https://django-postgres-denormalize.readthedocs.io/en/latest/getting_started/when_to_use.html>`_, and the
123
+ `alternatives <https://django-postgres-denormalize.readthedocs.io/en/latest/getting_started/alternatives.html>`_ page,
124
+ which compares it with django-pgconstraints and django-computedfields.
125
+
126
+ Installation
127
+ ------------
128
+
129
+ .. code-block:: bash
130
+
131
+ pip install django-postgres-denormalize
132
+
133
+ .. code-block:: python
134
+
135
+ INSTALLED_APPS = [
136
+ # ...
137
+ 'pgtrigger',
138
+ 'postgres_denormalize',
139
+ ]
140
+
141
+ Requires PostgreSQL and django-pgtrigger, with pgtrigger's migration support turned on (the default). See the
142
+ `settings reference <https://django-postgres-denormalize.readthedocs.io/en/latest/reference/settings.html>`_ for the
143
+ options.
144
+
145
+ Relationship to DjanQuiltDB
146
+ ---------------------------
147
+
148
+ While this library was written in conjunction with `DjanQuiltDB <https://github.com/djanquiltdb/djanquiltdb>`_ and the
149
+ libraries were designed to work together seamlessly in projects where both are installed, both libraries are written to
150
+ be used as standalone libraries as well. For using django-postgres-denormalize, it is not necessary to use or be
151
+ familiar with DjanQuiltDB.
152
+
153
+ If you wish to use DjanQuiltDB features for the copies that django-postgres-denormalize keeps, you can install the
154
+ ``djanquiltdb[postgres-denormalize]`` extra. For more information, please refer to the DjanQuiltDB documentation.
@@ -0,0 +1,107 @@
1
+ .. image:: https://raw.githubusercontent.com/DjanQuiltDB/django-postgres-denormalize/master/assets/icon-128.png
2
+ :alt: django-postgres-denormalize
3
+ :width: 128
4
+
5
+ ===========================
6
+ django-postgres-denormalize
7
+ ===========================
8
+
9
+ .. image:: https://img.shields.io/pypi/v/django-postgres-denormalize.svg
10
+ :target: https://pypi.org/project/django-postgres-denormalize/
11
+ :alt: PyPI
12
+
13
+ .. image:: https://img.shields.io/pypi/pyversions/django-postgres-denormalize.svg
14
+ :target: https://pypi.org/project/django-postgres-denormalize/
15
+ :alt: Supported Python versions
16
+
17
+ .. image:: https://img.shields.io/pypi/frameworkversions/django/django-postgres-denormalize.svg
18
+ :target: https://pypi.org/project/django-postgres-denormalize/
19
+ :alt: Supported Django versions
20
+
21
+ .. image:: https://img.shields.io/badge/postgres-17%20%7C%2018-4169e1?logo=postgresql&logoColor=white
22
+ :target: https://www.postgresql.org/
23
+ :alt: Supported PostgreSQL versions
24
+
25
+ .. image:: https://github.com/djanquiltdb/django-postgres-denormalize/actions/workflows/ci.yml/badge.svg
26
+ :target: https://github.com/djanquiltdb/django-postgres-denormalize/actions/workflows/ci.yml
27
+ :alt: CI
28
+
29
+ .. image:: https://readthedocs.org/projects/django-postgres-denormalize/badge/?version=latest
30
+ :target: https://django-postgres-denormalize.readthedocs.io/
31
+ :alt: Documentation
32
+
33
+ .. image:: https://img.shields.io/pypi/l/django-postgres-denormalize.svg
34
+ :target: https://github.com/djanquiltdb/django-postgres-denormalize/blob/master/LICENSE
35
+ :alt: BSD-3-Clause licence
36
+
37
+ PostgreSQL cannot index, sort or constrain on a value that is stored in another table. A value that is several joins
38
+ away, such as the tenant that a row belongs to through its parent, cannot be part of an index or a unique constraint on
39
+ the row's table, and every query that filters or orders by it pays for the joins.
40
+
41
+ This package lets a model store a copy of such a value in an ordinary column, and keeps the copy correct with
42
+ PostgreSQL triggers. That works for every write, whether it comes from the ORM, a bulk update, raw SQL or the previous
43
+ release that is still serving during a rolling deploy. The model declares the lookup path and nothing else;
44
+ ``makemigrations`` writes the column, the triggers and their changes.
45
+
46
+ Full documentation is at https://django-postgres-denormalize.readthedocs.io/.
47
+
48
+ .. code-block:: python
49
+
50
+ from django.db import models
51
+ from postgres_denormalize.fields import DenormalizedField, DenormalizedForeignKey
52
+
53
+
54
+ class Task(models.Model):
55
+ project = models.ForeignKey('Project', on_delete=models.CASCADE)
56
+ title = models.CharField(max_length=100)
57
+ # A copy of project.tenant: a real foreign key, its target and on_delete rule inferred from the path.
58
+ tenant = DenormalizedForeignKey('project__tenant')
59
+ # A copy of project.name: a column of the same type as the source.
60
+ project_name = DenormalizedField('project__name')
61
+
62
+ class Meta:
63
+ ordering = ['project_name', 'title']
64
+ constraints = [models.UniqueConstraint(fields=['tenant', 'title'], name='unique_task_title_per_tenant')]
65
+ indexes = [models.Index(fields=['tenant', 'project_name', 'title'], name='task_listing')]
66
+
67
+ From then on, saving a task computes its copies in the database and reads them back onto the instance. Moving a
68
+ project to another tenant, or renaming it, rewrites the copies of its tasks in the same transaction, but only the
69
+ copies that change.
70
+
71
+ **Use it for** values that an index or a unique constraint needs, and values that most queries sort or filter by across
72
+ several joins. **Do not use it** to save a join for rendering, because ``select_related`` does that for free. Do not use
73
+ it either for values whose source is often updated in bulk by code that you cannot wrap in one of its batch helpers.
74
+ See `when to use it, and when not to
75
+ <https://django-postgres-denormalize.readthedocs.io/en/latest/getting_started/when_to_use.html>`_, and the
76
+ `alternatives <https://django-postgres-denormalize.readthedocs.io/en/latest/getting_started/alternatives.html>`_ page,
77
+ which compares it with django-pgconstraints and django-computedfields.
78
+
79
+ Installation
80
+ ------------
81
+
82
+ .. code-block:: bash
83
+
84
+ pip install django-postgres-denormalize
85
+
86
+ .. code-block:: python
87
+
88
+ INSTALLED_APPS = [
89
+ # ...
90
+ 'pgtrigger',
91
+ 'postgres_denormalize',
92
+ ]
93
+
94
+ Requires PostgreSQL and django-pgtrigger, with pgtrigger's migration support turned on (the default). See the
95
+ `settings reference <https://django-postgres-denormalize.readthedocs.io/en/latest/reference/settings.html>`_ for the
96
+ options.
97
+
98
+ Relationship to DjanQuiltDB
99
+ ---------------------------
100
+
101
+ While this library was written in conjunction with `DjanQuiltDB <https://github.com/djanquiltdb/djanquiltdb>`_ and the
102
+ libraries were designed to work together seamlessly in projects where both are installed, both libraries are written to
103
+ be used as standalone libraries as well. For using django-postgres-denormalize, it is not necessary to use or be
104
+ familiar with DjanQuiltDB.
105
+
106
+ If you wish to use DjanQuiltDB features for the copies that django-postgres-denormalize keeps, you can install the
107
+ ``djanquiltdb[postgres-denormalize]`` extra. For more information, please refer to the DjanQuiltDB documentation.
@@ -0,0 +1,16 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2
+ <path d="M15,42V36H49V42" fill="none" stroke="#31936D" stroke-width="5"
3
+ stroke-linecap="round" stroke-linejoin="round"/>
4
+
5
+ <rect x="16" y="3" width="32" height="19" rx="5" fill="#44B78B"/>
6
+ <rect x="23" y="10" width="18" height="5" rx="2.5" fill="#FFC757"/>
7
+
8
+ <rect x="1" y="42" width="29" height="19" rx="5" fill="#31936D"/>
9
+ <rect x="7" y="49" width="17" height="5" rx="2.5" fill="#FFC757"/>
10
+
11
+ <rect x="34" y="42" width="29" height="19" rx="5" fill="#1E6F50"/>
12
+ <rect x="40" y="49" width="17" height="5" rx="2.5" fill="#FFC757"/>
13
+
14
+ <path d="M37,21L25,37H32L28,47L41,31H34L37,21Z" fill="#FFC757"
15
+ stroke="#FFC757" stroke-width="2" stroke-linejoin="round"/>
16
+ </svg>
@@ -0,0 +1,16 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2
+ <path d="M16,43V37Q16,34 19,34H45Q48,34 48,37V43" fill="none" stroke="#31936D" stroke-width="3.5"
3
+ stroke-linecap="round" stroke-linejoin="round"/>
4
+
5
+ <rect x="19" y="5" width="26" height="17" rx="4.5" fill="#44B78B"/>
6
+ <rect x="25.5" y="11.6" width="13" height="3.8" rx="1.9" fill="#FFC757"/>
7
+
8
+ <rect x="3" y="43" width="26" height="16" rx="4.5" fill="#31936D"/>
9
+ <rect x="9.5" y="49.1" width="13" height="3.8" rx="1.9" fill="#FFC757"/>
10
+
11
+ <rect x="35" y="43" width="26" height="16" rx="4.5" fill="#1E6F50"/>
12
+ <rect x="41.5" y="49.1" width="13" height="3.8" rx="1.9" fill="#FFC757"/>
13
+
14
+ <path d="M35.5,22.5L27,34.5H32.5L29.5,42.5L38.5,30.5H33L35.5,22.5Z" fill="#FFC757"
15
+ stroke="#FFC757" stroke-width="1.2" stroke-linejoin="round"/>
16
+ </svg>
@@ -0,0 +1,21 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 640" width="1280" height="640">
2
+ <rect width="1280" height="640" fill="#092e20"/>
3
+ <g transform="translate(98 192) scale(4)">
4
+ <path d="M16,43V37Q16,34 19,34H45Q48,34 48,37V43" fill="none" stroke="#31936D" stroke-width="3.5"
5
+ stroke-linecap="round" stroke-linejoin="round"/>
6
+ <rect x="19" y="5" width="26" height="17" rx="4.5" fill="#44B78B"/>
7
+ <rect x="25.5" y="11.6" width="13" height="3.8" rx="1.9" fill="#FFC757"/>
8
+ <rect x="3" y="43" width="26" height="16" rx="4.5" fill="#31936D"/>
9
+ <rect x="9.5" y="49.1" width="13" height="3.8" rx="1.9" fill="#FFC757"/>
10
+ <rect x="35" y="43" width="26" height="16" rx="4.5" fill="#1E6F50"/>
11
+ <rect x="41.5" y="49.1" width="13" height="3.8" rx="1.9" fill="#FFC757"/>
12
+ <path d="M35.5,22.5L27,34.5H32.5L29.5,42.5L38.5,30.5H33L35.5,22.5Z" fill="#FFC757"
13
+ stroke="#FFC757" stroke-width="1.2" stroke-linejoin="round"/>
14
+ </g>
15
+
16
+ <g font-family="Noto Sans, DejaVu Sans, Liberation Sans, sans-serif">
17
+ <text id="title" x="420" y="296" font-size="53" font-weight="700" fill="#ffffff">django-postgres-denormalize</text>
18
+ <rect x="423" y="328" width="170" height="5" fill="#FFC757"/>
19
+ <text id="tagline" x="420" y="385" font-size="31" font-weight="400" fill="#44B78B">Trigger-maintained copies of ancestor columns</text>
20
+ </g>
21
+ </svg>
@@ -0,0 +1,14 @@
1
+ # Minimal makefile for Sphinx documentation
2
+
3
+ SPHINXOPTS ?= -W
4
+ SPHINXBUILD ?= sphinx-build
5
+ SOURCEDIR = .
6
+ BUILDDIR = _build
7
+
8
+ help:
9
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
10
+
11
+ .PHONY: help Makefile
12
+
13
+ %: Makefile
14
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@@ -0,0 +1,96 @@
1
+ :root {
2
+ --dpd-ink: #092e20;
3
+ --dpd-gold: #FFC757;
4
+ --dpd-mint: #E3F4EE;
5
+ --dpd-mint-soft: #EFF9F5;
6
+ --dpd-mint-deep: #CBEBDF;
7
+ --dpd-mint-line: #ABDFCB;
8
+ }
9
+
10
+ .wy-side-nav-search > a.wordmark {
11
+ display: flex;
12
+ align-items: center;
13
+ justify-content: center;
14
+ gap: 0.6em;
15
+ padding: 0;
16
+ }
17
+
18
+ .wy-side-nav-search > a.wordmark img.logo {
19
+ flex: none;
20
+ width: 72px;
21
+ height: 72px;
22
+ margin: 0;
23
+ }
24
+
25
+ .wy-side-nav-search > a.wordmark .name {
26
+ position: relative;
27
+ padding-bottom: 4px;
28
+ color: #ffffff;
29
+ font-size: 1.3em;
30
+ font-weight: 700;
31
+ line-height: 1.05;
32
+ text-align: left;
33
+ }
34
+
35
+ .wy-side-nav-search > a.wordmark .name::after {
36
+ content: "";
37
+ position: absolute;
38
+ bottom: 0;
39
+ left: 0;
40
+ width: 23%;
41
+ height: 2px;
42
+ background: var(--dpd-gold);
43
+ }
44
+
45
+ .wy-nav-side {
46
+ background: var(--dpd-mint);
47
+ color: var(--dpd-ink);
48
+ }
49
+
50
+ .wy-menu-vertical a {
51
+ color: var(--dpd-ink);
52
+ }
53
+
54
+ .wy-menu-vertical a:hover {
55
+ background-color: var(--dpd-mint-deep);
56
+ color: var(--dpd-ink);
57
+ }
58
+
59
+ .wy-menu-vertical header,
60
+ .wy-menu-vertical p.caption {
61
+ color: #1E6F50;
62
+ }
63
+
64
+ .wy-menu-vertical li.current {
65
+ background: var(--dpd-mint-soft);
66
+ }
67
+
68
+ .wy-menu-vertical li.current a {
69
+ border-right: 1px solid var(--dpd-mint-line);
70
+ color: var(--dpd-ink);
71
+ }
72
+
73
+ .wy-menu-vertical li.current a:hover {
74
+ background: var(--dpd-mint-deep);
75
+ }
76
+
77
+ .wy-menu-vertical li.current > a,
78
+ .wy-menu-vertical li.on a {
79
+ background: #ffffff;
80
+ color: var(--dpd-ink);
81
+ }
82
+
83
+ .wy-menu-vertical li.current > a:hover,
84
+ .wy-menu-vertical li.on a:hover {
85
+ background: #ffffff;
86
+ }
87
+
88
+ .wy-menu-vertical li.toctree-l1.current > a {
89
+ border-top: 1px solid var(--dpd-mint-line);
90
+ border-bottom: 1px solid var(--dpd-mint-line);
91
+ }
92
+
93
+ .wy-menu-vertical li.toctree-l2.current > a,
94
+ .wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a {
95
+ background: var(--dpd-mint-deep);
96
+ }
@@ -0,0 +1,30 @@
1
+ {% extends "!layout.html" %}
2
+
3
+ {%- block sidebartitle %}
4
+
5
+ {%- set _logo_url = logo_url|default(pathto('_static/' + (logo or ""), 1)) %}
6
+ {%- set _root_doc = root_doc|default(master_doc) %}
7
+ <a href="{{ pathto(_root_doc) }}" class="wordmark">
8
+ {%- if logo or logo_url %}
9
+ <img src="{{ _logo_url }}" class="logo" alt="{{ _('Logo') }}"/>
10
+ {%- endif %}
11
+ <span class="name">django-<br/>postgres-<br/>denormalize</span>
12
+ </a>
13
+
14
+ {%- if READTHEDOCS or DEBUG %}
15
+ {%- if theme_version_selector or theme_language_selector %}
16
+ <div class="switch-menus">
17
+ <div class="version-switch"></div>
18
+ <div class="language-switch"></div>
19
+ </div>
20
+ {%- endif %}
21
+ {%- endif %}
22
+
23
+ {%- include "searchbox.html" %}
24
+
25
+ {%- endblock %}
26
+
27
+ {%- block mobile_nav %}
28
+ <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
29
+ <a href="{{ pathto(master_doc) }}">django-postgres-denormalize</a>
30
+ {%- endblock %}
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ # django-postgres-denormalize documentation build configuration file.
4
+
5
+ import datetime
6
+ import os
7
+ import sys
8
+
9
+ sys.path.insert(0, os.path.abspath('../src/'))
10
+
11
+ from django.conf import settings # noqa: E402
12
+
13
+ from postgres_denormalize import __version__ # noqa: E402
14
+
15
+ settings.configure()
16
+
17
+
18
+ # -- General configuration ------------------------------------------------
19
+
20
+ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.githubpages', 'sphinx_rtd_theme']
21
+
22
+ source_suffix = '.rst'
23
+
24
+ master_doc = 'index'
25
+
26
+ project = 'django-postgres-denormalize'
27
+ copyright = '{} DjanQuiltDB Project'.format(datetime.date.today().year) # noqa: A001
28
+ author = 'DjanQuiltDB Project'
29
+
30
+ release = version = __version__
31
+
32
+ language = 'en'
33
+
34
+ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
35
+
36
+ pygments_style = 'sphinx'
37
+
38
+ todo_include_todos = False
39
+
40
+ # Members are documented in source order, which follows the order a reader meets them in.
41
+ autodoc_member_order = 'bysource'
42
+
43
+
44
+ # -- Options for HTML output ----------------------------------------------
45
+
46
+ html_theme = 'sphinx_rtd_theme'
47
+
48
+ # The header the sidebar and the narrow-screen bar share, in the dark green the social preview card uses.
49
+ html_theme_options = {'style_nav_header_background': '#092e20'}
50
+
51
+ # docs/_templates/layout.html restyles that header; without this it is not picked up at all.
52
+ templates_path = ['_templates']
53
+
54
+ html_static_path = ['_static']
55
+
56
+ # The favicon is a raster rather than the logo itself: below about 32 pixels the lightning bolt blurs into the branch
57
+ # beneath it, so those sizes are rendered from assets/icon-small.svg, which is drawn for them.
58
+ html_logo = '../assets/icon.svg'
59
+ html_favicon = '../assets/icon-32.png'
60
+
61
+ # Only to style the sidebar header the template above replaces; see the file itself.
62
+ html_css_files = ['custom.css']
63
+
64
+
65
+ # -- Options for HTMLHelp output ------------------------------------------
66
+
67
+ htmlhelp_basename = project
68
+
69
+
70
+ # -- Options for LaTeX output ---------------------------------------------
71
+
72
+ latex_elements = {}
73
+
74
+ latex_documents = [
75
+ (master_doc, 'django-postgres-denormalize.tex', '{} Documentation'.format(project), author, 'manual'),
76
+ ]
77
+
78
+
79
+ # -- Options for manual page output ---------------------------------------
80
+
81
+ man_pages = [(master_doc, project, '{} Documentation'.format(project), [author], 1)]
82
+
83
+
84
+ # -- Options for Texinfo output -------------------------------------------
85
+
86
+ texinfo_documents = [
87
+ (
88
+ master_doc,
89
+ project,
90
+ '{} Documentation'.format(project),
91
+ author,
92
+ project,
93
+ 'Trigger-maintained copies of ancestor columns for Django on PostgreSQL.',
94
+ 'Miscellaneous',
95
+ ),
96
+ ]