To start using django-migration-docs
in your project, first run the following command:
manage.py migration_docs bootstrap
This command will generate empty docs for all of the migrations currently in your project.
If you'd prefer to manually enter migration docs for every migration in your project, skip this step.
django-migration-docs
works by keeping additional documentation about all migrations in your project in the .migration-docs/docs.yaml
file. All information entered through django-migration-docs
commands will be kept in this file and viewable in any pull requests that have migration changes. For example, after running manage.py migration_docs bootstrap
, you will see a new .migration-docs/docs.yaml
file with keys for every migration.
After bootstrapping a project, the documentation is kept in sync by calling:
manage.py migration_docs sync
The sync
command operates in the following manner:
- Finds any new migrations in the project. Collects metadata about those migrations and prompts the user for more information about the migration. More on how this works later.
- Determines if any migration files have changed since the documentation was last synced. Automatically updates the metadata associated with any changed migrations.
- Checks if any migrations that were previously documented have been deleted from the project. Documentation for these migrations is removed.
By default, django-migration-docs
collects the following metadata about every migration:
- The
hash
of the migration, which is just an MD5 of the contents of the migration file. - If the migration is
atomic
. IfTrue
, this means the migration is executed in a transaction. - The raw
sql
for the migration. If the SQL cannot be determined for any reason, the exception and error message are stored in this attribute.
django-migration-docs
also prompts users to enter the following information about each migration:
- The
point_of_contact
for the migration. This is the person responsible for the migration, whether they authored it or installed/upgraded the third-party app that brought in the migrations. - An in-depth
description
. Prompts the user for a description about the migration.
For example, here's a full flow of migration docs being synced where some are updated, added, and deleted:
django-migration-docs
uses the formaldict library to prompt users for structured information about the migration.
django-migration-docs
can be configured to collect all of the attributes important to your organization by configuring a migration doc schema in .migration-docs/migration.yaml
. The migration doc schema is a representation of a formaldict
Schema
from the formaldict library used under the hood by django-migration-docs
. We will give some examples of schemas here, but check out the formaldict docs for more examples of configuring schemas.
The following .migration-docs/migration.yaml
file contains a schema that prompts for the migration author, a description, and the type of migration:
- label: author
help: The author of the migration.
- label: description
help: An in-depth description of the migration. Give some information
about what big tables might be impacted and how long it takes the
migration to run.
- label: type
help: When the migration should be executed in deployment. Choose "before"
to execute the migration before code is deployed and "after" to
executed it after code is deployed.
choices:
- before
- after
By specifying this schema in the .migration-docs/migration.yaml
file of your Django project, users will be prompted to enter this information when syncing migrations. For example:
The resulting .migration-docs/docs.yaml
file looks something like this:
auth.0011_update_proxy_permissions:
_hash: 4c2c17e4e9f7808c79a0f7a542e6c700
atomic: true
author: John Doe
description: This is an in-depth description of the migration!
sql: '--
-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE WRITTEN AS SQL:
-- Raw Python operation
--'
type: before
Tip
The formaldict library that parses the schema and prompts for information has many options, including the ability to conditionally prompt for more attributes based on previously-entered information. Check out these docs for more information!
Sometimes migration docs need to be updated after they are initially entered. This can be performed with:
manage.py migration_docs update <migration_label> <migration_label>..
For example, if we want to update the docs for the migration in our previous example, we'd do:
manage.py migration_docs update auth.0011_update_proxy_permissions
Similar to Django's manage.py showmigrations
command, django-migration-docs
allows users to render the migrations in whatever manner necessary for their organization with:
manage.py migration_docs show
By default, migration_docs show
will render all migrations and place an X
next to the ones that are applied. It does so by using the following Jinja2 template to render the migrations:
{% for migration in migrations %}
[{% if migration.applied %}X{% else %} {% endif %}] {{ migration.label }}
{% endfor %}
When manage.py migration_docs show
is called, the migrations are loaded in a migration_docs.Migrations object that is passed to the template.
Users can override the Jinja2 template used for rendering the docs by providing a .migration-docs/show.tpl
Jinja2 template. The migrations
variable in the template is a list of migration_docs.Migration objects, and each migration_docs.Migration not only has many of the base attributes (e.g. sql
, applied
, hash
, etc), but also has an attribute for every custom attribute specified in the .migration-docs/migration.yaml
file. This allows us to render whatever information about migrations are necessary.
For example, assume we are using the schema from our previous example. This template will render the migration author and type of migration (using Markdown format):
{% for migration in migrations %}
[{% if migration.applied %}X{% else %} {% endif %}] {{ migration.label }}
**author** {{ migration.author }}
**type** {{ migration.type }}
{% endfor %}
The migrations
variable is a migration_docs.Migrations object, which allows filtering and grouping on migration_docs.Migration attributes. For example, to group each migration by type
and show sections for each type, use the following template:
{% for type, migrations_by_type in migrations.group('type').items() %}
## {{ type|default('no type specified', True) }}
{% for migration in migrations_by_type %}
[{% if migration.applied %}X{% else %} {% endif %}] {{ migration.label }}
**author** {{ migration.author }}
{% endfor %}
{% endfor %}
The template from above will result in output looking like:
none
## no type specified
[X] auth.0008_alter_user_username_max_length
**author** None
## before
[X] auth.0009_alter_user_last_name_max_length
**author** John Doe
## after
[X] auth.0010_alter_group_name_max_length
**author** John Doe
Check out the migration_docs.Migrations doc for more methods that you can use in custom Jinja templates. The migration_docs.Migrations construct also allows for arbitrary filtering of migration_docs.Migration elements.
The manage.py migration_docs show
command also comes with the following options:
manage.py migration_docs show --unapplied
- The--unapplied
option automatically filters themigrations
variable to only contain unapplied migrations. Note that this can also be accomplished by runningmigrations.filter('applied', False)
in the template.manage.py migration_docs show app_label1 app_label2
- Provide an arbitrary number of app labels to only show migrations for those apps. Note that this can also be accomplished by runningmigrations.intersect('app_label', ['app_label1', 'app_label2'])
in the template.manage.py migration_docs show --style=value
- When given astyle
, the command looks for a template in the.migration-docs/show_{style}.tpl
file and uses that template.
Check that migration docs have been synced with:
manage.py migration_docs check
The command exits with an error code of 1 if any errors are found. This command is intended to be executed in a continuous integration environment with pull requests to ensure that migration docs are up to date.
Note
The check
subcommand does not currently verify that the contents of the .migration-docs/docs.yaml
file matches the schema in .migration-docs/schema.yaml
. We are considering adding this as an optional check in a later release of django-migration-docs
.
Migration docs can automatically be synced when running migrations. This can be useful so that engineers do not have to remember to add migrations. Set the MIGRATION_DOCS_PRE_MIGRATE_SYNC
setting to True
in your settings file, and migration docs will be synced when anyone runs manage.py migrate
.
Tip
It's recommended to only enable this setting in local development mode. Otherwise engineers may accidentally be prompted for migration docs when doing production deployments and migrations.
django-migration-docs
will automatically update docs for migrations when migration files change. It will similarly report that docs need to be synced during manage.py migration_docs check
if the contents of any migrations have been changed since the last time docs were synced.
This flow can be problematic for users that use automated code formatters like black. E.g. depending on the setup of the repo, a user could sync migration docs, automatically format the migration code with black
, and then have to re-sync docs again.
This flow can be avoided by configuring the MIGRATION_DOCS_PRE_SYNC_HOOKS
setting. Add commands that need to be executed before syncing migration docs to this setting and they will automatically be executed before syncing any migrations. For example, to run black
before syncing, do:
MIGRATION_DOCS_PRE_SYNC_HOOKS = ['black .']