0

首先,感谢您花时间阅读。我是 Rails 的新手,并且已经坚持了好几个小时。

在我的 Rails 3.2 应用程序中,我有三个模型:用户、组织和成员资格(最后一个是用户和组织之间的连接模型)。

当用户创建组织时,他/她应该在创建时成为成员。因此,在我的组织模型中,我包含了一个用于构建成员资格的 before_create 回调。问题是,虽然在创建新组织时会建立成员资格,但成员资格对象上的 user_id 设置为“nil”,因此当前用户不是成员。

回调中的 user_id 属性中的硬编码实际上确实正确地构建了成员资格,即(:user_id => "1"),但通常要求组织模型了解当前用户状态似乎是不好的 MVC 做法。

在新会员资格上设置当前用户 ID 的正确方法是什么?似乎我的协会应该处理这个问题,但我可能错了。

这是我的模型——为了便于阅读,我省略了一些验证行。提前非常感谢。

用户.rb

class User < ActiveRecord::Base
    has_many :memberships
    has_many :organizations, :through => :memberships
end

会员资格.rb

class Membership < ActiveRecord::Base
    belongs_to :user
    belongs_to :organization
end

组织.rb

class Organization < ActiveRecord::Base
    has_many :memberships
    has_many :users, :through => :memberships
    accepts_nested_attributes_for :memberships, :allow_destroy => true
    ...
    before_create :add_membership

    protected
    def add_membership
        self.memberships.build
    end
end
4

1 回答 1

0

你是对的,让你的模型神奇地知道当前用户是不好的 MVC 实践。所以你必须在创建过程中以某种方式传递当前的用户 ID。您可以通过多种方式做到这一点;例如在控制器中:

def create 
  @organization = Organization.new( params[:organization] ) do |org|
    org.memberships.build( user_id: current_user.id )
  end
  # save, etc.
end

在控制器中这样做很好,但如果您的业务逻辑能够反映创建组织的用户应该自动属于它的事实,那就更好了。您可以覆盖new和/或createon Organization(如果您害怕覆盖,也可以创建自己的方法):

def new( params = {}, options = {} )
  creator = options.delete( :creator )
  super( params, options ) do |org|
    org.memberships.build( user_id: creator.id ) if creator
    yield org if block_given?
  end
end

传递用户现在很容易:

def create
  @organization = Organization.new(params[:organization], creator: current_user)
end

如果您不喜欢这种方法,或者您不想覆盖new或创建特定的工厂方法,您还可以进行类似的操作nested_attributes

attr_accessible :creator_id

def creator_id=( user_id )
  memberships.build user_id: user_id
end

那么在你看来:

f.hidden_field :creator_id, current_user.id

可选

使用第一种方法,为了更加清晰/易于使用,您还可以在以下位置创建一个方法User

def new_organization( params = {}, options = {}, &block )
  Organization.new( params, options.merge(creator: self), &block )
end

...好的,Organization这里是硬编码的(不好!)但是您的工作流程现在很容易理解:

def create
  # we know at first glance that the user is responsible for the organization
  # creation, and that there must be specific logic associated to this
  @organization = current_user.new_organization( params[:organization] )
  # etc
end

稍加思考,应该可以避免硬编码(例如使用关联扩展OrganizationUser

编辑

为了能够对会员的组织存在进行验证,您需要执行以下操作:

class Organization < ActiveRecord::Base
  has_many :memberships, inverse_of: :organization
end

class Membership < ActiveRecord::Base
  belongs_to :organization, inverse_of: :memberships

  validates :organization, presence: true
end

让我们解释一下:

  • inverse_of将您的关联设置为双向。默认情况下,关联是单向的,这意味着当您这样做时organization.memberships.first.organization,rails 会尝试再次加载组织,因为它不知道如何“爬回”关联。使用时inverse_of,rails 知道它不必重新加载组织。
  • validates必须设置为 onorganization而不是 on organization_id。这样验证者就知道我们正在“爬回”关联,它知道这organization是一个“父”记录并且它正在被保存 - 所以它不会抱怨。
于 2013-05-25T15:09:29.867 回答