Error: Provide a one-off default now

Si en alguna ocasión al trabajar con Django, y correr una migración recibes el error Provide a one-off default now, entonces este post puede serte de ayuda.

Este error, suele suceder cuando añadimos a un modelo ya existente, una nueva llave (ForeingKey), con un valor determinado.

En este post mostraré dos formas de hacerlo.

Siguiendo la documentación, Añadir una ForeignKey (non-nullable) en Django se divide en tres pasos:

Veamos primero la situación inicial:

class Post(models.Model):
    name = models.CharField(max_length=100)

Paso 1.

Primero se añade la nueva llave, donde primero definiremos null, como null=TRUE y luego desde consola correremos makemigrations().

Esto creará una nueva migración que añade el campo que hemos creado.a la tabla, por tanto ahora tenemos una nueva columna con todas sus filas con valor Null.

class Post(models.Model):
    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category, null=True, on_delete=models.CASCADE))

$ python manage.py makemigrations
Migrations for 'mi_App':
  mi_App/migrations/0002_post_category.py
    - Add field author to post

Paso 2

Ahora correremos nuevamente makemigrations, creando una una nueva migración vacía  (makemigrations --empty).

$ python manage.py makemigrations  --empty -n mi_App assign_category
Migrations for 'mi_App':
 mi_App/migrations/0003_assign_category.py

Ahora  editamos esta migración para que contenga los datos que deseamos, y es en este momento donde de acuerdo a nuestra necesidades colocaremos el valor que debe tener la nueva Foreign key.

from django.db import migrations

def assign_category(apps, schema_editor):
    Category= apps.get_model('category', 'Category')  
    Post = apps.get_model('mi_App', 'Post')               
    
    Post.objects.all().update(category=category)  # and bulk update all posts.


class Migration(migrations.Migration):

    dependencies = [...]

    operations = [
        migrations.RunPython(assign_category, migrations.RunPython.noop),
    ]

Otros posts sobre Django

Paso 3

En el tercer paso modificaremos la nueva ForeignKey en nuestro modelo, eliminando la posibilidad de valor nulo:

null=False 

class Post(models.Model):
    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category, null=False, on_delete=models.CASCADE))

Y creamos una nueva migración corriendo nuevamente makemigrations.

$ python manage.py makemigrations mi_App -n post_category_non_null

Recibirás este mensaje:

You are trying to change the nullable field 'author' on something. to non-nullable without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Ignore for now, and let me handle existing rows with NULL myself (e.g. because you added a RunPython or RunSQL operation to handle NULL values in a previous data migration)
 3) Quit, and let me add a default in models.py

Seleccionamos la opción 2 y problema resuelto.

Migrations for 'mi_App':
  something/migrations/0004_post_author_non_null.py
    - Alter field category on post

Este es el camino más largo, que sugiere la documentación, pero como siempre hay una posibilidad mas corta:

  1. En la carpeta migraciones eliminamos todas la migraciones existentes,
  2. corremos nuevamente makemigrations, con las modificaciones nuevas.
  3. corremos migrate
  4. creamos nuevamente el superusuario

Con esto quedaría resuelto.

Espero sinceramente, que esto sirva de ayuda a alguien .

……..y para cuando a ti te estén rompiendo el alma, ya el tiempo habrá cicatrizado mis heridas.

A. Torres

Un saludo