ruby-on-rails - migraciones - llaves foraneas ruby on rails
¿Cómo agregar secuencias a una migración y usarlas en un modelo? (4)
Creo que la respuesta de los roboles no es correcta.
Intenté implementar esto en mi aplicación (exactamente el mismo env: RoR + PostgreSQL), y descubrí que cuando se save
en RoR con el objeto que tiene atributos vacíos, intenta realizar un INSERT en la base de datos mencionando que todos los VALORES se establecerá en NULL. El problema es la forma en que PostgreSQL maneja los NULL: en este caso, se creará la nueva fila pero con todos los valores vacíos, es decir, se omitirá el valor predeterminado. Si save
solo escribió en los atributos de la instrucción INSERT rellenados en RoR, esto funcionaría bien.
En otras palabras, y centrándose solo en el atributo type
y customer_no
mencionado anteriormente, esta es la forma en que PostgreSQL se comporta:
SITUACIÓN 1:
INSERT INTO accounts_customers (type, customer_no) VALUES (NULL, NULL);
(Así es como funciona el save
Rails)
Resultado: una nueva fila con el type
vacío y el customer_no
vacío_no
SITUACIÓN 2:
INSERT INTO accounts_customers (type) VALUES (NULL);
Resultado: una nueva fila con el type
vacío y customer_no
rellenados con NEXTVAL de la secuencia
Tengo un hilo sobre esto, échale un vistazo a:
Ruby on Rails + PostgreSQL: uso de secuencias personalizadas
Quiero tener un Modelo de " Customer
" con una clave principal normal y otra columna para almacenar un "Número de Cliente" personalizado. Además, quiero que la base de datos maneje los números de cliente predeterminados. Creo que definir una secuencia es la mejor manera de hacerlo. Yo uso PostgreSQL. Echa un vistazo a mi migración:
class CreateAccountsCustomers < ActiveRecord::Migration
def up
say "Creating sequenze for customer number starting at 1002"
execute ''CREATE SEQUENCE customer_no_seq START 1002;''
create_table :accounts_customers do |t|
t.string :type
t.integer :customer_no, :unique => true
t.integer :salutation, :limit => 1
t.string :cp_name_1
t.string :cp_name_2
t.string :cp_name_3
t.string :cp_name_4
t.string :name_first, :limit => 55
t.string :name_last, :limit => 55
t.timestamps
end
say "Adding NEXTVAL(''customer_no_seq'') to column cust_id"
execute "ALTER TABLE accounts_customers ALTER COLUMN customer_no SET DEFAULT NEXTVAL(''customer_no_seq'');"
end
def down
drop_table :accounts_customers
execute ''DROP SEQUENCE IF EXISTS customer_no_seq;''
end
end
Si conoces un mejor enfoque "similar a rieles" para agregar secuencias, sería increíble que me lo hicieras saber.
Ahora, si hago algo como
cust = Accounts::Customer.new
cust.save
el campo customer_no
no se rellena previamente con el siguiente valor de la secuencia (debe ser 1002).
¿Conoces una buena forma de integrar secuencias? ¿O hay un buen plugin? ¡Saludos a todas las respuestas!
Me enfrenté a un problema similar, pero también puse :null => false
en el salto de campo que se rellenará automáticamente con nextval.
Bueno, en mi caso, AR aún intentaba insertar NULL
si no se proporcionó ningún atributo en la solicitud, y esto dio lugar a una excepción por infracción de restricción no nula.
Aquí está mi solución. Acabo de eliminar esta clave de atributo de @attributes
y @changed_attributes
y, en este caso, postgres coloca correctamente la secuencia esperada nextval.
He puesto esto en el modelo:
before_save do
if (@attributes["customer_no"].nil? || @attributes["customer_no"].to_i == 0)
@attributes.delete("customer_no")
@changed_attributes.delete("customer_no")
end
end
Rieles 3.2 / Postgres 9.1
No tengo sugerencias para una forma más ''directa'' de manejar secuencias personalizadas, pero puedo decirle por qué el campo customer_no parece no estar poblado después de guardar.
Cuando ActiveRecord guarda un nuevo registro, la instrucción SQL solo devolverá el ID del nuevo registro, no todos sus campos, puede ver dónde ocurre esto en la fuente de los rieles actuales aquí https://github.com/rails/rails/blob/cf013a62686b5156336d57d57cb12e9e17b5d462/activerecord/lib/active_record/persistence.rb#L313
Para ver el valor, deberá volver a cargar el objeto ...
cust = Accounts::Customer.new
cust.save
cust.reload
Si siempre desea hacer esto, considere agregar un gancho after_create a su clase de modelo ...
class Accounts::Customer < ActiveRecord::Base
after_create :reload
end
Si estás usando PostgreSQL, echa un vistazo a la gema que escribí, pg_sequencer:
https://github.com/code42/pg_sequencer
Proporciona un DSL para crear, eliminar y modificar secuencias en las migraciones de ActiveRecord.