Django Evolution 2.0

Release date: August 13, 2020

New Features

All-New Documentation

We have new documentation for Django Evolution, covering installation, usage, a FAQ, and all release notes.

Support for Python 3

Django Evolution is now fully compatible with Python 2.7 and 3.5 through 3.8, allowing it to work across all supported versions of Django.

Speaking of that…

Support for Django 1.6 through 3.1

Django Evolution 2.0 supports Django 1.6 through 3.1. Going forward, it will continue to support newer versions of Django as they come out.

This includes modern features, like Meta.indexes and Meta.conditions.

We can offer this due to the new cooperative support for Django’s schema migrations.

Compatibility with Django Migrations

Historically, Django Evolution has been a standalone schema migration framework, and was stuck with supporting versions of Django prior to 1.7, since evolutions and migrations could not co-exist.

That’s been resolved. Django Evolution now controls the entire process, applying both migrations and evolutions together, ensuring a smooth upgrade. Projects get the best of both worlds:

  • The ability to use apps that use migrations (most everything, including Django itself)

  • Optimized upgrades for the project’s own evolution-based models (especially when applying large numbers of evolutions to the same table)

New Evolve Command

In Django Evolution 2.0, the evolve command becomes the sole way of applying both evolutions and migrations, replacing the migrate/syncdb commands.

To set up or upgrade a database (using both evolutions and migrations), you’ll simply run evolve --execute. This will work across all versions of Django.

The old migrate and syncdb commands will still technically work, but they’ll wrap evolve --execute.

This can all be disabled by setting DJANGO_EVOLUTION_ENABLED = False in settings.py.

Note

initial_data fixtures will no longer be loaded. These have already been deprecated in Django, but it’s worth mentioning for users of older versions of Django.

Also, the migrate command will no longer allow individual migrations to be applied.

Moving Apps to Migrations

Projects can transition some or all of their apps to migrations once the last of the evolutions are applied, allowing them to move entirely onto migrations if needed. This is done with the new MoveToMigrations mutation.

Simply add one last evolution for an app:

from django_evolution.mutations import MoveToDjangoMigrations


 MUTATIONS = [
     MoveToDjangoMigrations(),
 ]

This will apply after the last evolution is applied, and from then on all changes to the models will be controlled via migrations.

Note

Once an app has been moved to migrations, it cannot be moved back to evolutions.

Improved Database Compatibility

  • Support for constraints on modern versions of MySQL/MariaDB.

    Modern versions of MySQL and MariaDB are now explicitly supported, allowing projects using Django 2.2+ to take advantage of CHECK constraints. This requires MySQL 8.0.16+ or MariaDB 10.2.1+ on Django 3.0+.

  • Faster and safer SQLite table rebuilds.

    Changes to SQLite databases are now optimized, resulting in far fewer table rebuilds when changes are made to a model.

  • Support for SQLite 3.25+ column renaming.

    SQLite 3.25 introduced ALTER TABLE ... RENAME COLUMN syntax, which is faster than a table rebuild and avoids a lot of issues with preserving column references.

  • We use Django 1.7’s schema rewriting for more of the SQL generation.

    This helps ensure future compatibility with new releases of Django, and allows for leveraging more of Django’s work toward database compatibility.

Project-Defined Custom Evolutions

Projects can provide a new settings.CUSTOM_EVOLUTIONS setting to define custom evolution modules for apps that don’t otherwise make use of evolutions or migrations. The value is a mapping of app module names (same ones you’d see in settings.INSTALLED_APPS to an evolutions module path.

This looks like:

CUSTOM_EVOLUTIONS = {
    'other_project.contrib.foo': 'my_project.compat.foo.evolutions',
}

Evolver API

The entire evolution/migration process can now be controlled programmatically through the Evolver class. This allows an entire database, or just select apps, to be evolved without calling out to a management command.

While most projects will not have a need for this, it’s available to those that might want some form of specialized control over the evolution process (for automation, selectively evolving models from an extension/plug-in, or providing an alternative management/upgrade experience).

During an evolution, new signals are emitted, allowing apps to hook into the process and perform any updates they might need:

New Database Signature Format

Django Evolution stores a representation of the database in the Version table, in order to track what’s been applied and what changes have been made since.

Historically, this has used some older structured data schema serialized in Pickle Protocol 0 format. As of Django Evolution 2.0, it’s now using a new schema stored in JSON format, which is designed for future extensibility.

Internally, this is represented by a set of classes with a solid API that’s independent of the storage format. This eases the addition of new features, and makes it easier to diagnose problems or write custom tools.

Warning

This will impact any SQLMutations that modify a signature. These will need to be updated to use the new classes, instead of modifying the older schema dictionaries.

Bug Fixes

SQLite

  • Fixed constraint references from other tables when renaming primary key columns.

  • Fixed restoring all table indexes after rebuilding a table.

Contributors

  • Christian Hammond