Skip to content

Commit 9881e9d

Browse files
authored
Merge pull request #268 from rpkilby/migration-example
Resolves #255 - deprecate project, add migration example
2 parents 48ed9c5 + 9eb6e53 commit 9881e9d

18 files changed

Lines changed: 240 additions & 4 deletions

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
strategy:
1515
matrix:
1616
python-version: ["3.13"]
17-
toxenv: ["dist", "lint", "warnings"]
17+
toxenv: ["dist", "lint", "warnings", "migration-example"]
1818
continue-on-error: ${{ matrix.toxenv == 'warnings' }}
1919

2020
steps:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ dist/
99
.env
1010
.env/
1111
.venv/
12+
*.sqlite3

README.rst

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,33 @@ jsonfield
1515
**jsonfield** is a reusable model field that allows you to store validated JSON, automatically handling
1616
serialization to and from the database. To use, add ``jsonfield.JSONField`` to one of your models.
1717

18-
**Warning!**
1918

20-
Django 3.1 `introduced`_ a native ``JSONField`` that supports all database backends. New projects should
21-
preference Django's implemenation to ``jsonfield``, and existing users should migrate off of this package.
19+
Deprecation & Migration to Django's native ``JSONField``
20+
--------------------------------------------------------
21+
22+
Django 3.1 `introduced`_ a native ``JSONField`` that supports all database backends. As such, this package is
23+
considered deprecated and will be archived in the future. Existing projects should migrate to Django's implemenation.
2224

2325
.. _introduced: https://docs.djangoproject.com/en/stable/releases/3.1/#jsonfield-for-all-supported-database-backends
2426

2527

28+
Migrating from ``jsonfield.JSONField`` to ``models.JSONField`` *should* generally be straightforward. After swapping
29+
field classes, ``python manage.py migrate`` will generate ``AlterField`` operations that should correctly migrate
30+
your field data. However, if this does not work for your case, you will instead need to create a data migration.
31+
The process will roughly look like:
32+
33+
* Rename ``<field>`` to ``old_<field>``, create migration.
34+
* Add a nullable ``<field> = models.JSONField(null=True, ...)``, create migration.
35+
* Create an empty migration file, add ``RunPython`` operation that reserializes
36+
the ``old_<field>`` data into the new ``<field>``.
37+
* Update ``<field>`` to not nullable, delete ``old_<field>``, create migration.
38+
* Manually combine the operations into a single migration file.
39+
40+
Examples can be found in the `migration-example`_ project.
41+
42+
.. _migration-example: https://github.com/rpkilby/jsonfield/tree/master/migration-example/
43+
44+
2645
Installation
2746
------------
2847

migration-example/manage.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env python
2+
"""Django's command-line utility for administrative tasks."""
3+
import os
4+
import sys
5+
6+
7+
def main():
8+
"""Run administrative tasks."""
9+
sys.path.append('src')
10+
sys.path.append('tests')
11+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
12+
try:
13+
from django.core.management import execute_from_command_line
14+
except ImportError as exc:
15+
raise ImportError(
16+
"Couldn't import Django. Are you sure it's installed and "
17+
"available on your PYTHONPATH environment variable? Did you "
18+
"forget to activate a virtual environment?"
19+
) from exc
20+
execute_from_command_line(sys.argv)
21+
22+
23+
if __name__ == '__main__':
24+
main()

migration-example/src/alterfield/__init__.py

Whitespace-only changes.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Generated by Django 5.2.4 on 2025-07-04 21:22
2+
3+
import jsonfield.fields
4+
from django.db import migrations, models
5+
6+
7+
def populate(apps, schema_editor):
8+
AlterFieldModel = apps.get_model('alterfield', 'AlterFieldModel')
9+
10+
AlterFieldModel.objects.create(data=1)
11+
AlterFieldModel.objects.create(data="foobar")
12+
AlterFieldModel.objects.create(data={"foo": "bar"})
13+
14+
15+
class Migration(migrations.Migration):
16+
17+
initial = True
18+
19+
dependencies = [
20+
]
21+
22+
operations = [
23+
migrations.CreateModel(
24+
name='AlterFieldModel',
25+
fields=[
26+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
27+
('data', jsonfield.fields.JSONField()),
28+
],
29+
),
30+
migrations.RunPython(populate, migrations.RunPython.noop)
31+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 5.2.4 on 2025-07-04 21:51
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('alterfield', '0001_initial'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='alterfieldmodel',
15+
name='data',
16+
field=models.JSONField(),
17+
),
18+
]

migration-example/src/alterfield/migrations/__init__.py

Whitespace-only changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from django.db import models
2+
3+
4+
class AlterFieldModel(models.Model):
5+
data = models.JSONField()

migration-example/src/datamigration/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)