django-sqlitetrigger 0.1.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 (43) hide show
  1. django_sqlitetrigger-0.1.0/.github/workflows/python-publish.yml +32 -0
  2. django_sqlitetrigger-0.1.0/.gitignore +350 -0
  3. django_sqlitetrigger-0.1.0/LICENSE +27 -0
  4. django_sqlitetrigger-0.1.0/PKG-INFO +69 -0
  5. django_sqlitetrigger-0.1.0/README.md +55 -0
  6. django_sqlitetrigger-0.1.0/example/example/__init__.py +0 -0
  7. django_sqlitetrigger-0.1.0/example/example/admin.py +13 -0
  8. django_sqlitetrigger-0.1.0/example/example/apps.py +5 -0
  9. django_sqlitetrigger-0.1.0/example/example/asgi.py +16 -0
  10. django_sqlitetrigger-0.1.0/example/example/migrations/0001_initial.py +23 -0
  11. django_sqlitetrigger-0.1.0/example/example/migrations/0002_book_sheets_update.py +18 -0
  12. django_sqlitetrigger-0.1.0/example/example/migrations/0003_alter_book_title.py +18 -0
  13. django_sqlitetrigger-0.1.0/example/example/migrations/0004_remove_book_sheets_update_book_sheets_update.py +22 -0
  14. django_sqlitetrigger-0.1.0/example/example/migrations/__init__.py +0 -0
  15. django_sqlitetrigger-0.1.0/example/example/models.py +21 -0
  16. django_sqlitetrigger-0.1.0/example/example/settings.py +119 -0
  17. django_sqlitetrigger-0.1.0/example/example/tests.py +3 -0
  18. django_sqlitetrigger-0.1.0/example/example/urls.py +22 -0
  19. django_sqlitetrigger-0.1.0/example/example/views.py +3 -0
  20. django_sqlitetrigger-0.1.0/example/example/wsgi.py +16 -0
  21. django_sqlitetrigger-0.1.0/example/manage.py +22 -0
  22. django_sqlitetrigger-0.1.0/pyproject.toml +33 -0
  23. django_sqlitetrigger-0.1.0/sqlitetrigger/__init__.py +45 -0
  24. django_sqlitetrigger-0.1.0/sqlitetrigger/apps.py +81 -0
  25. django_sqlitetrigger-0.1.0/sqlitetrigger/conditions.py +178 -0
  26. django_sqlitetrigger-0.1.0/sqlitetrigger/contrib.py +200 -0
  27. django_sqlitetrigger-0.1.0/sqlitetrigger/core.py +316 -0
  28. django_sqlitetrigger-0.1.0/sqlitetrigger/installation.py +130 -0
  29. django_sqlitetrigger-0.1.0/sqlitetrigger/management/__init__.py +0 -0
  30. django_sqlitetrigger-0.1.0/sqlitetrigger/management/commands/__init__.py +0 -0
  31. django_sqlitetrigger-0.1.0/sqlitetrigger/management/commands/sqlitetrigger.py +66 -0
  32. django_sqlitetrigger-0.1.0/sqlitetrigger/migrations.py +384 -0
  33. django_sqlitetrigger-0.1.0/sqlitetrigger/registry.py +85 -0
  34. django_sqlitetrigger-0.1.0/tests/__init__.py +0 -0
  35. django_sqlitetrigger-0.1.0/tests/models.py +84 -0
  36. django_sqlitetrigger-0.1.0/tests/settings.py +18 -0
  37. django_sqlitetrigger-0.1.0/tests/test_commands.py +35 -0
  38. django_sqlitetrigger-0.1.0/tests/test_conditions.py +139 -0
  39. django_sqlitetrigger-0.1.0/tests/test_contrib.py +184 -0
  40. django_sqlitetrigger-0.1.0/tests/test_core.py +160 -0
  41. django_sqlitetrigger-0.1.0/tests/test_migrations.py +201 -0
  42. django_sqlitetrigger-0.1.0/tests/test_registry.py +72 -0
  43. django_sqlitetrigger-0.1.0/uv.lock +150 -0
@@ -0,0 +1,32 @@
1
+ name: "Publish"
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ # Publish on any tag starting with a `v`, e.g., v0.1.0
7
+ - v*
8
+
9
+ jobs:
10
+ run:
11
+ runs-on: ubuntu-latest
12
+ environment:
13
+ name: pypi
14
+ permissions:
15
+ id-token: write
16
+ contents: read
17
+ steps:
18
+ - name: Checkout
19
+ uses: actions/checkout@v6
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v7
22
+ - name: Install Python
23
+ run: uv python install 3.14
24
+ - name: Build
25
+ run: uv build
26
+ # Check that basic features work and we didn't miss to include crucial files
27
+ # - name: Smoke test (wheel)
28
+ # run: uv run --isolated --no-project --with dist/*.whl tests/smoke_test.py
29
+ # - name: Smoke test (source distribution)
30
+ # run: uv run --isolated --no-project --with dist/*.tar.gz tests/smoke_test.py
31
+ - name: Publish
32
+ run: uv publish
@@ -0,0 +1,350 @@
1
+
2
+ # Created by https://www.gitignore.io/api/vim,osx,python,django,pycharm,komodoedit,elasticbeanstalk,visualstudiocode
3
+ # Edit at https://www.gitignore.io/?templates=vim,osx,python,django,pycharm,komodoedit,elasticbeanstalk,visualstudiocode
4
+
5
+ ### Django ###
6
+ *.log
7
+ *.pot
8
+ *.pyc
9
+ __pycache__/
10
+ local_settings.py
11
+ db.sqlite3
12
+ media
13
+
14
+ # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/
15
+ # in your Git repository. Update and uncomment the following line accordingly.
16
+ # <django-project-name>/staticfiles/
17
+
18
+ ### Django.Python Stack ###
19
+ # Byte-compiled / optimized / DLL files
20
+ *.py[cod]
21
+ *$py.class
22
+
23
+ # C extensions
24
+ *.so
25
+
26
+ # Distribution / packaging
27
+ .Python
28
+ build/
29
+ develop-eggs/
30
+ dist/
31
+ downloads/
32
+ eggs/
33
+ .eggs/
34
+ lib/
35
+ lib64/
36
+ parts/
37
+ sdist/
38
+ var/
39
+ wheels/
40
+ pip-wheel-metadata/
41
+ share/python-wheels/
42
+ *.egg-info/
43
+ .installed.cfg
44
+ *.egg
45
+ MANIFEST
46
+
47
+ # PyInstaller
48
+ # Usually these files are written by a python script from a template
49
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
50
+ *.manifest
51
+ *.spec
52
+
53
+ # Installer logs
54
+ pip-log.txt
55
+ pip-delete-this-directory.txt
56
+
57
+ # Unit test / coverage reports
58
+ htmlcov/
59
+ .tox/
60
+ .nox/
61
+ .coverage
62
+ .coverage.*
63
+ .cache
64
+ nosetests.xml
65
+ coverage.xml
66
+ *.cover
67
+ .hypothesis/
68
+ .pytest_cache/
69
+
70
+ # Translations
71
+ *.mo
72
+
73
+ # Django stuff:
74
+ db.sqlite3-journal
75
+
76
+ # Flask stuff:
77
+ instance/
78
+ .webassets-cache
79
+
80
+ # Scrapy stuff:
81
+ .scrapy
82
+
83
+ # PyBuilder
84
+ target/
85
+
86
+ # Jupyter Notebook
87
+ .ipynb_checkpoints
88
+
89
+ # IPython
90
+ profile_default/
91
+ ipython_config.py
92
+
93
+ # pyenv
94
+ .python-version
95
+
96
+ # pipenv
97
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
98
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
99
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
100
+ # install all needed dependencies.
101
+ #Pipfile.lock
102
+
103
+ # celery beat schedule file
104
+ celerybeat-schedule
105
+
106
+ # SageMath parsed files
107
+ *.sage.py
108
+
109
+ # Environments
110
+ .env
111
+ .venv
112
+ env/
113
+ venv/
114
+ ENV/
115
+ env.bak/
116
+ venv.bak/
117
+
118
+ # Spyder project settings
119
+ .spyderproject
120
+ .spyproject
121
+
122
+ # Rope project settings
123
+ .ropeproject
124
+
125
+ # mkdocs documentation
126
+ /site
127
+
128
+ # mypy
129
+ .mypy_cache/
130
+ .dmypy.json
131
+ dmypy.json
132
+
133
+ # Pyre type checker
134
+ .pyre/
135
+
136
+ ### ElasticBeanstalk ###
137
+ .elasticbeanstalk/
138
+
139
+ ### KomodoEdit ###
140
+ *.komodoproject
141
+ .komodotools
142
+
143
+ ### OSX ###
144
+ # General
145
+ .DS_Store
146
+ .AppleDouble
147
+ .LSOverride
148
+
149
+ # Icon must end with two \r
150
+ Icon
151
+
152
+ # Thumbnails
153
+ ._*
154
+
155
+ # Files that might appear in the root of a volume
156
+ .DocumentRevisions-V100
157
+ .fseventsd
158
+ .Spotlight-V100
159
+ .TemporaryItems
160
+ .Trashes
161
+ .VolumeIcon.icns
162
+ .com.apple.timemachine.donotpresent
163
+
164
+ # Directories potentially created on remote AFP share
165
+ .AppleDB
166
+ .AppleDesktop
167
+ Network Trash Folder
168
+ Temporary Items
169
+ .apdisk
170
+
171
+ ### PyCharm ###
172
+ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
173
+ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
174
+
175
+ # User-specific stuff
176
+ .idea/**/workspace.xml
177
+ .idea/**/tasks.xml
178
+ .idea/**/usage.statistics.xml
179
+ .idea/**/dictionaries
180
+ .idea/**/shelf
181
+
182
+ # Generated files
183
+ .idea/**/contentModel.xml
184
+
185
+ # Sensitive or high-churn files
186
+ .idea/**/dataSources/
187
+ .idea/**/dataSources.ids
188
+ .idea/**/dataSources.local.xml
189
+ .idea/**/sqlDataSources.xml
190
+ .idea/**/dynamic.xml
191
+ .idea/**/uiDesigner.xml
192
+ .idea/**/dbnavigator.xml
193
+
194
+ # Gradle
195
+ .idea/**/gradle.xml
196
+ .idea/**/libraries
197
+
198
+ # Gradle and Maven with auto-import
199
+ # When using Gradle or Maven with auto-import, you should exclude module files,
200
+ # since they will be recreated, and may cause churn. Uncomment if using
201
+ # auto-import.
202
+ # .idea/modules.xml
203
+ # .idea/*.iml
204
+ # .idea/modules
205
+ # *.iml
206
+ # *.ipr
207
+
208
+ # CMake
209
+ cmake-build-*/
210
+
211
+ # Mongo Explorer plugin
212
+ .idea/**/mongoSettings.xml
213
+
214
+ # File-based project format
215
+ *.iws
216
+
217
+ # IntelliJ
218
+ out/
219
+
220
+ # mpeltonen/sbt-idea plugin
221
+ .idea_modules/
222
+
223
+ # JIRA plugin
224
+ atlassian-ide-plugin.xml
225
+
226
+ # Cursive Clojure plugin
227
+ .idea/replstate.xml
228
+
229
+ # Crashlytics plugin (for Android Studio and IntelliJ)
230
+ com_crashlytics_export_strings.xml
231
+ crashlytics.properties
232
+ crashlytics-build.properties
233
+ fabric.properties
234
+
235
+ # Editor-based Rest Client
236
+ .idea/httpRequests
237
+
238
+ # Android studio 3.1+ serialized cache file
239
+ .idea/caches/build_file_checksums.ser
240
+
241
+ ### PyCharm Patch ###
242
+ # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
243
+
244
+ # *.iml
245
+ # modules.xml
246
+ # .idea/misc.xml
247
+ # *.ipr
248
+
249
+ # Sonarlint plugin
250
+ .idea/sonarlint
251
+
252
+ ### Python ###
253
+ # Byte-compiled / optimized / DLL files
254
+
255
+ # C extensions
256
+
257
+ # Distribution / packaging
258
+
259
+ # PyInstaller
260
+ # Usually these files are written by a python script from a template
261
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
262
+
263
+ # Installer logs
264
+
265
+ # Unit test / coverage reports
266
+
267
+ # Translations
268
+
269
+ # Django stuff:
270
+
271
+ # Flask stuff:
272
+
273
+ # Scrapy stuff:
274
+
275
+ # Sphinx documentation
276
+
277
+ # PyBuilder
278
+
279
+ # Jupyter Notebook
280
+
281
+ # IPython
282
+
283
+ # pyenv
284
+
285
+ # pipenv
286
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
287
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
288
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
289
+ # install all needed dependencies.
290
+
291
+ # celery beat schedule file
292
+
293
+ # SageMath parsed files
294
+
295
+ # Environments
296
+
297
+ # Spyder project settings
298
+
299
+ # Rope project settings
300
+
301
+ # mkdocs documentation
302
+
303
+ # mypy
304
+
305
+ # Pyre type checker
306
+
307
+ ### Vim ###
308
+ # Swap
309
+ [._]*.s[a-v][a-z]
310
+ [._]*.sw[a-p]
311
+ [._]s[a-rt-v][a-z]
312
+ [._]ss[a-gi-z]
313
+ [._]sw[a-p]
314
+
315
+ # Session
316
+ Session.vim
317
+ Sessionx.vim
318
+
319
+ # Temporary
320
+ .netrwhist
321
+ *~
322
+ # Auto-generated tag files
323
+ tags
324
+ # Persistent undo
325
+ [._]*.un~
326
+
327
+ ### VisualStudioCode ###
328
+ .vscode/*
329
+ !.vscode/settings.json
330
+ !.vscode/tasks.json
331
+ !.vscode/launch.json
332
+ !.vscode/extensions.json
333
+
334
+ ### VisualStudioCode Patch ###
335
+ # Ignore all local history of files
336
+ .history
337
+
338
+ # End of https://www.gitignore.io/api/vim,osx,python,django,pycharm,komodoedit,elasticbeanstalk,visualstudiocode
339
+
340
+ # Ignore custom Docker compose DB data
341
+ .db
342
+
343
+ # Ignore PyCharm
344
+ .idea/
345
+
346
+ # Ignore local poetry settings
347
+ poetry.toml
348
+
349
+ # Ignore PyCharm idea folder
350
+ .idea
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2026, django-sqlitetrigger authors and community
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright
8
+ notice, this list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in the
12
+ documentation and/or other materials provided with the distribution.
13
+
14
+ * Neither the name of the copyright holder nor the names of its
15
+ contributors may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL AMBITION BE LIABLE FOR ANY
22
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-sqlitetrigger
3
+ Version: 0.1.0
4
+ Summary: SQLite trigger support integrated with Django models.
5
+ License-Expression: BSD-3-Clause
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.14
8
+ Requires-Dist: django>=6.0
9
+ Provides-Extra: dev
10
+ Requires-Dist: django-dynamic-fixture>=4.0; extra == 'dev'
11
+ Requires-Dist: pytest-django>=4.11; extra == 'dev'
12
+ Requires-Dist: pytest>=9.0; extra == 'dev'
13
+ Description-Content-Type: text/markdown
14
+
15
+ # django-sqlitetrigger
16
+
17
+ SQLite trigger support integrated with Django models.
18
+
19
+ Inspired by [django-pgtrigger](https://github.com/AmbitionEng/django-pgtrigger), this package provides declarative trigger support for SQLite-backed Django projects. I'm very grateful for the existence of `django-pgtrigger`, without which I don't think this package could exist.
20
+
21
+ ## AI disclosure
22
+
23
+ To be super clear: this project was built by prompting GitHub Copilot to modify `django-pgtrigger` for use with SQLite. I was in the process of doing something very similar manually, when it dawned on me that this is the ideal use for today's agentic AI coding systems.
24
+
25
+ The resulting package is usable by me for my purposes. As the license notes, it comes with no warranty or expectation that it'll solve your problems.
26
+
27
+ ## Quick start
28
+
29
+ Install and add `sqlitetrigger` to `INSTALLED_APPS`:
30
+
31
+ ```python
32
+ INSTALLED_APPS = [
33
+ "sqlitetrigger",
34
+ # ...
35
+ ]
36
+ ```
37
+
38
+ Add triggers to your models:
39
+
40
+ ```python
41
+ import sqlitetrigger
42
+
43
+ class ProtectedModel(models.Model):
44
+ """This model cannot be deleted!"""
45
+
46
+ class Meta:
47
+ triggers = [
48
+ sqlitetrigger.Protect(name="protect_deletes", operation=sqlitetrigger.Delete)
49
+ ]
50
+ ```
51
+
52
+ Install triggers:
53
+
54
+ ```bash
55
+ python manage.py sqlitetrigger install
56
+ ```
57
+
58
+ ## Built-in triggers
59
+
60
+ - **Protect** — prevent insert, update, or delete operations
61
+ - **ReadOnly** — prevent changes to specific fields
62
+ - **SoftDelete** — intercept deletes and set a field instead
63
+ - **FSM** — enforce valid field state transitions
64
+
65
+ ## Running tests
66
+
67
+ ```bash
68
+ uv run pytest
69
+ ```
@@ -0,0 +1,55 @@
1
+ # django-sqlitetrigger
2
+
3
+ SQLite trigger support integrated with Django models.
4
+
5
+ Inspired by [django-pgtrigger](https://github.com/AmbitionEng/django-pgtrigger), this package provides declarative trigger support for SQLite-backed Django projects. I'm very grateful for the existence of `django-pgtrigger`, without which I don't think this package could exist.
6
+
7
+ ## AI disclosure
8
+
9
+ To be super clear: this project was built by prompting GitHub Copilot to modify `django-pgtrigger` for use with SQLite. I was in the process of doing something very similar manually, when it dawned on me that this is the ideal use for today's agentic AI coding systems.
10
+
11
+ The resulting package is usable by me for my purposes. As the license notes, it comes with no warranty or expectation that it'll solve your problems.
12
+
13
+ ## Quick start
14
+
15
+ Install and add `sqlitetrigger` to `INSTALLED_APPS`:
16
+
17
+ ```python
18
+ INSTALLED_APPS = [
19
+ "sqlitetrigger",
20
+ # ...
21
+ ]
22
+ ```
23
+
24
+ Add triggers to your models:
25
+
26
+ ```python
27
+ import sqlitetrigger
28
+
29
+ class ProtectedModel(models.Model):
30
+ """This model cannot be deleted!"""
31
+
32
+ class Meta:
33
+ triggers = [
34
+ sqlitetrigger.Protect(name="protect_deletes", operation=sqlitetrigger.Delete)
35
+ ]
36
+ ```
37
+
38
+ Install triggers:
39
+
40
+ ```bash
41
+ python manage.py sqlitetrigger install
42
+ ```
43
+
44
+ ## Built-in triggers
45
+
46
+ - **Protect** — prevent insert, update, or delete operations
47
+ - **ReadOnly** — prevent changes to specific fields
48
+ - **SoftDelete** — intercept deletes and set a field instead
49
+ - **FSM** — enforce valid field state transitions
50
+
51
+ ## Running tests
52
+
53
+ ```bash
54
+ uv run pytest
55
+ ```
File without changes
@@ -0,0 +1,13 @@
1
+ from django.contrib import admin
2
+
3
+ from example.models import Book
4
+
5
+
6
+ class BookAdmin(admin.ModelAdmin):
7
+ model = Book
8
+ fields = ('title', 'pages', 'sheets')
9
+ readonly_fields = ('sheets',)
10
+
11
+
12
+ # Register your models here.
13
+ admin.site.register(Book, BookAdmin)
@@ -0,0 +1,5 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class ExampleConfig(AppConfig):
5
+ name = 'example'
@@ -0,0 +1,16 @@
1
+ """
2
+ ASGI config for example project.
3
+
4
+ It exposes the ASGI callable as a module-level variable named ``application``.
5
+
6
+ For more information on this file, see
7
+ https://docs.djangoproject.com/en/6.0/howto/deployment/asgi/
8
+ """
9
+
10
+ import os
11
+
12
+ from django.core.asgi import get_asgi_application
13
+
14
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'example.settings')
15
+
16
+ application = get_asgi_application()
@@ -0,0 +1,23 @@
1
+ # Generated by Django 6.0.2 on 2026-02-09 20:47
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ initial = True
9
+
10
+ dependencies = [
11
+ ]
12
+
13
+ operations = [
14
+ migrations.CreateModel(
15
+ name='Book',
16
+ fields=[
17
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18
+ ('title', models.CharField(max_length=100)),
19
+ ('pages', models.PositiveIntegerField()),
20
+ ('sheets', models.PositiveIntegerField(editable=False)),
21
+ ],
22
+ ),
23
+ ]
@@ -0,0 +1,18 @@
1
+ # Generated by Django 6.0.2 on 2026-02-10 14:57
2
+
3
+ import sqlitetrigger.migrations
4
+ from django.db import migrations
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('example', '0001_initial'),
11
+ ]
12
+
13
+ operations = [
14
+ sqlitetrigger.migrations.AddTrigger(
15
+ model_name='book',
16
+ trigger=sqlitetrigger.migrations.CompiledTrigger(drop_sql=['DROP TRIGGER IF EXISTS sqlitetrigger_example_book_sheets_update;'], install_sql=['CREATE TRIGGER sqlitetrigger_example_book_sheets_update\n AFTER UPDATE ON example_book\n FOR EACH ROW\nBEGIN\n UPDATE example_book SET sheets = new.pages / 2 WHERE id = new.id;\nEND;'], name='sheets_update'),
17
+ ),
18
+ ]
@@ -0,0 +1,18 @@
1
+ # Generated by Django 6.0.2 on 2026-02-10 15:06
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('example', '0002_book_sheets_update'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='book',
15
+ name='title',
16
+ field=models.CharField(max_length=200),
17
+ ),
18
+ ]
@@ -0,0 +1,22 @@
1
+ # Generated by Django 6.0.2 on 2026-02-10 15:21
2
+
3
+ import sqlitetrigger.migrations
4
+ from django.db import migrations
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('example', '0003_alter_book_title'),
11
+ ]
12
+
13
+ operations = [
14
+ sqlitetrigger.migrations.RemoveTrigger(
15
+ model_name='book',
16
+ name='sheets_update',
17
+ ),
18
+ sqlitetrigger.migrations.AddTrigger(
19
+ model_name='book',
20
+ trigger=sqlitetrigger.migrations.CompiledTrigger(drop_sql=['DROP TRIGGER IF EXISTS sqlitetrigger_example_book_sheets_update;'], install_sql=['CREATE TRIGGER sqlitetrigger_example_book_sheets_update\n AFTER UPDATE ON example_book\n FOR EACH ROW\nBEGIN\n UPDATE example_book SET sheets = ceil(new.pages / 2.0) WHERE id = new.id;\nEND;'], name='sheets_update'),
21
+ ),
22
+ ]
@@ -0,0 +1,21 @@
1
+ from django.db import models
2
+
3
+ from sqlitetrigger import Trigger, After, Update, Func
4
+
5
+ # Create your models here.
6
+ class Book(models.Model):
7
+ class Meta:
8
+ triggers = [
9
+ Trigger(
10
+ name='sheets_update',
11
+ when=After,
12
+ operation=Update,
13
+ func=Func("UPDATE {meta.db_table} SET {columns.sheets} = ceil(new.{columns.pages} / 2.0) WHERE {columns.id} = new.{columns.id};"),
14
+ )
15
+ ]
16
+ title = models.CharField(max_length=200)
17
+ pages = models.PositiveIntegerField()
18
+ sheets = models.PositiveIntegerField(editable=False)
19
+
20
+ def __str__(self):
21
+ return self.title