Don't be afraid of writing migrations

Cd7648c536b4dbe940246b74044fbc52?s=47 Markus H
April 01, 2016

Don't be afraid of writing migrations

Cd7648c536b4dbe940246b74044fbc52?s=128

Markus H

April 01, 2016
Tweet

Transcript

  1. Don’t be afraid of writing migrations

  2. I’m Markus Holtermann @m_holtermann • github.com/MarkusH • markusholtermann.eu • Traveller

    • Django Core Developer
  3. Don’t be afraid of writing migrations

  4. 3 Recipes

  5. General layout

  6. from django.db import migrations class Migration(migrations.Migration):

  7. from django.db import migrations class Migration(migrations.Migration): dependencies = [ ]

  8. from django.db import migrations class Migration(migrations.Migration): dependencies = [ ]

    operations = [ ]
  9. Recipe 1 optimizing makemigrations output

  10. from django.db import models class Book(models.Model): title = models.CharField(max_length=200) library

    = models.ForeignKey('Library') class Library(models.Model): name = models.CharField(max_length=200)
  11. $ python manage.py makemigrations Migrations for 'optimize_makemigrations': 0001_initial.py: - Create

    model Book - Create model Library - Add field library to book
  12. from django.db import migrations, models class Migration(migrations.Migration): dependencies = []

    operations = [ migrations.CreateModel(name='Book', fields=[ ('id', models.AutoField(...)), ('title', models.CharField(max_length=200))])
  13. from django.db import migrations, models class Migration(migrations.Migration): dependencies = []

    operations = [ migrations.CreateModel(name='Book', fields=[ ('id', models.AutoField(...)), ('title', models.CharField(max_length=200))]), migrations.CreateModel(name='Library', fields=[ ('id', models.AutoField(...)), ('name', models.CharField(max_length=200))])
  14. from django.db import migrations, models class Migration(migrations.Migration): dependencies = []

    operations = [ migrations.CreateModel(name='Book', fields=[ ('id', models.AutoField(...)), ('title', models.CharField(max_length=200))]), migrations.CreateModel(name='Library', fields=[ ('id', models.AutoField(...)), ('name', models.CharField(max_length=200))]), migrations.AddField(model_name='book', name='library', field=models.ForeignKey(to='app.Library')) ]
  15. from django.db import migrations, models class Migration(migrations.Migration): dependencies = []

    operations = [ ]
  16. from django.db import migrations, models class Migration(migrations.Migration): dependencies = []

    operations = [ migrations.CreateModel(name='Library', fields=[ ('id', models.AutoField(...)), ('name', models.CharField(max_length=200)) ]), migrations.CreateModel(name='Book', fields=[ ('id', models.AutoField(...)), ('title', models.CharField(max_length=200)), ('library', models.ForeignKey(to='app.Library')) ]), ]
  17. Recipe 2 adding a non-nullable column

  18. from django.db import models class Author(models.Model): name = models.CharField(max_length=50) @classmethod

    def create(cls, name): return cls.objects.create(name=name)
  19. $ python manage.py makemigrations Migrations for 'non_nullable_field': 0001_initial.py: - Create

    model Author $ python manage.py makemigrations non_nullable_field --empty --name initial_data Migrations for 'non_nullable_field': 0002_initial_data.py:
  20. from django.db import migrations def forwards(apps, schema_editor): Author = apps.get_model('non_nullable_field',

    'Author') Author.objects.create(name='Author 1') Author.objects.create(name='Author 2') def backwards(apps, schema_editor): Author = apps.get_model('non_nullable_field', 'Author') Author.objects.filter(name='Author 1').delete() Author.objects.filter(name='Author 2').delete() class Migration(migrations.Migration): dependencies = [('non_nullable_field', '0001_initial')] operations = [migrations.RunPython(forwards, backwards)]
  21. $ python manage.py migrate non_nullable_field Operations to perform: Apply all

    migrations: non_nullable_field Running migrations: Rendering model states... DONE Applying non_nullable_field.0001_initial... OK Applying non_nullable_field.0002_initial_data... OK
  22. from django.db import models class Author(models.Model): name = models.CharField(max_length=50) homepage

    = models.URLField(null=True) @classmethod def create(cls, name): return cls.objects.create(name=name)
  23. $ python manage.py makemigrations Migrations for 'non_nullable_field': 0003_author_homepage.py: - Add

    field homepage to author $ python manage.py migrate non_nullable_field Operations to perform: Apply all migrations: non_nullable_field Running migrations: Rendering model states... DONE Applying non_nullable_field.0003_author_homepage... OK
  24. from django.db import models from django.utils import html, safestring class

    Author(models.Model): name = models.CharField(max_length=50) homepage = models.URLField(null=True) @classmethod def create(cls, name, homepage): return cls.objects.create(name=name, homepage=homepage) @property def homepage_tag(self): if self.homepage: return html.format_html('<a href="{u}">{u}</a>', u=self.homepage) return safestring.mark_safe('<i>No homepage</i>')
  25. $ python manage.py makemigrations non_nullable_field --empty --name populate_data Migrations for

    'non_nullable_field': 0004_populate_data.py:
  26. from django.db import migrations LOOKUP_DATA = {'Author 1': 'http://example.com', 'Author

    2': 'http://other.org'} def forwards(apps, schema_editor): Author = apps.get_model('non_nullable_field', 'Author') for author in Author.objects.filter(homepage__isnull=True): author.homepage = LOOKUP_DATA[author.name] author.save(update_fields=['homepage']) class Migration(migrations.Migration): dependencies = [('non_nullable_field', '0003_homepage')] operations = [ migrations.RunPython(forwards, migrations.RunPython.noop) ]
  27. $ python manage.py migrate non_nullable_field Operations to perform: Apply all

    migrations: non_nullable_field Running migrations: Rendering model states... DONE Applying non_nullable_field.0004_populate_data... OK
  28. from django.db import models from django.utils import html, safestring class

    Author(models.Model): name = models.CharField(max_length=50) homepage = models.URLField() @classmethod def create(cls, name, homepage): return cls.objects.create(name=name, homepage=homepage) @property def homepage_tag(self): if self.homepage: return html.format_html('<a href="{u}">{u}</a>', u=self.homepage) return safestring.mark_safe('<i>No homepage</i>')
  29. $ python manage.py makemigrations --name not_null_constraint You are trying to

    change the nullable field to non-nullable without a default ... Please select a fix: 1) Provide a one-off value ... 2) Ignore for now ... 3) Quit ... Select an option: 2 Migrations for 'non_nullable_field': 0005_not_null_constraint.py: - Alter field homepage on author
  30. from django.db import migrations, models class Migration(migrations.Migration): dependencies = [('non_nullable_field',

    '0004_populate')] operations = [ migrations.AlterField(model_name='author', name='homepage', field=models.URLField()) ]
  31. $ python manage.py migrate non_nullable_field Operations to perform: Apply all

    migrations: non_nullable_field Running migrations: Rendering model states... DONE Applying non_nullable_field.0005_not_null_constraint... OK
  32. from django.db import models from django.utils import html class Author(models.Model):

    name = models.CharField(max_length=50) homepage = models.URLField() @classmethod def create(cls, name, homepage): return cls.objects.create(name=name, homepage=homepage) @property def homepage_tag(self): return html.format_html('<a href="{u}">{u}</a>', u=self.homepage)
  33. Recipe 3 rename an app without dependencies

  34. # rename_app/models.py from django.db import models class Author(models.Model): name =

    models.CharField(max_length=50) class Book(models.Model): title = models.CharField(max_length=50) author = models.ForeignKey('rename_app.Author')
  35. $ python manage.py makemigrations $ python manage.py makemigrations rename_app --empty

    --name initial_data Edit rename_app/migrations/0002_initial_data.py $ python manage.py migrate rename_app
  36. # rename_app/models.py from django.db import models class Author(models.Model): name =

    models.CharField(max_length=50) class Meta: db_table = 'rename_app_author' class Book(models.Model): title = models.CharField(max_length=50) author = models.ForeignKey('rename_app.Author') class Meta: db_table = 'rename_app_book'
  37. $ python manage.py makemigrations --name pin_db_tables # rename_app/migrations/0003_pin_db_tables.py from django.db

    import migrations, models class Migration(migrations.Migration): dependencies = [('rename_app', '0002_initial_data')] operations = [ migrations.AlterModelTable(name='author', table='rename_app_author'), migrations.AlterModelTable(name='book', table='rename_app_book'), ] $ python manage.py migrate
  38. $ python manage.py migrate rename_app zero --fake Operations to perform:

    Unapply all migrations: rename_app Running migrations: Rendering model states... DONE Unapplying rename_app.0003_pin_db_tables... FAKED Unapplying rename_app.0002_initial_data... FAKED Unapplying rename_app.0001_initial... FAKED
  39. # in settings.py INSTALLED_APPS = [ # ... 'new_app_name.apps.NewAppNameConfig', ]

    # in new_app_name/models.py author = models.ForeignKey('new_app_name.Author') class Meta: db_table = 'rename_app_book' # Keep as is for now! # in new_app_name/migrations/0003_pin_db_tables.py and others dependencies = [('new_app_name', '0002_initial_data')]
  40. $ python manage.py migrate new_app_name --fake Operations to perform: Apply

    all migrations: new_app_name Running migrations: Rendering model states... DONE Applying new_app_name.0001_initial... FAKED Applying new_app_name.0002_initial_data... FAKED Applying new_app_name.0003_pin_db_tables... FAKED
  41. # new_app_name/models.py from django.db import models class Author(models.Model): name =

    models.CharField(max_length=50) class Book(models.Model): title = models.CharField(max_length=50) author = models.ForeignKey('new_app_name.Author')
  42. $ python manage.py makemigrations --name rename_tables # new_app_name/migrations/0004_rename_tables.py from django.db

    import migrations, models class Migration(migrations.Migration): dependencies = [('new_app_name', '0003_pin_db_tables')] operations = [ migrations.AlterModelTable(name='author', table=None), migrations.AlterModelTable(name='book', table=None), ] $ python manage.py migrate
  43. Thank you @m_holtermann • github.com/MarkusH • markusholtermann.eu