题目
实现用户关注功能与粉丝列表展示
信息
- 类型:问答
- 难度:⭐⭐
考点
模型关联设计, ActiveRecord查询优化, 数据库索引应用
快速回答
实现用户关注功能的核心要点:
- 使用
has_many :through建立自连接多对多关系 - 创建
FollowRelationship连接模型记录关注状态 - 添加数据库唯一索引防止重复关注
- 使用
counter_cache优化粉丝数统计 - N+1查询优化通过
includes预加载关联数据
原理说明
用户关注功能本质是自引用多对多关系:一个用户可关注多个其他用户(following),同时被多个用户关注(followers)。需要中间表(follow_relationships)存储关联关系,通过ActiveRecord的has_many :through实现模型关联。
代码实现
1. 数据迁移文件
# db/migrate/xxx_create_follow_relationships.rb
class CreateFollowRelationships < ActiveRecord::Migration[7.0]
def change
create_table :follow_relationships do |t|
t.references :follower, foreign_key: { to_table: :users }
t.references :followed, foreign_key: { to_table: :users }
t.timestamps
end
# 关键优化:添加复合唯一索引和单字段索引
add_index :follow_relationships, [:follower_id, :followed_id], unique: true
add_index :follow_relationships, :followed_id # 用于粉丝列表查询
end
end2. 模型关联设计
# app/models/user.rb
class User < ApplicationRecord
# 关注他人关系
has_many :active_relationships,
class_name: 'FollowRelationship',
foreign_key: 'follower_id',
dependent: :destroy
has_many :following,
through: :active_relationships,
source: :followed
# 被关注关系(粉丝)
has_many :passive_relationships,
class_name: 'FollowRelationship',
foreign_key: 'followed_id',
dependent: :destroy
has_many :followers,
through: :passive_relationships,
source: :follower,
counter_cache: :followers_count # 粉丝数缓存
# 关注/取消关注方法
def follow(other_user)
active_relationships.create(followed_id: other_user.id)
end
def unfollow(other_user)
active_relationships.find_by(followed_id: other_user.id)&.destroy
end
def following?(other_user)
following.include?(other_user)
end
end
# app/models/follow_relationship.rb
class FollowRelationship < ApplicationRecord
belongs_to :follower, class_name: 'User'
belongs_to :followed, class_name: 'User'
# 数据验证
validates :follower_id, uniqueness: { scope: :followed_id }
validate :cannot_follow_self
private
def cannot_follow_self
errors.add(:base, 'Cannot follow yourself') if follower_id == followed_id
end
end最佳实践
- 索引优化:复合唯一索引防止重复关注,单字段索引加速粉丝列表查询
- counter_cache:在users表添加
followers_count字段,避免COUNT查询性能问题 - N+1解决方案:控制器中使用
includes预加载关联数据
控制器优化示例
# app/controllers/users_controller.rb
def followers
@user = User.includes(followers: :profile).find(params[:id])
# 渲染粉丝列表视图
end常见错误
- 缺少唯一索引:导致数据库层可能产生重复关注记录
- N+1查询问题:列表页未预加载关联数据导致性能瓶颈
- 未处理自关注:允许用户关注自己导致数据逻辑错误
- 事务缺失:关注/取消关注操作未包裹事务,可能产生数据不一致
扩展知识
- 异步处理:使用ActiveJob发送关注通知邮件
- 高级查询:使用SQL的
EXISTS优化互关检测:User.where(id: other_user.id).exists?(id: following_ids) - 安全考虑:控制器中验证当前用户权限,防止越权操作
- 测试要点:验证关注/取消关注的边界条件(如重复操作、自关注等)