Ruby on Rails:嵌套属性、belongs_to 关系
我有一个具有当前位置字段(城市和国家/地区)的用户实体。为了保存这些信息,我创建了一个名为 Location 的实体,它有很多用户。
我不完全确定是否应该放入用户模型“has_one”或“belongs_to”,但根据我读到的内容,如果我希望它具有位置的外键,我应该放入“belongs_to”。我还希望能够在编辑用户时编辑用户的当前位置。所以我使用嵌套属性。但是,当我编辑用户时,我最终每次都会添加一个新位置,而不会将其与编辑的用户相关联。你能帮我一下吗?
我的代码如下:
#User Model
class User < ActiveRecord::Base
## Relationships
belongs_to :current_location, :class_name => 'Location'
accepts_nested_attributes_for :current_location
end
#Location Model
class Location < ActiveRecord::Base
#Relationship
has_many :users
end
# part of the _form_edit.haml
- form_edit.fields_for :current_location do |location_form|
= location_form.label :location, "Current Location"
= location_form.text_field :location
#Application Helper
#nested attributes for user and location
def setup_user(user)
returning(user) do |u|
u.build_current_location if u.current_location.nil?
end
end
#in the user controller (added after edit)
def update
@user = @current_user
if @user.update_attributes(params[:user])
flash[:notice] = "Account updated!"
redirect_to account_url
else
render :action => :edit
end
end
I have a User entity that has a Current Location field (city and country). To hold this info I created an entity called Location which has_many Users.
I'm not entirely sure if I should put in the User model "has_one" or "belongs_to", but for what I read if I wanted it to have the foreign key of the location I should put "belongs_to". I also want to be able to edit the user's Current Location when editing the User. so I am using nested attributes. But when I edit the user I end up adding a new Location each time without ever associating it to the user that was edited. Can you help me out?
My code is the following:
#User Model
class User < ActiveRecord::Base
## Relationships
belongs_to :current_location, :class_name => 'Location'
accepts_nested_attributes_for :current_location
end
#Location Model
class Location < ActiveRecord::Base
#Relationship
has_many :users
end
# part of the _form_edit.haml
- form_edit.fields_for :current_location do |location_form|
= location_form.label :location, "Current Location"
= location_form.text_field :location
#Application Helper
#nested attributes for user and location
def setup_user(user)
returning(user) do |u|
u.build_current_location if u.current_location.nil?
end
end
#in the user controller (added after edit)
def update
@user = @current_user
if @user.update_attributes(params[:user])
flash[:notice] = "Account updated!"
redirect_to account_url
else
render :action => :edit
end
end
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
正如其他人指出的那样,您面临的确切问题是您的控制器没有收到应有的位置 ID。在我看来,位置 ID 是通过错误的参数传递的。不幸的是,新记录中不存在位置 ID,因此在表单中这是不可能的。
您的问题源于对belongs_to关系使用accepts_nested_attributes_for。该行为没有明确定义。这似乎是一个已记录的错误。因此,accepts_nested_attributes_for 应该位于关系的一个或多个方面。
以下是一些可能的解决方案:
将 Accepted_nested_attributes_for 移至位置模型,并以相反的方式构建表单。
不幸的是,这不允许以逻辑方式呈现信息。并且使编辑正确的用户变得困难。
使用连接模型,并建立一个具有一:通过关系。
老实说,我不确定accept_nested_attributes_for与:through关系的配合效果如何,但它肯定会解决链接记录的问题。
忽略accepts_nested_attributes_for并以老式方式处理控制器中的关联。
实际上保留accepts_nested_attributes_for。它提供了一些方便的方法,只是不要让它到达 update_attributes/create 语句。
如果不创建新位置,则 的字段将自动填充 current_location_attributes 哈希中的 id 字段。但是,find_or_create_by_id 需要哈希中的 :id 条目才能工作。如果 id 不在数据库中,它将使用正确的自动递增 id 创建。如果您要创建新位置,则需要添加它。最简单的方法是使用
=location_form.hidden_field :id, 0 除非 current\_location.new\_record?
将其添加到表单中。但是,您可能希望减少重复的位置创建,并将 Location.find_or_create_by_id 行更改为 Location.find_or_create_by_location。这也将减少因唯一性验证失败而产生的任何错误。
The exact problem you're facing, as others have pointed out is that your controller is not receiving the location id as it should. Looks to me the location id is being passed through the wrong parameter. Unfortunately a location id doesn't exist on a new record, so this is not possible in the form.
Your problem stems from the use accepts_nested_attributes_for on a belongs_to relationship. The behaviour isn't clearly defined. This appears to be a documented bug. So the accepts_nested_attributes_for should be on a has one or has many side of a relationship.
Here are some possible solutions:
Move The accepted_nested_attributes_for to the Location model and build your forms the other way around.
Unfortunately this doesn't allow for a logical way of presenting information. And makes editing the right user difficult.
Use a join model, and make a has one :through relationship.
I'm honestly not sure how well accept_nested_attributes_for works with a :through relationship, but it will definitely solve your problem with linking records.
Ignore accepts_nested_attributes_for and handle the association in your controller the old fashioned way.
Actually keep the accepts_nested_attributes_for. It provides some handy convenience methods, just don't let it get to the update_attributes/create statement.
Fields for will populate an id field in the current_location_attributes hash automatically, if it's not creating a new location. However, find_or_create_by_id, requires an :id entry in the hash for it to work. It will create with a correctly auto incremented id if the id isn't in the database. If you are creating a new location you will need to add it. Easiest to add it to the form with
=location_form.hidden_field :id, 0 unless current\_location.new\_record?
.However, you might want to cut down on duplicate location creation, and change the Location.find_or_create_by_id line to Location.find_or_create_by_location. This will also cut down on any errors from failed uniqueness validations.
您不提供嵌套属性的 ID。所以 Rails 认为这是一个新的。
You do not provide the nested attribute's id. So rails thinks it's a new one.
不确定之前的答案是否真的正确。您需要指定该位置的用户 ID,而不是位置本身。
Not sure if the previous answer is really correct. What you need is to specify the id of the user for the location, not the location itself.
默认情况下
belongs_to :current_location, :class_name => “Location”
期望Users
表具有current_location_id
字段。一旦你有了这个,你应该能够做类似的事情:By default
belongs_to :current_location, :class_name => 'Location'
will expect theUsers
table have acurrent_location_id
field. Once you have this you should be able to do something like: