Ruby-on-rails 在保存之前获取 Rails 模型的 ID...?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3466908/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 23:02:07  来源:igfitidea点击:

Get ID of Rails Model before saving...?

ruby-on-rails

提问by amaseuk

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 是在保存时根据数据库创建的,但是有解决方法吗?

采纳答案by Max Williams

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.

通常,当人们认为他们需要这样做时,他们实际上并不需要这样做。就像约翰说的那样,解释你想要做什么,有人可能会建议一种方法来做到这一点,而无需事先知道 id。

回答by Ruby Racer

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

模型.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

如果它被保存,这将输出您的记录将为 id 取的值

回答by John Topley

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?

你真正想解决什么问题?

回答by haydenmuhl

This is less a Rails question and more a database question. This is a problem that will present itself in any web application framework, and the solution is the same in all places. You have to use a database transaction.

这与其说是一个 Rails 问题,不如说是一个数据库问题。这是任何 Web 应用程序框架中都会出现的问题,所有地方的解决方案都是一样的。您必须使用数据库事务。

The basic flow will work like this.

基本流程将像这样工作。

  • Open a transaction
  • Save your model
  • Use the ID assigned by the database
  • 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.
  • 开启交易
  • 保存您的模型
  • 使用数据库分配的ID
  • 如果事实证明您实际上不想在数据库中保留此模型,请回滚事务。
  • 如果事实证明您希望将模型保留在数据库中,请提交事务。

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 中会有间隙。

回答by GEkk

Most of the time when I needed an id can be grouped into a short list. When creating nested associations or connectin of the associations through. Let's assume we have: :userthat have :petsthrough :user_petsassociation, where we will save their type.

大多数时候,当我需要一个 id 时,可以将其分组到一个简短的列表中。创建嵌套关联或通过关联连接时。假设我们有::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 :pettype in :user_pets.

如果我们有一个正确配置的“has_many: through Association”,我们可以, User.pets.create(name: "Rex")但这太简单了,因为我们想:pet: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? Let's move to something even more complex! Now we have an additional table :shampoothat exactly as :user_pet, belongs to a :petand a :userWe need to create it without knowing the id of the :userand :pet

你看到它是如何为我们创建所有 id 的了吗?让我们转向更复杂的事情!现在我们有一个额外的表:shampoo是完全一样:user_pet,属于:pet:user我们需要在不知道的ID,以创建:user: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!

I tried to cover most common causes when you need element id, and how to overcome this. I hope it will be useful.

当您需要元素 id 时,我试图涵盖最常见的原因,以及如何克服这个问题。我希望它会很有用。

回答by Curtis Edmond

I don't believe there are any workarounds since the id is actually generated by the database itself. The id should not be available until after the object has been saved to the database.

我不相信有任何解决方法,因为 id 实际上是由数据库本身生成的。直到对象被保存到数据库后,id 才可用。

回答by mcmlxxxiii

Consider doing what you want right after the instance is created.

考虑在创建实例后立即执行您想要的操作。

after_create do
  print self.id
end

回答by the_minted

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. You can get the next id in this sequence and increment it with the following function.

如果您使用的是 postgres,活动记录会通过在数据库中保留一个名为 models_id_seq(sales_id_seq for Sale 等)的序列来增加它分配给模型的 id。您可以获取此序列中的下一个 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. 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.

但是,这种解决方案不是一个好的做法,因为不能保证活动记录将来会以这种方式保留 id 序列。如果它在我的项目中只使用一次,我只会使用它,为我节省了很多工作,并且在为什么不应该经常使用它方面有很好的记录。

回答by Amol Mohite

First understand the structure of database.

首先了解数据库的结构。

  • Id is gerated using sequence.
  • increament done by 1 (specified while creating sequence)
  • Last entry to database will have highest value of id
  • Id 是使用序列生成的。
  • 由 1 完成的增量(在创建序列时指定)
  • 数据库的最后一个条目将具有最高值 id

If you wanted to get idof 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.

希望这会帮助你。

回答by theStig

I know it's an old question, but might as well throw my answer in case anyone needs to reference it

我知道这是一个老问题,但不妨抛出我的答案,以防有人需要参考它

UserModel

用户模型

class User < ActiveRecord::Base
before_create :set_default_value

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