0

まず、お読みいただきありがとうございます。私はRailsに不慣れで、これに何時間もこだわっています。

私の Rails 3.2 アプリには、User、Organization、Membership の 3 つのモデルがあります (最後のモデルは、User と Organization の間の結合モデルです)。

ユーザーが組織を作成すると、作成時にメンバーになる必要があります。そのため、私の Organization モデルには、メンバーシップを構築する before_create コールバックを含めました。問題は、新しい組織が作成されたときにメンバーシップが構築されている間、メンバーシップ オブジェクトの user_id が「nil.」に設定されているため、現在のユーザーがメンバーではないことです。

コールバックで user_id 属性をハードコーディングすると、実際にはメンバーシップが正しく構築(:user_id => "1")されます。

新しいメンバーシップに現在のユーザー ID を設定する適切な方法は何ですか? 私の協会がそれを処理する必要があるようですが、私は間違っているかもしれません.

これが私のモデルです。読みやすくするために、いくつかの検証行を省略しています。よろしくお願いします。

user.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

コントローラーでこれを行うのは問題ありませんが、組織を作成するユーザーが自動的に組織に所属する必要があるという事実をビジネス ロジックに反映させるとより効果的です。newand / or 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と、レールはアソシエーションを「元に戻す」方法がわからないため、組織を再度ロードしようとします。を使用する場合inverse_of、Rails は組織をリロードする必要がないことを認識しています。
  • validatesに設定する必要があり、 に設定しorganizationないでorganization_idください。このようにして、バリデーターは、関連付けを「遡って」いることを認識し、それがorganization「親」レコードであり、保存中であることを認識します。したがって、エラーは発生しません。
于 2013-05-25T15:09:29.867 に答える