Cómo resolver “No se puede agregar una columna NOT NULL con valor predeterminado NULL” en SQLite3?

Recibo el siguiente error al intentar agregar una columna NOT NULL a una tabla existente. ¿Por qué está sucediendo? Intenté rake db: restablecer pensando que los registros existentes son el problema, pero incluso después de restablecer el DB, el problema persiste. ¿Puedes ayudarme a resolver esto?

Archivo de migración

class AddDivisionIdToProfile  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 valor predeterminado NULL: ALTER TABLE “profiles” ADD “division_id” entero NOT NULL

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 NULL, pero has especificado que no puede ser NULL, entonces, ¿cuál debería ser? No tiene forma de saberlo.

Consulte Agregar 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 obtener un ejemplo de un script de migración que realiza este proceso de tres pasos.

Esto es (lo que consideraría) un problema con SQLite. Este error se produce si hay algún registro 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 cuando agrega 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 externa NOT NULL, es decir, integridad de datos.

Esta es una forma de evitar este problema, y ​​puedes hacerlo todo en la misma migración. NOTA: esto es para el caso donde 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, luego alteramos inmediatamente la columna para agregar la restricción. Podemos hacer esto porque aunque SQLite aparentemente está muy preocupado durante el agregado de una columna, no es tan quisquilloso con los cambios de columna. Este es un olor de diseño claro en mi libro.

Definitivamente es un truco, pero es más corto que migraciones múltiples y aún funcionará con bases de datos SQL más robustas en su entorno de producción.

Si tiene una tabla con filas existentes, necesitará actualizar las filas existentes antes de agregar su restricción null . La Guía sobre migraciones recomienda usar un modelo local, así:

Rieles 4 y más:

 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 

Carriles 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