Migraciones de rails-cambiar columna con conversión de tipo


Yo ya aroung Google un poco y parece que no hay respuesta satisfactoria para mi problema.

Tengo una tabla con columna de tipo string. Me gustaría ejecutar la siguiente migración:

class ChangeColumnToBoolean < ActiveRecord::Migration
    def up
        change_column :users, :smoking, :boolean
    end
end

Cuando corro esto obtengo el siguiente error

PG::Error: ERROR:  column "smoking" cannot be cast automatically to type boolean
HINT:  Specify a USING expression to perform the conversion.
: ALTER TABLE "users" ALTER COLUMN "smoking" TYPE boolean

Sé que puedo realizar esta migración usando SQL puro, pero aún así sería mejor si pudiera hacerlo con Rails. Pasé por el código Rails y parece que no hay tal posibilidad, pero tal vez alguien sabe una manera?

No soy interesar: - SQL puro - dejar caer la columna - la creación de otra columna, la conversión de datos, dejar caer original y luego cambiar el nombre

Author: Mike Szyndel, 2013-06-13

4 answers

Si sus cadenas en la columna smoking ya son valores booleanos válidos, la siguiente instrucción cambiará el tipo de columna sin perder datos:

change_column :users, :smoking, 'boolean USING CAST(smoking AS boolean)'

De manera similar, puede usar esta instrucción para convertir columnas en enteros:

change_column :table_name, :column_name, 'integer USING CAST(column_name AS integer)'

Estoy usando Postgres. No estoy seguro de si esta solución funciona para otras bases de datos.

 102
Author: Brian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-03 08:35:34

No todas las bases de datos permiten cambiar el tipo de columna, el enfoque generalmente adoptado es agregar una nueva columna del tipo deseado, traer cualquier dato, eliminar la columna anterior y cambiar el nombre de la nueva.

add_column :users, :smoking_tmp, :boolean

User.reset_column_information # make the new column available to model methods
User.all.each do |user|
  user.smoking_tmp = user.smoking == 1 ? true : false # If smoking was an int, for example
  user.save
end

# OR as an update all call, set a default of false on the new column then update all to true if appropriate.
User.where(:smoking => 1).update_all(:smoking_tmp = true) 

remove_column :users, :smoking
rename_column :users, :smoking_tmp, :smoking
 34
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-14 12:47:35

Así que correcto para booleano en postgres:

change_column :table_name, :field,'boolean USING (CASE field WHEN \'your any string as true\' THEN \'t\'::boolean ELSE \'f\'::boolean END)'

Y usted puede añadir un poco más WHEN - THEN condición en su expresión

Para otros servidores de base de datos, la expresión se construirá en base a la sintaxis de su servidor de base de datos, pero el principio es el mismo. Solo algoritmo de conversión manual, totalmente sin SQL no hay suficiente por desgracia.

Way with syntax change_column :table, :filed, 'boolean USING CAST(field AS boolean)' es adecuado solo si el contenido del campo es algo como: true / false / null

 6
Author: Денис Епишкин,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-03 15:05:23

Ya que estoy usando Postgres, me fui con la solución SQL por ahora. Consulta utilizada:

    execute 'ALTER TABLE "users" ALTER COLUMN "smoking" TYPE boolean USING CASE WHEN "flatshare"=\'true\' THEN \'t\'::boolean ELSE \'f\'::boolean END'

Funciona solo si uno tiene un campo lleno de cadenas true/false (como el ayudante de colección de botones de opción predeterminados con tipo booleano forzado generaría)

 5
Author: Mike Szyndel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-12 21:34:59