创建具有两个belongs_to关联的模型的最佳实践?

时间:2011-07-26 17:27:02

标签: ruby-on-rails ruby ruby-on-rails-3

这是我经常遇到的一致性问题。

让我们考虑一个典型的论坛:

  • 用户可以创建帖子
  • 帖子属于主题
  • 帖子也属于创建它们的用户

选择这两个选项的最佳做法是什么:

# Initialize @post on the User
def create
  @post = current_user.posts.build(params[:post])
  @post.topic_id = @topic.id
  if @post.save
    ...
  end
end

或者

# Initialize @post on the Topic
def create
  @post = @topic.posts.build(params[:post])
  @post.user_id = current_user.id
  if @post.save
    ...
  end
end

或者有更好的方法,考虑到在上面的例子中,@ post user_idtopic_id必须添加到attr_accesssible(感觉hacky)?

3 个答案:

答案 0 :(得分:3)

我设法找到的最干净的方法是使用CanCan:当你有一个规则can :create, Post, :user_id => user.id并在控制器中添加load_resource时,它会设置属性。

但它并不总是合适的。如果有一个通用的解决方案可以一次性初始化嵌套对象,那将会很不错。

更新。我想出了另一个选择:

@post = @topic.posts.where(user_id: current_user.id).build(params[:post])

一般来说,所有这些方法都会打破Law of Demeter。最好将其封装在模型的方法中,如下所示:

class Topic < ActiveRecord::Base
  def new_post(params={}, author=nil)
    posts.build(params).tap {|p| p.user = author}
  end
end

然后在控制器中:

@post = @topic.new_post(params[:post], current_user)

答案 1 :(得分:2)

你永远不需要使用ID或attr_accessible。如果用户has_many帖子和主题has_many帖子比您可以做的那样

# Initialize @post on the User
def create
  @post = current_user.posts.build(params[:post])
  @post.topic = @topic #assuming you've gotten the topic from somewhere
  if @post.save
    ...
  end
end 

从用户或主题构建起来确实没有太大区别,但对用户而言,从用户看起来更自然。

答案 2 :(得分:0)

我更喜欢

@post = @topic.posts.build(params[:post])
@post.user = current_user

虽然我没有看到其他方法有任何问题,但通过主题构建帖子对我来说更自然(因为帖子主要显示在其主题的上下文中而不是用户本身)。