11

I'm using Django 3.2. I've changed added this line to settings.py:

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

I then ran these commands:

$ python manage.py makemigrations
$ python manage.py migrate

The makemigrations command creates new migration files for my apps, not just the apps that I have created, but also in my dependencies. For example, I'm using django-allauth, and this file was created in my virtual environment (virtualenv):

 .venv/lib/python3.8/site-packages/allauth/account/migrations/0003_auto_20210408_1526.py

This file is not shipped with django-allauth. When I deploy this application from git, this file is not included.

What should I do instead? How can I switch DEFAULT_AUTO_FIELD without the need to create new migration files for dependencies like django-allauth?

Flimm
  • 136,138
  • 45
  • 251
  • 267
  • Don't update to Django 3.2 until your dependencies do that. Individual apps can set [AppConfig.default_auto_field](https://docs.djangoproject.com/en/3.2/ref/applications/#django.apps.AppConfig.default_auto_field) to specify the default auto field to use for their app. I believe most of the popular packages would add this to their app configs to prevent such situations. – Abdul Aziz Barkat Apr 08 '21 at 14:52
  • Note that new versions of allauth don't have this problem any more. – Flimm Jan 19 '23 at 11:38

2 Answers2

13

Ideally, your third party dependencies would include this line in the config found in apps.py:

from django.apps import AppConfig

class ExampleConfig(AppConfig):
    default_auto_field = 'django.db.models.AutoField'

While waiting for upstream dependencies to update their apps.py or migration files, you can override the app config yourself. If it doesn't exist already, create an apps.py file in your main app directory (eg: project/apps.py), and override the config of a dependency. In this example, I'm overriding the config of django-allauth:

from allauth.account.apps import AccountConfig
from allauth.socialaccount.apps import SocialAccountConfig

class ModifiedAccountConfig(AccountConfig):
    default_auto_field = 'django.db.models.AutoField'

class ModifiedSocialAccountConfig(SocialAccountConfig):
    default_auto_field = 'django.db.models.AutoField'

Then modify INSTALLED_APPS in settings.py to look like this, replacing the old entries for django-allauth in this example:

INSTALLED_APPS = [
    # ....
    # replace: "allauth.account", with
    "projectname.apps.ModifiedAccountConfig",
    # replace: "allauth.socialaccount", with
    "projectname.apps.ModifiedSocialAccountConfig",
]

If the dependency doesn't have an apps.py file to override, you can still create an AppConfig sub-class in project/apps.py like this:

from django.apps import AppConfig

class ModifiedExampleDependencyConfig(AppConfig):
    name = 'exampledependency' # the python module
    default_auto_field = 'django.db.models.AutoField'

Now when you run python manage.py makemigrations, no migration files should be created for the dependencies.

Kim Stacks
  • 10,202
  • 35
  • 151
  • 282
Flimm
  • 136,138
  • 45
  • 251
  • 267
1

I work on a big project, we upgraded Django from 2.2. to 3.2 and then have got a need to create all new models with Big Integer (Int8) (PostgreSQL) field instead of default Integer (Int4).

When I defined it in settings.py:

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

I got the same problem, but with own apps - Django tried to make me to migrate 135 models I had, but I didn't want to do it. I only wanted to create new models with BigInt and manipuate olds manually.

I found the next solution. I changed the field to custom:

DEFAULT_AUTO_FIELD = 'project.db.models.CustomBigAutoField'

And then overrided its deconstruction:

from django.db import models

class CustomBigAutoField(models.BigAutoField):
    """Int8 field that is applied only for new models."""

    def deconstruct(self):
        name, path, args, kwargs = super().deconstruct()
        if getattr(self, 'model', None):
            path = 'django.db.models.AutoField'
        return name, path, args, kwargs

As I discovered, fields of new models don't have a back reference to their models, so path wouldn't be overridden for them. We override path because Django checks whether a model is changed by a key, that includes the path to this field. So we deceive Django and it thinks that existing model didn't changed.

I might not see the whole picture, but I tested it with different existing and new models and it worked for me. If someone tells me why this solution is bad, I'd be grateful.

Praetorian
  • 99
  • 1
  • 7