rails: 配合 react-admin API adapter
const dataProvider = {
async getList(resource, { sort, filter, pagination }) => ({ data: Record[], total: number }),
async getOne(resource, { id }) => ({ data: Record }),
async getMany(resource, { ids }) => ({ data: Record[] }),
async getManyReference(resource, { target, id, sort, filter, pagination }) => ({ data: Record[], total: number }),
async create(resource, { data }) => ({ data: Record }),
async update(resource, { id, data }) => ({ data: Record }),
async updateMany(resource, { ids, data }) => ({ data: Identifier[] }),
async delete(resource, { id } ) => ({ data: Record }),
async deleteMany(resource, { ids }) => ({ data: Identifier[] }),
}
你提供的 dataProvider 接口是典型的 React Admin(或类似 Admin UI 框架)所使用的数据提供者契约,它要求后端提供一套符合 RESTful 风格 的 API。在 Ruby on Rails 中,可以通过标准的 Rails RESTful 路由 + 控制器 来实现这套接口。
下面是如何在 Rails 中实现这套 API 的详细说明(假设你使用的是 Rails 7+,并启用 JSON API 响应)。
✅ 1. 路由设计(RESTful)
假设你的 resource 是 posts,那么 Rails 默认的 RESTful 路由已经覆盖了大部分操作:
# config/routes.rb
Rails.application.routes.draw do
resources :posts, only: [:index, :show, :create, :update, :destroy]
end
但注意:React Admin 的某些操作(如 getMany, updateMany, deleteMany, getManyReference)不是标准 REST,需要额外自定义路由。
补充非标准路由:
Rails.application.routes.draw do
resources :posts do
collection do
post :get_many # POST /posts/get_many
patch :update_many # PATCH /posts/update_many
delete :delete_many # DELETE /posts/delete_many
end
end
# getManyReference: 例如 /posts?target=author_id&id=5
# 这个其实可以通过 index + filter 实现,无需额外路由
end
✅ 2. 控制器实现(PostsController 示例)
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
before_action :set_post, only: [:show, :update, :destroy]
# getList
def index
@posts = Post.all
# 分页(假设使用 pagy 或 kaminari)
@posts = @posts.page(params[:page]).per(params[:perPage]) if params[:page]
# 排序
if (sort_field = params.dig(:sort, :field)) && (sort_order = params.dig(:sort, :order))
@posts = @posts.order(sort_field => sort_order)
end
# 过滤(简单示例)
if params[:filter].is_a?(Hash)
params[:filter].each do |key, value|
@posts = @posts.where(key => value) unless value.blank?
end
end
render json: {
data: @posts.map(&:as_json_for_admin),
total: Post.count # 或 @posts.total_count(分页时)
}
end
# getOne
def show
render json: { data: @post.as_json_for_admin }
end
# getMany
def get_many
ids = Array(params[:ids])
posts = Post.where(id: ids)
render json: { data: posts.map(&:as_json_for_admin) }
end
# getManyReference
# 本质是带 filter 的 index,例如:/posts?target=author_id&id=5
# 可在 index 中处理:
# if params[:target] && params[:id]
# @posts = @posts.where(params[:target] => params[:id])
# end
# create
def create
@post = Post.new(post_params)
if @post.save
render json: { data: @post.as_json_for_admin }, status: :created
else
render json: { error: @post.errors }, status: :unprocessable_entity
end
end
# update
def update
if @post.update(post_params)
render json: { data: @post.as_json_for_admin }
else
render json: { error: @post.errors }, status: :unprocessable_entity
end
end
# updateMany
def update_many
ids = Array(params[:ids])
updated_ids = []
Post.where(id: ids).find_each do |post|
if post.update(post_params)
updated_ids << post.id
end
end
render json: { data: updated_ids }
end
# delete
def destroy
data = @post.as_json_for_admin
@post.destroy
render json: { data: data }
end
# deleteMany
def delete_many
ids = Array(params[:ids])
deleted_ids = Post.where(id: ids).map(&:id)
Post.where(id: ids).destroy_all
render json: { data: deleted_ids }
end
private
def set_post
@post = Post.find(params[:id])
end
def post_params
params.require(:data).permit(:title, :body, :author_id) # 根据你的模型调整
end
end
✅ 3. 数据序列化建议
为保持一致性,建议在模型中定义 as_json_for_admin 方法:
# app/models/post.rb
class Post < ApplicationRecord
def as_json_for_admin
{
id: id,
title: title,
body: body,
author_id: author_id,
created_at: created_at,
updated_at: updated_at
}
end
end
或者使用 ActiveModel::Serializers / Jbuilder / Blueprinter 等序列化工具。
✅ 4. 请求参数格式说明
React Admin 默认发送的请求体结构如下:
create:{ data: { title: "xxx", body: "yyy" } }update:{ id: 123, data: { title: "new" } }updateMany:{ ids: [1,2,3], data: { published: true } }
所以控制器中使用 params[:data] 提取数据是正确的。
✅ 5. 分页与排序参数(兼容 React Admin)
React Admin 默认发送:
pagination: { page: 1, perPage: 10 }
sort: { field: 'title', order: 'ASC' }
对应 Rails 接收为:
params[:pagination][:page] # 或直接 params[:page](可做映射)
params[:sort][:field]
params[:sort][:order]
你可以在 ApplicationController 中做参数标准化:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
before_action :normalize_params
private
def normalize_params
if params[:pagination]
params[:page] = params[:pagination][:page]
params[:perPage] = params[:pagination][:perPage]
end
end
end
✅ 6. 错误处理(可选)
统一错误格式,便于前端处理:
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
def record_not_found(exception)
render json: { error: 'Record not found' }, status: :not_found
end
✅ 总结
| React Admin 方法 | Rails 对应实现 |
|------------------------|----------------------------------|
| getList | index(带分页/排序/过滤) |
| getOne | show |
| getMany | 自定义 get_many action |
| getManyReference | index + ?target=xxx&id=yyy |
| create | create |
| update | update |
| updateMany | 自定义 update_many |
| delete | destroy |
| deleteMany | 自定义 delete_many |
如果你正在开发的是 mockapi-rails 项目,建议:
- 使用
resources :<resource>自动生成基础路由; - 为
getMany/updateMany/deleteMany添加collection路由; - 统一返回
{ data: ..., total?: ... }结构; - 使用强参数(
require(:data).permit(...))确保安全。
需要我为你生成一个通用的 BaseController 或脚手架代码吗?