django-migrate-sql-deux 0.2.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 (103) hide show
  1. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/django_migrate_sql_deux-0.2.0-py3.6.egg-info/PKG-INFO +294 -0
  2. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/django_migrate_sql_deux-0.2.0-py3.6.egg-info/SOURCES.txt +57 -0
  3. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/django_migrate_sql_deux-0.2.0-py3.6.egg-info/dependency_links.txt +1 -0
  4. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/django_migrate_sql_deux-0.2.0-py3.6.egg-info/not-zip-safe +1 -0
  5. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/django_migrate_sql_deux-0.2.0-py3.6.egg-info/top_level.txt +2 -0
  6. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/__init__.py +4 -0
  7. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/__pycache__/__init__.cpython-36.pyc +0 -0
  8. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/__pycache__/autodetector.cpython-36.pyc +0 -0
  9. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/__pycache__/config.cpython-36.pyc +0 -0
  10. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/__pycache__/graph.cpython-36.pyc +0 -0
  11. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/__pycache__/operations.cpython-36.pyc +0 -0
  12. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/autodetector.py +256 -0
  13. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/config.py +29 -0
  14. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/graph.py +122 -0
  15. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/management/__init__.py +0 -0
  16. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/management/__pycache__/__init__.cpython-36.pyc +0 -0
  17. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/management/commands/__init__.py +0 -0
  18. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/management/commands/__pycache__/__init__.cpython-36.pyc +0 -0
  19. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/management/commands/__pycache__/makemigrations.cpython-36.pyc +0 -0
  20. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/management/commands/makemigrations.py +138 -0
  21. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/migrate_sql/operations.py +197 -0
  22. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/__init__.py +0 -0
  23. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/__pycache__/__init__.cpython-36.pyc +0 -0
  24. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/__pycache__/manage.cpython-36.pyc +0 -0
  25. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/manage.py +13 -0
  26. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/__init__.py +1 -0
  27. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/__pycache__/__init__.cpython-36.pyc +0 -0
  28. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/__pycache__/apps.cpython-36.pyc +0 -0
  29. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/__pycache__/models.cpython-36.pyc +0 -0
  30. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/__pycache__/sql_config.cpython-36.pyc +0 -0
  31. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/__pycache__/test_migrations.cpython-36.pyc +0 -0
  32. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/__pycache__/test_utils.cpython-36.pyc +0 -0
  33. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/__pycache__/urls.cpython-36.pyc +0 -0
  34. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/apps.py +9 -0
  35. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations/0001_initial.py +23 -0
  36. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations/__init__.py +0 -0
  37. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations/__pycache__/0001_initial.cpython-36.pyc +0 -0
  38. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations/__pycache__/__init__.cpython-36.pyc +0 -0
  39. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_change/0001_initial.py +24 -0
  40. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_change/0002_auto_20151224_1827.py +20 -0
  41. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_change/__init__.py +0 -0
  42. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_change/__pycache__/0001_initial.cpython-36.pyc +0 -0
  43. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_change/__pycache__/0002_auto_20151224_1827.cpython-36.pyc +0 -0
  44. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_change/__pycache__/__init__.cpython-36.pyc +0 -0
  45. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_delete/0001_initial.py +23 -0
  46. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_delete/0002_auto_20160106_0947.py +32 -0
  47. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_delete/0003_auto_20160108_0048.py +30 -0
  48. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_delete/0004_auto_20160108_0048.py +42 -0
  49. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_delete/__init__.py +0 -0
  50. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_delete/__pycache__/0001_initial.cpython-36.pyc +0 -0
  51. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_delete/__pycache__/0002_auto_20160106_0947.cpython-36.pyc +0 -0
  52. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_delete/__pycache__/0003_auto_20160108_0048.cpython-36.pyc +0 -0
  53. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_delete/__pycache__/0004_auto_20160108_0048.cpython-36.pyc +0 -0
  54. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_delete/__pycache__/__init__.cpython-36.pyc +0 -0
  55. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_update/0001_initial.py +23 -0
  56. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_update/0002_auto_20160106_0947.py +32 -0
  57. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_update/__init__.py +0 -0
  58. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_update/__pycache__/0001_initial.cpython-36.pyc +0 -0
  59. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_update/__pycache__/0002_auto_20160106_0947.cpython-36.pyc +0 -0
  60. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_deps_update/__pycache__/__init__.cpython-36.pyc +0 -0
  61. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_recreate/0001_initial.py +24 -0
  62. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_recreate/0002_auto_20151224_1827.py +20 -0
  63. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_recreate/0003_auto_20160106_1038.py +20 -0
  64. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_recreate/__init__.py +0 -0
  65. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_recreate/__pycache__/0001_initial.cpython-36.pyc +0 -0
  66. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_recreate/__pycache__/0002_auto_20151224_1827.cpython-36.pyc +0 -0
  67. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_recreate/__pycache__/0003_auto_20160106_1038.cpython-36.pyc +0 -0
  68. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/migrations_recreate/__pycache__/__init__.cpython-36.pyc +0 -0
  69. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/models.py +13 -0
  70. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/sql_config.py +6 -0
  71. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/test_migrations.py +598 -0
  72. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/test_utils.py +35 -0
  73. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app/urls.py +13 -0
  74. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/__init__.py +1 -0
  75. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/__pycache__/__init__.cpython-36.pyc +0 -0
  76. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/__pycache__/apps.cpython-36.pyc +0 -0
  77. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/__pycache__/models.cpython-36.pyc +0 -0
  78. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/__pycache__/sql_config.cpython-36.pyc +0 -0
  79. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/__pycache__/urls.cpython-36.pyc +0 -0
  80. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/apps.py +6 -0
  81. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/migrations/__init__.py +0 -0
  82. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/migrations/__pycache__/__init__.cpython-36.pyc +0 -0
  83. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/migrations_deps_delete/0001_initial.py +19 -0
  84. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/migrations_deps_delete/0002_auto_20160108_0041.py +26 -0
  85. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/migrations_deps_delete/__init__.py +0 -0
  86. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/migrations_deps_delete/__pycache__/0001_initial.cpython-36.pyc +0 -0
  87. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/migrations_deps_delete/__pycache__/0002_auto_20160108_0041.cpython-36.pyc +0 -0
  88. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/migrations_deps_delete/__pycache__/__init__.cpython-36.pyc +0 -0
  89. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/migrations_deps_update/0001_initial.py +19 -0
  90. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/migrations_deps_update/__init__.py +0 -0
  91. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/migrations_deps_update/__pycache__/0001_initial.cpython-36.pyc +0 -0
  92. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/migrations_deps_update/__pycache__/__init__.cpython-36.pyc +0 -0
  93. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/models.py +0 -0
  94. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/sql_config.py +3 -0
  95. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_app2/urls.py +10 -0
  96. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_project/__init__.py +1 -0
  97. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_project/__pycache__/__init__.cpython-36.pyc +0 -0
  98. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_project/__pycache__/settings.cpython-36.pyc +0 -0
  99. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_project/__pycache__/urls.cpython-36.pyc +0 -0
  100. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_project/__pycache__/wsgi.cpython-36.pyc +0 -0
  101. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_project/settings.py +94 -0
  102. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_project/urls.py +10 -0
  103. Users/brunoalla/.virtualenvs/fes-django-migrate-sql/lib/python3.6/site-packages/tests/test_project/wsgi.py +14 -0
@@ -0,0 +1,294 @@
1
+ Metadata-Version: 1.1
2
+ Name: django-migrate-sql-deux
3
+ Version: 0.2.0
4
+ Summary: Migration support for raw SQL in Django
5
+ Home-page: https://github.com/festicket/django-migrate-sql
6
+ Author: Festicket
7
+ Author-email: dev@festicket.com
8
+ License: BSD
9
+ Description: django-migrate-sql-deux
10
+ =======================
11
+
12
+ .. note::
13
+
14
+ This package is a fork of the ``django-migrate-sql`` package, originally published
15
+ by Bogdan Klichuk. This package appears unmaintained, so we decided to start a fork
16
+ as we depended on it. Most of the code is from him.
17
+
18
+ |Build Status| |codecov.io|
19
+
20
+ Django Migrations support for raw SQL.
21
+
22
+ About
23
+ -----
24
+
25
+ This tool implements mechanism for managing changes to custom SQL
26
+ entities (functions, types, indices, triggers) using built-in migration
27
+ mechanism. Technically creates a sophistication layer on top of the
28
+ ``RunSQL`` Django operation.
29
+
30
+ What it does
31
+ ------------
32
+
33
+ - Makes maintaining your SQL functions, custom composite types, indices
34
+ and triggers easier.
35
+ - Structures SQL into configuration of **SQL items**, that are
36
+ identified by names and divided among apps, just like models.
37
+ - Automatically gathers and persists changes of your custom SQL into
38
+ migrations using ``makemigrations``.
39
+ - Properly executes backwards/forwards keeping integrity of database.
40
+ - Create -> Drop -> Recreate approach for changes to items that do not
41
+ support altering and require dropping and recreating.
42
+ - Dependencies system for SQL items, which solves the problem of
43
+ updating items, that rely on others (for example custom
44
+ types/functions that use other custom types), and require dropping
45
+ all dependency tree previously with further recreation.
46
+
47
+ What it does not
48
+ ----------------
49
+
50
+ - Does not parse SQL nor validate queries during ``makemigrations`` or
51
+ ``migrate`` because is database-agnostic. For this same reason
52
+ setting up proper dependencies is user's responsibility.
53
+ - Does not create ``ALTER`` queries for items that support this, for
54
+ example ``ALTER TYPE`` in Postgre SQL, because is database-agnostic.
55
+ In case your tools allow rolling all the changes through ``ALTER``
56
+ queries, you can consider not using this app **or** restructure
57
+ migrations manually after creation by nesting generated operations
58
+ into ```state_operations`` of
59
+ ``RunSQL`` <https://docs.djangoproject.com/en/1.8/ref/migration-operations/#runsql>`__
60
+ that does ``ALTER``.
61
+ - (**TODO**)During ``migrate`` does not restore full state of items for
62
+ analysis, thus does not notify about existing changes to schema that
63
+ are not migrated **nor** does not recognize circular dependencies
64
+ during migration execution.
65
+
66
+ Installation
67
+ ------------
68
+
69
+ Install from PyPi:
70
+
71
+ ::
72
+
73
+ $ pip install django-migrate-sql-deux
74
+
75
+ Add ``migrate_sql`` to ``INSTALLED_APPS``:
76
+
77
+ .. code:: python
78
+
79
+ INSTALLED_APPS = [
80
+ # ...
81
+ 'migrate_sql',
82
+ ]
83
+
84
+ App defines a custom ``makemigrations`` command, that inherits from
85
+ Django's core one, so in order ``migrate_sql`` app to kick in put it
86
+ after any other apps that redefine ``makemigrations`` command too.
87
+
88
+ Usage
89
+ -----
90
+
91
+ 1) Create ``sql_config.py`` module to root of a target app you want to
92
+ manage custom SQL for.
93
+
94
+ 2) Define SQL items in it (``sql_items``), for example:
95
+
96
+ .. code:: python
97
+
98
+ # PostgreSQL example.
99
+ # Let's define a simple function and let `migrate_sql` manage it's changes.
100
+
101
+ from migrate_sql.config import SQLItem
102
+
103
+ sql_items = [
104
+ SQLItem(
105
+ 'make_sum', # name of the item
106
+ 'create or replace function make_sum(a int, b int) returns int as $$ '
107
+ 'begin return a + b; end; '
108
+ '$$ language plpgsql;', # forward sql
109
+ reverse_sql='drop function make_sum(int, int);', # sql for removal
110
+ ),
111
+ ]
112
+
113
+ 3) Create migration ``./manage.py makemigrations``:
114
+
115
+ ::
116
+
117
+ Migrations for 'app_name':
118
+ 0002_auto_xxxx.py:
119
+ - Create SQL "make_sum"
120
+
121
+ You can take a look at content this generated:
122
+
123
+ .. code:: python
124
+
125
+ # -*- coding: utf-8 -*-
126
+ from __future__ import unicode_literals
127
+ from django.db import migrations, models
128
+ import migrate_sql.operations
129
+
130
+
131
+ class Migration(migrations.Migration):
132
+ dependencies = [
133
+ ('app_name', '0001_initial'),
134
+ ]
135
+ operations = [
136
+ migrate_sql.operations.CreateSQL(
137
+ name='make_sum',
138
+ sql='create or replace function make_sum(a int, b int) returns int as $$ begin return a + b; end; $$ language plpgsql;',
139
+ reverse_sql='drop function make_sum(int, int);',
140
+ ),
141
+ ]
142
+
143
+ 4) Execute migration ``./manage.py migrate``:
144
+
145
+ ::
146
+
147
+ Operations to perform:
148
+ Apply all migrations: app_name
149
+ Running migrations:
150
+ Rendering model states... DONE
151
+ Applying app_name.0002_xxxx... OK
152
+
153
+ Check result in ``./manage.py dbshell``:
154
+
155
+ ::
156
+
157
+ db_name=# select make_sum(12, 15);
158
+ make_sum
159
+ ----------
160
+ 27
161
+ (1 row)
162
+
163
+ Now, say, you want to change the function implementation so that it
164
+ takes a custom type as argument:
165
+
166
+ 5) Edit your ``sql_config.py``:
167
+
168
+ .. code:: python
169
+
170
+ # PostgreSQL example #2.
171
+ # Function and custom type.
172
+
173
+ from migrate_sql.config import SQLItem
174
+
175
+ sql_items = [
176
+ SQLItem(
177
+ 'make_sum', # name of the item
178
+ 'create or replace function make_sum(a mynum, b mynum) returns mynum as $$ '
179
+ 'begin return (a.num + b.num, 'result')::mynum; end; '
180
+ '$$ language plpgsql;', # forward sql
181
+ reverse_sql='drop function make_sum(mynum, mynum);', # sql for removal
182
+ # depends on `mynum` since takes it as argument. we won't be able to drop function
183
+ # without dropping `mynum` first.
184
+ dependencies=[('app_name', 'mynum')],
185
+ ),
186
+ SQLItem(
187
+ 'mynum' # name of the item
188
+ 'create type mynum as (num int, name varchar(20));', # forward sql
189
+ reverse_sql='drop type mynum;', # sql for removal
190
+ ),
191
+ ]
192
+
193
+ 6) Generate migration ``./manage.py makemigrations``:
194
+
195
+ ::
196
+
197
+ Migrations for 'app_name':
198
+ 0003_xxxx:
199
+ - Reverse alter SQL "make_sum"
200
+ - Create SQL "mynum"
201
+ - Alter SQL "make_sum"
202
+ - Alter SQL state "make_sum"
203
+
204
+ You can take a look at the content this generated:
205
+
206
+ .. code:: python
207
+
208
+ # -*- coding: utf-8 -*-
209
+ from __future__ import unicode_literals
210
+ from django.db import migrations, models
211
+ import migrate_sql.operations
212
+
213
+
214
+ class Migration(migrations.Migration):
215
+ dependencies = [
216
+ ('app_name', '0002_xxxx'),
217
+ ]
218
+ operations = [
219
+ migrate_sql.operations.ReverseAlterSQL(
220
+ name='make_sum',
221
+ sql='drop function make_sum(int, int);',
222
+ reverse_sql='create or replace function make_sum(a int, b int) returns int as $$ begin return a + b; end; $$ language plpgsql;',
223
+ ),
224
+ migrate_sql.operations.CreateSQL(
225
+ name='mynum',
226
+ sql='create type mynum as (num int, name varchar(20));',
227
+ reverse_sql='drop type mynum;',
228
+ ),
229
+ migrate_sql.operations.AlterSQL(
230
+ name='make_sum',
231
+ sql='create or replace function make_sum(a mynum, b mynum) returns mynum as $$ begin return (a.num + b.num, \'result\')::mynum; end; $$ language plpgsql;',
232
+ reverse_sql='drop function make_sum(mynum, mynum);',
233
+ ),
234
+ migrate_sql.operations.AlterSQLState(
235
+ name='make_sum',
236
+ add_dependencies=(('app_name', 'mynum'),),
237
+ ),
238
+ ]
239
+
240
+ ***NOTE:** Previous function is completely dropped before creation
241
+ because definition of it changed. ``CREATE OR REPLACE`` would create
242
+ another version of it, so ``DROP`` makes it clean.*
243
+
244
+ ***If you put ``replace=True`` as kwarg to an ``SQLItem`` definition, it
245
+ will NOT drop + create it, but just rerun forward SQL, which is
246
+ ``CREATE OR REPLACE`` in this example.***
247
+
248
+ 7) Execute migration ``./manage.py migrate``:
249
+
250
+ ::
251
+
252
+ Operations to perform:
253
+ Apply all migrations: app_name
254
+ Running migrations:
255
+ Rendering model states... DONE
256
+ Applying brands.0003_xxxx... OK
257
+
258
+ Check results:
259
+
260
+ ::
261
+
262
+ db_name=# select make_sum((5, 'a')::mynum, (3, 'b')::mynum);
263
+ make_sum
264
+ ------------
265
+ (8,result)
266
+ (1 row)
267
+
268
+ db_name=# select make_sum(12, 15);
269
+ ERROR: function make_sum(integer, integer) does not exist
270
+ LINE 1: select make_sum(12, 15);
271
+ ^
272
+ HINT: No function matches the given name and argument types. You might need to add explicit type casts.
273
+
274
+ For more examples see ``tests``.
275
+
276
+ Feel free to `open new
277
+ issues <https://github.com/klichukb/django-migrate-sql/issues>`__.
278
+
279
+ .. |Build Status| image:: https://travis-ci.org/klichukb/django-migrate-sql.svg?branch=master
280
+ :target: https://travis-ci.org/klichukb/django-migrate-sql
281
+ .. |codecov.io| image:: https://img.shields.io/codecov/c/github/klichukb/django-migrate-sql/master.svg
282
+ :target: https://codecov.io/github/klichukb/django-migrate-sql?branch=master
283
+
284
+ Platform: UNKNOWN
285
+ Classifier: Development Status :: 3 - Alpha
286
+ Classifier: Framework :: Django
287
+ Classifier: Intended Audience :: Developers
288
+ Classifier: License :: OSI Approved :: BSD License
289
+ Classifier: Natural Language :: English
290
+ Classifier: Programming Language :: Python :: 2.7
291
+ Classifier: Programming Language :: Python :: 3.3
292
+ Classifier: Programming Language :: Python :: 3.4
293
+ Classifier: Programming Language :: Python :: 3.5
294
+ Classifier: Programming Language :: Python :: 3.6
@@ -0,0 +1,57 @@
1
+ README.rst
2
+ setup.cfg
3
+ setup.py
4
+ django_migrate_sql_deux.egg-info/PKG-INFO
5
+ django_migrate_sql_deux.egg-info/SOURCES.txt
6
+ django_migrate_sql_deux.egg-info/dependency_links.txt
7
+ django_migrate_sql_deux.egg-info/not-zip-safe
8
+ django_migrate_sql_deux.egg-info/top_level.txt
9
+ migrate_sql/__init__.py
10
+ migrate_sql/autodetector.py
11
+ migrate_sql/config.py
12
+ migrate_sql/graph.py
13
+ migrate_sql/operations.py
14
+ migrate_sql/management/__init__.py
15
+ migrate_sql/management/commands/__init__.py
16
+ migrate_sql/management/commands/makemigrations.py
17
+ tests/__init__.py
18
+ tests/manage.py
19
+ tests/test_app/__init__.py
20
+ tests/test_app/apps.py
21
+ tests/test_app/models.py
22
+ tests/test_app/sql_config.py
23
+ tests/test_app/test_migrations.py
24
+ tests/test_app/test_utils.py
25
+ tests/test_app/urls.py
26
+ tests/test_app/migrations/0001_initial.py
27
+ tests/test_app/migrations/__init__.py
28
+ tests/test_app/migrations_change/0001_initial.py
29
+ tests/test_app/migrations_change/0002_auto_20151224_1827.py
30
+ tests/test_app/migrations_change/__init__.py
31
+ tests/test_app/migrations_deps_delete/0001_initial.py
32
+ tests/test_app/migrations_deps_delete/0002_auto_20160106_0947.py
33
+ tests/test_app/migrations_deps_delete/0003_auto_20160108_0048.py
34
+ tests/test_app/migrations_deps_delete/0004_auto_20160108_0048.py
35
+ tests/test_app/migrations_deps_delete/__init__.py
36
+ tests/test_app/migrations_deps_update/0001_initial.py
37
+ tests/test_app/migrations_deps_update/0002_auto_20160106_0947.py
38
+ tests/test_app/migrations_deps_update/__init__.py
39
+ tests/test_app/migrations_recreate/0001_initial.py
40
+ tests/test_app/migrations_recreate/0002_auto_20151224_1827.py
41
+ tests/test_app/migrations_recreate/0003_auto_20160106_1038.py
42
+ tests/test_app/migrations_recreate/__init__.py
43
+ tests/test_app2/__init__.py
44
+ tests/test_app2/apps.py
45
+ tests/test_app2/models.py
46
+ tests/test_app2/sql_config.py
47
+ tests/test_app2/urls.py
48
+ tests/test_app2/migrations/__init__.py
49
+ tests/test_app2/migrations_deps_delete/0001_initial.py
50
+ tests/test_app2/migrations_deps_delete/0002_auto_20160108_0041.py
51
+ tests/test_app2/migrations_deps_delete/__init__.py
52
+ tests/test_app2/migrations_deps_update/0001_initial.py
53
+ tests/test_app2/migrations_deps_update/__init__.py
54
+ tests/test_project/__init__.py
55
+ tests/test_project/settings.py
56
+ tests/test_project/urls.py
57
+ tests/test_project/wsgi.py
@@ -0,0 +1,4 @@
1
+ # -*- coding: utf-8 -*-
2
+ from __future__ import unicode_literals
3
+
4
+ __version__ = '0.2.0'
@@ -0,0 +1,256 @@
1
+ # -*- coding: utf-8 -*-
2
+ from __future__ import unicode_literals
3
+
4
+ from django.db.migrations.autodetector import MigrationAutodetector as DjangoMigrationAutodetector
5
+ from django.db.migrations.operations import RunSQL
6
+
7
+ from migrate_sql.operations import (AlterSQL, ReverseAlterSQL, CreateSQL, DeleteSQL, AlterSQLState)
8
+ from migrate_sql.graph import SQLStateGraph
9
+
10
+
11
+ class SQLBlob(object):
12
+ pass
13
+
14
+ # Dummy object used to identify django dependency as the one used by this tool only.
15
+ SQL_BLOB = SQLBlob()
16
+
17
+
18
+ def _sql_params(sql):
19
+ """
20
+ Identify `sql` as either SQL string or 2-tuple of SQL and params.
21
+ Same format as supported by Django's RunSQL operation for sql/reverse_sql.
22
+ """
23
+ params = None
24
+ if isinstance(sql, (list, tuple)):
25
+ elements = len(sql)
26
+ if elements == 2:
27
+ sql, params = sql
28
+ else:
29
+ raise ValueError("Expected a 2-tuple but got %d" % elements)
30
+ return sql, params
31
+
32
+
33
+ def is_sql_equal(sqls1, sqls2):
34
+ """
35
+ Find out equality of two SQL items.
36
+
37
+ See https://docs.djangoproject.com/en/1.8/ref/migration-operations/#runsql.
38
+ Args:
39
+ sqls1, sqls2: SQL items, have the same format as supported by Django's RunSQL operation.
40
+ Returns:
41
+ (bool) `True` if equal, otherwise `False`.
42
+ """
43
+ is_seq1 = isinstance(sqls1, (list, tuple))
44
+ is_seq2 = isinstance(sqls2, (list, tuple))
45
+
46
+ if not is_seq1:
47
+ sqls1 = (sqls1,)
48
+ if not is_seq2:
49
+ sqls2 = (sqls2,)
50
+
51
+ if len(sqls1) != len(sqls2):
52
+ return False
53
+
54
+ for sql1, sql2 in zip(sqls1, sqls2):
55
+ sql1, params1 = _sql_params(sql1)
56
+ sql2, params2 = _sql_params(sql2)
57
+ if sql1 != sql2 or params1 != params2:
58
+ return False
59
+ return True
60
+
61
+
62
+ class MigrationAutodetector(DjangoMigrationAutodetector):
63
+ """
64
+ Substitutes Django's MigrationAutodetector class, injecting SQL migrations logic.
65
+ """
66
+ def __init__(self, from_state, to_state, questioner=None, to_sql_graph=None):
67
+ super(MigrationAutodetector, self).__init__(from_state, to_state, questioner)
68
+ self.to_sql_graph = to_sql_graph
69
+ self.from_sql_graph = getattr(self.from_state, 'sql_state', None) or SQLStateGraph()
70
+ self.from_sql_graph.build_graph()
71
+ self._sql_operations = []
72
+
73
+ def assemble_changes(self, keys, resolve_keys, sql_state):
74
+ """
75
+ Accepts keys of SQL items available, sorts them and adds additional dependencies.
76
+ Uses graph of `sql_state` nodes to build `keys` and `resolve_keys` into sequence that
77
+ starts with leaves (items that have not dependents) and ends with roots.
78
+
79
+ Changes `resolve_keys` argument as dependencies are added to the result.
80
+
81
+ Args:
82
+ keys (list): List of migration keys, that are one of create/delete operations, and
83
+ dont require respective reverse operations.
84
+ resolve_keys (list): List of migration keys, that are changing existing items,
85
+ and may require respective reverse operations.
86
+ sql_sate (graph.SQLStateGraph): State of SQL items.
87
+ Returns:
88
+ (list) Sorted sequence of migration keys, enriched with dependencies.
89
+ """
90
+ result_keys = []
91
+ all_keys = keys | resolve_keys
92
+ for key in all_keys:
93
+ node = sql_state.node_map[key]
94
+ sql_item = sql_state.nodes[key]
95
+ ancs = node.ancestors()[:-1]
96
+ ancs.reverse()
97
+ pos = next((i for i, k in enumerate(result_keys) if k in ancs), len(result_keys))
98
+ result_keys.insert(pos, key)
99
+
100
+ if key in resolve_keys and not sql_item.replace:
101
+ # ancestors() and descendants() include key itself, need to cut it out.
102
+ descs = reversed(node.descendants()[:-1])
103
+ for desc in descs:
104
+ if desc not in all_keys and desc not in result_keys:
105
+ result_keys.insert(pos, desc)
106
+ # these items added may also need reverse operations.
107
+ resolve_keys.add(desc)
108
+ return result_keys
109
+
110
+ def add_sql_operation(self, app_label, sql_name, operation, dependencies):
111
+ """
112
+ Add SQL operation and register it to be used as dependency for further
113
+ sequential operations.
114
+ """
115
+ deps = [(dp[0], SQL_BLOB, dp[1], self._sql_operations.get(dp)) for dp in dependencies]
116
+
117
+ self.add_operation(app_label, operation, dependencies=deps)
118
+ self._sql_operations[(app_label, sql_name)] = operation
119
+
120
+ def _generate_reversed_sql(self, keys, changed_keys):
121
+ """
122
+ Generate reversed operations for changes, that require full rollback and creation.
123
+ """
124
+ for key in keys:
125
+ if key not in changed_keys:
126
+ continue
127
+ app_label, sql_name = key
128
+ old_item = self.from_sql_graph.nodes[key]
129
+ new_item = self.to_sql_graph.nodes[key]
130
+ if not old_item.reverse_sql or old_item.reverse_sql == RunSQL.noop or new_item.replace:
131
+ continue
132
+
133
+ # migrate backwards
134
+ operation = ReverseAlterSQL(sql_name, old_item.reverse_sql, reverse_sql=old_item.sql)
135
+ sql_deps = [n.key for n in self.from_sql_graph.node_map[key].children]
136
+ sql_deps.append(key)
137
+ self.add_sql_operation(app_label, sql_name, operation, sql_deps)
138
+
139
+ def _generate_sql(self, keys, changed_keys):
140
+ """
141
+ Generate forward operations for changing/creating SQL items.
142
+ """
143
+ for key in reversed(keys):
144
+ app_label, sql_name = key
145
+ new_item = self.to_sql_graph.nodes[key]
146
+ sql_deps = [n.key for n in self.to_sql_graph.node_map[key].parents]
147
+ reverse_sql = new_item.reverse_sql
148
+
149
+ if key in changed_keys:
150
+ operation_cls = AlterSQL
151
+ kwargs = {}
152
+ # in case of replace mode, AlterSQL will hold sql, reverse_sql and
153
+ # state_reverse_sql, the latter one will be used for building state forward
154
+ # instead of reverse_sql.
155
+ if new_item.replace:
156
+ kwargs['state_reverse_sql'] = reverse_sql
157
+ reverse_sql = self.from_sql_graph.nodes[key].sql
158
+ else:
159
+ operation_cls = CreateSQL
160
+ kwargs = {'dependencies': list(sql_deps)}
161
+
162
+ operation = operation_cls(
163
+ sql_name, new_item.sql, reverse_sql=reverse_sql, **kwargs)
164
+ sql_deps.append(key)
165
+ self.add_sql_operation(app_label, sql_name, operation, sql_deps)
166
+
167
+ def _generate_altered_sql_dependencies(self, dep_changed_keys):
168
+ """
169
+ Generate forward operations for changing/creating SQL item dependencies.
170
+
171
+ Dependencies are only in-memory and should be reflecting database dependencies, so
172
+ changing them in SQL config does not alter database. Such actions are persisted in separate
173
+ type operation - `AlterSQLState`.
174
+
175
+ Args:
176
+ dep_changed_keys (list): Data about keys, that have their dependencies changed.
177
+ List of tuples (key, removed depndencies, added_dependencies).
178
+ """
179
+ for key, removed_deps, added_deps in dep_changed_keys:
180
+ app_label, sql_name = key
181
+ operation = AlterSQLState(sql_name, add_dependencies=tuple(added_deps),
182
+ remove_dependencies=tuple(removed_deps))
183
+ sql_deps = [key]
184
+ self.add_sql_operation(app_label, sql_name, operation, sql_deps)
185
+
186
+ def _generate_delete_sql(self, delete_keys):
187
+ """
188
+ Generate forward delete operations for SQL items.
189
+ """
190
+ for key in delete_keys:
191
+ app_label, sql_name = key
192
+ old_node = self.from_sql_graph.nodes[key]
193
+ operation = DeleteSQL(sql_name, old_node.reverse_sql, reverse_sql=old_node.sql)
194
+ sql_deps = [n.key for n in self.from_sql_graph.node_map[key].children]
195
+ sql_deps.append(key)
196
+ self.add_sql_operation(app_label, sql_name, operation, sql_deps)
197
+
198
+ def generate_sql_changes(self):
199
+ """
200
+ Starting point of this tool, which identifies changes and generates respective
201
+ operations.
202
+ """
203
+ from_keys = set(self.from_sql_graph.nodes.keys())
204
+ to_keys = set(self.to_sql_graph.nodes.keys())
205
+ new_keys = to_keys - from_keys
206
+ delete_keys = from_keys - to_keys
207
+ changed_keys = set()
208
+ dep_changed_keys = []
209
+
210
+ for key in from_keys & to_keys:
211
+ old_node = self.from_sql_graph.nodes[key]
212
+ new_node = self.to_sql_graph.nodes[key]
213
+
214
+ # identify SQL changes -- these will alter database.
215
+ if not is_sql_equal(old_node.sql, new_node.sql):
216
+ changed_keys.add(key)
217
+
218
+ # identify dependencies change
219
+ old_deps = self.from_sql_graph.dependencies[key]
220
+ new_deps = self.to_sql_graph.dependencies[key]
221
+ removed_deps = old_deps - new_deps
222
+ added_deps = new_deps - old_deps
223
+ if removed_deps or added_deps:
224
+ dep_changed_keys.append((key, removed_deps, added_deps))
225
+
226
+ # we do basic sort here and inject dependency keys here.
227
+ # operations built using these keys will properly set operation dependencies which will
228
+ # enforce django to build/keep a correct order of operations (stable_topological_sort).
229
+ keys = self.assemble_changes(new_keys, changed_keys, self.to_sql_graph)
230
+ delete_keys = self.assemble_changes(delete_keys, set(), self.from_sql_graph)
231
+
232
+ self._sql_operations = {}
233
+ self._generate_reversed_sql(keys, changed_keys)
234
+ self._generate_sql(keys, changed_keys)
235
+ self._generate_delete_sql(delete_keys)
236
+ self._generate_altered_sql_dependencies(dep_changed_keys)
237
+
238
+ def check_dependency(self, operation, dependency):
239
+ """
240
+ Enhances default behavior of method by checking dependency for matching operation.
241
+ """
242
+ if isinstance(dependency[1], SQLBlob):
243
+ # NOTE: we follow the sort order created by `assemble_changes` so we build a fixed chain
244
+ # of operations. thus we should match exact operation here.
245
+ return dependency[3] == operation
246
+ return super(MigrationAutodetector, self).check_dependency(operation, dependency)
247
+
248
+ def generate_altered_fields(self):
249
+ """
250
+ Injecting point. This is quite awkward, and i'm looking forward Django for having the logic
251
+ divided into smaller methods/functions for easier enhancement and substitution.
252
+ So far we're doing all the SQL magic in this method.
253
+ """
254
+ result = super(MigrationAutodetector, self).generate_altered_fields()
255
+ self.generate_sql_changes()
256
+ return result
@@ -0,0 +1,29 @@
1
+ # -*- coding: utf-8 -*-
2
+ from __future__ import unicode_literals
3
+
4
+
5
+ class SQLItem(object):
6
+ """
7
+ Represents any SQL entity (unit), for example function, type, index or trigger.
8
+ """
9
+ def __init__(self, name, sql, reverse_sql=None, dependencies=None, replace=False):
10
+ """
11
+ Args:
12
+ name (str): Name of the SQL item. Should be unique among other items in the current
13
+ application. It is the name that other items can refer to.
14
+ sql (str/tuple): Forward SQL that creates entity.
15
+ drop_sql (str/tuple, optional): Backward SQL that destroyes entity. (DROPs).
16
+ dependencies (list, optional): Collection of item keys, that the current one depends on.
17
+ Each element is a tuple of two: (app, item_name). Order does not matter.
18
+ replace (bool, optional): If `True`, further migrations will not drop previous version
19
+ of item before creating, assuming that a forward SQL replaces. For example Postgre's
20
+ `create or replace function` which does not require dropping it previously.
21
+ If `False` then each changed item will get two operations: dropping previous version
22
+ and creating new one.
23
+ Default = `False`.
24
+ """
25
+ self.name = name
26
+ self.sql = sql
27
+ self.reverse_sql = reverse_sql
28
+ self.dependencies = dependencies or []
29
+ self.replace = replace