从 RoR 后端渲染 JSON 时To_param或 slug 不起作用



我有一个Ruby后端和React前端。在我的Ruby应用程序中有一个模型游戏。这个模型有一个非常漂亮的URL——>像"http://localhost: 3000/游戏/BT526";而不是"http://localhost:3000/games/2"。BT526是游戏模型中的:代码字符串。它在Ruby中工作得很好,但当我将其渲染为JSON并在我的前端应用程序中获取它时,链接到Game就不起作用了。我想,这个问题是段塞为null。在我的JSON中,但我不确定。我怎么能使这个修改后的URL工作后取回?: _ (

game.rb

class Game < ApplicationRecord
validates_uniqueness_of :name
def to_param
code
end
has_many :players
has_many :monsters
end

games_controller.rb

PREFACE      = ('A'..'Z').to_a << ?_
SUFFIX       = ('0'..'9').to_a
PREFACE_SIZE = 2
SUFFIX_SIZE  = 3
class GamesController < ApplicationController
before_action :set_game, only: %i[ show edit update destroy ]
# GET /games or /games.json
def index
@games = Game.all
render json: @games, include: [:players, :slug]
end
# GET /games/1 or /games/1.json
def show
# @game = Game.find_by_slug(params[:code])
# @game = Game.all
@game = set_game
render json: @game
@qr = RQRCode::QRCode.new(game_url)
end
# GET /games/new
def new
@game = Game.new
@game.code = gen_name
end
def gen_name
PREFACE.sample(PREFACE_SIZE).join << SUFFIX.sample(SUFFIX_SIZE).join
end
# GET /games/1/edit
def edit
end
# POST /games or /games.json
def create
@game = Game.new(game_params)
respond_to do |format|
if @game.save
format.html { redirect_to game_url(@game), notice: "Game was successfully created." }
format.json { render :show, status: :created, location: @game }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @game.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /games/1 or /games/1.json
def update
respond_to do |format|
if @game.update(game_params)
format.html { redirect_to game_url(@game), notice: "Game was successfully updated." }
format.json { render :show, status: :ok, location: @game }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @game.errors, status: :unprocessable_entity }
end
end
end
# DELETE /games/1 or /games/1.json
def destroy
@game.destroy
respond_to do |format|
format.html { redirect_to games_url, notice: "Game was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_game
@game = Game.find_by_slug(params[:code])
end
# Only allow a list of trusted parameters through.
def game_params
params.require(:game).permit(:code, :name, :fight)
end
end

routes.rb

Rails.application.routes.draw do
resources :effects
get 'welcome/index'
resources :games do
resources :players
resources :monsters
end
get 'effects/index'
get 'games/index'
post "", to: "welcome#redirect", as: :redirect
root 'welcome#index'
scope '/api/v1' do
resources :games 
resources :players
resources :effects
resources :slug
end
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

schema.rb/游戏

create_table "games", force: :cascade do |t|
t.string "code"
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.boolean "fight", default: false
t.index ["slug"], name: "index_games_on_slug", unique: true
end

和渲染json,以防:

[{"id":1,"code":"KR984","name":"Hello world","created_at":"2022-12-02T13:34:51.992Z","updated_at":"2022-12-07T10:32:35.827Z","slug":null,"fight":false,"players":[{"id":1,"name":"PLAYER 1","initiative":null,"hp":20,"languages":"sjdads","perc":"12","inv":"12","ins":"12","armor":44,"conc":true,"created_at":"2022-12-11T17:18:33.745Z","updated_at":"2022-12-11T17:18:33.745Z","game_id":1}]},{"id":2,"code":"BT526","name":"Random game","created_at":"2022-12-07T10:14:16.948Z","updated_at":"2022-12-07T10:14:16.948Z","slug":null,"fight":false,"players":[]}]

您在这里所做的基本上只是添加一个额外的唯一标识符到记录除了代理键(id列)用作外部标识符。这是现有解决方案(如FriendlyId)的一个已知问题。

你真的把整个事情弄得太复杂了,为同一个东西创建两个不同的列让你自己感到困惑。选择一个。如果你以后想给它起别名,可以这样做,但要确保你记录了它。

那么您应该将唯一标识符的生成从控制器移到模型中(甚至是一个单独的服务对象)。

class Game < ApplicationRecord
PREFACE      = ('A'..'Z').to_a << ?_
SUFFIX       = (0..9).to_a
PREFACE_SIZE = 2
SUFFIX_SIZE  = 3
validates_uniqueness_of :name
validates :code, uniqueness: true, 
presence: true
has_many :players
has_many :monsters

def to_param
code
end
# Generates a unique random code (hopefully) consisting of 
# three letters and three numbers
def generate_code
loop do
random_code = PREFACE.sample(PREFACE_SIZE).join << SUFFIX.sample(SUFFIX_SIZE).join
break random_code unless self.class.exists?(code: random_code)
end
end
def generate_code!
self.code = generate_code
end
end

这可以让你测试它的隔离性,并且你不会用常量污染全局命名空间。

您还应该确保根据鸽子洞原理实际生成的是唯一的代码。无论碰撞看起来多么不可能,它仍然可能发生——尤其是当你重新发明轮子的时候。

class GamesController < ApplicationController
before_action :set_game, only: %i[ show edit update destroy ]
# GET /games or /games.json
def index
@games = Game.all
render json: @games, include: [:players, :slug]
end
# GET /games/1 or /games/1.json
def show
# You should probally just generate the QR code once when creating the record
# instead of doing it over and over
@qr = RQRCode::QRCode.new(game_url(@game)) 
render json: @game
end
# GET /games/new
def new
@game = Game.new
end
# GET /games/1/edit
def edit
end
# POST /games or /games.json
def create
@game = Game.new(game_params)
@game.generate_code! # Do you actually need or want the user to be able assign the code?
respond_to do |format|
if @game.save
format.html { redirect_to game_url(@game), notice: "Game was successfully created." }
format.json { render :show, status: :created, location: @game }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @game.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /games/1 or /games/1.json
def update
respond_to do |format|
if @game.update(game_params)
format.html { redirect_to game_url(@game), notice: "Game was successfully updated." }
format.json { render :show, status: :ok, location: @game }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @game.errors, status: :unprocessable_entity }
end
end
end
# DELETE /games/1 or /games/1.json
def destroy
@game.destroy
respond_to do |format|
format.html { redirect_to games_url, notice: "Game was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_game
# params[:id] doesn't mean the id column 
# it means the unique external identifier used in the route.
@game = Game.find_by_code(params[:id])
end
# Only allow a list of trusted parameters through.
def game_params
params.require(:game).permit(:name, :fight)
end
end

最新更新