【问题标题】:PUT with nested objectPUT 与嵌套对象
【发布时间】:2017-11-19 21:34:11
【问题描述】:

我的 Rails 5.1 API 应用程序中有 DishComment 模型,如下所示 - 代码库 here 。我需要帮助将新的Comment 添加到Dish

发帖

class Dish < ApplicationRecord
    has_many :comments
end

评论

class Comment < ApplicationRecord
    belongs_to :dish
end

Post Serializer(使用 ActiveModel Seriazlier)

class DishSerializer < ActiveModel::Serializer
  attributes :id, :name, :image, :category, :label, :price, :featured, :description, :created_at

  has_many :comments
end

评论序列化器

class CommentSerializer < ActiveModel::Serializer
  attributes :id, :rating, :comment, :author, :date

  def date
    object.created_at
  end
end

Post Controller - 默认的 Rails 脚手架

class DishesController < ApplicationController
  before_action :set_dish, only: [:show, :update, :destroy]

  # GET /dishes
  def index
    @dishes = Dish.all

    render json: @dishes
  end

  # GET /dishes/1
  def show
    render json: @dish
  end

  # POST /dishes
  def create
    @dish = Dish.new(dish_params)

    if @dish.save
      render json: @dish, status: :created, location: @dish
    else
      render json: @dish.errors, status: :unprocessable_entity
    end
  end

  # PATCH/PUT /dishes/1
  def update
    # byebug
    if @dish.update(dish_params)
      render json: @dish
    else
      render json: @dish.errors, status: :unprocessable_entity
    end
  end

  # DELETE /dishes/1
  def destroy
    @dish.destroy
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_dish
      @dish = Dish.find(params[:id])
    end

    # Only allow a trusted parameter "white list" through.
    def dish_params
      params.require(:dish).permit(:name, :image, :category, :label, :price, :featured, :description)
    end
end

评论控制器 - 默认的 rails 脚手架

class CommentsController < ApplicationController
  before_action :set_comment, only: [:show, :update, :destroy]

  # GET /comments
  def index
    @comments = Comment.all

    render json: @comments
  end

  # GET /comments/1
  def show
    render json: @comment
  end

  # POST /comments
  def create
    @comment = Comment.new(comment_params)

    if @comment.save
      render json: @comment, status: :created, location: @comment
    else
      render json: @comment.errors, status: :unprocessable_entity
    end
  end

  # PATCH/PUT /comments/1
  def update
    if @comment.update(comment_params)
      render json: @comment
    else
      render json: @comment.errors, status: :unprocessable_entity
    end
  end

  # DELETE /comments/1
  def destroy
    @comment.destroy
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_comment
      @comment = Comment.find(params[:id])
    end

    # Only allow a trusted parameter "white list" through.
    def comment_params
      params.require(:comment).permit(:rating, :comment, :author)
    end
end

问题

当用户访问 /dishes/:id 并通过前端应用程序 (Angular 2) 向菜品添加评论时,comment 被推送到当前 cmets 数组,我调用 PUT /dishes:id 并使用dish 对象嵌套在现有的 comments 和新注释中。然而,新的comment 并没有被 rails 保存——没有返回错误,而是返回了 dish 对象。但是我确实在rails s 控制台中看到了Unpermitted parameters: :id, :created_at。如何让 rails 保存新评论?

我将评论添加到菜肴的页面 (dishes/9) 在 Angular 客户端如下所示。

Rails 服务器日志

在 Rails 方面,下面是我在 params 中看到的内容 - 我确实看到了新评论 - {"author"=&gt;"JANE7777", "rating"=&gt;3, "comment"=&gt;"COMMENT7777", "date"=&gt;"2017-11-12T12:58:12.555Z"}

Started PUT "/dishes/9" for 127.0.0.1 at 2017-11-12 18:28:12 +0530
Processing by DishesController#update as HTML
  Parameters: {"id"=>"9", "name"=>"Uthappizza", "image"=>"images/uthappizza.png", "category"=>"mains", "label"=>"Hot", "price"=>"4.99", "featured"=>true, "description"=>"A unique combination of Indian Uthappam (pancake) and Italian pizza, topped with Cerignola olives, ripe vine cherry tomatoes, Vidalia onion, Guntur chillies and Buffalo Paneer.", "created_at"=>"2017-11-01T04:30:09.407Z", "comments"=>[{"id"=>46, "rating"=>5, "comment"=>"Imagine all the eatables, living in conFusion!", "author"=>"John Lemon", "date"=>"2012-10-16T17:57:28.556Z"}, {"id"=>47, "rating"=>4, "comment"=>"Sends anyone to heaven, I wish I could get my mother-in-law to eat it!", "author"=>"Paul McVites", "date"=>"2014-09-05T17:57:28.556Z"}, {"id"=>48, "rating"=>3, "comment"=>"Eat it, just eat it!", "author"=>"Michael Jaikishan", "date"=>"2015-02-13T17:57:28.556Z"}, {"id"=>49, "rating"=>4, "comment"=>"Ultimate, Reaching for the stars!", "author"=>"Ringo Starry", "date"=>"2013-12-02T17:57:28.556Z"}, {"id"=>50, "rating"=>2, "comment"=>"It's your birthday, we're gonna party!", "author"=>"25 Cent", "date"=>"2011-12-02T17:57:28.556Z"}, {"id"=>51, "rating"=>4, "comment"=>"great dish", "author"=>"Jogesh", "date"=>"2017-10-30T05:03:39.656Z"}, {"author"=>"JANE7777", "rating"=>3, "comment"=>"COMMENT7777", "date"=>"2017-11-12T12:58:12.555Z"}], "dish"=>{"id"=>"9", "name"=>"Uthappizza", "image"=>"images/uthappizza.png", "category"=>"mains", "label"=>"Hot", "price"=>"4.99", "featured"=>true, "description"=>"A unique combination of Indian Uthappam (pancake) and Italian pizza, topped with Cerignola olives, ripe vine cherry tomatoes, Vidalia onion, Guntur chillies and Buffalo Paneer.", "created_at"=>"2017-11-01T04:30:09.407Z"}}
  Dish Load (1.0ms)  SELECT  "dishes".* FROM "dishes" WHERE "dishes"."id" = $1 LIMIT $2  [["id", 9], ["LIMIT", 1]]

[25, 34] in C:/apps/railsApi/app/controllers/dishes_controller.rb
   25:   end
   26:
   27:   # PATCH/PUT /dishes/1
   28:   def update
   29:     byebug
=> 30:     if @dish.update(dish_params)
   31:       render json: @dish
   32:     else
   33:       render json: @dish.errors, status: :unprocessable_entity
   34:     end
(byebug) params
<ActionController::Parameters {"id"=>"9", "name"=>"Uthappizza", "image"=>"images/uthappizza.png", "category"=>"mains", "label"=>"Hot", "price"=>"4.99", "featured"=>true, "description"=>"A unique combination of Indian Uthappam (pancake) and Italian pizza, topped with Cerignola olives, ripe vine cherry tomatoes, Vidalia onion, Guntur chillies and Buffalo Paneer.", "created_at"=>"2017-11-01T04:30:09.407Z", "comments"=>[{"id"=>46, "rating"=>5, "comment"=>"Imagine all the eatables, living in conFusion!", "author"=>"John Lemon", "date"=>"2012-10-16T17:57:28.556Z"}, {"id"=>47, "rating"=>4, "comment"=>"Sends anyone to heaven, I wish I could get my mother-in-law to eat it!", "author"=>"Paul McVites", "date"=>"2014-09-05T17:57:28.556Z"}, {"id"=>48, "rating"=>3, "comment"=>"Eat it, just eat it!", "author"=>"Michael Jaikishan", "date"=>"2015-02-13T17:57:28.556Z"}, {"id"=>49, "rating"=>4, "comment"=>"Ultimate, Reaching for the stars!", "author"=>"Ringo Starry", "date"=>"2013-12-02T17:57:28.556Z"}, {"id"=>50, "rating"=>2, "comment"=>"It's your birthday, we're gonna party!", "author"=>"25 Cent", "date"=>"2011-12-02T17:57:28.556Z"}, {"id"=>51, "rating"=>4, "comment"=>"great dish", "author"=>"Jogesh", "date"=>"2017-10-30T05:03:39.656Z"}, {"author"=>"JANE7777", "rating"=>3, "comment"=>"COMMENT7777", "date"=>"2017-11-12T12:58:12.555Z"}], "controller"=>"dishes", "action"=>"update", "dish"=>{"id"=>9, "name"=>"Uthappizza", "image"=>"images/uthappizza.png", "category"=>"mains", "label"=>"Hot", "price"=>"4.99", "featured"=>true, "description"=>"A unique combination of Indian Uthappam (pancake) and Italian pizza, topped with Cerignola olives, ripe vine cherry tomatoes, Vidalia onion, Guntur chillies and Buffalo Paneer.", "created_at"=>"2017-11-01T04:30:09.407Z"}} permitted: false>
(byebug) c
Unpermitted parameters: :id, :created_at
   (0.0ms)  BEGIN
   (0.0ms)  COMMIT
[active_model_serializers]   Comment Load (0.0ms)  SELECT "comments".* FROM "comments" WHERE "comments"."dish_id" = $1  [["dish_id", 9]]
[active_model_serializers] Rendered DishSerializer with ActiveModelSerializers::Adapter::Attributes (31.29ms)
Completed 200 OK in 1901725ms (Views: 37.5ms | ActiveRecord: 5.0ms)

客户端模型

Dish 模型有 Comment[] 作为成员之一。当通过表单添加新评论时,comment 被推送到 dish.comments 数组,然后将 Dish 对象发送到 Rails API 后端。

Comment 客户端模型

export class Comment {
    rating: number;
    comment: string;
    author: string;
    date: string;
}

Post 客户端模型

import { Comment } from './comment';
export class Dish {
  id: number;
  name: string;
  image: string;
  category: string;
  label: string;
  price: string;
  featured: boolean;
  description: string;
  comments: Comment[];
}

【问题讨论】:

  • 您是否检查了 CSRF 问题以及它显示的与未经许可的参数相关的错误? PUT 用于更新,POST 用于创建。您可以尝试使用嵌套资源,或者如果您想继续使用 push addaccepts_nested_attributes for in disc.rb for comment.rb。尝试编写一个小测试用例,而不是从 UI 重试或测试,以帮助理解或运行 byebug 控制台中的代码。
  • 我使用 rack-cors 并已配置 - 我的 GET 请求运行良好!!
  • 你为什么不做一个单独的动作 cmets#create?你的解决方案很老套。即使它会起作用,它也不是你真正应该寻找的东西。
  • create 中有 CommentsController 方法 - 我只是在这里添加的。
  • 但是您在添加新评论时没有提及它的任何用法。您是否尝试过接受 Accept_nested_attributes?

标签: ruby-on-rails angular


【解决方案1】:

我建议您使用 Comments 控制器并形成一个嵌套路由以实现 RESTful。

首先,POST 请求,包括到 /cmets 的 disc_id。您正在尝试创建评论,而不是更新 Dish。

其次,形成嵌套路由: /dishes/:dish_id/cmets

以下是嵌套资源的指南:http://guides.rubyonrails.org/routing.html#nested-resources

错误的原因是您破坏了预期的参数结构。对于 PUT,您的参数应该是:

{
   id: <record to update>,
   dish: {
      name: “blah”,
      comments: [{...},{...}]
   }
 }

再次,如果您只想添加评论,我真的建议您不要 PUT 到 Dish。例如:上面的内容需要替换 Dish 上的所有 cmets!如果您正在创建记录,请创建该记录,不要更新它的父记录。

如果您想在添加新评论时更新 Dish,您可以在创建时向评论模型添加回调,或在评论创建控制器操作中执行此操作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-13
    • 2018-12-23
    • 2013-02-23
    • 1970-01-01
    • 2018-11-30
    • 2011-12-02
    • 2014-07-28
    • 2013-07-04
    相关资源
    最近更新 更多