67 votes

Comment résoudre "Impossible d'ajouter une colonne NOT NULL avec la valeur par défaut NULL" dans SQLite3?

J'obtiens l'erreur suivante en essayant d'ajouter une colonne not NULL à une table existante. Pourquoi est-ce qui se passe ?. J'ai essayé de rake db:réinitialisation de penser que les enregistrements existants sont le problème, mais même après la réinitialisation de la base de données, le problème persiste. Pouvez-vous svp m'aider à comprendre cela.

Fichier De Migration

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

Message D'Erreur

SQLite3::SQLException: Impossible d'ajouter une colonne not NULL avec la valeur par défaut NULL: ALTER TABLE "profils" AJOUTER "division_id" integer not NULL

175voto

Jaime Bellmyer Points 13815

C'est (ce que je considère être) un pépin avec SQLite. Cette erreur se produit s'il existe des enregistrements dans la table ou pas.

Lors de l'ajout d'une table à partir de zéro, vous pouvez spécifier de ne PAS NULLE, ce qui est ce que vous faites avec l' ":null => false" notation. Cependant, vous ne pouvez pas le faire lors de l'ajout d'une colonne. SQLite de la spécification indique que vous devez avoir une valeur par défaut pour ce, qui est un mauvais choix. L'ajout d'une valeur par défaut n'est pas une option, car il défait le but d'avoir un PAS NULL, foreign key - à savoir, l'intégrité des données.

Voici un moyen de contourner ce problème, et vous pouvez le faire dans la même migration. REMARQUE: c'est pour le cas où vous n'avez pas d'enregistrements dans la base de données.

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

Nous sommes l'ajout de la colonne sans la contrainte de NON nullité, puis immédiatement à modifier la colonne à ajouter la contrainte. Nous pouvons le faire parce que, tandis que SQLite est apparemment très concerné au cours d'une colonne à ajouter, il n'est pas si difficile avec des modifications de colonne. C'est un design clair odeur dans mon livre.

C'est certainement un hack, mais il est plus court que de multiples migrations et il faudra encore travailler avec les plus robustes, les bases de données SQL dans votre environnement de production.

51voto

Bill Karwin Points 204877

Vous avez déjà des lignes dans la table, et vous êtes en train d'ajouter une nouvelle colonne division_id. Il a besoin de quelque chose dans cette nouvelle colonne dans chacune des lignes existantes.

SQLite choisissez généralement NULLE, mais vous avez spécifié qu'il ne peut pas être NULLE, de sorte que devrait-il être? Il n'a aucun moyen de le savoir.

Voir Ajout d'un Non-nulle de la Colonne avec pas de Valeur par Défaut dans un Rails de la Migration

Ce blog est la recommandation est d'ajouter la colonne sans la contrainte de non nullité, et il sera ajouté à la valeur NULL dans chaque ligne. Ensuite, vous pouvez spécifier les valeurs en division_id , puis utiliser change_column d'ajouter la contrainte not null.

Voir le blog de je lien pour un exemple de script de migration qui ne ce processus en trois étapes.

6voto

JosephL Points 4786

Si vous avez une table avec des lignes existantes, vous devrez mettre à jour les lignes existantes avant d'ajouter votre contrainte null. Le Guide sur les migrations recommande d'utiliser un modèle local comme suit:

Rails 4 et plus:

 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 |profile|
      profile.update_attributes!(:devision_id => Division.first.id)
    end

    change_column :profiles, :division_id, :integer, :null => false
  end

end
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X