ruby on rails - ¿Cómo resolver "No se puede agregar una columna NOT NULL con el valor predeterminado NULL" en SQLite3?
ruby-on-rails database (3)
Esto es (lo que yo consideraría) una falla con SQLite. Este error se produce si hay registros en la tabla o no.
Al agregar una tabla desde cero, puede especificar NOT NULL, que es lo que está haciendo con la notación ": null => false". Sin embargo, no puede hacer esto al agregar una columna. La especificación de SQLite dice que tiene que tener un valor predeterminado para esto, que es una mala elección. Agregar un valor predeterminado no es una opción porque anula el propósito de tener una clave foránea NO NULA, es decir, la integridad de los datos.
Aquí hay una manera de evitar este problema, y puede hacerlo todo en la misma migración. NOTA: esto es para el caso en el que aún no tiene registros en la base de datos.
class AddDivisionIdToProfile < ActiveRecord::Migration
def self.up
add_column :profiles, :division_id, :integer
change_column :profiles, :division_id, :integer, :null => false
end
def self.down
remove_column :profiles, :division_id
end
end
Estamos agregando la columna sin la restricción NOT NULL, y luego la alteramos inmediatamente para agregar la restricción. Podemos hacer esto porque si bien SQLite aparentemente está muy preocupado durante un agregado de columna, no es tan delicado con los cambios de columna. Este es un claro olor a diseño en mi libro.
Definitivamente es un truco, pero es más corto que múltiples migraciones y aún funcionará con bases de datos SQL más robustas en su entorno de producción.
Recibo el siguiente error al intentar agregar una columna NOT NULL a una tabla existente. ¿Por qué está sucediendo? Intenté rake db: reset pensando que los registros existentes son el problema, pero incluso después de reiniciar el DB, el problema persiste. ¿Puedes por favor ayudarme a resolver esto?
Archivo de migración
class AddDivisionIdToProfile < ActiveRecord::Migration
def self.up
add_column :profiles, :division_id, :integer, :null => false
end
def self.down
remove_column :profiles, :division_id
end
end
Mensaje de error
SQLite3 :: SQLException: No se puede agregar una columna NOT NULL con el valor predeterminado NULL: ALTER TABLE "perfiles" ADD "division_id" entero NOT NULL
Si tiene una tabla con filas existentes, deberá actualizar las filas existentes antes de agregar su restricción null
. La Guía sobre migraciones recomienda usar un modelo local, como por ejemplo:
Rieles 4 y superiores:
class AddDivisionIdToProfile < ActiveRecord::Migration
class Profile < ActiveRecord::Base
end
def change
add_column :profiles, :division_id, :integer
Profile.reset_column_information
reversible do |dir|
dir.up { Profile.update_all division_id: Division.first.id }
end
change_column :profiles, :division_id, :integer, :null => false
end
end
Rieles 3
class AddDivisionIdToProfile < ActiveRecord::Migration
class Profile < ActiveRecord::Base
end
def change
add_column :profiles, :division_id, :integer
Profile.reset_column_information
Profile.all.each do |profile|
profile.update_attributes!(:division_id => Division.first.id)
end
change_column :profiles, :division_id, :integer, :null => false
end
end
Ya tiene filas en la tabla y está agregando una nueva columna division_id
. Necesita algo en esa nueva columna en cada una de las filas existentes.
SQLite normalmente elegiría NULL, pero ha especificado que no puede ser NULL, entonces, ¿qué debería ser? No tiene forma de saberlo.
Consulte Adición de una columna no nula sin valor predeterminado en una migración de Rails
La recomendación de ese blog es agregar la columna sin la restricción no nula, y se agregará con NULL en cada fila. Luego puede completar los valores en el division_id
y luego usar change_column
para agregar la restricción no nula.
Vea el blog al que me he vinculado para ver un ejemplo de un script de migración que realiza este proceso de tres pasos.