From 08d562670452221b759cd9de20028b9d5c639ff3 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Thu, 9 May 2019 09:37:09 +0200 Subject: [PATCH] Simplify the future of our migration history --- .../management/commands/makemigrations.py | 51 +++++++++++++ .../base/management/commands/migrate.py | 28 +++++++ .../migrations/0120_auto_20190509_0736.py | 76 +++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 src/pretix/base/management/commands/makemigrations.py create mode 100644 src/pretix/base/management/commands/migrate.py create mode 100644 src/pretix/base/migrations/0120_auto_20190509_0736.py diff --git a/src/pretix/base/management/commands/makemigrations.py b/src/pretix/base/management/commands/makemigrations.py new file mode 100644 index 000000000..e65aafc31 --- /dev/null +++ b/src/pretix/base/management/commands/makemigrations.py @@ -0,0 +1,51 @@ +""" +Django, for theoretically very valid reasons, creates migrations for *every single thing* +we change on a model. Even the `help_text`! This makes sense, as we don't know if any +database backend unknown to us might actually use this information for its database schema. + +However, pretix only supports PostgreSQL, MySQL, MariaDB and SQLite and we can be pretty +certain that some changes to models will never require a change to the database. In this case, +not creating a migration for certain changes will save us some performance while applying them +*and* allow for a cleaner git history. Win-win! + +Only caveat is that we need to do some dirty monkeypatching to achieve it... +""" +from django.core.management.commands.makemigrations import Command as Parent +from django.db import models +from django.db.migrations.operations import models as modelops +from django_countries.fields import CountryField + +modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("verbose_name") +modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("verbose_name_plural") +modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("ordering") +modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("get_latest_by") +modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("default_manager_name") +modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("permissions") +modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("default_permissions") +IGNORED_ATTRS = [ + # (field type, attribute name, blacklist of field sub-types) + (models.Field, 'verbose_name', []), + (models.Field, 'help_text', []), + (models.Field, 'validators', []), + (models.Field, 'editable', [models.DateField, models.DateTimeField, models.DateField, models.BinaryField]), + (models.Field, 'blank', [models.DateField, models.DateTimeField, models.AutoField, models.NullBooleanField, + models.TimeField]), + (models.CharField, 'choices', [CountryField]) +] + +original_deconstruct = models.Field.deconstruct + + +def new_deconstruct(self): + name, path, args, kwargs = original_deconstruct(self) + for ftype, attr, blacklist in IGNORED_ATTRS: + if isinstance(self, ftype) and not any(isinstance(self, ft) for ft in blacklist): + kwargs.pop(attr, None) + return name, path, args, kwargs + + +models.Field.deconstruct = new_deconstruct + + +class Command(Parent): + pass diff --git a/src/pretix/base/management/commands/migrate.py b/src/pretix/base/management/commands/migrate.py new file mode 100644 index 000000000..5e7c11c0b --- /dev/null +++ b/src/pretix/base/management/commands/migrate.py @@ -0,0 +1,28 @@ +""" +Django tries to be helpful by suggesting to run "makemigrations" in red font on every "migrate" +run when there are things we have no migrations for. Usually, this is intended, and running +"makemigrations" can really screw up the environment of a user, so we want to prevent novice +users from doing that by going really dirty and fitlering it from the output. +""" +import sys + +from django.core.management.base import OutputWrapper +from django.core.management.commands.migrate import Command as Parent + + +class OutputFilter(OutputWrapper): + blacklist = ( + "Your models have changes that are not yet reflected", + "Run 'manage.py makemigrations' to make new " + ) + + def write(self, msg, style_func=None, ending=None): + if any(b in msg for b in self.blacklist): + return + super().write(msg, style_func, ending) + + +class Command(Parent): + def __init__(self, stdout=None, stderr=None, no_color=False, force_color=False): + super().__init__(stdout, stderr, no_color, force_color) + self.stdout = OutputFilter(stdout or sys.stdout) diff --git a/src/pretix/base/migrations/0120_auto_20190509_0736.py b/src/pretix/base/migrations/0120_auto_20190509_0736.py new file mode 100644 index 000000000..f5a94d8e5 --- /dev/null +++ b/src/pretix/base/migrations/0120_auto_20190509_0736.py @@ -0,0 +1,76 @@ +# Generated by Django 2.2 on 2019-05-09 07:36 + +from django.db import migrations, models +import django.db.models.deletion +import jsonfallback.fields +import pretix.base.models.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0119_auto_20190509_0654'), + ] + + operations = [ + migrations.AlterField( + model_name='cartposition', + name='attendee_name_parts', + field=jsonfallback.fields.FallbackJSONField(default=dict), + ), + migrations.AlterField( + model_name='cartposition', + name='subevent', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.SubEvent'), + ), + migrations.AlterField( + model_name='cartposition', + name='voucher', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.Voucher'), + ), + migrations.AlterField( + model_name='event', + name='is_public', + field=models.BooleanField(default=True), + ), + migrations.AlterField( + model_name='invoiceaddress', + name='name_parts', + field=jsonfallback.fields.FallbackJSONField(default=dict), + ), + migrations.AlterField( + model_name='item', + name='sales_channels', + field=pretix.base.models.fields.MultiStringField(default=['web']), + ), + migrations.AlterField( + model_name='order', + name='sales_channel', + field=models.CharField(default='web', max_length=190), + ), + migrations.AlterField( + model_name='orderposition', + name='attendee_name_parts', + field=jsonfallback.fields.FallbackJSONField(default=dict), + ), + migrations.AlterField( + model_name='orderposition', + name='subevent', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.SubEvent'), + ), + migrations.AlterField( + model_name='orderposition', + name='voucher', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.Voucher'), + ), + migrations.AlterField( + model_name='staffsessionauditlog', + name='method', + field=models.CharField(max_length=255), + ), + migrations.AlterField( + model_name='user', + name='email', + field=models.EmailField(db_index=True, max_length=190, null=True, unique=True), + ), + ]