2010-08-12 13 views
13

¿Cómo se obtiene el id de un modelo de rieles antes de guardarlo?Obtenga la identificación del modelo Rails antes de guardar ...?

Por ejemplo, si creo una nueva instancia de modelo, ¿cómo puedo obtener su ID antes de que se guarde?

Sé que el ID se creó en un solo archivo y de acuerdo con la base de datos, pero ¿hay alguna solución?

+1

Alguien con más conocimiento de Rails puede confirmar esto, pero generalmente la ID genera la ID así que no hay forma de saber qué es hasta que inserte la fila. Por supuesto, puede generar ID usted mismo, pero eso abre una lata de lombrices ... – roryf

Respuesta

4

Generalmente, cuando las personas piensan que necesitan hacer esto, en realidad no necesitan hacerlo. Como dice John, explique lo que está tratando de hacer y alguien probablemente le sugiera una forma de hacerlo sin tener que saber la identificación por adelantado.

+0

Es cierto, es un proceso complicado que estoy tratando de desarrollar. Debería replantear el diseño ...! – amaseuk

+0

¿Podría indicarme un enlace donde pueda leer sobre la manera de hacerlo? – simo

3

Utilizando la convención de Rails predeterminada de una clave primaria de entero autoincrementado, no hay forma de obtener la ID de un modelo antes de que se guarde porque es generado por el RDBMS cuando la nueva fila se inserta en la tabla correspondiente.

¿Qué problema estás tratando de resolver?

+0

Creo que debería publicar eso como una pregunta separada aquí. –

0

No creo que haya soluciones provisionales, ya que la propia base de datos genera el ID. La identificación no debería estar disponible hasta después de que el objeto se haya guardado en la base de datos.

-1

Acabo de encontrarme en una situación similar al crear un importador de datos. Estaba creando un montón de registros de diferentes tipos y los asociaba antes de guardarlos. Al guardar, algunos de los registros arrojaron errores de validación porque tenían validate_presence_of un registro que aún no se había guardado.

Si está utilizando postgres, el registro activo incrementa el id que asigna a un modelo al mantener una secuencia llamada models_id_seq (sales_id_seq for Sale, etc.) en la base de datos. Puede obtener la siguiente identificación en esta secuencia e incrementarla con la siguiente función.

def next_model_id 
    ActiveRecord::Base.connection.execute("SELECT NEXTVAL('models_id_seq')").first["nextval"].to_i 
end 

Sin embargo, esta solución no es una buena práctica ya que no hay garantía de que mantendrá registro activo secuencias de identificación de esta manera en el futuro. Solo usaría esto si se usó solo una vez en mi proyecto, me ahorró mucho trabajo y estaba bien documentado en términos de por qué no debería usarse con frecuencia.

6

que estaba buscando esto también, y he encontrado una respuesta:

nombre del modelo supongamos que nos dejó es "modelo" y el nombre de tabla es "modelos"

model.rb

before_save { 
    next_id=Model.connection.select_value("Select nextval('models_id_seq')") 
} 

Esto arrojará el valor que su registro tomará para el ID SI se guarda

+4

No creo que esto sea 'thread-safe', lo que significa que dos procesos simultáneos adquirirán la misma identificación y luego ambos la usarán. –

+0

Puede incrementar manualmente el seq, no es una buena práctica, pero existen formas de implementar subprocesos. –

+1

Esto debería ser seguro para los hilos tal cual; 'nextval' incrementa la secuencia y devuelve el nuevo valor atómicamente (http://www.postgresql.org/docs/8.1/static/functions-sequence.html) – Dathan

-6

Sé que es una vieja pregunta, pero también podría arrojar mi respuesta en caso de que alguien necesite referenciarla

UserModel

class User < ActiveRecord::Base 
before_create :set_default_value 

def set_default_value 
    self.value ||= "#{User.last.id+1}" 
end 
+2

Esto está abierto a todo tipo de condiciones de carrera. Si hay alguna diferencia en el tiempo entre guardar el registro y establecer el valor, obtendrás duplicados en un sitio con tráfico razonable. – Jay

+0

que se puede mitigar mediante validaciones – theStig

+0

Las validaciones de rieles no harán nada para mitigar este riesgo, solo las restricciones de unicidad de la base de datos ayudarán con esto (y generalmente generarán una excepción). Para explicar, si el viaje de ida y vuelta a su base de datos es de 10 ms para extraer/verificar la última identificación de usuario, y se crea otro usuario dentro de este marco de tiempo, tendrá dos valores iguales. Si se trata de un sitio de usuario único, a nadie le importa, pero cualquier cosa con un tráfico remotamente alto es un riesgo que debe evitar.Tal vez, mire las secuencias de postgres o similares si realmente necesita este tipo de funcionalidad. – Jay

0

Considere hacer lo que quieres justo después de crear la instancia.

after_create do 
    print self.id 
end 
+0

¿No estoy seguro de que esto funcionaría? http://apidock.com/rails/ActiveRecord/Callbacks/after_create dice que aún no existe ningún registro en este punto. Tal vez necesitas after_save? – Nick

2

Esto es menos una cuestión de Rails y más una pregunta de la base de datos. Este es un problema que se presentará en cualquier marco de aplicación web, y la solución es la misma en todos los lugares. Tienes que usar una transacción de base de datos.

El flujo básico funcionará así.

  • Abrir una transacción
  • guardar su modelo
  • utilizar el ID asignado por la base de datos
  • Si resulta que en realidad no quieren mantener este modelo en la base de datos, deshacer la transacción .
  • Si resulta que desea mantener el modelo en la base de datos, confirme la transacción.

Lo principal que notará de este enfoque es que habrá lagunas en sus identificaciones en las que revierte la transacción.

0

La mayoría de las veces, cuando necesitaba una identificación, puedo agruparla en una lista corta. Al crear asociaciones anidadas o la conexión de las asociaciones a través de. Supongamos que tenemos: :user que tienen :pets hasta :user_pets asociación, donde guardaremos su tipo.

Si tenemos un bien configurado "has_many: a través de la Asociación" podemos simplemente User.pets.create(name: "Rex") pero esto es demasiado simplista, ya que queremos para conformar :pet tipo de :user_pets.

u = User.create(name: "Cesar") 
u.id # => 1 # works fine 

p = u.pets.create(name: 'Rex') 
# => rails will create UserPets => {id: 1, user_id: 1, pet_id: 1} for us 

# But now we have a problem, how do we find :user_pets of our :pet? 
# remember we still need to update the :type, the ugly (wrong) way: 
up = p.user_pets.first 
up.type = 'dog' 
up.save! # working but wrong 

# Do you see the problems here? We could use an id 
P = Pet.new(name: "Destroyer") 
p.id # will not work, as the pet not saved yet to receive an id 
up = UserPet.new(user_id: U.id, pet_id: p.id) 
# => UserPet {id: 2, user_id: 1, pet_id: nil} # as we expected there is no id. 

# What solutions do we have? Use nested creation! 
# Good 
up = UserPet.new(user_id: u.id, type: "dog") 
# even better 
up = u.user_pets.new(type: "dog") 
# it's just a shortcut for the code above, 
# it will add :user_id for us, so let's just remember it. 

# Now lets add another 'new' from our creatd 'user_pet' 
p = up.pets.new(name: "Millan") 
user.save! 
# => UserPet: {id: 3, user_id: 1, pet_id: 2, type: 'dog'} # => Pet: {id: 2, name: "Sam"} 
# everything is working! YEY! 

# we can even better, than writing in the beginning "User.create", 
# we can write "User.new" and save it with all the nested elements. 

¿Viste cómo esto creó todos los identificadores para nosotros? ¡Pasemos a algo aún más complejo! Ahora tenemos una tabla adicional :shampoo que exactamente como :user_pet, pertenece a una :pet y una :user Necesitamos crear sin saber el id de la :user y :pet

u = User.new('Mike') 
up = u.user_pets.new(type: "cat") 
p = up.pets.new(name: "Rowe") 

# But what are we doing now? 
# If we do: 
s = u.shampoos.new(name: "Dirty Job") 
# => Shampoo: {user_id: 2, pet_id: nil, name: "..."} 
# We get "pet_id: nil", not what we want. 

# Or if we do: 
s = p.shampoos.new(name: "Schneewittchen") 
# => Shampoo: {user_id: nil, pet_id: 3, name: "..."} 
# We get "user_id: nil", in both cases we do not get what we want. 

# So we need to get the id of not created record, again. 
# For this we can just do as in the first example (order is not important) 
s = u.shampoos.new(name: "Mission Impossible") 
# => Shampoo: {id: 3, user_id: 2, pet_id: nil, name: "..."} 
s.pet = p # this will give the missing id, to the shampoo on save. 
# Finish with save of the object: 
u.save! #=> Shampoo: {id: 3, user_id: 2, pet_id: 3, name: '...'} # => Pet: ... 
# Done! 

traté de cubrir causas más comunes a la hora necesitas identificación de elemento y cómo superar esto. Espero que sea útil.

Cuestiones relacionadas