简体   繁体   English

has_many通过而不是联接查询

[英]has_many through instead of join query

I have relationship between User models defined through Friendship model. 我通过Friendship模型定义了User模型之间的关系。 (ROR 4) (ROR 4)

User 用户

class User < ActiveRecord::Base
  has_many :friendships, ->(object) { where('user_id = :id OR friend_id = :id', id: object.id) }

  has_many :friends, ->(object) { where(friendships: {status: 'accepted'}).where('user_id = :id OR friend_id = :id', id: object.id) }, through: :friendships, source: :friend

  has_many :requested_friends, -> { where(friendships: {status: 'pending'}) }, through: :friendships, source: :friend
end

Friendship 友谊

class Friendship < ActiveRecord::Base
  belongs_to :user
  belongs_to :friend, class_name: 'User'

  def self.request(user, friend)
    unless user == friend or find_friendship(user, friend) != nil
      create(user: user, friend: friend, status: 'pending')
    end
  end

  def self.find_friendship(user, friend)
    ids = [user.id, friend.id]
    where(user_id: ids, friend_id: ids).first
  end
end

However, this does not work and my tests are failing because of SQL queries produced. 但是,这不起作用,并且由于生成了SQL查询,我的测试失败了。

Friendships relation 友谊关系

> user.friendships

Query: 查询:

SELECT "friendships".* FROM "friendships" 
    WHERE "friendships"."user_id" = ? 
      AND (user_id = 1 OR friend_id = 1)  [["user_id", 1]]

So part of WHERE before AND "kills" my actual where. 因此,在AND之前 WHERE的一部分会“杀死”我的实际位置。 I made a workaround by making instance method: 我通过制作实例方法来解决:

def friendships
  self.class
    .select('friendships.* FROM `friendships`')
    .where('user_id = :id OR friend_id = :id', id)
end

Is there a way I can remove my instance method and modify has_many relation to produce the SQL I want? 有什么方法可以删除实例方法并修改has_many关系以生成所需的SQL?

Requested_friends relation Requested_friends关系

> Friendship.request(user, friend)
> friend.requested_friends

Query: 查询:

SELECT "users".* FROM "users" 
    INNER JOIN "friendships" ON "users"."id" = "friendships"."friend_id" 
    WHERE "friendships"."status" = 'pending' 
      AND "friendships"."user_id" = ? 
      AND (user_id = 2 OR friend_id = 2)  [["user_id", 2]]

It obviously isn't what I need so I made a workaround by removing has_many :requested_friends and making an instance method: 显然,这不是我所需要的,因此我通过删除has_many :requested_friends并创建了一个实例方法来进行变通:

def requested_friends
  self.class
    .joins('JOIN `friendships` friendships ON users.id = friendships.user_id')
    .where('friendships.status = ?', 'pending')
    .where('friendships.friend_id = ?', id)
end

Is there any way I can modify my has_many :requested_friends relation to produce same SQL as my instance method? 有什么方法可以修改has_many :requested_friends关系以产生与实例方法相同的SQL?

Very confusing - I'd do something like this: 非常令人困惑-我会做这样的事情:

#app/models/user.rb
Class User < ActiveRecord::Base
    has_many :friendships, class_name: "user_friendships", association_foreign_key: "user_id", foreign_key: "friend_id", 
    has_many :friends, class_name: "User", through: :friendships
end

#app/models/user_friendship.rb
Class UserFriendship < ActiveRecord::Base
    belongs_to :user
    belongs_to :friend, class_name: "User"
end 

You'd have a join table which looks like this: 您将有一个如下所示的联接表:

user_friendships
id | user_id | friend_id | other | info | created_at | updated_at

This should work (I'm not sure about the self referential association). 这应该工作(我不确定自我参照关联)。 If it does, it will allow you to call: 如果是这样,它将允许您调用:

@user.friends

I hope this helps? 我希望这有帮助?

You might also benefit from this gem 您可能还会从这个宝石中受益

you cannot achieve the SQL you want using has_many method with condition. 您不能通过条件使用has_many方法来实现所需的SQL。 The reason is that the block you pass to the method is only additional condition, on top of the standard query which checks if user_id = ? 原因是,传递给该方法的块只是附加条件,它位于检查user_id = ?的标准查询之上user_id = ? .

Instead you can simplify your instance method a little bit 相反,您可以稍微简化实例方法

def friendships Friendship.where('user_id = :id or friend_id = :id', id) end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM