djangobible 0.0.13__tar.gz → 0.3.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 (46) hide show
  1. djangobible-0.3.0/PKG-INFO +270 -0
  2. djangobible-0.3.0/README.md +242 -0
  3. djangobible-0.3.0/djangobible/__init__.py +11 -0
  4. djangobible-0.3.0/djangobible/apps.py +11 -0
  5. djangobible-0.3.0/djangobible/fields.py +99 -0
  6. {djangobible-0.0.13 → djangobible-0.3.0}/djangobible/migrations/0001_initial.py +43 -42
  7. djangobible-0.3.0/djangobible/models.py +89 -0
  8. djangobible-0.3.0/djangobible/templatetags/__init__.py +0 -0
  9. djangobible-0.3.0/djangobible/templatetags/verse_tags.py +61 -0
  10. djangobible-0.3.0/djangobible/validators.py +30 -0
  11. djangobible-0.3.0/pyproject.toml +132 -0
  12. djangobible-0.0.13/.github/workflows/codeql-analysis.yml +0 -62
  13. djangobible-0.0.13/.github/workflows/django.yml +0 -37
  14. djangobible-0.0.13/.gitignore +0 -134
  15. djangobible-0.0.13/LICENSE +0 -21
  16. djangobible-0.0.13/PKG-INFO +0 -7
  17. djangobible-0.0.13/README.md +0 -34
  18. djangobible-0.0.13/__init__.py +0 -1
  19. djangobible-0.0.13/djangobible/__init__.py +0 -10
  20. djangobible-0.0.13/djangobible/apps.py +0 -5
  21. djangobible-0.0.13/djangobible/migrations/__init__.py +0 -1
  22. djangobible-0.0.13/djangobible/models.py +0 -62
  23. djangobible-0.0.13/djangobible/templatetags/verse_tags.py +0 -58
  24. djangobible-0.0.13/manage.py +0 -22
  25. djangobible-0.0.13/pyproject.toml +0 -37
  26. djangobible-0.0.13/pytest.ini +0 -5
  27. djangobible-0.0.13/requirements.txt +0 -6
  28. djangobible-0.0.13/setup.py +0 -34
  29. djangobible-0.0.13/test_django_app/__init__.py +0 -1
  30. djangobible-0.0.13/test_django_app/apps.py +0 -5
  31. djangobible-0.0.13/test_django_app/migrations/0001_initial.py +0 -31
  32. djangobible-0.0.13/test_django_app/migrations/__init__.py +0 -1
  33. djangobible-0.0.13/test_django_app/models.py +0 -11
  34. djangobible-0.0.13/test_django_app/templates/verse_tags.html +0 -19
  35. djangobible-0.0.13/test_django_app/tests/__init__.py +0 -1
  36. djangobible-0.0.13/test_django_app/tests/conftest.py +0 -142
  37. djangobible-0.0.13/test_django_app/tests/test_models.py +0 -81
  38. djangobible-0.0.13/test_django_app/tests/test_search.py +0 -20
  39. djangobible-0.0.13/test_django_app/tests/test_tags.py +0 -98
  40. djangobible-0.0.13/test_django_app/views.py +0 -11
  41. djangobible-0.0.13/test_django_project/__init__.py +0 -1
  42. djangobible-0.0.13/test_django_project/asgi.py +0 -16
  43. djangobible-0.0.13/test_django_project/settings.py +0 -117
  44. djangobible-0.0.13/test_django_project/urls.py +0 -25
  45. djangobible-0.0.13/test_django_project/wsgi.py +0 -16
  46. {djangobible-0.0.13/djangobible/templatetags → djangobible-0.3.0/djangobible/migrations}/__init__.py +0 -0
@@ -0,0 +1,270 @@
1
+ Metadata-Version: 2.4
2
+ Name: djangobible
3
+ Version: 0.3.0
4
+ Author: Nathan Patton
5
+ Author-email: Nathan Patton <npatton@gmail.com>
6
+ License-Expression: MIT
7
+ Classifier: Framework :: Django
8
+ Classifier: Intended Audience :: Information Technology
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
+ Classifier: Topic :: Software Development :: Libraries
13
+ Classifier: Topic :: Software Development
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: License :: OSI Approved :: MIT License
23
+ Requires-Dist: django>=4.2.0
24
+ Requires-Dist: pythonbible==0.14.0
25
+ Requires-Python: >=3.10
26
+ Project-URL: Source, https://github.com/avendesora/djangobible
27
+ Description-Content-Type: text/markdown
28
+
29
+ # djangobible
30
+
31
+ The djangobible library is a Django app that wraps the [pythonbible](https://github.com/avendesora/pythonbible) library and provides models, managers, and other tools to easily index an object by a scripture reference.
32
+
33
+ <table>
34
+ <tr>
35
+ <td>Latest Version</td>
36
+ <td>
37
+ <a href="https://pypi.org/project/djangobible/"><img src="https://img.shields.io/pypi/v/djangobible?color=gold&logo=pypi&logoColor=lightgray"></a>
38
+ <img src="https://img.shields.io/pypi/dm/djangobible?color=gold">
39
+ </td>
40
+ </tr>
41
+ <tr>
42
+ <td>License</td>
43
+ <td><a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-orange.svg"></a></td>
44
+ </tr>
45
+ <tr>
46
+ <td>Tests</td>
47
+ <td>
48
+ <img src="https://github.com/avendesora/djangobible/actions/workflows/tests.yml/badge.svg">
49
+ <img src="https://github.com/avendesora/djangobible/workflows/Django%20CI/badge.svg"><br/>
50
+ <a href="https://app.codacy.com/gh/avendesora/djangobible/coverage/dashboard"><img src="https://app.codacy.com/project/badge/Coverage/83a28131bf6642ed9e439344122686fc"></a>
51
+ </td>
52
+ </tr>
53
+ <tr>
54
+ <td>Code Quality</td>
55
+ <td>
56
+ <img src="https://github.com/avendesora/djangobible/workflows/CodeQL/badge.svg">
57
+ <a href="https://app.codacy.com/gh/avendesora/djangobible?utm_source=github.com&utm_medium=referral&utm_content=avendesora/djangobible&utm_campaign=Badge_Grade_Settings"><img src="https://api.codacy.com/project/badge/Grade/ca34603bdaf8446ba288430b69092093"></a><br/>
58
+ <a href="https://results.pre-commit.ci/latest/github/avendesora/djangobible/main"><img src="https://results.pre-commit.ci/badge/github/avendesora/djangobible/main.svg"></a>
59
+ <a href="https://github.com/psf/black"><img src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
60
+ </td>
61
+ </tr>
62
+ <tr>
63
+ <td>Supported Python/Django Versions</td>
64
+ <td>
65
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue?logo=python&logoColor=lightgray"></a><br />
66
+ <a href="https://www.djangoproject.com/download/"><img src="https://img.shields.io/badge/Django-4.2%20%7C%205.0%20%7C%205.1%20%7C%205.2-blue"></a><br />
67
+ </td>
68
+ </tr>
69
+ </table>
70
+
71
+ ## Installation
72
+
73
+ Pip install the djangobible library.
74
+
75
+ ```shell script
76
+ pip install djangobible
77
+ ```
78
+
79
+ Add djangobible to your Django project's ``INSTALLED_APPS`` setting:
80
+
81
+ ```python
82
+ INSTALLED_APPS = [
83
+ ..., # other apps
84
+ "djangobible",
85
+ ]
86
+ ```
87
+
88
+ Run the django migrations for djangobible
89
+
90
+ ```shell script
91
+ ./manage.py migrate djangobible
92
+ ```
93
+
94
+ ### Settings
95
+
96
+ There currently are no settings (other than INSTALLED_APPS) related to the djangobible project. In the future, it would be nice to have settings that determine things like the available versions of the Bible and the default version.
97
+
98
+ Also, once support is implemented for multiple locales and languages, there could be related settings for that functionality.
99
+
100
+ ## Features
101
+
102
+ The djangobible library is a complete wrapper for the [pythonbible](https://github.com/avendesora/pythonbible) library, so importing the djangobible library as:
103
+
104
+ ```python
105
+ import djangobible as bible
106
+ ```
107
+
108
+ will provide all the same functionality as importing the pythonbible library as:
109
+
110
+ ```python
111
+ import pythonbible as bible
112
+ ```
113
+
114
+ This includes features such as:
115
+ - Searching text for Scripture references
116
+ - Converting a normalized scripture reference into a list of integer verse ids
117
+ - Converting a list of verse id integers into a list of normalized scripture references
118
+ - Converting a list of normalized scripture references into a formatted string scripture reference
119
+ - Retrieving the Biblical text (in one or more open-source or public domain versions) for a given verse ID integer
120
+
121
+ For more information, see the [pythonbible documentation](https://github.com/avendesora/pythonbible).
122
+
123
+ In addition, the djangobible library includes the following features:
124
+
125
+ ### Template Tags
126
+
127
+ There are currently two template tags provided by the djangobible library: ``verse_reference`` and ``verse_text``.
128
+
129
+ #### verse_reference
130
+
131
+ The ``verse_reference`` template tag, given a verse ID and a Bible version, returns the appropriate Scripture reference string.
132
+
133
+ For example, given ``verse_id = 1001001`` and ``version = djangobible.Version.KING_JAMES``, the following snippet from a Django template:
134
+
135
+ ```html
136
+ {% load verse_tags %}
137
+ ...
138
+ {% verse_reference verse_id version=version %}
139
+ ```
140
+
141
+ would display:
142
+
143
+ ```
144
+ Genesis 1:1
145
+ ```
146
+
147
+ The version parameter is optional, and the current default is King James, though that will ideally be configurable in the future.
148
+
149
+ There is another optional parameter, ``full_title``, which is a boolean flag to determine whether to display the long version or the short version of the book of the Bible title. It defaults to ``False``, which displays the short version. For example, given ``verse_id = 1001001`` and ``version = djangobible.Version.KING_JAMES`` and ``full_title = True``, the following snippet from a Django template:
150
+
151
+ ```html
152
+ {% load verse_tags %}
153
+ ...
154
+ {% verse_reference verse_id version=version full_title=full_title %}
155
+ ```
156
+
157
+ would display:
158
+
159
+ ```
160
+ The First Book of Moses, called Genesis 1:1
161
+ ```
162
+
163
+ #### verse_text
164
+
165
+ The ``verse_text`` template tag, given a verse ID and a Bible version, returns the appropriate text of that Bible verse.
166
+
167
+ For example, given ``verse_id = 1001001`` and ``version = djangobible.Version.KING_JAMES``, the following snippet from a Django template:
168
+
169
+ ```html
170
+ {% load verse_tags %}
171
+ ...
172
+ {% verse_text verse_id %}
173
+ ```
174
+
175
+ would display:
176
+
177
+ ```
178
+ In the beginning God created the heaven and the earth.
179
+ ```
180
+
181
+ The version parameter is optional, and the current default is King James, though that will ideally be configurable in the future.
182
+
183
+ ### One-to-many style relationships between verses and Django models
184
+
185
+ For situations where an instance of a Django model needs to be associated with a single verse, that Django model can have a field of type ``VerseField``.
186
+
187
+ For example:
188
+
189
+ ```python
190
+ from django.db import models
191
+
192
+ import djangobible as bible
193
+
194
+
195
+ class MyModel(models.Model):
196
+ ... # other fields
197
+
198
+ verse = bible.VerseField()
199
+ ```
200
+
201
+ The underlying implementation of ``VerseField`` is an ``IntegerField`` which stores the verse ID of the associated verse.
202
+
203
+ Having this custom field type provides several benefits:
204
+ - The Django admin (and Wagtail) form contains a text field for the verse rather than an integer field and allows the user to enter the Scripture reference text rather than the verse id, which they may not know. It then validates that text to ensure it references one, and only one, verse.
205
+ - As just mentioned, this allows for validation, not only that the value is one, and only one, verse id, but that it is also a valid verse id (i.e. that it represents a book, chapter, and verse of the Bible that actually exists).
206
+ - More readable query filters (e.g. ``MyModel.objects.filter(verse=1001001)`` is valid, but so is ``MyModel.objects.filter(verse="Genesis 1:1")``).
207
+
208
+ You can set the verse field with either the int verse ID or the string reference:
209
+
210
+ ```python
211
+ my_object = MyModel.objects.create(name="my object")
212
+ my_object.verse = 1001001
213
+ my_object.save()
214
+ ```
215
+
216
+ or
217
+
218
+ ```python
219
+ my_object = MyModel.objects.create(name="my object")
220
+ my_object.verse = "Genesis 1:1"
221
+ my_object.save()
222
+ ```
223
+
224
+ You can filter the objects in the query set by either the int verse ID or the string reference:
225
+
226
+ ```python
227
+ MyModel.objects.filter(verse=1001001)
228
+ ```
229
+
230
+ or
231
+
232
+ ```python
233
+ MyModel.objects.filter(verse="Genesis 1:1")
234
+ ```
235
+
236
+ In any of the above examples, if the verse is not a valid verse ID integer or string reference for a single verse, then a ``ValidationError`` will be raised.
237
+
238
+ ### Many-to-many style relationships between verses and Django models
239
+
240
+ > **WARNING**: This is still a work in progress, and this functionality does not yet exist in a stable form.
241
+
242
+ There are situations where an instance of a Django model needs to be associated with multiple verses. The current intended solution, inspired by the [django-taggit](https://github.com/jazzband/django-taggit) library, is to implement this feature in such a way that you would add this relationship to your model like:
243
+
244
+ ```python
245
+ from django.db import models
246
+
247
+ import djangobible as bible
248
+
249
+
250
+ class MyModel(models.Model):
251
+ ... # other fields
252
+
253
+ verses = bible.VerseManager()
254
+ ```
255
+
256
+ Then you could add, remove, and reference those verses with something like:
257
+
258
+ ```
259
+ >>> my_object = MyModel.objects.create(name="My Object")
260
+ >>> my_object.verses.add("Genesis 1:1-3")
261
+ >>> my_object.verses.all()
262
+ [<Verse: Genesis 1:1>, <Verse: Genesis 1:2>, <Verse: Genesis 1:3>]
263
+ >>> my_object.verses.remove("Genesis 1:2")
264
+ >>> my_object.verses.all()
265
+ [<Verse: Genesis 1:1>, <Verse: Genesis 1:3>]
266
+ >>> MyModel.objects.filter(verses__in=[1001001])
267
+ [<MyModel: My Object>]
268
+ ```
269
+
270
+ Ideally, the form field would be a text field where the user could enter a list of Scripture references (e.g. "Genesis 1:1,3-10;Psalm 119;Luke 2:1-18;John 3:16")
@@ -0,0 +1,242 @@
1
+ # djangobible
2
+
3
+ The djangobible library is a Django app that wraps the [pythonbible](https://github.com/avendesora/pythonbible) library and provides models, managers, and other tools to easily index an object by a scripture reference.
4
+
5
+ <table>
6
+ <tr>
7
+ <td>Latest Version</td>
8
+ <td>
9
+ <a href="https://pypi.org/project/djangobible/"><img src="https://img.shields.io/pypi/v/djangobible?color=gold&logo=pypi&logoColor=lightgray"></a>
10
+ <img src="https://img.shields.io/pypi/dm/djangobible?color=gold">
11
+ </td>
12
+ </tr>
13
+ <tr>
14
+ <td>License</td>
15
+ <td><a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-orange.svg"></a></td>
16
+ </tr>
17
+ <tr>
18
+ <td>Tests</td>
19
+ <td>
20
+ <img src="https://github.com/avendesora/djangobible/actions/workflows/tests.yml/badge.svg">
21
+ <img src="https://github.com/avendesora/djangobible/workflows/Django%20CI/badge.svg"><br/>
22
+ <a href="https://app.codacy.com/gh/avendesora/djangobible/coverage/dashboard"><img src="https://app.codacy.com/project/badge/Coverage/83a28131bf6642ed9e439344122686fc"></a>
23
+ </td>
24
+ </tr>
25
+ <tr>
26
+ <td>Code Quality</td>
27
+ <td>
28
+ <img src="https://github.com/avendesora/djangobible/workflows/CodeQL/badge.svg">
29
+ <a href="https://app.codacy.com/gh/avendesora/djangobible?utm_source=github.com&utm_medium=referral&utm_content=avendesora/djangobible&utm_campaign=Badge_Grade_Settings"><img src="https://api.codacy.com/project/badge/Grade/ca34603bdaf8446ba288430b69092093"></a><br/>
30
+ <a href="https://results.pre-commit.ci/latest/github/avendesora/djangobible/main"><img src="https://results.pre-commit.ci/badge/github/avendesora/djangobible/main.svg"></a>
31
+ <a href="https://github.com/psf/black"><img src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
32
+ </td>
33
+ </tr>
34
+ <tr>
35
+ <td>Supported Python/Django Versions</td>
36
+ <td>
37
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue?logo=python&logoColor=lightgray"></a><br />
38
+ <a href="https://www.djangoproject.com/download/"><img src="https://img.shields.io/badge/Django-4.2%20%7C%205.0%20%7C%205.1%20%7C%205.2-blue"></a><br />
39
+ </td>
40
+ </tr>
41
+ </table>
42
+
43
+ ## Installation
44
+
45
+ Pip install the djangobible library.
46
+
47
+ ```shell script
48
+ pip install djangobible
49
+ ```
50
+
51
+ Add djangobible to your Django project's ``INSTALLED_APPS`` setting:
52
+
53
+ ```python
54
+ INSTALLED_APPS = [
55
+ ..., # other apps
56
+ "djangobible",
57
+ ]
58
+ ```
59
+
60
+ Run the django migrations for djangobible
61
+
62
+ ```shell script
63
+ ./manage.py migrate djangobible
64
+ ```
65
+
66
+ ### Settings
67
+
68
+ There currently are no settings (other than INSTALLED_APPS) related to the djangobible project. In the future, it would be nice to have settings that determine things like the available versions of the Bible and the default version.
69
+
70
+ Also, once support is implemented for multiple locales and languages, there could be related settings for that functionality.
71
+
72
+ ## Features
73
+
74
+ The djangobible library is a complete wrapper for the [pythonbible](https://github.com/avendesora/pythonbible) library, so importing the djangobible library as:
75
+
76
+ ```python
77
+ import djangobible as bible
78
+ ```
79
+
80
+ will provide all the same functionality as importing the pythonbible library as:
81
+
82
+ ```python
83
+ import pythonbible as bible
84
+ ```
85
+
86
+ This includes features such as:
87
+ - Searching text for Scripture references
88
+ - Converting a normalized scripture reference into a list of integer verse ids
89
+ - Converting a list of verse id integers into a list of normalized scripture references
90
+ - Converting a list of normalized scripture references into a formatted string scripture reference
91
+ - Retrieving the Biblical text (in one or more open-source or public domain versions) for a given verse ID integer
92
+
93
+ For more information, see the [pythonbible documentation](https://github.com/avendesora/pythonbible).
94
+
95
+ In addition, the djangobible library includes the following features:
96
+
97
+ ### Template Tags
98
+
99
+ There are currently two template tags provided by the djangobible library: ``verse_reference`` and ``verse_text``.
100
+
101
+ #### verse_reference
102
+
103
+ The ``verse_reference`` template tag, given a verse ID and a Bible version, returns the appropriate Scripture reference string.
104
+
105
+ For example, given ``verse_id = 1001001`` and ``version = djangobible.Version.KING_JAMES``, the following snippet from a Django template:
106
+
107
+ ```html
108
+ {% load verse_tags %}
109
+ ...
110
+ {% verse_reference verse_id version=version %}
111
+ ```
112
+
113
+ would display:
114
+
115
+ ```
116
+ Genesis 1:1
117
+ ```
118
+
119
+ The version parameter is optional, and the current default is King James, though that will ideally be configurable in the future.
120
+
121
+ There is another optional parameter, ``full_title``, which is a boolean flag to determine whether to display the long version or the short version of the book of the Bible title. It defaults to ``False``, which displays the short version. For example, given ``verse_id = 1001001`` and ``version = djangobible.Version.KING_JAMES`` and ``full_title = True``, the following snippet from a Django template:
122
+
123
+ ```html
124
+ {% load verse_tags %}
125
+ ...
126
+ {% verse_reference verse_id version=version full_title=full_title %}
127
+ ```
128
+
129
+ would display:
130
+
131
+ ```
132
+ The First Book of Moses, called Genesis 1:1
133
+ ```
134
+
135
+ #### verse_text
136
+
137
+ The ``verse_text`` template tag, given a verse ID and a Bible version, returns the appropriate text of that Bible verse.
138
+
139
+ For example, given ``verse_id = 1001001`` and ``version = djangobible.Version.KING_JAMES``, the following snippet from a Django template:
140
+
141
+ ```html
142
+ {% load verse_tags %}
143
+ ...
144
+ {% verse_text verse_id %}
145
+ ```
146
+
147
+ would display:
148
+
149
+ ```
150
+ In the beginning God created the heaven and the earth.
151
+ ```
152
+
153
+ The version parameter is optional, and the current default is King James, though that will ideally be configurable in the future.
154
+
155
+ ### One-to-many style relationships between verses and Django models
156
+
157
+ For situations where an instance of a Django model needs to be associated with a single verse, that Django model can have a field of type ``VerseField``.
158
+
159
+ For example:
160
+
161
+ ```python
162
+ from django.db import models
163
+
164
+ import djangobible as bible
165
+
166
+
167
+ class MyModel(models.Model):
168
+ ... # other fields
169
+
170
+ verse = bible.VerseField()
171
+ ```
172
+
173
+ The underlying implementation of ``VerseField`` is an ``IntegerField`` which stores the verse ID of the associated verse.
174
+
175
+ Having this custom field type provides several benefits:
176
+ - The Django admin (and Wagtail) form contains a text field for the verse rather than an integer field and allows the user to enter the Scripture reference text rather than the verse id, which they may not know. It then validates that text to ensure it references one, and only one, verse.
177
+ - As just mentioned, this allows for validation, not only that the value is one, and only one, verse id, but that it is also a valid verse id (i.e. that it represents a book, chapter, and verse of the Bible that actually exists).
178
+ - More readable query filters (e.g. ``MyModel.objects.filter(verse=1001001)`` is valid, but so is ``MyModel.objects.filter(verse="Genesis 1:1")``).
179
+
180
+ You can set the verse field with either the int verse ID or the string reference:
181
+
182
+ ```python
183
+ my_object = MyModel.objects.create(name="my object")
184
+ my_object.verse = 1001001
185
+ my_object.save()
186
+ ```
187
+
188
+ or
189
+
190
+ ```python
191
+ my_object = MyModel.objects.create(name="my object")
192
+ my_object.verse = "Genesis 1:1"
193
+ my_object.save()
194
+ ```
195
+
196
+ You can filter the objects in the query set by either the int verse ID or the string reference:
197
+
198
+ ```python
199
+ MyModel.objects.filter(verse=1001001)
200
+ ```
201
+
202
+ or
203
+
204
+ ```python
205
+ MyModel.objects.filter(verse="Genesis 1:1")
206
+ ```
207
+
208
+ In any of the above examples, if the verse is not a valid verse ID integer or string reference for a single verse, then a ``ValidationError`` will be raised.
209
+
210
+ ### Many-to-many style relationships between verses and Django models
211
+
212
+ > **WARNING**: This is still a work in progress, and this functionality does not yet exist in a stable form.
213
+
214
+ There are situations where an instance of a Django model needs to be associated with multiple verses. The current intended solution, inspired by the [django-taggit](https://github.com/jazzband/django-taggit) library, is to implement this feature in such a way that you would add this relationship to your model like:
215
+
216
+ ```python
217
+ from django.db import models
218
+
219
+ import djangobible as bible
220
+
221
+
222
+ class MyModel(models.Model):
223
+ ... # other fields
224
+
225
+ verses = bible.VerseManager()
226
+ ```
227
+
228
+ Then you could add, remove, and reference those verses with something like:
229
+
230
+ ```
231
+ >>> my_object = MyModel.objects.create(name="My Object")
232
+ >>> my_object.verses.add("Genesis 1:1-3")
233
+ >>> my_object.verses.all()
234
+ [<Verse: Genesis 1:1>, <Verse: Genesis 1:2>, <Verse: Genesis 1:3>]
235
+ >>> my_object.verses.remove("Genesis 1:2")
236
+ >>> my_object.verses.all()
237
+ [<Verse: Genesis 1:1>, <Verse: Genesis 1:3>]
238
+ >>> MyModel.objects.filter(verses__in=[1001001])
239
+ [<MyModel: My Object>]
240
+ ```
241
+
242
+ Ideally, the form field would be a text field where the user could enter a list of Scripture references (e.g. "Genesis 1:1,3-10;Psalm 119;Luke 2:1-18;John 3:16")
@@ -0,0 +1,11 @@
1
+ """djangobible python library.
2
+
3
+ djangobible lets you easily associate Django models with Bible Scripture references and
4
+ search accordingly.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ __version__ = "0.3.0"
10
+
11
+ from pythonbible import *
@@ -0,0 +1,11 @@
1
+ """The app config for the djangobible library."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from django.apps import AppConfig
6
+
7
+
8
+ class DjangoBibleConfig(AppConfig):
9
+ """The app config for the djangobible library."""
10
+
11
+ name: str = "djangobible"
@@ -0,0 +1,99 @@
1
+ """Custom Django model fields for djangobible."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+ from typing import Callable
7
+
8
+ import pythonbible as bible
9
+ from django import forms
10
+ from django.core.exceptions import ValidationError
11
+ from django.db import models
12
+ from django.utils.functional import cached_property
13
+
14
+ from djangobible.validators import validate_verse
15
+
16
+
17
+ class VerseField(models.Field):
18
+ """Custom Django model field to represent a Verse."""
19
+
20
+ def get_prep_value(self: VerseField, value: int | str | None) -> int:
21
+ """Validate and convert the value to a verse id int."""
22
+ if isinstance(value, str):
23
+ validate_verse(value)
24
+ value = bible.convert_references_to_verse_ids(
25
+ bible.get_references(value),
26
+ )[0]
27
+ elif isinstance(value, int) and not bible.is_valid_verse_id(value):
28
+ error_message = f"{value} is not a valid verse id."
29
+ raise ValidationError(error_message)
30
+
31
+ return super().get_prep_value(value)
32
+
33
+ @cached_property
34
+ def validators(self: VerseField) -> list[Callable]:
35
+ """Return the list of validators for this field."""
36
+ return [validate_verse]
37
+
38
+ def to_python(
39
+ self: VerseField,
40
+ value: int | str | None,
41
+ ) -> str | None:
42
+ """Convert the value into a text scripture reference."""
43
+ if value is None or isinstance(value, str):
44
+ return value
45
+
46
+ return bible.format_scripture_references(
47
+ bible.convert_verse_ids_to_references([value]),
48
+ )
49
+
50
+ def from_db_value(
51
+ self: VerseField,
52
+ value: int | str | None,
53
+ expression: Any,
54
+ connection: Any,
55
+ ) -> str | None:
56
+ """Convert the value from the DB."""
57
+ return self.to_python(value)
58
+
59
+ def formfield(
60
+ self: VerseField,
61
+ form_class: type[forms.Field] | None = None,
62
+ choices_form_class: type[forms.Field] | None = None,
63
+ **kwargs: Any,
64
+ ) -> forms.Field:
65
+ """Make sure the form field is a CharField."""
66
+ return super().formfield(
67
+ **{
68
+ "form_class": forms.CharField,
69
+ **kwargs,
70
+ },
71
+ )
72
+
73
+ def get_db_prep_save(
74
+ self: VerseField,
75
+ value: int | str | None,
76
+ **kwargs: Any,
77
+ ) -> int | None:
78
+ """Validate and convert the value to a verse id int before saving to the DB."""
79
+ if not value:
80
+ return None
81
+
82
+ if isinstance(value, str):
83
+ references = bible.get_references(value)
84
+
85
+ if verse_ids := bible.convert_references_to_verse_ids(references):
86
+ value = verse_ids[0]
87
+ else:
88
+ error_message = f"{value} does not contain a valid Scripture reference."
89
+ raise ValidationError(error_message)
90
+
91
+ if not bible.is_valid_verse_id(value):
92
+ error_message = f"{value} is not a valid verse id."
93
+ raise ValidationError(error_message)
94
+
95
+ return int(value)
96
+
97
+ def get_internal_type(self: VerseField) -> str:
98
+ """Return the internal type (IntegerField) for the VerseField field."""
99
+ return "IntegerField"