rails migrations generate foreign column ruby-on-rails database migration primary-key

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:

  1. use: id => falso para no generar una clave primaria entera
  2. use el tipo de datos deseado y agregue: null => falso
  3. 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.