题目
实现用户关注功能
信息
- 类型:问答
- 难度:⭐⭐
考点
ActiveRecord关联设计, RESTful路由设计, 数据库迁移, 回调函数使用
快速回答
实现用户关注功能的核心步骤:
- 创建
Relationship中间表模型处理关注关系 - 设置用户模型的自连接多对多关联:
has_many :active_relationships和has_many :passive_relationships - 设计嵌套路由:
resources :users do
resources :relationships, only: [:create, :destroy]
end - 在
Relationship模型中添加唯一性验证防止重复关注 - 控制器中使用
current_user.follow(other_user)实现关注逻辑
1. 原理说明
用户关注功能本质是多对多自连接关系:
- 一个用户(follower)可以关注多个其他用户(followed)
- 一个用户可以被多个其他用户关注
通过中间表relationships存储关联关系,其中包含follower_id和followed_id两个外键。
2. 代码实现
数据库迁移文件(db/migrate/xxx_create_relationships.rb)
class CreateRelationships < ActiveRecord::Migration[7.0]
def change
create_table :relationships do |t|
t.references :follower, foreign_key: { to_table: :users }
t.references :followed, foreign_key: { to_table: :users }
t.timestamps
end
# 添加复合索引防止重复关注
add_index :relationships, [:follower_id, :followed_id], unique: true
end
end用户模型(app/models/user.rb)
class User < ApplicationRecord
# 主动关注关系
has_many :active_relationships,
class_name: 'Relationship',
foreign_key: 'follower_id',
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
# 被动关注关系(粉丝)
has_many :passive_relationships,
class_name: 'Relationship',
foreign_key: 'followed_id',
dependent: :destroy
has_many :followers, through: :passive_relationships, 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/relationship.rb)
class Relationship < ApplicationRecord
belongs_to :follower, class_name: 'User'
belongs_to :followed, class_name: 'User'
# 关键验证
validates :follower_id, presence: true
validates :followed_id, presence: true
validates :follower_id, uniqueness: { scope: :followed_id } # 防止重复关注
end路由配置(config/routes.rb)
resources :users do
member do
# 获取关注列表
get :following, :followers
# 关注/取消关注操作
post 'follow', to: 'relationships#create'
delete 'unfollow', to: 'relationships#destroy'
end
end
# 或使用嵌套资源
resources :relationships, only: [:create, :destroy]控制器示例(app/controllers/relationships_controller.rb)
class RelationshipsController < ApplicationController
before_action :authenticate_user!
def create
@user = User.find(params[:followed_id])
current_user.follow(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js # 支持AJAX
end
end
def destroy
@user = Relationship.find(params[:id]).followed
current_user.unfollow(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
end3. 最佳实践
- 数据库优化:为
follower_id和followed_id单独建立索引,并添加复合唯一索引 - N+1查询解决:在控制器中使用
includes(:following)预加载关联数据 - 安全防护:
- 禁止用户关注自己(在
follow方法中添加unless self == other_user) - 使用
dependent: :destroy确保用户删除时关联关系同步清除
- 禁止用户关注自己(在
- AJAX支持:通过
format.js响应实现无刷新关注/取消操作
4. 常见错误
- 缺少唯一性验证:导致用户可以重复关注同一个人
- 索引缺失:当用户关注量大时查询性能急剧下降
- 路由设计不当:使用非RESTful路由如
/follow_user破坏设计规范 - 未处理自关注:允许用户关注自己导致数据逻辑混乱
- 回调函数滥用:在关系模型中添加不必要的业务逻辑回调
5. 扩展知识
- 粉丝系统进阶:
- 添加
status字段实现关注请求审批(pending/approved) - 使用
counter_cache缓存粉丝数和关注数
- 添加
- 性能优化:
- 对超大规模用户使用Redis存储关注关系
- 使用后台作业处理关注通知邮件
- 测试要点:
- 编写模型测试验证关联关系和业务方法
- 控制器测试覆盖关注/取消关注的HTTP响应
- 集成测试模拟用户完整操作流程