【问题标题】:ActiveRecord Query: Create an instance method using scope chainingActiveRecord 查询:使用范围链接创建实例方法
【发布时间】:2017-11-18 12:38:35
【问题描述】:

我在通过 ActiveRecord 查询创建类方法时遇到困难:

我创建了一个名为 popular_users 的类方法来按用户数量对用户进行排序,但它没有返回任何结果。我相信问题在于friend_id: self。如果我使用friend_id: User.first 在rails 控制台中进行测试,它可以工作。 (friend_id是被关注者的id)

class User < ApplicationRecord

  # received friend (ie a follower): other user sends a request which has been accepted by current user
  has_many :friendships
  has_many :received_friendships, class_name: "Friendship", foreign_key: "friend_id"
  has_many :received_friends, -> { where(friendships: { accepted: true}) }, through: :received_friendships, source: :user

  def self.popular_users
    self.joins(:friendships)
        .where(friendships: {accepted: true, friend_id: self})
        .group("users.id")
        .select("users.id, count(friendships) AS followers_count")
        .order("followers_count DESC")
  end

我尝试了以下方法,但它导致了这个错误:

ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: 语法错误 在“SELECT”处或附近

  def self.popular_users
    self.joins(:friendships)
        .group("users.id")
        .select("SELECT COUNT(friend_id) FROM friendships WHERE (user_id = :user_id AND accepted = 't')) AS followers_count")
        .order("followers_count DESC")
  end

架构:

  create_table "friendships", force: :cascade do |t|
    t.integer "user_id"
    t.integer "friend_id"
    t.boolean "accepted", default: false
  end

  create_table "users", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "name"
  end

更新:这是我的回答

  def self.popular_users
    self.joins("RIGHT JOIN friendships ON friendships.friend_id = users.id AND friendships.accepted = 't'")
        .group("users.id")
        .select("users.id, count(users.id) AS followers_count")
        .order("followers_count DESC")
  end

例如在 Rails 控制台中测试:

Friendship.create(user_id: 1, friend_id: 2, accepted: true)
Friendship.create(user_id: 4, friend_id: 2, accepted: true)
Friendship.create(user_id: 5, friend_id: 2, accepted: true)
Friendship.create(user_id: 1, friend_id: 3, accepted: true)
Friendship.create(user_id: 2, friend_id: 3, accepted: true)

在控制台输入User.popular_users后返回的结果是#&lt;ActiveRecord::Relation [#&lt;User id: 2, followers_count: 3&gt;, #&lt;User id: 3, followers_count: 2&gt;]&gt;

【问题讨论】:

  • 您想用.where( friendships: { friend_id: self }) 实现什么目标?我看起来您只要求与某个(当前?)用户有友谊的用户。
  • 我试图将每个用户拥有的关注者数量相加(例如,如果用户接受了另一个用户发送的友谊,则关注者计数 = 1),然后按关注者降序对用户进行排序数数。 (如人气榜/记分牌)
  • 感谢您的澄清。请查看我发布的答案。

标签: ruby-on-rails ruby activerecord


【解决方案1】:

self 在类方法的上下文中将是类对象本身。

class Test
  def self.test
    self
  end

  def test
    self
  end
end

Test.test
#=> Test

Test.new.test 
#=> #<Test:0x0000010f624bb0>

您需要将所需的 id 传递到您的类方法中,例如:

User.popular_users_for(friend_id)

【讨论】:

    【解决方案2】:

    假设两个不同的用户只能有一个好友,请尝试以下操作:

    class User < ApplicationRecord
      # . . .
    
      def self.popular_users
        self.joins(:friendships)
          .where( friendships: { accepted: true } )
          .group("users.id")
          .select("users.id, COUNT(users.id) AS followers_count")
          .order("COUNT(users.id) DESC")
      end
    
      # . . .
    end
    

    usersfriendshipsjoin 将为每个用户从friendships 中提取所有行(并且where 将加入结果限制为仅接受您希望从原来的友谊中获得的友谊)。出于这个原因,COUNT(users.id) 就足够了 - 这将计算每个用户在联接中出现的总行数(友谊)。

    此外,大多数 SQL 变体不允许您像为 followers_count 命名那样命名别名,然后在 ORDER BY 子句中使用该别名,所以我更笼统地写了我的答案,以防万一这也可能是部分问题。

    【讨论】:

    • 您好,DRSE,感谢您的解释。上面的类方法返回一个用户被关注和关注的用户总数,因为两个不同的用户最多可以有 2 个友谊(他们可以在friendships.id 行的friend_id 或user_id 列中)。我设法通过对友谊使用正确的加入来解决它。我更新的类方法在上面。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-12-11
    • 2015-10-06
    • 2020-06-14
    • 1970-01-01
    • 1970-01-01
    • 2021-09-03
    • 1970-01-01
    相关资源
    最近更新 更多