侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

实现用户关注功能及其关联查询

2025-12-12 / 0 评论 / 6 阅读

题目

实现用户关注功能及其关联查询

信息

  • 类型:问答
  • 难度:⭐⭐

考点

多对多关联设计, ActiveRecord查询优化, 自连接关系实现

快速回答

实现用户关注功能的核心步骤:

  • 创建Follow连接模型处理关注关系
  • 使用has_many through建立自连接关联
  • 添加数据库索引和唯一验证防止重复关注
  • 通过ActiveRecord方法实现高效查询:
    • user.following获取关注列表
    • user.followers获取粉丝列表
## 解析

原理说明

用户关注功能本质是多对多自连接关系:一个用户可以关注多个其他用户,同时被多个用户关注。在Rails中需通过中间表(follows)实现,其中包含follower_id(发起关注者)和followed_id(被关注者)两个外键。

代码实现

1. 数据库迁移

# db/migrate/20230101000000_create_follows.rb
class CreateFollows < ActiveRecord::Migration[7.0]
  def change
    create_table :follows do |t|
      t.references :follower, foreign_key: { to_table: :users }
      t.references :followed, foreign_key: { to_table: :users }
      t.timestamps
    end

    # 关键索引优化
    add_index :follows, [:follower_id, :followed_id], unique: true
    add_index :follows, :followed_id
  end
end

2. 模型设计

# app/models/user.rb
class User < ApplicationRecord
  # 关注他人关系
  has_many :active_follows, 
           class_name: 'Follow', 
           foreign_key: 'follower_id',
           dependent: :destroy

  has_many :following, 
           through: :active_follows, 
           source: :followed

  # 被关注关系
  has_many :passive_follows, 
           class_name: 'Follow', 
           foreign_key: 'followed_id',
           dependent: :destroy

  has_many :followers, 
           through: :passive_follows, 
           source: :follower

  # 关注/取消关注方法
  def follow(other_user)
    following << other_user unless self == other_user
  end

  def unfollow(other_user)
    following.delete(other_user)
  end

  # 检查是否关注
  def following?(other_user)
    following.include?(other_user)
  end
end

# app/models/follow.rb
class Follow < ApplicationRecord
  belongs_to :follower, class_name: 'User'
  belongs_to :followed, class_name: 'User'

  # 防止重复关注和自关注
  validates :follower_id, uniqueness: { scope: :followed_id }
  validate :prevent_self_follow

  private

  def prevent_self_follow
    errors.add(:base, 'Cannot follow yourself') if follower_id == followed_id
  end
end

最佳实践

  • 查询优化:使用includes预加载避免N+1查询
    User.includes(:following).find(1).following
  • 计数器缓存:添加followers_countfollowing_count字段到users表,通过counter_cache实时更新
  • 异步处理:在after_create回调中使用ActiveJob发送关注通知邮件

常见错误

  • 缺少方向区分:混淆follower_idfollowed_id导致关系错乱
  • N+1查询问题:未使用includes预加载关联数据
  • 自关注漏洞:未验证follower_id != followed_id
  • 索引缺失:未给外键添加索引导致查询性能低下

扩展知识

  • 高级查询:使用SQL实现共同关注查询
    User.joins(:following).where(following: { id: target_user.following.ids })
  • 软删除:添加deleted_at字段实现关注关系的软删除
  • API设计:在控制器中使用分页
    @followers = user.followers.paginate(page: params[:page])
  • 测试要点:验证关注关系唯一性、自关注限制、回调触发等场景