【问题标题】:Rails: validate unique friendship relationshipsRails:验证独特的友谊关系
【发布时间】:2019-06-14 01:21:45
【问题描述】:

我正在关注http://railscasts.com/episodes/163-self-referential-association 建立一个友谊系统。

#Friendship model
id | user_id | friend_id

问题是

  1. 如何使user_idfriend_id 的组合独一无二? 防止从user_id = 2. friend_id = 1创建user_id = 1, friend_id = 2

  2. 我觉得这个模型设计真的很糟糕。我可以对其建模以实际存储 2 个 ID 的组合吗?点赞friendship_id_combination = [1, 2],然后验证一下。

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    您可以使用以下内容:

    validate :users_are_not_already_friends
    
    def users_are_not_already_friends
      combinations = ["user_id = #{user_id} AND friend_id = #{friend_id}",
      "user_id = #{friend_id} AND friend_id = #{user_id}"]
      if User.where(combinations.join(' OR ')).exists?
        self.errors.add(:user_id, 'Already friends!')
      end
    end
    

    2019 年更新:

    修改建议:

    def users_are_not_already_friends
      if User.where(user_id: friend_id, friend_id: user_id).exist?
        || User.where(user_id: user_id, friend_id: friend_id).exist?
        self.errors.add(:user_id, 'Already friends!')
      end
    end
    

    我强烈建议添加基于复合 user_idfriend_id 的 DB 约束(唯一索引):

    CREATE UNIQUE INDEX users_user_id_friend_id_key ON users (user_id, friend_id);
    

    【讨论】:

    • 我认为这可以防止创建两个user_id = 1, friend_id = 2,而不是user_id = 1, friend_id = 2user_id = 2, friend_id = 1
    • if self.class.where(user_id: friend_id, friend_id:user_id).exist? 呢?
    • 你可以这样做,但你也必须检查相反的情况(使用validates)。它可以工作,但会触发 2 个 SQL 查询而不是一个。这取决于你
    • 今天偶然发现了这个,很好的解决方案!顺便说一句,现在您可以通过使用.or 将验证变成单个查询(在Rails 5+ 中)来进一步改进它,例如:User.where(user_id: friend_id, friend_id: user_id).or(User.where(user_id: user_id, friend_id: friend_id)).exists?。此外,对于 DB this 上的 2 路索引,看起来是最佳选择。将两者结合起来是 IMO 的理想方法。
    【解决方案2】:

    是的。您正在沿着正确的道路思考。也许最简单的解决方案是使用数据库表通过将 ID 组合设为唯一索引来创建验证。

    class CreateFriendshipsUnq < ActiveRecord::Migration
      def change
        add_index :friendships, [:user_id, :friend_id], unique: true
      end
    end
    

    我认为这会满足您的需求。

    【讨论】:

    • 这将只验证一侧,而不是反向关系
    【解决方案3】:

    为什么您需要这种独特的组合。考虑如果你想获取单个用户的朋友,那么在当前场景中你可以使用简单的关联找到它。

    @user.friends
    

    如果您不想添加两个键,则需要触发两个查询以查找相同的详细信息。一个给你的朋友,另一个给请求你成为朋友的人。

    【讨论】:

    • 因为在大多数情况下,我想获取@user.friends,无论是谁创建的关系,(无论是user_id == @user,还是friends== @user)。
    猜你喜欢
    • 1970-01-01
    • 2012-07-09
    • 2012-10-28
    • 2010-12-22
    • 1970-01-01
    • 2011-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多