ruby-on-rails - migrations - rails migration default value
Usando Rails, ¿cómo puedo configurar mi clave principal para que no sea una columna de tipo entero? (14)
¿Qué tal esta solución?
Dentro del modelo Empleado, ¿por qué no podemos agregar código que verificará la singularidad en coloumn, por ejemplo: Supongamos que Empleado es Modelo en el sentido de que tiene EmpId, que es una cadena, entonces para eso podemos agregar ": unicidad => verdadero" a EmpId
class Employee < ActiveRecord::Base
validates :EmpId , :uniqueness => true
end
No estoy seguro de que esta sea la solución, pero esto funcionó para mí.
Estoy usando las migraciones de Rails para administrar un esquema de base de datos, y estoy creando una tabla simple en la que me gustaría utilizar un valor no entero como la clave principal (en particular, una cadena). Para abstraerme de mi problema, digamos que hay una tabla de employees
donde los empleados se identifican mediante una cadena alfanumérica, por ejemplo, "134SNW"
.
Intenté crear la tabla en una migración como esta:
create_table :employees, {:primary_key => :emp_id} do |t|
t.string :emp_id
t.string :first_name
t.string :last_name
end
Lo que esto me da es lo que parece que ignoró por completo la línea t.string :emp_id
y siguió adelante y la convirtió en una columna entera. ¿Hay alguna otra forma de que los rieles generen la restricción PRIMARY_KEY (estoy usando PostgreSQL) para mí, sin tener que escribir el SQL en una llamada de execute
?
NOTA : Sé que no es mejor utilizar columnas de cadena como claves principales, por lo que no hay respuestas que digan para agregar una clave primaria entera. De todos modos, puedo agregar uno, pero esta pregunta aún es válida.
Agregar el índice funciona para mí, estoy usando MySql por cierto.
create_table :cards, {:id => false} do |t|
t.string :id, :limit => 36
t.string :name
t.string :details
t.datetime :created_date
t.datetime :modified_date
end
add_index :cards, :id, :unique => true
Desafortunadamente, he determinado que no es posible hacerlo sin usar execute
.
Por qué no funciona
Al examinar el origen de ActiveRecord, podemos encontrar el código para create_table
:
En schema_statements.rb
:
def create_table(table_name, options={})
...
table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
...
end
Entonces, podemos ver que cuando intentas especificar una clave primaria en las opciones create_table
, crea una clave principal con ese nombre especificado (o, si no se especifica ninguno, id
). Lo hace llamando al mismo método que puede usar dentro de un bloque de definición de tabla: primary_key
.
En schema_statements.rb
:
def primary_key(name)
column(name, :primary_key)
end
Esto solo crea una columna con el nombre de tipo especificado :primary_key
. Esto se establece a lo siguiente en los adaptadores de base de datos estándar:
PostgreSQL: "serial primary key"
MySQL: "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
SQLite: "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL"
La solución
Como estamos atrapados con estos como tipos de clave primaria, debemos usar execute
para crear una clave primaria que no sea un entero (la serial
de PostgreSQL es un entero que usa una secuencia):
create_table :employees, {:id => false} do |t|
t.string :emp_id
t.string :first_name
t.string :last_name
end
execute "ALTER TABLE employees ADD PRIMARY KEY (emp_id);"
Y como mencionó Sean McCleary , su modelo ActiveRecord debe establecer la clave principal usando set_primary_key
:
class Employee < ActiveRecord::Base
set_primary_key :emp_id
...
end
Después de casi todas las soluciones que dicen "esto funcionó para mí en la base de datos X", veo un comentario del cartel original que dice "no funcionó para mí en Postgres". El verdadero problema aquí puede ser, de hecho, el soporte de Postgres en Rails, que no es perfecto, y probablemente fue peor en 2009 cuando esta pregunta se publicó originalmente. Por ejemplo, si recuerdo correctamente, si está en Postgres, básicamente no puede obtener resultados útiles de rake db:schema:dump
.
Yo no soy un ninja de Postgres, obtuve esta información del excelente video PeepCode de Xavier Shay en Postgres. Ese video en realidad pasa por alto una biblioteca de Aaron Patterson, creo que es Texticle pero podría estar recordando que estaba equivocado. Pero aparte de eso, es bastante bueno.
De todos modos, si te encuentras con este problema en Postgres, mira si las soluciones funcionan en otras bases de datos. Tal vez use los rails new
para generar una nueva aplicación como un cajón de arena, o simplemente crear algo así como
sandbox:
adapter: sqlite3
database: db/sandbox.sqlite3
pool: 5
timeout: 5000
en config/database.yml
.
Y si puede verificar que se trata de un problema de soporte de Postgres, y encuentra una solución, contribuya parches a Rails o empaque sus correcciones en una gema, porque la base de usuarios de Postgres dentro de la comunidad de Rails es bastante grande, principalmente gracias a Heroku. .
El truco que funcionó para mí en Rails 3 y MySQL fue este:
create_table :events, {:id => false} do |t|
t.string :id, :null => false
end
add_index :events, :id, :unique => true
Asi que:
- use: id => falso para no generar una clave primaria entera
- use el tipo de datos deseado y agregue: null => falso
- agregue un índice único en esa columna
¡Parece que MySQL convierte el índice único en una columna no nula en una clave primaria!
En Rails 5 puedes hacer
create_table :employees, id: :string do |t|
t.string :first_name
t.string :last_name
end
Ver la documentación de create_table .
Encontré una solución a esto que funciona con Rails 3:
El archivo de migración:
create_table :employees, {:primary_key => :emp_id} do |t|
t.string :emp_id
t.string :first_name
t.string :last_name
end
Y en el modelo employee.rb:
self.primary_key = :emp_id
Estoy en Rails 2.3.5 y mi siguiente manera funciona con SQLite3
create_table :widgets, { :primary_key => :widget_id } do |t|
t.string :widget_id
# other column definitions
end
No hay necesidad de: id => falso.
Lo he probado en Rails 4.2. Para agregar su clave principal personalizada, puede escribir su migración como:
# tracks_ migration
class CreateTracks < ActiveRecord::Migration
def change
create_table :tracks, :id => false do |t|
t.primary_key :apple_id, :string, limit: 8
t.string :artist
t.string :label
t.string :isrc
t.string :vendor_id
t.string :vendor_offer_code
t.timestamps null: false
end
add_index :tracks, :label
end
end
Mientras mira la documentación de la column(name, type, options = {})
y lee la línea:
El parámetro
type
es normalmente uno de los tipos nativos de las migraciones, que es uno de los siguientes: clave_primaria, cadena: texto, entero, flotación, decimal, fecha de tiempo, fecha, fecha, binario, booleano
Obtuve los ides anteriores como he mostrado. Aquí están los metadatos de la tabla después de ejecutar esta migración:
[arup@music_track (master)]$ rails db
psql (9.2.7)
Type "help" for help.
music_track_development=# /d tracks
Table "public.tracks"
Column | Type | Modifiers
-------------------+-----------------------------+-----------
apple_id | character varying(8) | not null
artist | character varying |
label | character varying |
isrc | character varying |
vendor_id | character varying |
vendor_offer_code | character varying |
created_at | timestamp without time zone | not null
updated_at | timestamp without time zone | not null
title | character varying |
Indexes:
"tracks_pkey" PRIMARY KEY, btree (apple_id)
"index_tracks_on_label" btree (label)
music_track_development=#
Y desde la consola de Rails:
Loading development environment (Rails 4.2.1)
=> Unable to load pry
>> Track.primary_key
=> "apple_id"
>>
Parece que es posible hacerlo con este enfoque:
create_table :widgets, :id => false do |t|
t.string :widget_id, :limit => 20, :primary => true
# other column definitions
end
class Widget < ActiveRecord::Base
set_primary_key "widget_id"
end
Esto hará que la columna widget_id sea la clave principal para la clase Widget, luego depende de usted completar el campo cuando se crean los objetos. Debería poder hacerlo utilizando antes de crear una devolución de llamada.
Así que algo a lo largo de las líneas de
class Widget < ActiveRecord::Base
set_primary_key "widget_id"
before_create :init_widget_id
private
def init_widget_id
self.widget_id = generate_widget_id
# generate_widget_id represents whatever logic you are using to generate a unique id
end
end
Sé que este es un hilo viejo con el que tropecé ... pero estoy algo sorprendido de que nadie haya mencionado a DataMapper.
Creo que si necesitas alejarte de la convención de ActiveRecord, he encontrado que es una gran alternativa. También es un mejor enfoque para el legado y puede soportar la base de datos "tal como está".
¡Ruby Object Mapper (DataMapper 2) es muy prometedor y también se basa en los principios de AREL!
Tengo una forma de manejar esto. El SQL ejecutado es ANSI SQL, por lo que probablemente funcione en la mayoría de las bases de datos relacionales compatibles con ANSI SQL. He probado que esto funciona para MySQL.
Migración:
create_table :users, :id => false do |t|
t.string :oid, :limit => 10, :null => false
...
end
execute "ALTER TABLE users ADD PRIMARY KEY (oid);"
En tu modelo haz esto:
class User < ActiveRecord::Base
set_primary_key :oid
...
end
tienes que usar la opción: id => falso
create_table :employees, :id => false, :primary_key => :emp_id do |t|
t.string :emp_id
t.string :first_name
t.string :last_name
end
Esto funciona:
create_table :employees, :primary_key => :emp_id do |t|
t.string :first_name
t.string :last_name
end
change_column :employees, :emp_id, :string
Puede que no sea lindo, pero el resultado final es exactamente lo que quieres.