Estoy usando Rieles de las migraciones para administrar un esquema de base de datos, y estoy creando una tabla simple, donde me gustaría usar un valor entero como la clave principal (en particular, una cadena). A lo abstracto, lejos de mi problema, digamos que hay una tabla employees donde los empleados se identifican por una cadena alfanumérica, por ejemplo,"134SNW".

He tratado de creación de la tabla en una migración como este:

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ó completamente la línea de t.string :emp_id y siguió adelante e hizo una columna de tipo entero. Existe alguna otra manera de tener rieles de generar la PRIMARY_KEY restricción (estoy usando PostgreSQL) para mí, sin tener que escribir el SQL en un execute llamar?

NOTA: sé que no es mejor el uso de las columnas de la cadena como claves principales, así que por favor no contesta diciendo simplemente para añadir un número entero de clave principal. Me pueden agregar de todos modos, pero esta cuestión es todavía válida.

  • Tengo el mismo problema y me gustaría ver una respuesta para esto. Ninguna de las sugerencias de los que hasta ahora han trabajado.
  • Ninguno de ellos va a trabajar si usted está usando Postgres. Dan Chak de la «Empresa » Rieles» tiene algunos consejos para el uso de los recursos naturales/claves compuestas.
  • Ten en cuenta que cuando se utiliza algo como: <!– idioma: lang-rb –> ejecutar «ALTER TABLE empleados ADD PRIMARY KEY (emp_id);» a Pesar de la restricción de tabla se establece correctamente después de ejecutar rake db:migrate La auto-generado definición de esquema no contiene esta restricción!

14 Comentarios

  1. 107

    Por desgracia, he determinado que no es posible hacerlo sin el uso de execute.

    ¿Por qué no funciona

    Mediante el examen de la ActiveRecord fuente, 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

    Así que podemos ver que cuando se intenta especificar una clave principal en la create_table opciones, se crea una clave principal con el nombre especificado (o, si no se especifica ninguno, id). Esto se hace llamando el mismo método que se puede utilizar dentro de una tabla de definición de bloque: primary_key.

    En schema_statements.rb:

    def primary_key(name)
      column(name, :primary_key)
    end

    Esto sólo crea una columna en la que se especifica el nombre del tipo de :primary_key. Este número es el siguiente en la base de datos estándar adaptadores:

    PostgreSQL: "serial primary key"
    MySQL: "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
    SQLite: "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL"

    La solución

    Ya que estamos atascados con estos como los de clave principal, hemos de utilizar execute para crear una clave principal que no es un número entero (de PostgreSQL serial es un entero usando 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 Sean McCleary mencionado, su modelo ActiveRecord debe establecer la clave principal con set_primary_key:

    class Employee < ActiveRecord::Base
      set_primary_key :emp_id
      ...
    end
    • la solución va a hacer el rake db:prueba:clon o rake db:esquema:carga a crear emp_id como columna de tipo entero.
    • en realidad, ahora usted debe estar utilizando self.primary_key= en lugar de set_primary_key, ya que este último está en desuso.
    • Aquí está el solo la solución que funcionó para mí. Espero ayuda a los demás: stackoverflow.com/a/15297616/679628
    • El problema con este enfoque es que cuando el programa de instalación de la base de datos de schema.rb emp_id será un integer tipo de columna de nuevo. Parece que schema.rb no puede almacenar execute "ALTER TABLE employees ADD PRIMARY KEY (emp_id);" de alguna manera.
    • Usted puede cambiar el esquema de volcado de schema.rb a structure.sql a través de config.active_record.schema_format configuración, la cual puede ser :sql o :ruby. guides.rubyonrails.org/v3.2.13/…
    • para aquellos que quieren utilizar :ruby formato de esquema, hay una solución no utilizar execute declaración, que se describe a continuación: ruby-journal.com/…
    • Existe una última alternativa a las respuestas de @DonnyKurnia @Tintin81 y @SvilenIvanov si usted realmente necesita el db:test:clone y db:schema:load los procesos de trabajo (que hicimos para rspec pruebas). No es lo ideal, pero es de menor riesgo que el cambio a structure.sql si usted tiene una aplicación de gran tamaño. Es que hay clave primaria en la DB. En su lugar, añadir un índice único en que el uso de add_index :employees, :emp_id, unique: true, y establecer el primary_key en el modelo de código. De esa manera, se obtiene DB singularidad y el rendimiento, los Rieles de la trata como un primary_key, y su schema.rb coincide con su DB perfectamente.

  2. 21

    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

    No puede ser bonito, pero el resultado final es exactamente lo que usted desea.

    • Por fin! Esta es la única solución que trabajó para mí.
    • Qué tenemos que especificar el código para la migración de abajo también? o es Rieles lo suficientemente inteligente como para saber qué hacer cuando migraron?
    • La migración: drop_table :employees
    • Desafortunadamente, este método también nos deja con un schema.rb que no está en sintonía … conservará la :primary_key => :emp_id declaración, pero cuando rake db:schema:load se llama, como es en el comienzo de las pruebas, se va a producir una columna de tipo entero. Sin embargo, puede darse el caso de que si usted cambia a utilizar structure.sql (opción de configuración) pruebas conservar la configuración y utilizar ese archivo para cargar el esquema.
  3. 18

    Tengo una manera de manejar esto. La ejecuta SQL ANSI SQL por lo que es probable que funcione en la mayoría de ANSI SQL compatible con bases de datos relacionales. He comprobado 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 su modelo de hacer esto:

    class User < ActiveRecord::Base
        set_primary_key :oid
        ...
    end

  4. 9

    Lo he probado en Rails 4.2. Para agregar su costumbre de clave principal, usted 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 columna(nombre, tipo, opciones = {}) y leer la línea :

    La type parámetro es normalmente una de las migraciones de tipos nativos, que es uno de los siguientes: :primary_key, :cadena de texto:, : integer :float :decimal :datetime :tiempo :fecha :binario :boolean.

    Tengo el anterior ides como ya he demostrado. Aquí está la tabla de datos de metadatos después de la ejecución de esta migración :

    [[email protected]_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"
    >>
    • El problema es, sin embargo, que la schema.rb archivo que se genera no refleja la string tipo de la clave primaria, por lo que cuando la generación de la base de datos con un load, la clave principal se crea como un entero.
  5. 8

    Parece que es posible hacer uso de 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

    Que va a hacer que la columna widget_id la clave principal para el Widget de la clase, entonces le corresponde a usted para rellenar el campo cuando se crean objetos. Usted debe ser capaz de hacerlo con el antes de crear de devolución de llamada.

    Algo así 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
    • Esto no funciona para mí, al menos no en PostgreSQL. Ninguna clave principal se especifica en absoluto.
    • Hmm interesante, lo he probado con MySQL on Rails 2.3.2 – podría estar relacionado con la versión de rails, demasiado tal vez?
    • No estoy seguro, no creo que sea la versión de Rails. Estoy usando Rails 2.3.3.
    • Por desgracia, un real de clave principal no está establecido por la tabla de uso de este enfoque.
    • Este enfoque funciona con Rails 4.2 y Postgresql, por lo menos. Lo he probado, pero necesitas utilizar primary_key: true en lugar de sólo primary dado en la respuesta
  6. 8

    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 es necesario para :id => false.

    • Gracias por que funcionó a la perfección
    • lo sentimos, pero widget_id cambiado a entero cuando puedo aplicar su técnica…¿tienes eny solución?
    • Esto ya no funciona, al menos no en ActiveRecord 4.1.4. Da el siguiente error: you can't redefine the primary key column 'widgets'. To define a custom primary key, pass { id: false } to create_table.
  7. 8

    En Rails 5 usted puede hacer

    create_table :employees, id: :string do |t|
      t.string :first_name
      t.string :last_name
    end

    Ver create_table documentación.

    • Vale la pena señalar que sería necesario agregar algún tipo de before_create :set_id método en el modelo para asignar el valor de la clave principal
    • Parece funcionar bien con Rails 4.2 así!
  8. 4

    Después de casi cada una de las soluciones que dice «esto funcionó para mí en X de la base de datos», veo un comentario por el cartel original para el efecto de «no trabajo para mí en Postgres.» El verdadero problema aquí puede ser en realidad el Postgres apoyo en los Rieles, lo cual no es impecable, y probablemente fue peor en 2009 cuando esta pregunta publicado originalmente. Por ejemplo, si recuerdo correctamente, si estás en Postgres, que, básicamente, no se puede obtener un resultado útil de rake db:schema:dump.

    Yo no soy un Postgres ninja, yo tengo esta info de Xavier Shay excelente PeepCode video en Postgres. El video que en realidad ofrece vistas a una biblioteca por Aaron Patterson, creo que Texticle, pero podría estar recordando mal. Pero aparte de que es bastante grande.

    De todos modos, si usted está corriendo en este problema en Postgres, a ver si las soluciones de trabajo en otras bases de datos. Tal vez de usar rails new para generar una nueva aplicación como una caja de arena, o simplemente crear algo como

    sandbox:
      adapter: sqlite3
      database: db/sandbox.sqlite3
      pool: 5
      timeout: 5000

    en config/database.yml.

    Y si se puede comprobar que es un Postgres problema de soporte, y que figura a cabo una revisión, por favor, contribuya parches a los Carriles o el paquete de correcciones en una joya, porque el Postgres base de usuarios dentro de los Carriles de la comunidad es bastante grande, sobre todo gracias a Heroku.

    • Downvoting para FUD y la inexactitud. He usado Postgres en la mayoría de los de mi Rieles de proyectos desde el año 2007. Postgres apoyo en Rails es excelente, y no hay ningún problema con el esquema dumper.
    • por un lado, es cierto que Postgres no era el problema aquí. en el otro, he aquí un pg-sólo esquema dumper error a partir de 2009, rails.lighthouseapp.com/projects/8994/tickets/2418 y aquí hay otra rails.lighthouseapp.com/projects/8994/tickets/2514
    • Había decidido un problema con Rieles y Postgres que rodea el Postgres 8.3 actualización donde ellos (correctamente, OMI) decidió cambiar al SQL estándar para los literales de cadena y citando. Los Rieles adaptador de no ver Postgres versiones y tuvo que ser monkeypatched por un tiempo.
    • Interesante. Nunca corrió en que.
  9. 4

    He encontrado una solución 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 empleado.rb modelo:

    self.primary_key = :emp_id
  10. 3

    El truco que trabajó 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

    Así:

    1. uso :id => false para no generar un integer primary key
    2. utilizar el tipo de datos deseado, y agregar :null => false
    3. añadir un índice único en la columna

    Parece que MySQL se convierte en el único índice que no nulos de la columna a una clave principal!

    • En Rails 3.22 con MySQL 5.5.15 crea una clave única solo, pero no de la clave principal.
  11. 2

    usted tiene que utilizar la opción :id => false

    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 no funciona para mí, al menos no en PostgreSQL. Ninguna clave principal se especifica en absoluto.
    • Este enfoque se omite la columna id pero la clave principal no ser puesto en la mesa.
  12. 1

    Cómo acerca de esta solución,

    Dentro Empleado modelo ¿por qué no podemos agregar código que se va a comprobar la unicidad en la columna, por ejemplo: Supongamos que el Empleado es el Modelo en el que tiene EmpId que es de la cadena, a continuación, para que podamos añadir «:singularidad => true» a EmpId

        class Employee < ActiveRecord::Base
          validates :EmpId , :uniqueness => true
        end

    No estoy seguro de que esta es la solución, pero esto funcionó para mí.

  13. 1

    Sé que este es un hilo viejo me topé… pero estoy un poco sorprendido de que nadie menciona DataMapper.

    Puedo encontrar si usted necesita para apartan de la ActiveRecord convención, he encontrado que es una gran alternativa. También es un mejor enfoque para el legado y usted puede apoyar a la base de datos «tal como es».

    Ruby Mapeador De Objetos (DataMapper 2) tiene un montón de promesas y construir en AREL principios, también!

  14. 1

    La adición de índice a mí me funciona, estoy usando MySql btw.

    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

Dejar respuesta

Please enter your comment!
Please enter your name here