Django migrations on a Lino site¶
Lino applications cannot deliver out-of-the-box Django migrations because the
database schema of a Lino site also depends on local settings. For example the
languages
setting affects your database
structure. Or you may locally disable a plugin. Or some plugin options can
cause the structure to change.
Which just means that you must always run makemigrations
before
running migrate
.
The migrations
directory¶
- migrations¶
Django migrations are automatically enabled on a site when its
project_dir
has a subdirectory named
migrations
. Lino then automatically sets the migrations_package
to the corresponding Python package
name derived from the DJANGO_SETTINGS_MODULE
.
Running pm prep
without Django migrations¶
Let's use the lino_book.projects.migs
project to play with migrations.
>>> from atelier.sheller import Sheller
>>> shell = Sheller("lino_book/projects/migs")
We begin with Django migrations disabled:
>>> shell("cat clean.sh")
#!/usr/bin/env bash
set -e
rm -rf settings/migrations
rm -f settings/default.db
#touch migrations/__init__.py
echo "Removed migrations and database."
>>> shell("./clean.sh")
Removed migrations and database.
The pm prep
command works also when Django migrations are disabled. In
this context Django considers all Lino plugins as "unmigrated". Only some
native Django plugins (contenttypes, sessions, staticfiles) are managed by
Django:
>>> shell("python manage.py prep --noinput")
...
Run worker process ...
`initdb std minimal_ledger demo demo2 demo_bookings checksummaries` started on database .../default.db.
Operations to perform:
Synchronize unmigrated apps: about, appypod, bootstrap3, cal, calview, changes, channels, checkdata, comments, contacts, countries, dashboard, excerpts, export_excel, extjs, gfks, groups, help, invoicing, jinja, ledger, lino, linod, lists, memo, noi, notify, office, pages, printing, products, publisher, rest_framework, restful, sales, smtpd, staticfiles, subscriptions, summaries, system, tickets, tinymce, uploads, users, userstats, vat, weasyprint, working, xl
Apply all migrations: contenttypes, sessions
Synchronizing apps without migrations:
Creating tables...
Creating table system_siteconfig
...
Running deferred SQL...
Running migrations:
Applying contenttypes.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying sessions.0001_initial... OK
Loading data from ...
...
Updating summary data for Tickets ...
Updating summary data for User Statistics ...
Updating summary data for Site summaries ...
Updating summary data for User summaries ...
Installed 1009 object(s) from 28 fixture(s)
Terminate worker process ...
Tidy up:
>>> shell("./clean.sh")
Removed migrations and database.
Running pm prep
with Django migrations¶
We enable Django migrations by creating an empty migrations
directory.
>>> shell("mkdir settings/migrations")
When Django migrations are enabled, the pm prep
command does the same,
but in a different way. Django now considers all Lino plugins as "migrated":
>>> shell("python manage.py prep --noinput")
...
Run worker process ...
`initdb std minimal_ledger demo demo2 demo_bookings checksummaries` started on database .../default.db.
Operations to perform:
Synchronize unmigrated apps: staticfiles
Apply all migrations: cal, calview, changes, checkdata, comments, contacts, contenttypes, countries, dashboard, excerpts, gfks, groups, invoicing, ledger, lists, memo, notify, pages, products, sales, sessions, subscriptions, system, tickets, tinymce, uploads, users, userstats, vat, working
Synchronizing apps without migrations:
Creating tables...
Running deferred SQL...
Running migrations:
...
Installed 1009 object(s) from 28 fixture(s)
Terminate worker process ...
>>> from lino import startup
>>> startup("lino_book.projects.migs.settings.demo")
>>> from lino.api.doctest import *
The application developer can see whether Django migrations are enabled
or not by looking at the
migrations_package
site attribute.
>>> print(settings.SITE.migrations_package)
lino_book.projects.migs.settings.migrations
When Django migrations are enabled, Lino automatically fills the
migrations
directory with many subdirectories (one for each installed
plugin) and sets the MIGRATION_MODULES
setting.
>>> pprint(settings.MIGRATION_MODULES)
{'about': 'lino_book.projects.migs.settings.migrations.about',
'appypod': 'lino_book.projects.migs.settings.migrations.appypod',
'bootstrap3': 'lino_book.projects.migs.settings.migrations.bootstrap3',
'cal': 'lino_book.projects.migs.settings.migrations.cal',
'calview': 'lino_book.projects.migs.settings.migrations.calview',
'changes': 'lino_book.projects.migs.settings.migrations.changes',
'channels': 'lino_book.projects.migs.settings.migrations.channels',
'checkdata': 'lino_book.projects.migs.settings.migrations.checkdata',
'comments': 'lino_book.projects.migs.settings.migrations.comments',
'contacts': 'lino_book.projects.migs.settings.migrations.contacts',
'countries': 'lino_book.projects.migs.settings.migrations.countries',
'dashboard': 'lino_book.projects.migs.settings.migrations.dashboard',
'excerpts': 'lino_book.projects.migs.settings.migrations.excerpts',
'export_excel': 'lino_book.projects.migs.settings.migrations.export_excel',
'extjs': 'lino_book.projects.migs.settings.migrations.extjs',
'gfks': 'lino_book.projects.migs.settings.migrations.gfks',
'groups': 'lino_book.projects.migs.settings.migrations.groups',
'help': 'lino_book.projects.migs.settings.migrations.help',
'invoicing': 'lino_book.projects.migs.settings.migrations.invoicing',
'jinja': 'lino_book.projects.migs.settings.migrations.jinja',
'ledger': 'lino_book.projects.migs.settings.migrations.ledger',
'lino': 'lino_book.projects.migs.settings.migrations.lino',
'linod': 'lino_book.projects.migs.settings.migrations.linod',
'lists': 'lino_book.projects.migs.settings.migrations.lists',
'memo': 'lino_book.projects.migs.settings.migrations.memo',
'noi': 'lino_book.projects.migs.settings.migrations.noi',
'notify': 'lino_book.projects.migs.settings.migrations.notify',
'office': 'lino_book.projects.migs.settings.migrations.office',
'pages': 'lino_book.projects.migs.settings.migrations.pages',
'printing': 'lino_book.projects.migs.settings.migrations.printing',
'products': 'lino_book.projects.migs.settings.migrations.products',
'publisher': 'lino_book.projects.migs.settings.migrations.publisher',
'rest_framework': 'lino_book.projects.migs.settings.migrations.rest_framework',
'restful': 'lino_book.projects.migs.settings.migrations.restful',
'sales': 'lino_book.projects.migs.settings.migrations.sales',
'smtpd': 'lino_book.projects.migs.settings.migrations.smtpd',
'subscriptions': 'lino_book.projects.migs.settings.migrations.subscriptions',
'summaries': 'lino_book.projects.migs.settings.migrations.summaries',
'system': 'lino_book.projects.migs.settings.migrations.system',
'tickets': 'lino_book.projects.migs.settings.migrations.tickets',
'tinymce': 'lino_book.projects.migs.settings.migrations.tinymce',
'uploads': 'lino_book.projects.migs.settings.migrations.uploads',
'users': 'lino_book.projects.migs.settings.migrations.users',
'userstats': 'lino_book.projects.migs.settings.migrations.userstats',
'vat': 'lino_book.projects.migs.settings.migrations.vat',
'weasyprint': 'lino_book.projects.migs.settings.migrations.weasyprint',
'working': 'lino_book.projects.migs.settings.migrations.working',
'xl': 'lino_book.projects.migs.settings.migrations.xl'}
Note that the lino_book.projects.migs
uses a settings package (not a
settings file), so the migrations
directory is under the
settings
directory, not under the project's root directory.
>>> print(settings.SITE.project_dir)
...
/.../lino_book/projects/migs/settings
TODO: write tests to show a site upgrade using Django migrations.