HABTM 关系和accepts_nested_attributes_for

发布于 2024-09-28 07:04:10 字数 1302 浏览 6 评论 0原文

我有一个可以创建新博客文章的表单,并且我希望能够从同一表单创建新类别

我在帖子和类别之间有一个习惯关系,这就是我遇到麻烦的原因。

我有以下 2 个模型:

class Post < ActiveRecord::Base
  has_and_belongs_to_many :categories
  attr_accessible :title, :body, :category_ids

  accepts_nested_attributes_for :categories # should this be singular? 
end

class Category < ActiveRecord::Base
  has_and_belongs_to_many :posts
  attr_accessible :name
end

我的表单允许我从一堆现有类别中进行选择或创建一个全新的类别。我的表格如下。

# using simple_form gem
.inputs
  = f.input :title
  = f.input :body

  # the line below lets me choose from existing categories
  = f.association :categories, :label => 'Filed Under'

  # I was hoping that the code below would let me create new categories
  = f.fields_for :category do |builder|
    = builder.label :content, "Name"
    = builder.text_field :content

当我提交表单时,它会被处理,但不会创建新类别。我的命令提示符输出告诉我:

WARNING: Can't mass-assign protected attributes: category

但是,如果我添加 attr_accessible :category,我会遇到严重崩溃,并显示错误消息“未知属性:类别”。

如果我将 fields_for 目标更改为 :categories (而不是类别),那么我的表单甚至不会显示。

我花了一段时间试图解决这个问题,并观看了最近关于nested_models和simple_form的railscasts,但无法解决我的问题。

如果我使用 has_many :through 关系(带有连接模型)而不是 habtm ,这会更容易吗?

I have a form that lets me create new blog posts and I'd like to be able to create new categories from the same form.

I have a habtm relationship between posts and categories, which is why I'm having trouble with this.

I have the following 2 models:

class Post < ActiveRecord::Base
  has_and_belongs_to_many :categories
  attr_accessible :title, :body, :category_ids

  accepts_nested_attributes_for :categories # should this be singular? 
end

class Category < ActiveRecord::Base
  has_and_belongs_to_many :posts
  attr_accessible :name
end

My form lets me pick from a bunch of existing categories or create a brand new one. My form is as follows.

# using simple_form gem
.inputs
  = f.input :title
  = f.input :body

  # the line below lets me choose from existing categories
  = f.association :categories, :label => 'Filed Under'

  # I was hoping that the code below would let me create new categories
  = f.fields_for :category do |builder|
    = builder.label :content, "Name"
    = builder.text_field :content

When I submit my form, it gets processed but the new category is not created. My command prompt output tells me:

WARNING: Can't mass-assign protected attributes: category

But, if I add attr_accessible :category, I get a big fat crash with error message "unknown attribute: category".

If I change the fields_for target to :categories (instead of category) then my form doesn't even display.

I've spent a while trying to figure this out, and watched the recent railscasts on nested_models and simple_form but couldn't get my problem fixed.

Would this be easier if I was using a has_many :through relationship (with a join model) instead of a habtm?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蓝天白云 2024-10-05 07:04:10

感谢所有回答的人。经过多次尝试和错误,我设法找到了解决办法。

首先,我从 HABTM 切换到 has_many :through 关系,调用我的连接模型 categorization.rb (而不是 categorizations_posts.rb) - 注意:详细修复下面的内容也可能适用于 HABTM:

第 1 步:我将模型更改为如下所示:

# post.rb
class Post < ActiveRecord::Base
  has_many :categorizations
  has_many :categories, :through => :categorizations
  attr_accessible :title, :body, :category_ids
  accepts_nested_attributes_for :categories
end

#category.rb
class Category < ActiveRecord::Base
  has_many :categorizations
  has_many :posts, :through => :categorizations
  attr_accessible :name, :post_ids
end

#categorization.rb
class Categorization < ActiveRecord::Base
  belongs_to :post
  belongs_to :category
end

从上面的帖子模型来看:显然,名为 :category_ids 的访问器必须是如果您想要启用选择多个现有类别,但您不需要需要访问器方法来创建新类别...我没有'我不知道。

第 2 步:我将视图更改为如下所示:

-# just showing the relevent parts
= fields_for :category do |builder|
  = builder.label :name, "Name"
  = builder.text_field :name

从上面的视图代码中,请务必注意 fields_for :category 的使用,而不是有些不直观的 fields_for :categories_attributes

第 3 步
最后,我向控制器添加了一些代码:

# POST /posts
# POST /posts.xml
def create
  @post = Post.new(params[:post])
  @category = @post.categories.build(params[:category]) unless params[:category][:name].blank?
  # stuff removed
end


def update
  @post = Post.find(params[:id])
  @category = @post.categories.build(params[:category]) unless params[:category][:name].blank?
  # stuff removed
end

现在,当我创建新帖子时,我可以同时从选择菜单中选择多个现有类别同时创建一个全新的类别 - 这不是一个或另一个的情况

有一个小错误,编辑和更新现有帖子时发生;在这种情况下,它不会让我同时创建一个新类别选择多个现有类别 - 如果我尝试同时执行这两个操作,则只有现有类别与该帖子关联,并且全新的被拒绝(没有错误消息)。 但是我可以通过编辑帖子两次来解决这个问题,一次创建新类别(自动将其与帖子关联),然后第二次从菜单中选择一些其他现有类别 - 例如我说这不是什么大问题,因为它一切都很好,否则我的用户可以适应这些限制

无论如何,我希望这对某人有帮助。

阿门。

Thanks to everyone who answered. After much trial and error, I managed to come up with a fix.

First of all, I switched from a HABTM to a has_many :through relationship, calling my join model categorization.rb (instead of categorizations_posts.rb) - NB: the fix detailed below will likely work with a HABTM too:

Step 1: I changed my models to look like this:

# post.rb
class Post < ActiveRecord::Base
  has_many :categorizations
  has_many :categories, :through => :categorizations
  attr_accessible :title, :body, :category_ids
  accepts_nested_attributes_for :categories
end

#category.rb
class Category < ActiveRecord::Base
  has_many :categorizations
  has_many :posts, :through => :categorizations
  attr_accessible :name, :post_ids
end

#categorization.rb
class Categorization < ActiveRecord::Base
  belongs_to :post
  belongs_to :category
end

From the post model above: obviously, the accessor named :category_ids must be present if you want to enable selecting multiple existing categories, but you do not need an accessor method for creating new categories... I didn't know that.

Step 2: I changed my view to look like this:

-# just showing the relevent parts
= fields_for :category do |builder|
  = builder.label :name, "Name"
  = builder.text_field :name

From the view code above, it's important to note the use of fields_for :category as opposed to the somewhat unintuitive fields_for :categories_attributes

Step 3
Finally, I added some code to my controller:

# POST /posts
# POST /posts.xml
def create
  @post = Post.new(params[:post])
  @category = @post.categories.build(params[:category]) unless params[:category][:name].blank?
  # stuff removed
end


def update
  @post = Post.find(params[:id])
  @category = @post.categories.build(params[:category]) unless params[:category][:name].blank?
  # stuff removed
end

Now, when I create a new post, I can simultaneously choose multiple existing categories from the select menu and create a brand new category at the same time - it's not a case of one-or-the-other

There is one tiny bug which only occurs when editing and updating existing posts; in this case it won't let me simultaneously create a new category and select multiple existing categories - if I try to do both at the same time, then only the existing categories are associated with the post, and the brand-new one is rejected (with no error message). But I can get round this by editing the post twice, once to create the new category (which automagically associates it with the post) and then a second time to select some additional existing categories from the menu - like I said this is not a big deal because it all works really well otherwise and my users can adapt to these limits

Anyway, I hope this helps someone.

Amen.

幸福不弃 2024-10-05 07:04:10

在您的表单中,您可能应该为每个类别渲染一次 fields_for (每个帖子可以有多个类别,因此有 habtm 关系)。尝试类似的方法:

- for category in @post.categories
  = fields_for "post[categories_attributes][#{category.new_record? ? category.object_id : category.id}]", category do |builder|
    = builder.hidden_field :id unless category.new_record?
    = builder.label :content, "Name"
    = builder.text_field :content

In your form you probably should render the fields_for once per category (you can have multiple categories per post, hence the habtm relation). Try something like:

- for category in @post.categories
  = fields_for "post[categories_attributes][#{category.new_record? ? category.object_id : category.id}]", category do |builder|
    = builder.hidden_field :id unless category.new_record?
    = builder.label :content, "Name"
    = builder.text_field :content
漫漫岁月 2024-10-05 07:04:10

我已经提交了申请,并且我的嵌套表单适用于 HABTM。
我的模型是:

class UserProfile < ActiveRecord::Base

    attr_accessible :name, :profession

    has_and_belongs_to_many :cities

    belongs_to :user

    attr_accessible :city_ids, :cities
    def self.check_city(user,city)

     user.cities.find_by_id(city.id).present?

    end 

end

class City < ActiveRecord::Base

    attr_accessible :city_name

    has_and_belongs_to_many :user_profiles

end

在我的表单中,我有:

-# just showing the relevent parts


= f.fields_for :cities do|city| 

            = city.text_field :city_name 

在我的控制器上:

 def create

    params[:user_profile][:city_ids] ||= [] 
    if params[:user_profile][:cities][:city_name].present?
      @city= City.create(:city_name=>params[:user_profile][:cities][:city_name])
      @city.save
      params[:user_profile][:city_ids] << @city.id
    end

    @user=current_user
    params[:user_profile].delete(:cities)
    @user_profile = @user.build_user_profile(params[:user_profile])

    respond_to do |format|
      if @user_profile.save

        format.html { redirect_to @user_profile, notice: 'User profile was successfully created.' }
        format.json { render json: @user_profile, status: :created, location: @user_profile }
      else
        format.html { render action: "new" }
        format.json { render json: @user_profile.errors, status: :unprocessable_entity }
      end
    end
  end

def update

    params[:user_profile][:city_ids] ||= [] 
    if params[:user_profile][:cities][:city_name].present?
      @city= City.create(:city_name=>params[:user_profile][:cities][:city_name])
      @city.save
      params[:user_profile][:city_ids] << @city.id
    end
    @user=current_user
    params[:user_profile].delete(:cities)
    @user_profile = @user.user_profile

    respond_to do |format|
      if @user_profile.update_attributes(params[:user_profile])

        format.html { redirect_to @user_profile, notice: 'User profile was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @user_profile.errors, status: :unprocessable_entity }
      end
    end
  end

此代码有效。

I have made my application and my nested form works with HABTM.
My model is :

class UserProfile < ActiveRecord::Base

    attr_accessible :name, :profession

    has_and_belongs_to_many :cities

    belongs_to :user

    attr_accessible :city_ids, :cities
    def self.check_city(user,city)

     user.cities.find_by_id(city.id).present?

    end 

end

class City < ActiveRecord::Base

    attr_accessible :city_name

    has_and_belongs_to_many :user_profiles

end

In my form I have:

-# just showing the relevent parts


= f.fields_for :cities do|city| 

            = city.text_field :city_name 

And at my controller:

 def create

    params[:user_profile][:city_ids] ||= [] 
    if params[:user_profile][:cities][:city_name].present?
      @city= City.create(:city_name=>params[:user_profile][:cities][:city_name])
      @city.save
      params[:user_profile][:city_ids] << @city.id
    end

    @user=current_user
    params[:user_profile].delete(:cities)
    @user_profile = @user.build_user_profile(params[:user_profile])

    respond_to do |format|
      if @user_profile.save

        format.html { redirect_to @user_profile, notice: 'User profile was successfully created.' }
        format.json { render json: @user_profile, status: :created, location: @user_profile }
      else
        format.html { render action: "new" }
        format.json { render json: @user_profile.errors, status: :unprocessable_entity }
      end
    end
  end

def update

    params[:user_profile][:city_ids] ||= [] 
    if params[:user_profile][:cities][:city_name].present?
      @city= City.create(:city_name=>params[:user_profile][:cities][:city_name])
      @city.save
      params[:user_profile][:city_ids] << @city.id
    end
    @user=current_user
    params[:user_profile].delete(:cities)
    @user_profile = @user.user_profile

    respond_to do |format|
      if @user_profile.update_attributes(params[:user_profile])

        format.html { redirect_to @user_profile, notice: 'User profile was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @user_profile.errors, status: :unprocessable_entity }
      end
    end
  end

This code works.

呆° 2024-10-05 07:04:10

也许你应该尝试一下(不是测试):

attr_accessible :category_attributes

并且 HBTM 关系并不真正推荐......但我自己使用它们:P

Maybe you should try it with (not testet):

attr_accessible :category_attributes

And HBTM relations arent really recommened... But I use them on my own :P

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文