Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

290
Vistas
Conditional Django migration based on a field only present in new version

My app that currently depends on Postgres and Django's Postgres-only JSONField. The field works well and I've no interest in another project, but I have prospective-users who want to use my app, but can't while it relies on Postgres.

Django 3.1 has a cross-platform version of this field —which will work for my needs— but I don't want to force everybody up to Django 3.1; I would like to offer people a choice between Postgres or Django 3.1+. On paper, this is simple enough with a conditional import...

try:
    from django.db.models import JSONField
except ImportError:
    from django.contrib.postgres.fields import JSONField

And if I installed Django 3.1 and generated a migration, it could take me from django.contrib.postgres.fields.JSONField to django.db.models.JSONField. But...

  • New users will still execute the initial migration. I will still have a dependency on Postgres.
  • Sub-Django 3.1 users won't be able to execute the new migration. I now have a dependency on Django 3.1.

This is worse than when I started. How do I do this sort of field migration in a way that will work for everybody?

over 4 years ago · Santiago Trujillo
4 Respuestas
Responde la pregunta

0

Migrations are just code. Just because they're auto-generated doesn't mean you shouldn't change them. You're encouraged to, at least to check they're generated correctly, but also there's no harm in writing them yourself.

This works for me:

Model:

from django.db import models

try:
    from django.db.models import JSONField
except ImportError:
    from django.contrib.postgres.fields import JSONField

class MyModel(models.Model):
    stuff = JSONField()

Migration:

from django.db import migrations, models

try:
    from django.db.models import JSONField
except ImportError:
    from django.contrib.postgres.fields import JSONField


class Migration(migrations.Migration):
    dependencies = [('testapp', '0001_initial')]

    operations = [
        migrations.CreateModel(
            name='MyModel',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('stuff', JSONField()),
            ],
        ),
    ]

Keep in mind that if you need to change this field in the future, you will need to go through this process again.

over 4 years ago · Santiago Trujillo Denunciar

0

I have got this from Django source code

from django.db.models import JSONField as BuiltinJSONField

class JSONField(BuiltinJSONField):
    system_check_deprecated_details = {
        'msg': (
            'django.contrib.postgres.fields.JSONField is deprecated. Support '
            'for it (except in historical migrations) will be removed in '
            'Django 4.0.'
        ),
        'hint': 'Use django.db.models.JSONField instead.',
        'id': 'fields.W904',
    }

This indicates that, django.contrib.postgres.fields.JSONField is going to be deprecated. Also, Django uses the django.db.models.JSONField as postgres special JSONField.

Apart from that, I have generated the SQL command using sqlmigrate command and it was like,

BEGIN;
--
-- Create model MyModel
--
CREATE TABLE "myapp_mymodel" ("id" serial NOT NULL PRIMARY KEY, "stuff" jsonb NOT NULL);
COMMIT;

Surprisingly, I have got same SQL command using Django==3.0 and Django==3.1 and in the database, the field is a jsonb type

These pieces of information conclude that you don't have to worry about this new JSONField upgrade.

What changes need to be done?

You don't need to generate a new migration file, but edit existing migration files which have django.contrib.postgres.fields.JSONField reference with the try...except block.

That's it!!!

over 4 years ago · Santiago Trujillo Denunciar

0

I gave point to Tom Carrick for his answer but I think it should also include the usage of Meta.required_db_vendor = 'postgres' on Django < 3.1

# models.py
from django.db import models

try:
    from django.db.models import JSONField
    postgres_only = False
except ImportError:
    from django.contrib.postgres.fields import JSONField
    postgres_only = True

class MyModel(models.Model):
    stuff = JSONField()

    class Meta:
        if postgres_only:
            required_db_vendor = 'postgres'
# migrations/0001_initial.py
from django.db import migrations, models

try:
    from django.db.models import JSONField
    postgres_only = False
except ImportError:
    from django.contrib.postgres.fields import JSONField
    postgres_only = True


class Migration(migrations.Migration):
    dependencies = [('testapp', '0001_initial')]

    operations = [
        migrations.CreateModel(
            name='MyModel',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('stuff', JSONField()),
            ],
            options={'required_db_vendor': 'postgres'} if postgres_only else None,
        ),
    ]

This will ensure that users of your reusable app cannot use it on a different database than PostgreSQL on Django < 3.1 and seamlessly enable support on 3.1 without requiring a migration on PostgreSQL users using your library and updating to Django 3.1.

over 4 years ago · Santiago Trujillo Denunciar

0

All new migrations will be correct automatically if this solution with a deconstruct() method will be used.

You can create a compatible custom JSONField that encapsulates both variants.

Create a small file fields.py in your application:

try:
    from django.db.models import JSONField as OrigJSONField
except ImportError:
    from django.contrib.postgres.fields import JSONField as OrigJSONField


class JSONField(OrigJSONField):
    def deconstruct(self):
        # the original path was 'django.db.models.JSONField' or 'django.contrib.postgres.fields....'
        name, path, args, kwargs = super().deconstruct()
        # Substitute 'my_app' by your application name everywhere.
        path = 'my_app.fields.JSONField'
        return name, path, args, kwargs

Change a line in your models.py:

from my_app.fields import JSONField

Edit all existing migrations that use a JSONField:

import my_app.fields
    ...
        ('stuff', my_app.fields.JSONField()),

  (or equivalently)

from my_app.fields import JSONField
    ...
        ('stuff', JSONField()).

No migration is created after that because no difference is found by makemigrations.

All future makemigrations after a changed model will be created automatically with my_app.fields.JSONField(). Compatibility is a benefit of this solution.


Reflection about Django Release Notes 3.1 and future:

They describe a plain upgrade that requires to create a new migration that only upgrades the import path, but it generates no SQL e.g. by sqlmigrate. It is easier than to edit old migrations.

Maybe you you also upgrade in two years to Django >= 3.1 only and you will have two alternatives:

A) Only the import in models.py will be edited and you create a new nearly empty formal migration similarly to release notes. You will be not able to remove the import path my_app.fields.JSONField because it must be importable from old migrations similarly that Django can never remove the path django.contrib.postgres.fields.JSONField. The file my_app/fields.py could be simplified to one line from django.db.models import JSONField as JSONField after an unconditional upgrade of requirements.

B) Editing old migrations again to only the new JSONField is possible, but unreasonable.

(It is every user's responsibility that an edit in a migrations file doesn't require a changed database state and all migrations remain consistent each other. That is why not much about it can be found, except such clear cases.)

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda