web-dev-qa-db-fra.com

Rails Migrations: a essayé de changer le type de colonne de chaîne en entier

J'ai créé une table dans mon Rails avec Rails commande de génération de migrations. Voici ce fichier de migration:

class CreateListings < ActiveRecord::Migration
  def change
    create_table :listings do |t|
      t.string :name
      t.string :telephone
      t.string :latitude
      t.string :longitude

      t.timestamps
    end
  end
end

Ensuite, je voulais stocker la latitude et la longitude sous forme d'entiers alors j'ai essayé de lancer:

Rails generate migration changeColumnType

et le contenu de ce fichier sont:

class ChangeColumnType < ActiveRecord::Migration
  def up
    #change latitude columntype from string to integertype
    change_column :listings, :latitude, :integer
    change_column :listings, :longitude, :integer
    #change longitude columntype from string to integer type
  end

  def down  
  end
end

Je m'attendais à ce que le type de colonne change, mais le râteau a été abandonné et le message d'erreur suivant est apparu. Je me demandais pourquoi cela n'a pas abouti? J'utilise postgresql dans mon application.

rake db:migrate
==  ChangeColumnType: migrating ===============================================
-- change_column(:listings, :latitude, :integer)
rake aborted!
An error has occurred, this and all later migrations canceled:

PG::Error: ERROR:  column "latitude" cannot be cast to type integer
: ALTER TABLE "listings" ALTER COLUMN "latitude" TYPE integer

Tasks: TOP => db:migrate
(See full trace by running task with --trace)

REMARQUE: le tableau ne contient aucune donnée. Merci

30
banditKing

Je cite le manuel environ ALTER TABLE :

Une clause USING doit être fournie s'il n'y a pas de conversion implicite ou d'affectation de l'ancien vers le nouveau type.

Ce dont vous avez besoin c'est:

 ALTER TABLE listes ALTER longitude TYPE entier UTILISATION longitude :: int; 
 ALTER TABLE listes ALTER latitude TYPE entier UTILISATION de latitude :: int; 

Ou plus court et plus rapide (pour les grandes tables) en une seule commande:

ALTER TABLE listings ALTER longitude TYPE integer USING longitude::int
                    ,ALTER latitude  TYPE integer USING latitude::int;

Cela fonctionne avec ou sans données tant que toutes les entrées sont convertibles en integer.
Si vous avez défini un DEFAULT pour la colonne, vous devrez peut-être le supprimer et le recréer pour le nouveau type.

Voici article de blog sur la façon de procéder avec ActiveRecord .
Ou suivez les conseils de @ mu dans le commentaire. Il connaît son Ruby. Je ne suis bon qu'avec PostgreSQL ici.

25
Erwin Brandstetter

J'inclurais le SQL brut dans votre fichier de migration comme ci-dessous pour qu'il mette à jour schema.rb.

class ChangeColumnType < ActiveRecord::Migration
  def up
    execute 'ALTER TABLE listings ALTER COLUMN latitude TYPE integer USING (latitude::integer)'
    execute 'ALTER TABLE listings ALTER COLUMN longitude TYPE integer USING (longitude::integer)'
  end

  def down
    execute 'ALTER TABLE listings ALTER COLUMN latitude TYPE text USING (latitude::text)'
    execute 'ALTER TABLE listings ALTER COLUMN longitude TYPE text USING (longitude::text)'
  end
end
22
neverbendeasy

Je sais que c'est un peu moche, mais je préfère simplement supprimer la colonne et ajouter à nouveau avec le nouveau type:

 def change
     remove_column :mytable, :mycolumn
     add_column :mytable, :mycolumn, :integer, default: 0
 end
21
rizidoro

Ce qui suit est un plus Rails way pour aborder le problème. Pour mon cas, j'avais deux colonnes dans ma table d'achats que j'avais besoin de convertir du type chaîne en flottant.

def change
    change_column :purchases, :mc_gross, 'float USING CAST(mc_gross AS float)'
    change_column :purchases, :mc_fee, 'float USING CAST(mc_fee AS float)'
end

Cela a fait l'affaire pour moi.

11
Joseph N.
  1. Avez-vous des données existantes dans ces colonnes?
  2. Vous ne devez pas utiliser int pour la latitude et la longitude. Ils devraient plutôt être en virgule flottante.
2
Victor

la latitude et la longitude sont décimales

Rails g scaffold client name:string email:string 'latitude:decimal{12,3}' 'longitude:decimal{12,3}' 

class CreateClients < ActiveRecord::Migration[5.0]
  def change
    create_table :clients do |t|
      t.string :name
      t.string :email
      t.decimal :latitude, precision: 12, scale: 3
      t.decimal :longitude, precision: 12, scale: 3

      t.timestamps
    end
  end
end
0
gilcierweb