返回false然后无法将项目添加到has_many中:through

时间:2013-02-26 14:22:02

标签: ruby-on-rails ruby activerecord database-relations

我有这种模式关系

class User < ActiveRecord::Base
  has_many :trip_memberships, dependent: :destroy
  has_many :trips, through: :trip_memberships, uniq: true
end

class Trip < ActiveRecord::Base
  has_many :trip_memberships, dependent: :destroy
  has_many :members, through: :trip_memberships, source: :user, uniq: true
end

class TripMembership < ActiveRecord::Base
  belongs_to :trip
  belongs_to :user
end

当我将一些用户添加为旅行会员时,就像使用此代码一样:

trip = Trip.last
john = User.last
trip.members # => []  Empty right now
trip.members << john
trip.members # => [john]  Contains John
trip.members << john
trip.members # => [john]  Contains only John, but...
TripMembership.all # => [TripMembership(trip, john), TripMembership(trip, john)]
                   # There is 2 memberships, even the accessor methods only show one
                   # member because of the :uniq option

我不想在trip_memberships表格中有重复项,但我想遵循“告诉,不要问”的原则。我不想检查给定记录是否存在。

我将此验证添加到TripMembership模型

class TripMembership < ActiveRecord::Base
  belongs_to :trip
  belongs_to :user
  validates_presence_of :trip_id, :user_id
  validates_uniqueness_of :trip_id, scope: [:user_id]
  validates_uniqueness_of :user_id, scope: [:trip_id]
end

当我尝试添加不能记录的记录时,我希望<<concat方法返回false 被保存,就像有许多关系一样,但似乎与through关系不一样。

trip.members << john # => ActiveRecord::RecordInvalid: Validation failed: Trip has already been taken, User has already been taken

任何人都知道一种优雅的方式来实现这个目标吗?我认为应该像这样使用api。

if @trip.members << new_member
   flash[:success] = "new member added"
else
   flash[:error] = "can't add this member to trip"
end

2 个答案:

答案 0 :(得分:3)

您可以尝试这样:

 if @trip.members.include?(new_member)
    flash[:error] = "Member already exists"
 else
    @trip.members << new_member
    flash[:success] = "new member added"
 end

修改

has_many :members, through: :trip_memberships, source: :user do
  def <<(member)
    if self.include?(member)
      false
    else
      super(Array(member)-self)
    end
  end
end

答案 1 :(得分:1)

我刚才有了一个主意。

因为我刚刚发现rails的标准行为是在用户不是新记录时引发异常,并且我不能改变众所周知的方法的默认行为,所以我将扩展我的关联使用名为add_member的自定义方法,该方法只是在开始/救援中调用<<

has_many :members, through: :trip_memberships, source: :user, uniq: true do
  def add(*records)
    self.<< records
  rescue ActiveRecord::RecordInvalid
    false
  end
end

我有自己的方法,可以按预期工作,而无需更改rails方法。