Why does Django make migrations for help_text and verbose_name changes?
Asked Answered
E

8

58

When I change help_text or verbose_name for any of my model fields and run python manage.py makemigrations, it detects these changes and creates a new migration, say, 0002_xxxx.py.

I am using PostgreSQL and I think these changes are irrelevant to my database (I wonder if a DBMS for which these changes are relevant exists at all).

Why does Django generate migrations for such changes? Is there an option to ignore them?

Can I apply the changes from 0002_xxxx.py to the previous migration (0001_initial.py) manually and safely delete 0002_xxxx.py?

Is there a way to update previous migration automatically?

Exclamatory answered 22/10, 2014 at 9:1 Comment(1)
This comment from andrewgodwin answers the question partly but I still want to be able to change help_text without needing to update migrations: code.djangoproject.com/ticket/21498#comment:6Exclamatory
E
26

You can squash it with the previous migration, sure.

Or if you don't want to output those migrations at all, you can override the makemigrations and migrate command by putting this in management/commands/makemigrations.py in your app:

from django.core.management.commands.makemigrations import Command
from django.db import models

IGNORED_ATTRS = ['verbose_name', 'help_text', 'choices']

original_deconstruct = models.Field.deconstruct

def new_deconstruct(self):
  name, path, args, kwargs = original_deconstruct(self)
  for attr in IGNORED_ATTRS:
    kwargs.pop(attr, None)
  return name, path, args, kwargs

models.Field.deconstruct = new_deconstruct
Ecumenicism answered 30/9, 2016 at 23:12 Comment(2)
Thank you for pointing this out! Seems so obvious after the fact. This is a philosophical thing for some (complete migrations); that's nice in theory except it doesn't work in practice. Especially with help_text, choices, and storage! Our storage manager & path is determined at runtime so... what, you want me to make a fake storage for migrations? Gah. This solves it... separate runtime concerns from model concerns, as it should be.Methaemoglobin
You'll probably want to do this for the migrate command as well, to avoid the "Your models have changes that are not yet reflected" warning. I moved new_deconstruct to __init__.py and just assigned models.Field.deconstruct = new_deconstruct for both commands in separate files.Furthermore
T
15

This ticket addressed the problem.

If you have changed only help_text & django generates a new migration; then you can apply changes from latest migration to previous migration and delete the latest migration.

Just change the help_text in the previous migration to help_text present in latest migration and delete the latest migration file. Make sure to remove corresponding *.pyc file if it is present. Otherwise an exception will be raised.

Tradespeople answered 22/10, 2014 at 17:36 Comment(0)
S
9

To avoid unnecessary migrations You can do as follows:

  1. Subclass field that causes migration
  2. Write custom deconstruct method inside that field
  3. Profit

Example:

from django.db import models

class CustomCharField(models.CharField):  # or any other field

    def deconstruct(self):
        name, path, args, kwargs = super(CustomCharField, self).deconstruct()
        # exclude all fields you dont want to cause migration, my example below:
        if 'help_text' in kwargs:
            del kwargs['help_text']
        if 'verbose_name' in kwargs:
            del kwargs['verbose_name']
        return name, path, args, kwargs

Hope that helps

Sirenasirenic answered 24/3, 2015 at 13:11 Comment(0)
L
2

As @ChillarAnand noted there is a ticket solving this issue but until now (django 1.9.1) the migrations commands were not fixed.

The least intrusive way of fixing it is to create your own maketranslatedmigrations command in <your-project>/management/commands/maketranslatedmigrations.py as

#coding: utf-8

from django.core.management.base import BaseCommand
from django.core.management.commands.makemigrations import Command as MakeMigrations


class Command(MakeMigrations):
    leave_locale_alone = True
    can_import_settings = True

    def handle(self, *app_labels, **options):
        super(Command, self).handle(*app_labels, **options)

And then you can use it exactly the same as original makemigrations.

P.S. Don't forget to add __init__.py files everywhere on the path

Lysenkoism answered 10/1, 2016 at 8:56 Comment(1)
The original question was about makemigrations, not maketranslations.Exclamatory
V
1

I have written custom module for that purpose

All you need is to save it in some utils/models.db and in all your models instead of from django.db import models write from utils import models

If somebody is interested in it I can write a component and publish it on pypi

UPD: try this https://github.com/FeroxTL/django-migration-control

# models.py

# -*- coding: utf-8 -*-
from types import FunctionType
from django.db import models


class NoMigrateMixin(object):
    """
    Позволяет исключить из миграций различные поля
    """
    def deconstruct(self):
        name, path, args, kwargs = super(NoMigrateMixin, self).deconstruct()
        kwargs.pop('help_text', None)
        kwargs.pop('verbose_name', None)
        return name, path, args, kwargs


# =============================================================================
# DJANGO CLASSES
# =============================================================================

for name, cls in models.__dict__.items():
    if isinstance(cls, type):
        if issubclass(cls, models.Field):
            # Поля
            globals()[name] = type(name, (NoMigrateMixin, cls), {})
        else:
            # Всякие менеджеры
            globals()[name] = cls
    elif isinstance(cls, FunctionType):
        # Прочие функции
        globals()[name] = cls
Valvular answered 20/5, 2016 at 7:36 Comment(0)
L
1

The ticket that ChillarAnand mentions is very helpfull, but at final of changelog, I did not realize if it was fixed or not, or it was fixed in newest version of Django.

So, due to I did not found any solution for Django 1.9.13, I added a little hack to settings.py:

if 'makemigrations' in sys.argv:
    USE_I18N = False
    USE_L10N = False

Not elegant, but it works ok.

Lavern answered 11/6, 2018 at 18:48 Comment(0)
P
1

Stop to generate migrations files when changes verbose_name or verbose_name_plural in any of your model fields.

In a new file like src/monkey_patching/django/db/migrations/operations/change_model_options.py add this:

from django.db.migrations.operations import models

models.AlterModelOptions.ALTER_OPTION_KEYS = [
    "base_manager_name",
    "default_manager_name",
    "get_latest_by",
    "managed",
    "ordering",
    "permissions",
    "default_permissions",
    "select_on_save",
    # "verbose_name",
    # "verbose_name_plural",
]

Tested in django 1.11.10.

Patric answered 2/3, 2020 at 18:29 Comment(1)
but can you just show how this is used ? It seems not related anyhow to the question.Goatherd
S
0

From Django 2.X, using ugettext_lazy instead of ugettext or gettext fixes it.

Skateboard answered 22/8, 2019 at 6:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.