2009-04-15 7 views
24

Tengo dos modelos, Estado y Adquisición. El estado tiene muchas adquisiciones. Sentí que una clave primaria entera autoincrementada para 51 registros era bastante tonta. Así que alteró el modelo para que el Estado sea el PK (Estado es abreviatura de dos letras; no estoy almacenar el nombre del estado real en cualquier lugar:Alterar la clave primaria en Rieles para que sea una cadena

class State < ActiveRecord::Base 
    self.primary_key = "state" 
    has_many :acquisition_histories 
end 

El problema es cuando creé mi modelo de Adquisición, se creó la columna de clave externa state_id como un entero Más específicamente, el script/migración generada hizo:..

class CreateAcquisitions < ActiveRecord::Migration 
    def self.up 
    create_table :acquisitions do |t| 
     t.date :date 
     t.string :category 
     t.text :notes 
     t.references :state 
     t.timestamps 
    end 
    end 
end 

estoy asumiendo que t.references tipo de datos se establece a int el problema es mi método de crear en mi Adquisición clase está tratando de poner una abreviatura de estado en el campo state_id en las adquisiciones de tabla (y sí, se llama state_id en la base de datos, aunque es ays: estado en el script de migración). El método no falla, pero pone un 0 en el campo state_id y los registros entran en el éter.

Respuesta

-15

Rieles funciona mejor cuando no lucha contra los valores predeterminados. ¿Qué daño hace tener una clave primaria entera en su tabla de estado?

A menos que esté atascado con un esquema heredado del que no tiene control, le aconsejo que se adhiera a la convención sobre configuración predeterminada de Rails —, ¿no? — y concéntrese en las partes importantes de su aplicación, como la interfaz de usuario y la lógica comercial.

+0

Bueno, hay una sobrecarga de base de datos (probablemente en memoria y en caché) cuando tiene que seguir incluyendo la tabla de estado para obtener el código. Pero es probable que sea una optimización excesiva. Hago eso, pero soy viejo y solía importar más en VAX 8700 días. –

+2

Para ese tipo de cosas, tiendo a definir una constante en la clase de estado, es decir, ALL = State.all y luego uso State :: ALL como origen para los menús desplegables, etc. De esta forma, la consulta SQL solo se ejecuta al inicio en Producción . –

+1

No me preocupa el rendimiento en absoluto, simplemente no me gusta el concepto de un identificador único adicional. Pero voy a retroceder con la convención, suponiendo que pueda encontrar la manera de respaldar todo. Soy bastante nuevo en Rails. – fr0man

2

Desea seguir las convenciones de Rails. La clave primaria adicional no es un problema de ninguna manera. Solo úsalo.

+6

Costo de almacenamiento (obvio). Costo computacional (clustering + autoincrement IDs no es necesariamente bueno). Costo de complejidad (el esquema es confuso si tiene ruido sobre lo que realmente necesita estar allí). Usar los UUID como claves primarias tiene mucho sentido en las aplicaciones fuera de línea que deben correlacionar sus datos más adelante con un DB central) – sethcall

1

Tenía un poco de experiencia con la cuerda utilizada como teclas principales y es un dolor en el culo. Recuerde que, de forma predeterminada, si desea pasar un objeto con el patrón predeterminado: controller /: action /: id, the: id será una cadena y esto probablemente dará lugar a problemas de enrutamiento si algunos identificadores se formatean extrañamente;)

6

Estoy trabajando en un proyecto que utiliza UUID como claves principales y, sinceramente, no lo recomiendo a menos que esté seguro de que lo necesita. Hay una gran cantidad de complementos de Rails por ahí que no funcionarán sin modificaciones con una base de datos que usa cadenas como claves principales.

1
class CreateAcquisitions < ActiveRecord::Migration 
    def self.up 
     create_table :acquisitions, :id => false do |t| 
      t.date :date 
      t.string :category 
      t.text :notes 
      t.references :state 
      t.timestamps 
     end 
    end 
end 
35

Aunque, de acuerdo en que esto podría ser more trouble than it's worth teniendo en cuenta el esfuerzo adicional de trabajar en contra de los valores por defecto en otro lugar, en caso de que en realidad quiere hacer lo que has pedido:

Crear migración de estados :

class CreateStatesTable < ActiveRecord::Migration 
    def change 
    create_table :states, id: false do |t| 
     t.string :state, limit: 2 
     t.string :name 
     t.index :state, unique: true 
    end 
    end 
end 

Unidos modelo:

class State < ActiveRecord::Base 
    self.primary_key = :state 
end 

Tenga en cuenta que antes de Rails 3.2, esto era set_primary_key = :state en lugar de self.primary_key= véase: http://guides.rubyonrails.org/3_2_release_notes.html#active-record-deprecations

2

Tenga en cuenta que crea mkirk's answer una clave primaria falsa. Esto explica por qué ActiveRecord necesita que se le diga cuál es la clave principal.Inspeccionar la mesa revela

  Table "public.acquisitions" 
Column |   Type   | Modifiers 
--------+----------------------+----------- 
state | character varying(2) | 
name | character varying | 
Indexes: 
    "index_acquisitions_on_state" UNIQUE, btree (state) 

En la práctica, esto funciona como se esperaba, por lo que no hay nada malo, pero podría ser mejor.


Podemos mantener la columna de la id y cambiar su tipo a string *. La migración parece

class CreateAcquisitionsTable < ActiveRecord::Migration 
    def change 
    create_table :acquisitions do |t| 
     t.string :name 
    end 
    change_column :acquisitions, :id, :string, limit: 2 
    end 
end 

Inspección de la tabla, vemos que tiene una clave principal real con todos los extras, tales como la restricción de clave única (sin índice único necesario), no restricción nula, y la clave de incremento automático.

       Table "public.acquisitions" 
Column |   Type   |      Modifiers 
--------+----------------------+--------------------------------------------------- 
id  | character varying(2) | not null default nextval('acquisitions_id_seq'::regclass) 
name | character varying | 
Indexes: 
    "acquisitions_pkey" PRIMARY KEY, btree (id) 

Y usted no tendrá que decirle explícitamente ActiveRecord lo que es la primaria.

Le recomendamos que establezca un ID predeterminado si no se proporciona ninguno.

class MyModel < ActiveRecord::Base 
    before_create do 
    self.id = SecureRandom.uuid unless self.id 
    end 
end 

* Nota: no se debe cambiar la clave principal por defecto a menos que tenga una buena razón para

+1

¿Revisó lo que se generó en el archivo schema.rb? Cuando probé esto, la migración funcionó, pero el esquema no contenía la conversión de "cadena", lo que ocasionó que los índices de clave externa fallaran debido a los diferentes tipos de columna. – Andreas

1

En Rails 5.1 se puede especificar el tipo de la clave principal en la creación:

create_table :states, id: :string do |t| 
# ... 
end 

De the documentation:

un símbolo puede ser utilizado para especificar el tipo de la columna de clave primaria generada.

Cuestiones relacionadas