2010-07-03 17 views
49

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?Cómo solucionar "No se puede agregar una columna NOT NULL con valor predeterminado NULL" en SQLite3?

Migración de archivos

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 :: excepción de SQL: No se puede agregar una columna NOT NULL con el valor predeterminado NULL: ALTER TABLE "perfiles" ADD " division_id "entero NOT NULL

Respuesta

34

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

Ver Adding a Non-null Column with no Default Value in a Rails Migration

que la recomendación del blog es añadir la columna sin la restricción no nula, y va a ser añadido con NULL en cada fila. Luego puede completar los valores en division_id y luego usar change_column para agregar la restricción no nula.

Consulte el blog al que he vinculado para obtener un ejemplo de una secuencia de comandos de migración que realiza este proceso de tres pasos.

+6

Su suposición acerca de que ya son filas de la tabla sonidos correctos, y se sería con cualquier otro rdbms. Sin embargo, noté en mi respuesta que SQLite es una excepción a esto. Este error aparece incluso cuando la tabla está vacía, así que publiqué una solución más corta. –

+0

Esto no es correcto: sqlite no tiene forma de cambiar columnas. No sé qué es 'change_column', pero no es sqlite. – Benubird

+0

@Benubird, 'change_column' es un método API en [migraciones de Ruby on Rails] (http://guides.rubyonrails.org/active_record_migrations.html), que es lo que el OP estaba preguntando. –

141

Esto es (lo que yo 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.

Aquí hay una forma de evitar este problema, y ​​puede 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, y 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.

+0

Sí, buena respuesta, +1 de mí –

+0

¡Gracias! Esto es exactamente lo que necesitaba. – PBJ

+0

funciona! Gracias por la ayuda. – Hendrik

6

Si tiene una tabla con filas existentes, entonces deberá actualizar las filas existentes antes de agregar su restricción null.El Guide on migrations recomienda el uso de un modelo local, así:

carriles 4 y hasta:

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 

Rails 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 
+0

Si desea que su migración sea reversible ('rake db: rollback') agregue el método' down' y reemplace 'change' with' up' – sampi

Cuestiones relacionadas