django-dynamic-initial-data 2.1.0__tar.gz → 3.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 (43) hide show
  1. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/LICENSE +20 -20
  2. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/MANIFEST.in +3 -2
  3. django_dynamic_initial_data-3.0.0/PKG-INFO +156 -0
  4. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/README.md +117 -117
  5. django_dynamic_initial_data-3.0.0/django_dynamic_initial_data.egg-info/PKG-INFO +156 -0
  6. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/django_dynamic_initial_data.egg-info/SOURCES.txt +4 -1
  7. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/django_dynamic_initial_data.egg-info/dependency_links.txt +0 -0
  8. django_dynamic_initial_data-3.0.0/django_dynamic_initial_data.egg-info/requires.txt +2 -0
  9. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/django_dynamic_initial_data.egg-info/top_level.txt +0 -0
  10. django_dynamic_initial_data-3.0.0/dynamic_initial_data/__init__.py +3 -0
  11. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/apps.py +6 -6
  12. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/base.py +0 -0
  13. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/exceptions.py +25 -25
  14. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/management/__init__.py +0 -0
  15. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/management/commands/__init__.py +0 -0
  16. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/management/commands/update_initial_data.py +23 -23
  17. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/migrations/0001_initial.py +30 -31
  18. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/migrations/__init__.py +0 -0
  19. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/models.py +24 -24
  20. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/tests/__init__.py +0 -0
  21. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/tests/base_tests.py +42 -42
  22. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/tests/fake_app_1/__init__.py +0 -0
  23. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/tests/fake_app_2/__init__.py +0 -0
  24. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/tests/fake_app_2/fixtures/__init__.py +0 -0
  25. django_dynamic_initial_data-3.0.0/dynamic_initial_data/tests/fake_app_2/fixtures/initial_data.py +3 -0
  26. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/tests/integration_tests.py +29 -29
  27. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/tests/management_command_tests.py +26 -26
  28. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/tests/migrations/0001_initial.py +46 -47
  29. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/tests/migrations/__init__.py +0 -0
  30. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/tests/mocks.py +24 -24
  31. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/dynamic_initial_data/tests/models.py +24 -24
  32. django_dynamic_initial_data-3.0.0/dynamic_initial_data/urls.py +1 -0
  33. django_dynamic_initial_data-3.0.0/dynamic_initial_data/version.py +1 -0
  34. django_dynamic_initial_data-3.0.0/requirements/requirements-testing.txt +4 -0
  35. django_dynamic_initial_data-3.0.0/requirements/requirements.txt +2 -0
  36. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/setup.cfg +0 -0
  37. {django-dynamic-initial-data-2.1.0 → django_dynamic_initial_data-3.0.0}/setup.py +18 -16
  38. django-dynamic-initial-data-2.1.0/PKG-INFO +0 -139
  39. django-dynamic-initial-data-2.1.0/django_dynamic_initial_data.egg-info/PKG-INFO +0 -139
  40. django-dynamic-initial-data-2.1.0/django_dynamic_initial_data.egg-info/requires.txt +0 -2
  41. django-dynamic-initial-data-2.1.0/dynamic_initial_data/__init__.py +0 -4
  42. django-dynamic-initial-data-2.1.0/dynamic_initial_data/tests/fake_app_2/fixtures/initial_data.py +0 -3
  43. django-dynamic-initial-data-2.1.0/dynamic_initial_data/version.py +0 -1
@@ -1,20 +1,20 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2014 Ambition
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy of
6
- this software and associated documentation files (the "Software"), to deal in
7
- the Software without restriction, including without limitation the rights to
8
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
- the Software, and to permit persons to whom the Software is furnished to do so,
10
- subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Ambition
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,2 +1,3 @@
1
- include README.md
2
- include LICENSE
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include requirements *
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-dynamic-initial-data
3
+ Version: 3.0.0
4
+ Summary: Dynamic initial data fixtures for Django apps
5
+ Home-page: https://github.com/ambitioninc/django-dynamic-initial-data
6
+ Author:
7
+ Author-email: opensource@ambition.com
8
+ License: MIT
9
+ Keywords: Django fixtures
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Framework :: Django
20
+ Classifier: Framework :: Django :: 5.2
21
+ Classifier: Framework :: Django :: 6.0
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: Django>=5.2
26
+ Requires-Dist: django-manager-utils>=4.0.0
27
+ Dynamic: author-email
28
+ Dynamic: classifier
29
+ Dynamic: description
30
+ Dynamic: description-content-type
31
+ Dynamic: home-page
32
+ Dynamic: keywords
33
+ Dynamic: license
34
+ Dynamic: license-file
35
+ Dynamic: requires-dist
36
+ Dynamic: requires-python
37
+ Dynamic: summary
38
+
39
+ [![Build Status](https://travis-ci.org/ambitioninc/django-dynamic-initial-data.png)](https://travis-ci.org/ambitioninc/django-dynamic-initial-data)
40
+
41
+ ## Django Initial Data
42
+
43
+ Django Dynamic Initial Data is a `django>=1.6` and *postgresql* only app that helps solve the problem of initializing data for apps with
44
+ dependencies and other conditional data. Rather than having static fixtures for each app, the initial data
45
+ can be created and updated dynamically. Furthermore, Django Dynamic Initial Data also handles when objects are
46
+ deleted from initial data, a feature that Django's initial data fixture system lacks.
47
+
48
+ ## Table of Contents
49
+
50
+ 1. [Installation] (#installation)
51
+ 2. [A Brief Overview] (#a-brief-overview)
52
+ 3. [Example] (#example)
53
+ 4. [Handling Deletions](#handling-deletions)
54
+
55
+ ## Installation
56
+ To install Django Dynamic Initial Data:
57
+
58
+ ```shell
59
+ pip install django-dynamic-initial-data
60
+ ```
61
+
62
+ Add Django Dynamic Initial Data to your `INSTALLED_APPS` to get started:
63
+
64
+ settings.py
65
+ ```python
66
+ INSTALLED_APPS = (
67
+ 'dynamic_initial_data',
68
+ )
69
+ ```
70
+
71
+ ## A Brief Overview
72
+
73
+ A management command `update_initial_data` is provided which will try to update all `INSTALLED_APPS`. This
74
+ command is intended to be called as part of the deployment process of your app. Any missing dependencies
75
+ will raise an `InitialDataMissingApp` exception and any circular dependencies will raise an
76
+ `InitialDataCircularDependency` exception.
77
+
78
+ Any app needing to define initial data needs a file called `initial_data.py` inside of a `fixtures`
79
+ directory. This will look like `{app_name}/fixtures/initial_data.py`. Don't forget to include
80
+ the `__init__.py` file in the fixtures directory. `initial_data.py` must define a class `InitialData`
81
+ that inherits from `BaseInitialData`.
82
+
83
+ When apps are being initialized, each `InitialData` class is instantiated and `update_initial_data` is called.
84
+ If `update_initial_data` is not implemented, then a `NotImplementedError` will be raised.
85
+
86
+ Any dependencies should be included in a list called `dependencies`. Each dependency is a string
87
+ of the app name as defined in `INSTALLED_APPS`.
88
+
89
+ ## Example:
90
+
91
+ ```python
92
+ from dynamic_initial_data.base import BaseInitialData
93
+
94
+ class InitialData(BaseInitialData):
95
+ dependencies = ['my_first_app', 'my.second.app']
96
+
97
+ def update_initial_data(self):
98
+ model_obj, created = TestModel.objects.upsert(int_field=5, defaults={'float_field': 2.0})
99
+
100
+ TestModel.objects.bulk_upsert([
101
+ TestModel(float_field=1.0, char_field='1', int_field=1),
102
+ TestModel(float_field=2.0, char_field='2', int_field=2),
103
+ TestModel(float_field=3.0, char_field='3', int_field=3),
104
+ ], ['int_field'], ['char_field'])
105
+ ```
106
+ In this example, the `update_initial_data` method will be called for `my_first_app` (following any dependencies first),
107
+ and then for `my.second.app`, before finally calling `update_initial_data` on this class. Again, this can be executed by calling
108
+
109
+ ```
110
+ python manage.py update_initial_data
111
+ ```
112
+
113
+ Similarly, to only initialize a single app, use
114
+
115
+ ```
116
+ python manage.py update_initial_data --app 'app_path'
117
+ ```
118
+
119
+ Documentation on using `upsert` and `bulk_upsert` can be found below:
120
+ - https://github.com/ambitioninc/django-manager-utils#upsert
121
+ - https://github.com/ambitioninc/django-manager-utils#bulk_upsert
122
+
123
+ ## Handling Deletions
124
+ One difficulty when specifying initial data in Django apps is the inability to deploy initial data to your project and then subsequently remove any initial data fixtures. If one removes an object in an initial_data.json file, Django does not handle its deletion next time it is deployed, which can cause headaches with lingering objects.
125
+
126
+ Django Dynamic Initial Data fixes this problem by allowing the user to either:
127
+
128
+ 1. Return all managed initial data objects as an array from the update_initial_data function.
129
+ 2. Explicitly register objects for deletion with the register_for_deletion(*model_objs) method.
130
+
131
+ Note that it is up to the user to be responsible for always registering every object every time, regardless if the object was updated or created by the initial data process. Doing this allows Django Dynamic Initial Data to remove any objects that were previosly managed. For example, assume you have an InitialData class that manages two users with the user names "hello" and "world".
132
+
133
+ ```python
134
+ from dynamic_initial_data.base import BaseInitialData
135
+
136
+ class InitialData(BaseInitialData):
137
+ def update_initial_data(self):
138
+ hello = Account.objects.get_or_create(name='hello')
139
+ world = Account.objects.get_or_create(name='world')
140
+ # register the accounts for deletion
141
+ self.register_for_deletion(hello, world)
142
+ ```
143
+
144
+ After this code is created, the initial data process now owns the "hello" and "world" account objects. If these objects are not registered for deletion in subsequent versions of the code, they will be deleted when the initial data process executes. For example, assume the first piece of code executed and then the user executed this piece of code:
145
+
146
+ ```python
147
+ from dynamic_initial_data.base import BaseInitialData
148
+
149
+ class InitialData(BaseInitialData):
150
+ def update_initial_data(self):
151
+ world = Account.objects.get_or_create(name='world')
152
+ # register the accounts for deletion
153
+ self.register_for_deletion(world)
154
+ ```
155
+
156
+ When this piece of code executes, the previous "hello" account would then be deleted since the initial data process no longer owns it. And don't worry, if it was already deleted by another process, the deletion will not throw an error.
@@ -1,118 +1,118 @@
1
- [![Build Status](https://travis-ci.org/ambitioninc/django-dynamic-initial-data.png)](https://travis-ci.org/ambitioninc/django-dynamic-initial-data)
2
-
3
- ## Django Initial Data
4
-
5
- Django Dynamic Initial Data is a `django>=1.6` and *postgresql* only app that helps solve the problem of initializing data for apps with
6
- dependencies and other conditional data. Rather than having static fixtures for each app, the initial data
7
- can be created and updated dynamically. Furthermore, Django Dynamic Initial Data also handles when objects are
8
- deleted from initial data, a feature that Django's initial data fixture system lacks.
9
-
10
- ## Table of Contents
11
-
12
- 1. [Installation] (#installation)
13
- 2. [A Brief Overview] (#a-brief-overview)
14
- 3. [Example] (#example)
15
- 4. [Handling Deletions](#handling-deletions)
16
-
17
- ## Installation
18
- To install Django Dynamic Initial Data:
19
-
20
- ```shell
21
- pip install django-dynamic-initial-data
22
- ```
23
-
24
- Add Django Dynamic Initial Data to your `INSTALLED_APPS` to get started:
25
-
26
- settings.py
27
- ```python
28
- INSTALLED_APPS = (
29
- 'dynamic_initial_data',
30
- )
31
- ```
32
-
33
- ## A Brief Overview
34
-
35
- A management command `update_initial_data` is provided which will try to update all `INSTALLED_APPS`. This
36
- command is intended to be called as part of the deployment process of your app. Any missing dependencies
37
- will raise an `InitialDataMissingApp` exception and any circular dependencies will raise an
38
- `InitialDataCircularDependency` exception.
39
-
40
- Any app needing to define initial data needs a file called `initial_data.py` inside of a `fixtures`
41
- directory. This will look like `{app_name}/fixtures/initial_data.py`. Don't forget to include
42
- the `__init__.py` file in the fixtures directory. `initial_data.py` must define a class `InitialData`
43
- that inherits from `BaseInitialData`.
44
-
45
- When apps are being initialized, each `InitialData` class is instantiated and `update_initial_data` is called.
46
- If `update_initial_data` is not implemented, then a `NotImplementedError` will be raised.
47
-
48
- Any dependencies should be included in a list called `dependencies`. Each dependency is a string
49
- of the app name as defined in `INSTALLED_APPS`.
50
-
51
- ## Example:
52
-
53
- ```python
54
- from dynamic_initial_data.base import BaseInitialData
55
-
56
- class InitialData(BaseInitialData):
57
- dependencies = ['my_first_app', 'my.second.app']
58
-
59
- def update_initial_data(self):
60
- model_obj, created = TestModel.objects.upsert(int_field=5, defaults={'float_field': 2.0})
61
-
62
- TestModel.objects.bulk_upsert([
63
- TestModel(float_field=1.0, char_field='1', int_field=1),
64
- TestModel(float_field=2.0, char_field='2', int_field=2),
65
- TestModel(float_field=3.0, char_field='3', int_field=3),
66
- ], ['int_field'], ['char_field'])
67
- ```
68
- In this example, the `update_initial_data` method will be called for `my_first_app` (following any dependencies first),
69
- and then for `my.second.app`, before finally calling `update_initial_data` on this class. Again, this can be executed by calling
70
-
71
- ```
72
- python manage.py update_initial_data
73
- ```
74
-
75
- Similarly, to only initialize a single app, use
76
-
77
- ```
78
- python manage.py update_initial_data --app 'app_path'
79
- ```
80
-
81
- Documentation on using `upsert` and `bulk_upsert` can be found below:
82
- - https://github.com/ambitioninc/django-manager-utils#upsert
83
- - https://github.com/ambitioninc/django-manager-utils#bulk_upsert
84
-
85
- ## Handling Deletions
86
- One difficulty when specifying initial data in Django apps is the inability to deploy initial data to your project and then subsequently remove any initial data fixtures. If one removes an object in an initial_data.json file, Django does not handle its deletion next time it is deployed, which can cause headaches with lingering objects.
87
-
88
- Django Dynamic Initial Data fixes this problem by allowing the user to either:
89
-
90
- 1. Return all managed initial data objects as an array from the update_initial_data function.
91
- 2. Explicitly register objects for deletion with the register_for_deletion(*model_objs) method.
92
-
93
- Note that it is up to the user to be responsible for always registering every object every time, regardless if the object was updated or created by the initial data process. Doing this allows Django Dynamic Initial Data to remove any objects that were previosly managed. For example, assume you have an InitialData class that manages two users with the user names "hello" and "world".
94
-
95
- ```python
96
- from dynamic_initial_data.base import BaseInitialData
97
-
98
- class InitialData(BaseInitialData):
99
- def update_initial_data(self):
100
- hello = Account.objects.get_or_create(name='hello')
101
- world = Account.objects.get_or_create(name='world')
102
- # register the accounts for deletion
103
- self.register_for_deletion(hello, world)
104
- ```
105
-
106
- After this code is created, the initial data process now owns the "hello" and "world" account objects. If these objects are not registered for deletion in subsequent versions of the code, they will be deleted when the initial data process executes. For example, assume the first piece of code executed and then the user executed this piece of code:
107
-
108
- ```python
109
- from dynamic_initial_data.base import BaseInitialData
110
-
111
- class InitialData(BaseInitialData):
112
- def update_initial_data(self):
113
- world = Account.objects.get_or_create(name='world')
114
- # register the accounts for deletion
115
- self.register_for_deletion(world)
116
- ```
117
-
1
+ [![Build Status](https://travis-ci.org/ambitioninc/django-dynamic-initial-data.png)](https://travis-ci.org/ambitioninc/django-dynamic-initial-data)
2
+
3
+ ## Django Initial Data
4
+
5
+ Django Dynamic Initial Data is a `django>=1.6` and *postgresql* only app that helps solve the problem of initializing data for apps with
6
+ dependencies and other conditional data. Rather than having static fixtures for each app, the initial data
7
+ can be created and updated dynamically. Furthermore, Django Dynamic Initial Data also handles when objects are
8
+ deleted from initial data, a feature that Django's initial data fixture system lacks.
9
+
10
+ ## Table of Contents
11
+
12
+ 1. [Installation] (#installation)
13
+ 2. [A Brief Overview] (#a-brief-overview)
14
+ 3. [Example] (#example)
15
+ 4. [Handling Deletions](#handling-deletions)
16
+
17
+ ## Installation
18
+ To install Django Dynamic Initial Data:
19
+
20
+ ```shell
21
+ pip install django-dynamic-initial-data
22
+ ```
23
+
24
+ Add Django Dynamic Initial Data to your `INSTALLED_APPS` to get started:
25
+
26
+ settings.py
27
+ ```python
28
+ INSTALLED_APPS = (
29
+ 'dynamic_initial_data',
30
+ )
31
+ ```
32
+
33
+ ## A Brief Overview
34
+
35
+ A management command `update_initial_data` is provided which will try to update all `INSTALLED_APPS`. This
36
+ command is intended to be called as part of the deployment process of your app. Any missing dependencies
37
+ will raise an `InitialDataMissingApp` exception and any circular dependencies will raise an
38
+ `InitialDataCircularDependency` exception.
39
+
40
+ Any app needing to define initial data needs a file called `initial_data.py` inside of a `fixtures`
41
+ directory. This will look like `{app_name}/fixtures/initial_data.py`. Don't forget to include
42
+ the `__init__.py` file in the fixtures directory. `initial_data.py` must define a class `InitialData`
43
+ that inherits from `BaseInitialData`.
44
+
45
+ When apps are being initialized, each `InitialData` class is instantiated and `update_initial_data` is called.
46
+ If `update_initial_data` is not implemented, then a `NotImplementedError` will be raised.
47
+
48
+ Any dependencies should be included in a list called `dependencies`. Each dependency is a string
49
+ of the app name as defined in `INSTALLED_APPS`.
50
+
51
+ ## Example:
52
+
53
+ ```python
54
+ from dynamic_initial_data.base import BaseInitialData
55
+
56
+ class InitialData(BaseInitialData):
57
+ dependencies = ['my_first_app', 'my.second.app']
58
+
59
+ def update_initial_data(self):
60
+ model_obj, created = TestModel.objects.upsert(int_field=5, defaults={'float_field': 2.0})
61
+
62
+ TestModel.objects.bulk_upsert([
63
+ TestModel(float_field=1.0, char_field='1', int_field=1),
64
+ TestModel(float_field=2.0, char_field='2', int_field=2),
65
+ TestModel(float_field=3.0, char_field='3', int_field=3),
66
+ ], ['int_field'], ['char_field'])
67
+ ```
68
+ In this example, the `update_initial_data` method will be called for `my_first_app` (following any dependencies first),
69
+ and then for `my.second.app`, before finally calling `update_initial_data` on this class. Again, this can be executed by calling
70
+
71
+ ```
72
+ python manage.py update_initial_data
73
+ ```
74
+
75
+ Similarly, to only initialize a single app, use
76
+
77
+ ```
78
+ python manage.py update_initial_data --app 'app_path'
79
+ ```
80
+
81
+ Documentation on using `upsert` and `bulk_upsert` can be found below:
82
+ - https://github.com/ambitioninc/django-manager-utils#upsert
83
+ - https://github.com/ambitioninc/django-manager-utils#bulk_upsert
84
+
85
+ ## Handling Deletions
86
+ One difficulty when specifying initial data in Django apps is the inability to deploy initial data to your project and then subsequently remove any initial data fixtures. If one removes an object in an initial_data.json file, Django does not handle its deletion next time it is deployed, which can cause headaches with lingering objects.
87
+
88
+ Django Dynamic Initial Data fixes this problem by allowing the user to either:
89
+
90
+ 1. Return all managed initial data objects as an array from the update_initial_data function.
91
+ 2. Explicitly register objects for deletion with the register_for_deletion(*model_objs) method.
92
+
93
+ Note that it is up to the user to be responsible for always registering every object every time, regardless if the object was updated or created by the initial data process. Doing this allows Django Dynamic Initial Data to remove any objects that were previosly managed. For example, assume you have an InitialData class that manages two users with the user names "hello" and "world".
94
+
95
+ ```python
96
+ from dynamic_initial_data.base import BaseInitialData
97
+
98
+ class InitialData(BaseInitialData):
99
+ def update_initial_data(self):
100
+ hello = Account.objects.get_or_create(name='hello')
101
+ world = Account.objects.get_or_create(name='world')
102
+ # register the accounts for deletion
103
+ self.register_for_deletion(hello, world)
104
+ ```
105
+
106
+ After this code is created, the initial data process now owns the "hello" and "world" account objects. If these objects are not registered for deletion in subsequent versions of the code, they will be deleted when the initial data process executes. For example, assume the first piece of code executed and then the user executed this piece of code:
107
+
108
+ ```python
109
+ from dynamic_initial_data.base import BaseInitialData
110
+
111
+ class InitialData(BaseInitialData):
112
+ def update_initial_data(self):
113
+ world = Account.objects.get_or_create(name='world')
114
+ # register the accounts for deletion
115
+ self.register_for_deletion(world)
116
+ ```
117
+
118
118
  When this piece of code executes, the previous "hello" account would then be deleted since the initial data process no longer owns it. And don't worry, if it was already deleted by another process, the deletion will not throw an error.
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-dynamic-initial-data
3
+ Version: 3.0.0
4
+ Summary: Dynamic initial data fixtures for Django apps
5
+ Home-page: https://github.com/ambitioninc/django-dynamic-initial-data
6
+ Author:
7
+ Author-email: opensource@ambition.com
8
+ License: MIT
9
+ Keywords: Django fixtures
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Framework :: Django
20
+ Classifier: Framework :: Django :: 5.2
21
+ Classifier: Framework :: Django :: 6.0
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: Django>=5.2
26
+ Requires-Dist: django-manager-utils>=4.0.0
27
+ Dynamic: author-email
28
+ Dynamic: classifier
29
+ Dynamic: description
30
+ Dynamic: description-content-type
31
+ Dynamic: home-page
32
+ Dynamic: keywords
33
+ Dynamic: license
34
+ Dynamic: license-file
35
+ Dynamic: requires-dist
36
+ Dynamic: requires-python
37
+ Dynamic: summary
38
+
39
+ [![Build Status](https://travis-ci.org/ambitioninc/django-dynamic-initial-data.png)](https://travis-ci.org/ambitioninc/django-dynamic-initial-data)
40
+
41
+ ## Django Initial Data
42
+
43
+ Django Dynamic Initial Data is a `django>=1.6` and *postgresql* only app that helps solve the problem of initializing data for apps with
44
+ dependencies and other conditional data. Rather than having static fixtures for each app, the initial data
45
+ can be created and updated dynamically. Furthermore, Django Dynamic Initial Data also handles when objects are
46
+ deleted from initial data, a feature that Django's initial data fixture system lacks.
47
+
48
+ ## Table of Contents
49
+
50
+ 1. [Installation] (#installation)
51
+ 2. [A Brief Overview] (#a-brief-overview)
52
+ 3. [Example] (#example)
53
+ 4. [Handling Deletions](#handling-deletions)
54
+
55
+ ## Installation
56
+ To install Django Dynamic Initial Data:
57
+
58
+ ```shell
59
+ pip install django-dynamic-initial-data
60
+ ```
61
+
62
+ Add Django Dynamic Initial Data to your `INSTALLED_APPS` to get started:
63
+
64
+ settings.py
65
+ ```python
66
+ INSTALLED_APPS = (
67
+ 'dynamic_initial_data',
68
+ )
69
+ ```
70
+
71
+ ## A Brief Overview
72
+
73
+ A management command `update_initial_data` is provided which will try to update all `INSTALLED_APPS`. This
74
+ command is intended to be called as part of the deployment process of your app. Any missing dependencies
75
+ will raise an `InitialDataMissingApp` exception and any circular dependencies will raise an
76
+ `InitialDataCircularDependency` exception.
77
+
78
+ Any app needing to define initial data needs a file called `initial_data.py` inside of a `fixtures`
79
+ directory. This will look like `{app_name}/fixtures/initial_data.py`. Don't forget to include
80
+ the `__init__.py` file in the fixtures directory. `initial_data.py` must define a class `InitialData`
81
+ that inherits from `BaseInitialData`.
82
+
83
+ When apps are being initialized, each `InitialData` class is instantiated and `update_initial_data` is called.
84
+ If `update_initial_data` is not implemented, then a `NotImplementedError` will be raised.
85
+
86
+ Any dependencies should be included in a list called `dependencies`. Each dependency is a string
87
+ of the app name as defined in `INSTALLED_APPS`.
88
+
89
+ ## Example:
90
+
91
+ ```python
92
+ from dynamic_initial_data.base import BaseInitialData
93
+
94
+ class InitialData(BaseInitialData):
95
+ dependencies = ['my_first_app', 'my.second.app']
96
+
97
+ def update_initial_data(self):
98
+ model_obj, created = TestModel.objects.upsert(int_field=5, defaults={'float_field': 2.0})
99
+
100
+ TestModel.objects.bulk_upsert([
101
+ TestModel(float_field=1.0, char_field='1', int_field=1),
102
+ TestModel(float_field=2.0, char_field='2', int_field=2),
103
+ TestModel(float_field=3.0, char_field='3', int_field=3),
104
+ ], ['int_field'], ['char_field'])
105
+ ```
106
+ In this example, the `update_initial_data` method will be called for `my_first_app` (following any dependencies first),
107
+ and then for `my.second.app`, before finally calling `update_initial_data` on this class. Again, this can be executed by calling
108
+
109
+ ```
110
+ python manage.py update_initial_data
111
+ ```
112
+
113
+ Similarly, to only initialize a single app, use
114
+
115
+ ```
116
+ python manage.py update_initial_data --app 'app_path'
117
+ ```
118
+
119
+ Documentation on using `upsert` and `bulk_upsert` can be found below:
120
+ - https://github.com/ambitioninc/django-manager-utils#upsert
121
+ - https://github.com/ambitioninc/django-manager-utils#bulk_upsert
122
+
123
+ ## Handling Deletions
124
+ One difficulty when specifying initial data in Django apps is the inability to deploy initial data to your project and then subsequently remove any initial data fixtures. If one removes an object in an initial_data.json file, Django does not handle its deletion next time it is deployed, which can cause headaches with lingering objects.
125
+
126
+ Django Dynamic Initial Data fixes this problem by allowing the user to either:
127
+
128
+ 1. Return all managed initial data objects as an array from the update_initial_data function.
129
+ 2. Explicitly register objects for deletion with the register_for_deletion(*model_objs) method.
130
+
131
+ Note that it is up to the user to be responsible for always registering every object every time, regardless if the object was updated or created by the initial data process. Doing this allows Django Dynamic Initial Data to remove any objects that were previosly managed. For example, assume you have an InitialData class that manages two users with the user names "hello" and "world".
132
+
133
+ ```python
134
+ from dynamic_initial_data.base import BaseInitialData
135
+
136
+ class InitialData(BaseInitialData):
137
+ def update_initial_data(self):
138
+ hello = Account.objects.get_or_create(name='hello')
139
+ world = Account.objects.get_or_create(name='world')
140
+ # register the accounts for deletion
141
+ self.register_for_deletion(hello, world)
142
+ ```
143
+
144
+ After this code is created, the initial data process now owns the "hello" and "world" account objects. If these objects are not registered for deletion in subsequent versions of the code, they will be deleted when the initial data process executes. For example, assume the first piece of code executed and then the user executed this piece of code:
145
+
146
+ ```python
147
+ from dynamic_initial_data.base import BaseInitialData
148
+
149
+ class InitialData(BaseInitialData):
150
+ def update_initial_data(self):
151
+ world = Account.objects.get_or_create(name='world')
152
+ # register the accounts for deletion
153
+ self.register_for_deletion(world)
154
+ ```
155
+
156
+ When this piece of code executes, the previous "hello" account would then be deleted since the initial data process no longer owns it. And don't worry, if it was already deleted by another process, the deletion will not throw an error.
@@ -13,6 +13,7 @@ dynamic_initial_data/apps.py
13
13
  dynamic_initial_data/base.py
14
14
  dynamic_initial_data/exceptions.py
15
15
  dynamic_initial_data/models.py
16
+ dynamic_initial_data/urls.py
16
17
  dynamic_initial_data/version.py
17
18
  dynamic_initial_data/management/__init__.py
18
19
  dynamic_initial_data/management/commands/__init__.py
@@ -30,4 +31,6 @@ dynamic_initial_data/tests/fake_app_2/__init__.py
30
31
  dynamic_initial_data/tests/fake_app_2/fixtures/__init__.py
31
32
  dynamic_initial_data/tests/fake_app_2/fixtures/initial_data.py
32
33
  dynamic_initial_data/tests/migrations/0001_initial.py
33
- dynamic_initial_data/tests/migrations/__init__.py
34
+ dynamic_initial_data/tests/migrations/__init__.py
35
+ requirements/requirements-testing.txt
36
+ requirements/requirements.txt
@@ -0,0 +1,2 @@
1
+ Django>=5.2
2
+ django-manager-utils>=4.0.0
@@ -0,0 +1,3 @@
1
+ # flake8: noqa
2
+ from .version import __version__
3
+
@@ -1,6 +1,6 @@
1
- from django.apps import AppConfig
2
-
3
-
4
- class DynamicInitialDataConfig(AppConfig):
5
- name = 'dynamic_initial_data'
6
- verbose_name = 'Django Dynamic Initial Data'
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class DynamicInitialDataConfig(AppConfig):
5
+ name = 'dynamic_initial_data'
6
+ verbose_name = 'Django Dynamic Initial Data'