Как получить дополнительную информацию о том, какие различия между полями были найдены с помощью управления makemigrations

Иногда при выполнении manage makemigrations в моем проекте Django я получаю неожиданную запись migrations.AlterField() в файле миграций. Я знаю, что это происходит из-за того, что Django видит различия между определением поля в моей модели и определением поля после применения всех миграций.

Есть ли способ узнать, какую разницу он видит? Я могу посмотреть на свои поля в моделях и использовать _meta для получения параметров поля, которые я не указал явно, но есть ли способ получить информацию о том, как выглядит поле на стороне миграции? Так сказать, сравнить целевое и фактическое состояние?

Я ищу информацию о том, есть ли, например, AlterField() в IntegerField, который был подхвачен, потому что max value изменился, или он имеет другой verbose_name.

На данный момент я просто вручную копаюсь в файлах миграции в поисках упоминаний соответствующего поля, но я работаю с устаревшим проектом, в котором их очень много. Поэтому мне интересно, сталкивался ли кто-нибудь с такой же проблемой и нашел ли лучшее решение.

Вы можете использовать python manage.py makemigrations app_name --dry-run -v 3 для проверки изменений в моделях приложений перед запуском реальных makemigrations.

Django has no means to print this, or at least not without patching the Django package. But the good news is, we can: you can look to the virtual environment for the location where Django is installed, and patch the django/db/migrations/autodetector.py file. Django uses this to detect changes. Indeed, in the source code, we see [GitHub]:

old_field_dec = self.deep_deconstruct(old_field)
new_field_dec = self.deep_deconstruct(new_field)
# ...
if old_field_dec != new_field_dec and old_field_name == field_name:
    # ...

Мы можем заменить это чем-то, что регистрирует изменения, например, какой-нибудь функцией для регистрации разницы:

MISSING = object()
def show_dict_diff(old, new):
    for k, v1 in new.items():
        v2 = old.get(k, MISSING)
        if v2 is MISSING:
            yield f"  Added: {k} = {v1}"
        elif v1 != v2:
            yield f"  Changed: {k} = {v2} -> {v1}"
    for k, v in old.items():
        if k not in new:
            yield f"  Removed: {k} = {v}"

и адаптируйте файл к:

old_field_dec = self.deep_deconstruct(old_field)
new_field_dec = self.deep_deconstruct(new_field)
# …
if old_field_dec != new_field_dec and old_field_name == field_name:
    print(f'change detected for {field_name}')
    show_dict_diff(old_field_dec[2], new_field_dec[2])
    # …

или как патч:

Index: django/db/migrations/autodetector.py
<+>UTF-8
===================================================================
diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py
--- a/django/db/migrations/autodetector.py  (revision 9dfcb719558c21dbc92754739cc8f4b6e6fdfa62)
+++ b/django/db/migrations/autodetector.py  (date 1726125682049)
@@ -19,6 +19,20 @@
 )
 from django.utils.functional import cached_property
 
+MISSING = object()
+
+
+def show_dict_diff(old, new):
+    for k, v1 in new.items():
+        v2 = old.get(k, MISSING)
+        if v2 is MISSING:
+            yield f"  Added: {k} = {v1}"
+        elif v1 != v2:
+            yield f"  Changed: {k} = {v2} -> {v1}"
+    for k, v in old.items():
+        if k not in new:
+            yield f"  Removed: {k} = {v}"
+
 
 class OperationDependency(
     namedtuple("OperationDependency", "app_label model_name field_name type")
@@ -1284,6 +1298,8 @@
             # db_column was allowed to change which generate_renamed_fields()
             # already accounts for by adding an AlterField operation.
             if old_field_dec != new_field_dec and old_field_name == field_name:
+                print(f'change detected for {field_name}')
+                show_dict_diff(old_field_dec[2], new_field_dec[2])
                 both_m2m = old_field.many_to_many and new_field.many_to_many
                 neither_m2m = not old_field.many_to_many and not new_field.many_to_many
                 if both_m2m or neither_m2m:
Вернуться на верх