【问题标题】:HABTM relationship with rails API does not workHABTM 与 rails API 的关系不起作用
【发布时间】:2017-10-09 18:53:42
【问题描述】:

我是 Ruby on Rails 的新手。我正在使用 Rails 5.1、活动记录序列化器、门卫和设计 gem 制作 Rails API。

我有一张Order 桌子,里面有很多产品。订单和产品的关系是多对多的。

订购型号:

class Order < ApplicationRecord      
  validates_presence_of :brute, :net
  has_and_belongs_to_many :products      
end

产品型号:

class Product < ApplicationRecord
  belongs_to :category
  validates_presence_of :name, :price
  validates_uniqueness_of :name
  has_and_belongs_to_many :orders
end

我有一个名为 orders_products 的连接表。

订单序列化器:

class OrderSerializer < ActiveModel::Serializer
  attributes :id, :discount, :brute, :net, :payed, :payed_at, :products      

  def products
    object.products.map do |product|
      ProductSerializer.new(product, scope: scope, root: false, event: object)
    end
  end

end

产品序列化器:

 class ProductSerializer < ActiveModel::Serializer
  attributes :id, :name, :price, :description
  has_one :category
end

订单控制器:

module Api
  class OrdersController < ApiController
    before_action :set_order, only: [:show, :update, :destroy]

    # GET /api/orders
    def index
      @orders = Order.all

      render json: @orders
    end

    # GET /api/orders/1
    def show
      render json: @order
    end

    # POST /api/orders
    def create
      @order = Order.new(order_params)

      if @order.save
        render json: @order, status: :created, location: api_order_url(@order)
      else
        render json: @order.errors, status: :unprocessable_entity
      end
    end

    # PATCH/PUT /api/orders/1
    def update
      if @order.present?
        if @order.update(order_params)
          render json: @order
        else
          render json: @order.errors, status: :unprocessable_entity
        end
      end
    end

    # DELETE /api/orders/1
    def destroy
      @order.destroy if @order.present?
    end

    private
    # Use callbacks to share common setup or constraints between actions.
    def set_order
      @order = Order.find(params[:id])
    rescue ActiveRecord::RecordNotFound
      Rails.logger.error{ 'Order record is not found' }
      nil
    end

    # Only allow a trusted parameter "white list" through.
    def order_params
      params.require(:order).permit(:discount, :brute, :net, :payed, :payed_at, product_ids: [])
    end
  end
end

当我从 API 生成器应用(如 Postman/Insomnia)发布一些订单 json 数据时,订单保存在订单表中,但没有数据保存在 orders_products 连接表中。

我的订单json请求(POST http://localhost:3000/api/orders):

{
        "discount": 110,
        "brute": 100,
        "net": 200,
        "payed": null,
        "payed_at": null,          
        "product_ids": [3]
}

我试图找到解决方案,但我失败了。

【问题讨论】:

  • 我不使用序列化程序,所以我对您的一些代码不太熟悉。虽然您能指出您在代码中的哪个位置将产品添加到您的订单中吗?也许您也可以复制处理此问题的视图(erb)文件..
  • 我也面临同样的问题。但是,当您使用活动记录序列化程序 gem 时,没有 erb 文件。

标签: ruby-on-rails json ruby api has-and-belongs-to-many


【解决方案1】:

我终于解决了你的问题。只需在你的模型中添加一个属性。

订购型号:

class Order < ApplicationRecord
  attribute :product_ids
  validates_presence_of :brute, :net
  has_and_belongs_to_many :products      
end

订单序列化器:

class OrderSerializer < ActiveModel::Serializer
  attributes :id, :discount, :brute, :net, :payed, :payed_at
  has_many :products
end

并在您的订单 api 中创建方法:

# POST /api/orders
    def create
      @order = Order.new(order_params)

      if @order.save
        # Find products
        @products = Product.where(id: order_params[:product_ids])
        # Create join table records
        @products.each { |product| product.orders << @order }

        render json: @order, status: :created, location: api_order_url(@order)
      else
        render json: @order.errors, status: :unprocessable_entity
      end
    end

我已经在本地进行了测试,并且可以正常工作!快乐编程:)

【讨论】:

  • 感谢@Engr。 Tanbir Hasan,你应该得到一枚奖牌。
【解决方案2】:

据我所知,Rails 不会在给定 id 列表时自动处理创建连接记录。因此,当您调用@order = Order.new(order_params) 并期望它知道如何处理product_ids: [3] 时,它只是忽略它。

如果您使用以下内容修改 create 端点,您应该会看到正在创建的连接记录。

# POST /api/orders
def create
  @order = Order.new(order_params)

  if @order.save
    # Find products
    @products = Product.where(id: order_params[:product_ids])
    # Create join table records
    @products.each { |product| product.orders << order }

    render json: @order, status: :created, location: api_order_url(@order)
  else
    render json: @order.errors, status: :unprocessable_entity
  end
end

这只是一种不进行任何错误检查的可能解决方案。根据您的应用程序需要的安全性和健壮程度,您可能需要创建一个服务来包装它并在创建订单和关联记录之前验证是否找到了产品。

编辑:OrderSerializer

一旦您确认连接表记录已正确创建。检查您的序列化程序是否正常工作,它们有很好的文档。我相信你可以用这个替换OrderSerializer 中当前的products 方法:

class OrderSerializer < ActiveModel::Serializer
  attributes :id, :discount, :brute, :net, :payed, :payed_at, :products      

  def products
    object.products.map do |product|
      ProductSerializer.new(product).serializable_hash
    end
  end

end

【讨论】:

  • 当我从 json API 请求时,order_params[:product_ids] 返回 nil。为什么?
  • 听起来你有两个问题。第一个问题是没有创建连接表记录。我上面提出的解决方案应该可以解决这个问题,您是否验证了现在正在创建记录?如果它们是并且您的 json 输出仍然不正确,那么您的 Order 序列化程序是错误的。在上面添加信息。
  • 您给定的方法是正确的,我检查了@products = Product.where(id: 3),然后正在创建连接表。但是当我调试并发现 order_params[:product_ids] 总是返回 nil 并且 order_params 不包含任何 product_ids 时!我的 json 格式不正确吗?您的 serializable_hash 返回错误,它需要两个缺少的参数。
  • 更新了代码,删除了您传递给ProductSerializer 的多余参数。我已经在本地存储库上对此进行了测试,它按预期工作。如果您仍然遇到错误,您将需要发布更多详细信息,甚至可能链接 repo。
  • 发布您收到的完整错误。没有详细信息,没有人可以为您解决这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多