Rails before_create回调用法

时间:2012-01-19 06:14:05

标签: ruby-on-rails callback

我有一个相当简单的问题,但我想不出简单的解决方案。我有3个型号。

class User < ActiveRecord::Base
  has_many :memberships
end

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

class School < ActiveRecord::Base
  has_many :memberships
end  

用户可以为学校创建会员资格。 “会员编号”由学校提供。到现在为止还挺好。

除了一个用例,其中成员资格记录由学校创建,而成员资格表中没有user_id值。

在这种情况下,使用“membernumber”,“school_id”和空“user_id”字段创建记录。

我想要做的是,当用户试图在学校创建他的会员资格(使用他的“会员编号”)时,首先我要查看“user_id”,“membernumber”。如果找到,school_id组合是唯一的(我使用了validates_uniqueness_of ....)。

如果找不到记录,则检查与“membernumber”的记录/关联,并且学校存在并且其user_id字段为空。如果是这种情况,那么不要创建新的成员资格,只需更新它的user_id字段(使用current_user.id),并使用msg返回新关联的创建成功(就好像它是一个新的成员资格记录)。

我该怎么做?我需要使用任何回调吗?在这种情况下如何使用回调? 任何帮助表示赞赏。

谢谢,

Atarangp

3 个答案:

答案 0 :(得分:3)

你也应该在你的迁移中强制执行唯一性限制,不管多少DHH都会在整个过程中宣传“愚蠢的持久性”,并且只在应用层面处理这个问题。

最简单的方法是在validates_presence_of :user_id模型中强制执行Membership。如果用户ID不存在,则不允许创建连接记录;无论如何,您的AR关联查询应该固有地传递此ID。

例如,请考虑 - User.last.memberships.build(@school),并且在您的应用程序的上下文中,用户将是current_user本身。

before_create和其他before_ AR回调绝对是方便的,但我总是将这种方法作为最后的手段。原因是你直觉地期望你的应用程序以某种方式运行,而这些before_回调可以实质上改变命中数据库的信息。当然,单元测试会处理这些问题。

答案 1 :(得分:1)

只有通过模型才能看出:

  1. 会员资格包含用户和学校的参考,因此可以作为用户和学校之间has_and_belongs_to_manyhas_many :through关系的联接表
  2. 如果我们考虑上述情况,那么当且仅当它能够同时引用用户和学校时,任何会员记录都有效。不知何故,我想不出一个用例,你想要创建一个会员记录而不引用用户。如果你能详细说明一下你的用例,那将会很棒。
  3. 如果上述方法适用于您,那么您的模型应如下所示:

    class User < ActiveRecord::Base
      has_many :memberships
      has_many :schools, :through => :memberships
    end
    
    class Membership < ActiveRecord::Base
      belongs_to :user
      belongs_to :school
    end
    
    class School < ActiveRecord::Base
      has_many :memberships
      has_many :users, :through => :memberships
    end
    

    现在,您应该可以将用户添加到学校,反之亦然。

    user = User.first
    user.schools << School.first
    

    这会自动在会员资格中创建一个带有适当引用的记录。并且用户将开始在School.first.users中显示。此外,您可以在school_id范围内添加唯一性验证,即validates_uniqueness_of :user_id, :scope => :school_id

    before_ after_回调很好,但在这种情况下,您需要一些自定义代码来保持同步。以上将消除对before_ after_回调的任何依赖。此外,您不需要为会员模型上的任何其他验证检查而烦恼,因为只有当用户记录添加到学校记录时才会自动创建记录,反之亦然。

答案 2 :(得分:0)

这就是我解决它的方法 在模型成员资格中,添加了一个方法membership membership,并从成员资格的create中调用它  行动。

class Membership < ActiveRecord::Base
  belongs_to :school
  belongs_to :user
  #validations, etc.

  def Membership.membership_exists(membernumber, school_id)
     membership = Membership.where(:membernumber =>  membernumber, :school_id => school_id, :user_id => nil).first
     if membership.nil?
        return false
     else
        membership.update_attribute :user_id, user_id
        return true
     end
  end
end

在会员控制器中(在创建操作中)

  def create
    @membership = Membership.new(params[:membership])
    @membership.user_id = current_user.id

    @flag = Membership.membership_exists(@membership.membernumber,@membership.school_id)
    if @flag == true
      flash[:notice] = "Membership creation is successful"
      redirect_to @membership
      return
    end

    respond_to do |format|
      if @membership.save
        format.html { redirect_to @membership, notice: 'Membership was successfully created.' }
        format.json { render json: @membership, status: :created, location: @membership }
      else
        format.html { render action: "new" }
        format.json { render json: @membership.errors, status: :unprocessable_entity }
      end
    end
end

这很有效。但这是一个很好的方法吗?

谢谢, Atarangp。