与rails API的HABTM关系不起作用

时间:2017-10-09 18:53:42

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

我是Ruby on Rails的新手。我正在使用Rails 5.1,活动记录序列化器,门卫和设计宝石制作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

当我从像Postman / Insomnia这样的API生成器应用程序发布一些订单json数据时,订单将保存在订单表中,但没有数据保存在orders_products联接表中。

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

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

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

2 个答案:

答案 0 :(得分:3)

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

订单型号:

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

我已经在当地测试过并且有效!快乐编程:)

答案 1 :(得分:1)

据我所知,在给定id列表时,Rails不会自动处理创建连接记录。因此,当您呼叫@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

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

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