题目
实现用户关注功能及其关联查询
信息
- 类型:问答
- 难度:⭐⭐
考点
多对多关联设计, 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
end2. 模型设计
# 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_count和following_count字段到users表,通过counter_cache实时更新 - 异步处理:在
after_create回调中使用ActiveJob发送关注通知邮件
常见错误
- 缺少方向区分:混淆
follower_id和followed_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]) - 测试要点:验证关注关系唯一性、自关注限制、回调触发等场景