Adding a non-nullable field to a populated table makes makemigrations ask for a one-off default. That value is used in a single UPDATE during the migration to fill existing rows. Historically Django did not then write a DEFAULT clause into the column, so inserts made outside the ORM, from raw SQL, another service, or a fixture loaded with a different code path, fail on a missing value.
Django 5.0 added db_default, which does put a real default in the schema and is what you want when anything other than Django writes to that table. default remains a Python-level value applied by the ORM at save time.
The safe sequence for a large table on Postgres is still to add the column as nullable, backfill in batches in a separate migration, then alter it to non-null, because a single migration that rewrites every row takes an ACCESS EXCLUSIVE lock for the duration. Inspect what Django will actually run with python manage.py sqlmigrate app_label 0002 before applying anything to production.