Come risolvere “Non è ansible aggiungere una colonna NOT NULL con il valore predefinito NULL” in SQLite3?

Sto ottenendo il seguente errore durante il tentativo di aggiungere una colonna NOT NULL a una tabella esistente. Perché sta succedendo? Ho provato rake db: reset pensando che i record esistenti sono il problema, ma anche dopo aver ripristinato il DB, il problema persiste. Per favore, aiutami a capirlo.

File di migrazione

class AddDivisionIdToProfile  false end def self.down remove_column :profiles, :division_id end end 

Messaggio di errore

SQLite3 :: SQLException: imansible aggiungere una colonna NOT NULL con il valore predefinito NULL: ALTER TABLE “profiles” ADD “division_id” intero NOT NOT NULL

    Hai già delle righe nella tabella e stai aggiungendo una nuova colonna division_id . Ha bisogno di qualcosa in quella nuova colonna in ciascuna delle righe esistenti.

    Solitamente SQLite sceglie NULL, ma hai specificato che non può essere NULL, quindi cosa dovrebbe essere? Non ha modo di saperlo.

    Vedere Aggiunta di una colonna non null senza valore predefinito in una migrazione Rails

    La raccomandazione di quel blog è di aggiungere la colonna senza il vincolo non nullo e verrà aggiunta con NULL in ogni riga. Quindi puoi compilare i valori in division_id e quindi usare change_column per aggiungere il vincolo non nullo.

    Vedi il blog a cui mi sono collegato per un esempio di uno script di migrazione che esegue questa procedura in tre passaggi.

    Questo è (quello che considererei) un problema tecnico con SQLite. Questo errore si verifica se ci sono dei record nella tabella o meno.

    Quando aggiungi una tabella da zero, puoi specificare NOT NULL, che è ciò che stai facendo con la notazione “: null => false”. Tuttavia, non puoi farlo quando aggiungi una colonna. Le specifiche di SQLite dicono che devi avere un valore predefinito per questo, che è una scelta sbagliata. L’aggiunta di un valore predefinito non è un’opzione perché elimina lo scopo di avere una chiave esterna NOT NULL, ovvero l’integrità dei dati.

    Ecco un modo per aggirare questo problema, e puoi fare tutto nella stessa migrazione. NOTA: questo è il caso in cui non si dispone già di record nel database.

     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 

    Stiamo aggiungendo la colonna senza il vincolo NOT NULL, quindi immediatamente alterando la colonna per aggiungere il vincolo. Possiamo farlo perché mentre SQLite è apparentemente molto preoccupato durante l’aggiunta di una colonna, non è così schizzinoso con le modifiche alle colonne. Questo è un chiaro odore di design nel mio libro.

    È decisamente un hack, ma è più breve di più migrazioni e continuerà a funzionare con database SQL più robusti nel tuo ambiente di produzione.

    Se si dispone di una tabella con righe esistenti, sarà necessario aggiornare le righe esistenti prima di aggiungere il vincolo null . La guida sulle migrazioni consiglia di utilizzare un modello locale, in questo modo:

    Rails 4 e versioni successive:

     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