Dit zijn belangrijke problemen die we als ontwikkelaars geheid een keer tegenkomen. Gelukkig hoef je hier niet over te piekeren – dat hebben anderen namelijk al gedaan voor ons. Het resultaat is een krachtige oplossing voor deze vraagstukken: Alembic.
Wat is Alembic
Alembic is een database migratie tool die is ontworpen voor gebruik met SQLAlchemy en Python. Met deze set aan tools kan je een relationele database ontwerpen met Python objecten, automatisch migraties genereren en migraties testen. Dit werkt met SQLAlchemy; een Object Relational Mapper (ORM) die data uit een object-oriented programmeertaal vertaald naar een relationele database. In de documentatie van SQLAlchemy lees je hoe je een database structuur onder kan brengen in objecten.
Dit biedt grote voordelen: je ontwerp is met één commando uit te rollen op een verse database, al je migraties worden met unit tests doorgetest op up- én down revisies, en je ontwerp wordt vergeleken met je migraties. Deze werkwijze helpt enorm om fouten te voorkomen in je werk.
De eerste migratie
Zodra je een Alembic omgeving hebt gemaakt (hier lees je hoe) kun je de eerste migratie genereren. Een migratie is een Python bestand wat alle database queries bevat die nodig zijn om de betreffende wijzigingen uit te voeren. Een migratie kan je automatisch genereren met het volgende command:
$ alembic revision --autogenerate -m “revision name”
Het gegenereerde bestand bevat alle (nieuwe) wijzigingen in je modellen. In dit bestand staan de commands om te upgraden en downgraden. Ook zie je het ID van de huidige en voorgaande revisie. Nieuwe wijzigingen in je modellen kun je borgen door hetzelfde command te draaien. Dan wordt er weer een nieuwe migratie gemaakt met de wijzigingen. Dit is in zekere zin versiebeheer voor je database.
Als je klaar bent met je migratie(s), kun je verbinden met de database en vervolgens de wijzigingen uitrollen:
alembic upgrade head
Of eventueel terugzetten, bijvoorbeeld één revisie terug:
alembic downgrade -1
Revisies testen
Deze opzet is heel fraai, maar hoe weet je nou dat het allemaal wel gaat (en blijft) werken? Dat kunnen we op voorhand testen met ingebouwde tests van Pytest Alembic. Deze plugin bevat de volgende tests:
- test_single_head_revision: Test of er maar één head revision bestaat.
- test_upgrade: Test of de revisies van eerste naar laatste uitgevoerd kunnen worden.
- test_model_definitions_match_ddl: Test of de staat van de migraties overeenkomt met de staat van de modellen.
- test_up_down_consistency: Test of de revisies van laastste naar eerste gedowngraded kunnen worden.
De tests kun je eenvoudig als volgt gebruiken, bijvoorbeeld in het bestand tests/test_migrations.py:
from pytest_alembic.tests import ( test_model_definitions_match_ddl, test_single_head_revision, test_up_down_consistency, test_upgrade,
Dit zorgt ervoor dat deze tests automatisch worden uitgevoerd als je Pytest draait. Om dat voor elkaar te krijgen moet je nog wel wat voorwerk doen. Minimaal moet je deze fixture toevoegen om tegen de Alembic runner te vertellen waar de tests moeten draaien. Ik zou aanraden om dit in `tests/conftest.py` te zetten:
@pytest.fixture def alembic_engine(): """Override this fixture to provide pytest-alembic powered tests with a database handle. """ return sqlalchemy.create_engine("<database>:///")
Meer informatie hierover is te vinden in de Pytest Alembic docs.
Deze tests zorgen er samen voor dat je veruit de meeste fouten in je wijzigingen op voorhand al detecteerd. Dat maakt deze werkwijze veel minder foutgevoelig dan wijzigingen handmatig of met scriptjes aanbrengen. En niet te vergeten, dit maakt het triviaal om een nieuwe instantie van de database uit te rollen. Al met al een zeer waardevolle tool!