简体   繁体   English

保存之前获取Rails模型的ID ...?

[英]Get ID of Rails Model before saving…?

How do you get the id of a rails model before it is saved? 如何在保存之前获取rails模型的id?

For example, if I create a new model instance, how can I get its ID before it is saved? 例如,如果我创建一个新的模型实例,如何在保存之前获取其ID?

I know that the id is created onsave and according to the database but is there a workaround? 我知道id是在onsave上创建的并且根据数据库但是有一个解决方法吗?

I was looking for this too, and I found an answer: 我也在寻找这个,我找到了答案:

Let's suppose model name is "Model" and table name is "models" 假设模型名称为“Model”,表名为“models”

model.rb model.rb

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

This will output the value your record will take for id IF it gets saved 这将输出您的记录将获取的值,如果它被保存

Usually when people think they need to do this they actually do not need to do it. 通常当人们认为他们需要这样做时,他们实际上并不需要这样做。 Like John says, explain what you are trying to do and someone can probably suggest a way to do it without having to know the id in advance. 就像John说的那样,解释一下你想要做什么,有人可以建议一种方法来做到这一点,而不必提前知道id。

Using the default Rails convention of an auto-incrementing integer primary key, there's no way to get the ID of a model before it's saved because it's generated by the RDBMS when the new row is inserted in the relevant table. 使用自动递增整数主键的默认Rails约定,在保存模型之前无法获取模型的ID,因为当新行插入相关表时,它是由RDBMS生成的。

What problem are you actually trying to solve? 你实际上试图解决什么问题?

This is less a Rails question and more a database question. 这不是一个Rails问题,而是一个数据库问题。 This is a problem that will present itself in any web application framework, and the solution is the same in all places. 这个问题将出现在任何Web应用程序框架中,并且解决方案在所有地方都是相同的。 You have to use a database transaction. 您必须使用数据库事务。

The basic flow will work like this. 基本流程将像这样工作。

  • Open a transaction 打开一个交易
  • Save your model 保存你的模型
  • Use the ID assigned by the database 使用数据库分配的ID
  • If it turns out you actually don't want to keep this model in the database, roll back the transaction. 如果事实证明您实际上不希望将此模型保留在数据库中,则回滚事务。
  • If it turns out you want to keep the model in the database, commit the transaction. 如果事实证明您希望将模型保留在数据库中,则提交事务。

The main thing you will notice from this approach is that there will be gaps in your IDs where you rolled back the transaction. 您将从这种方法中注意到的主要事项是,在您回滚事务的ID中存在空白。

Most of the time when I needed an id can be grouped into a short list. 大多数情况下,当我需要id时,可以将其分组为一个简短的列表。 When creating nested associations or connectin of the associations through. 在创建嵌套关联或连接关联时。 Let's assume we have: :user that have :pets through :user_pets association, where we will save their type. 假设我们有: :user:pets通过:user_pets协会,在这里我们将节省他们的类型。

If we have a properly configured "has_many: through Association" we can just User.pets.create(name: "Rex") but this is too simplistic, as we want to creat :pet type in :user_pets . 如果我们有一个正确配置的“has_many:通过关联”,我们可以只使用User.pets.create(name: "Rex")但这太简单了,因为我们想创建:pet type in :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.

You saw how this created all the ids for us? 你看到了这为我们创造了所有的ids? Let's move to something even more complex! 让我们转向更复杂的事情吧! Now we have an additional table :shampoo that exactly as :user_pet , belongs to a :pet and a :user We need to create it without knowing the id of the :user and :pet 现在我们有一个额外的表:shampoo完全如下:user_pet ,属于:pet和a :user我们需要创建它而不知道:user:pet的id

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!

I tried to cover most common causes when you need element id, and how to overcome this. 当你需要元素id时,我试图涵盖最常见的原因,以及如何克服这个问题。 I hope it will be useful. 我希望它会有用。

Consider doing what you want right after the instance is created. 考虑在创建实例后立即执行您想要的操作。

after_create do
  print self.id
end

I don't believe there are any workarounds since the id is actually generated by the database itself. 我不相信有任何变通方法,因为id实际上是由数据库本身生成的。 The id should not be available until after the object has been saved to the database. 在将对象保存到数据库之后,id才可用。

I just ran into a similar situation when creating a data importer. 我在创建数据导入器时遇到了类似的情况。 I was creating a bunch of records of different types and associating them before saving. 我正在创建一堆不同类型的记录,并在保存之前将它们关联起来。 When saving, some of the records threw validation errors because they had validate_presence_of a record that was not yet saved. 保存时,某些记录会抛出验证错误,因为它们有一个尚未保存的记录的validate_presence_of。

If you are using postgres, active record increments the id it assigns to a Model by keeping a sequence named models_id_seq (sales_id_seq for Sale etc.) in the database. 如果您正在使用postgres,则活动记录会通过在数据库中保留名为models_id_seq(sales_id_seq for Sale等)的序列来增加它分配给Model的ID。 You can get the next id in this sequence and increment it with the following function. 您可以在此序列中获取下一个id,并使用以下函数递增它。

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

However, this solution is not good practice as there is no guarantee that active record will keep id sequences in this way in the future. 但是,这种解决方案并不是一种好的做法,因为无法保证活动记录将来会以这种方式保留id序列。 I would only use this if it was used only once in my project, saved me a lot of work and was well documented in terms of why it should not be used frequently. 如果它在我的项目中只使用过一次,我只会使用它,为我节省了大量的工作,并且在不应经常使用它的原因方面有很好的记录。

First understand the structure of database. 首先要了解数据库的结构。

  • Id is gerated using sequence. Id使用序列进行gerated。
  • increament done by 1 (specified while creating sequence) 增量为1(在创建序列时指定)
  • Last entry to database will have highest value of id 最后一次输入数据库将具有最高的id

If you wanted to get id of record which is going to be saved, 如果你想获得将被保存的记录的id

Then you can use following: 然后你可以使用以下:

 1. id = Model.last.id + 1
    model = Model.new(id: id)
    model.save
    # But if data can be delete from that dataabse this will not work correctly.

 2. id = Model.connection.select_value("Select nextval('models_id_seq')")
    model = Model.new(id: id)
    model.save
    # Here in this case if you did not specified 'id' while creating new object, record will saved with next id. 

    e.g. 
    id
    => 2234
    model = Model.new(id: id) 
    model.save 
    # Record will be created using 'id' as 2234  

    model = Model.new()
    model.save
    # Record will be created using next value of 'id' as 2235  

Hope this will help you. 希望这会帮助你。

I know it's an old question, but might as well throw my answer in case anyone needs to reference it 我知道这是一个老问题,但如果有人需要引用它,也可以抛出我的答案

UserModel 的usermodel

class User < ActiveRecord::Base
before_create :set_default_value

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM