首先,感谢您花时间阅读。我是Rails的新手,并且已经坚持了几个小时。
在我的Rails 3.2应用程序中,我有三个模型:用户,组织和成员资格(最后一个是用户和组织之间的连接模型)。
当用户创建组织时,他/她应该在创建时成为成员。因此,在我的组织模型中,我已经包含了一个构建成员身份的before_create回调。 问题在于,在创建新组织时构建成员资格时,Membership对象上的user_id设置为“nil。”,,因此当前用户不是成员。
回调中的user_id属性中的硬编码实际上确实正确地构建了成员资格,即(:user_id => "1")
,但一般要求组织模型了解当前用户状态似乎是错误的MVC实践。
在新会员资格上设置当前用户ID的正确方法是什么?似乎我的协会应该处理这个问题,但我可能错了。
这是我的模型 - 为了便于阅读,我省略了一些验证线。非常感谢提前。
user.rb
class User < ActiveRecord::Base
has_many :memberships
has_many :organizations, :through => :memberships
end
membership.rb
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :organization
end
organization.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
答案 0 :(得分: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
上覆盖create
和/或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
稍微考虑一下,应该可以避免将Organization
硬编码到User
中(例如使用关联扩展名)
编辑
为了能够对会员的组织存在进行验证,您需要这样做:
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会再次尝试加载组织,因为它不知道如何“回归”#34;该协会。使用inverse_of
时,rails知道它不必重新加载组织。validates
必须在organization
而非organization_id
上设置。通过这种方式,验证者知道我们已经回归&#34;该协会,它知道organization
是&#34;父母&#34;记录并且它正在被保存 - 所以它不会抱怨。